diff --git a/NVDA-addon/.gitattributes b/.gitattributes similarity index 100% rename from NVDA-addon/.gitattributes rename to .gitattributes diff --git a/.github/workflows/automaticRelease.yaml b/.github/workflows/automaticRelease.yaml new file mode 100644 index 00000000..a9ace1de --- /dev/null +++ b/.github/workflows/automaticRelease.yaml @@ -0,0 +1,330 @@ +name: automatic-release +on: + push: + branches: + - main + # schedule: + # # * is a special character in YAML so you have to quote this string + # - cron: '0 0 * * 6' + +jobs: + l10n: + name: l10n + continue-on-error: true + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Push l10n updates + run: | + git config --global user.name github-actions + git config --global user.email github-actions@github.com + git remote add l10n https://github.com/nvdaaddons/MathCAT + git fetch l10n + git reset l10n/stable addon/doc addon/locale + git commit -m "Update translations" --allow-empty + git pull + git push + + zip-rules: + name: zip up the rules directory + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Build Rust Library + run: | + cargo build --target x86_64-unknown-linux-gnu # doesn't need a release build since all that we want is the Rules dir + - name: create rules.zip + uses: thedoctor0/zip-release@0.7.5 + with: + type: 'zip' + filename: 'Rules.zip' + directory: 'addon/globalPlugins/MathCAT' + path: 'Rules' + - name: Upload Rules.zip + uses: actions/upload-artifact@v4 + with: + name: 'Rules.zip' + path: 'addon/globalPlugins/MathCAT/Rules.zip' + compression-level: 0 + retention-days: 1 + + rust-32: + name: Build 32 bit windows pyd file + runs-on: windows-latest # needs to run on windows because of bzip2 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.11 + architecture: 'x86' + - name: Build Rust Library + run: | + cargo build --target i686-pc-windows-msvc --release + - name: Setup Example dir + run: | + cp target/i686-pc-windows-msvc/release/libmathcat_py.dll Example/libmathcat_py.pyd + mv addon/globalPlugins/MathCAT/Rules Example + - name: test the build + run: | + cd Example + python test.py + - name: create zip file for pyd file -- this allows the name to indicate arch, python version, etc., but unzipped it is correctly named + uses: thedoctor0/zip-release@0.7.5 + with: + type: 'zip' + filename: '../libmathcat_py-32-3.11-win.zip' + directory: 'Example' + path: 'libmathcat_py.pyd' + - name: Upload 32 bit pyd file + uses: actions/upload-artifact@v4 + with: + name: libmathcat_py-32-3.11-win.zip + path: libmathcat_py-32-3.11-win.zip + compression-level: 0 + retention-days: 1 + + rust-64: + name: Build 64 bit windows pyd file + runs-on: windows-latest # needs to run on windows because of bzip2 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + architecture: 'x64' + - name: Build Rust Library + run: | + cargo build --target x86_64-pc-windows-msvc --release + - name: Setup Example dir + run: | + cp target/x86_64-pc-windows-msvc/release/libmathcat_py.dll Example/libmathcat_py.pyd + mv addon/globalPlugins/MathCAT/Rules Example + - name: create zip file for pyd file -- this allows the name to indicate arch, python version, etc., but unzipped it is correctly named + uses: thedoctor0/zip-release@0.7.5 + with: + type: 'zip' + filename: '../libmathcat_py-64-3.13-win.zip' + directory: 'Example' + path: 'libmathcat_py.pyd' + - name: test the build + run: | + cd Example + python test.py + - name: Upload 64 bit pyd file + uses: actions/upload-artifact@v4 + with: + name: libmathcat_py-64-3.13-win.zip + path: libmathcat_py-64-3.13-win.zip + compression-level: 0 + retention-days: 1 + + linux-64: + name: Build linux pyd file (64-bit intel) + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + architecture: 'x64' + - name: Build Rust Library + run: | + cargo build --target x86_64-unknown-linux-gnu --release + - name: Setup Example dir + run: | + # don't know why it is "liblib...". For consistancy with other versions, it is renamed. + cp target/x86_64-unknown-linux-gnu/release/liblibmathcat_py.so Example/libmathcat_py.so + cp -r addon/globalPlugins/MathCAT/Rules Example + - name: test build + run: | + cd Example + python test.py + - name: create zip file for pyd file -- this allows the name to indicate arch, python version, etc., but unzipped it is correctly named + uses: thedoctor0/zip-release@0.7.5 + with: + type: 'zip' + filename: '../libmathcat_py-64-3.13-linux.zip' + directory: 'Example' + path: 'libmathcat_py.so' + - name: Upload 64 bit pyd file + uses: actions/upload-artifact@v4 + with: + name: libmathcat_py-64-3.13-linux.zip + path: libmathcat_py-64-3.13-linux.zip + compression-level: 0 + retention-days: 1 + + build-64-bit-addon: + name: build-64-bit-addon + continue-on-error: false + needs: [l10n, zip-rules, rust-64] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + # download the 64 build + - name: Download 64 bit build + uses: actions/download-artifact@v4 + with: + name: libmathcat_py-64-3.13-win.zip + # download the Rules dir (not part of checkout) + - name: Download 64 bit build + uses: actions/download-artifact@v4 + with: + name: Rules.zip + # put things where they belong + - name: Set up addons dir + run: | + unzip libmathcat_py-64-3.13-win.zip + mv libmathcat_py.pyd Rules.zip addon/globalPlugins/MathCAT + cd addon/globalPlugins/MathCAT + sed 's/^import wx\.xrc/# import wx.xrc/' --in-place MathCATgui.py # fix wx file + unzip Rules.zip + rm Rules.zip + # build the addon + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.13 # needed for scons + - name: Install scons dependencies + run: | + pip install scons markdown + sudo apt update + sudo apt install gettext + - name: Run scons to build .addon file + run: | + scons + - name: Rename addon for 64-bit + run: | + for f in *.nvda-addon; do + mv "$f" "${f%.nvda-addon}-64.nvda-addon" + done + - name: Upload the addon + uses: actions/upload-artifact@v4 + with: + name: addon-64 + path: "*.nvda-addon" + compression-level: 0 + retention-days: 1 + + build-32-bit-addon: + name: build-32-bit-addon + continue-on-error: false + needs: [l10n, zip-rules, rust-32] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + # download the 32 build + - name: Download 32 bit build + uses: actions/download-artifact@v4 + with: + name: libmathcat_py-32-3.11-win.zip + # download the Rules dir (not part of checkout) + - name: Download 32 bit build + uses: actions/download-artifact@v4 + with: + name: Rules.zip + # put things where they belong + - name: Set up addons dir + run: | + unzip libmathcat_py-32-3.11-win.zip + mv libmathcat_py.pyd Rules.zip addon/globalPlugins/MathCAT + cd addon/globalPlugins/MathCAT + sed 's/^import wx\.xrc/# import wx.xrc/' --in-place MathCATgui.py # fix wx file + unzip Rules.zip + rm Rules.zip + # build the addon + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 # needed for scons + - name: Install scons dependencies + run: | + pip install scons markdown + sudo apt update + sudo apt install gettext + - name: Update NVDA Version Constraints + run: | + # Adjust the version numbers for the old 32 bit NVDA version + sed -i 's/"addon_name": "MathCAT"/"addon_name": "MathCAT-for-NVDA2025"/' buildVars.py + sed -i 's/"addon_summary": _("MathCAT:/"addon_summary": _("MathCAT-for-NVDA2025:/' buildVars.py + sed -i 's/"addon_minimumNVDAVersion": "[^"]*"/"addon_minimumNVDAVersion": "2025.1"/' buildVars.py + sed -i 's/"addon_lastTestedNVDAVersion": "[^"]*"/"addon_lastTestedNVDAVersion": "2025.3"/' buildVars.py + cat buildVars.py + - name: Run scons to build .addon file + run: | + scons + - name: Rename addon for 32-bit + run: | + for f in *.nvda-addon; do + mv "$f" "${f%.nvda-addon}-32.nvda-addon" + done + - name: Upload the addon + uses: actions/upload-artifact@v4 + with: + name: addon-32 + path: "*.nvda-addon" + compression-level: 0 + retention-days: 1 + + pre-release: + name: Pre Release + continue-on-error: false + needs: [zip-rules, rust-32, rust-64, linux-64, build-32-bit-addon, build-64-bit-addon] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + # download the previous build artifacts and put them in their proper places + - name: Download Rules.zip + uses: actions/download-artifact@v4 + with: + name: Rules.zip + - name: Download 32 bit build + uses: actions/download-artifact@v4 + with: + name: libmathcat_py-32-3.11-win.zip + - name: Download 64 bit build + uses: actions/download-artifact@v4 + with: + name: libmathcat_py-64-3.13-win.zip + - name: Download 64 bit linux build + uses: actions/download-artifact@v4 + with: + name: libmathcat_py-64-3.13-linux.zip + - name: Download 64-bit addon + uses: actions/download-artifact@v4 + with: + name: addon-64 + - name: Download 32-bit addon + uses: actions/download-artifact@v4 + with: + name: addon-32 + - name: Consolidate addon files + run: | + mv addon-64/*.nvda-addon . 2>/dev/null || true + mv addon-32/*.nvda-addon . 2>/dev/null || true + # put the files into the release + - name: Automatic release + uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + automatic_release_tag: latest + prerelease: true + title: Development Build + files: | + *.nvda-addon + libmathcat_py-32-3.11-win.zip + libmathcat_py-64-3.13-win.zip + libmathcat_py-64-3.13-linux.zip + Rules.zip diff --git a/.github/workflows/check-for-extended-ascii-and-utf-bom.yaml b/.github/workflows/check-for-extended-ascii-and-utf-bom.yaml new file mode 100644 index 00000000..40d12575 --- /dev/null +++ b/.github/workflows/check-for-extended-ascii-and-utf-bom.yaml @@ -0,0 +1,8 @@ +name: Check that we dont have extended ascii or utf boms in our files + +on: + pull_request: + +jobs: + extendedAsciiAndBom: + uses: nvdaes/nvdaAddonWorkflows/.github/workflows/check-for-extended-ascii-and-utf-bom.yaml@main diff --git a/.github/workflows/checkTranslatorsComments.yaml b/.github/workflows/checkTranslatorsComments.yaml new file mode 100644 index 00000000..8cbe7aac --- /dev/null +++ b/.github/workflows/checkTranslatorsComments.yaml @@ -0,0 +1,60 @@ +name: Check that all translatable strings have translators comments + +on: + push: + # Run this workflow if push tag or in master branch + tags: ["*"] + branches: [ main , master ] + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + buildPotFileAndCheckTranslatorsComments: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + pip install scons markdown + sudo apt update + sudo apt install gettext + + - name: Generate the .pot file + run: scons pot + + - name: Download NVDA's checkPot.py + run: curl https://raw.githubusercontent.com/nvaccess/nvda/master/tests/checkPot.py -O + + - name: Run checkPot + id: runCheckPot + #run: | + # from . import checkPot + # checkPot.EXPECTED_MESSAGES_WITHOUT_COMMENTS = set() + # res = checkPot.checkPot('$(ls *.pot)') + # exit(res) + #shell: python + run: | + python -c "import checkPot;checkPot.EXPECTED_MESSAGES_WITHOUT_COMMENTS = set();exit(checkPot.checkPot('$(ls *.pot)'))" + echo "nb_errors=$?" >> "$GITHUB_OUTPUT" + + - name: Notify + run: | + if [[ ${{ steps.runCheckPot.outputs.nb_errors }} == 0 ]]; + then + echo "Translators comments: PASS" + exit 0; + else + echo "Translators comments: FAIL" + exit 1; + fi diff --git a/.github/workflows/manualRelease.yaml b/.github/workflows/manualRelease.yaml new file mode 100644 index 00000000..21952ea4 --- /dev/null +++ b/.github/workflows/manualRelease.yaml @@ -0,0 +1,84 @@ +name: Manual release + +on: + workflow_dispatch: + inputs: + version: + description: 'Add-on version' + required: true + default: '0.0.0' + prerelease: + description: 'Mark as prerelease on GitHub' + default: false + type: boolean + signAddOn: + description: 'Sign add-on with GPG' + default: true + type: boolean + +jobs: + buildAndUpload: + continue-on-error: true + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - id: checkoutCode + name: Checkout code + uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Build Rust Project + run: | + cargo build --release + - name: Install dependencies + run: | + pip install scons markdown + sudo apt update + sudo apt install gettext + - name: Add add-on version + run: | + import re + with open("buildVars.py", 'r+', encoding='utf-8') as f: + text = f.read() + version = "${{ github.event.inputs.version }}" + text = re.sub(r"\"addon_version\": .*?,", f"\"addon_version\": \"{version}\",", text) + f.seek(0) + f.write(text) + f.truncate() + shell: python + - name: Build add-on + run: scons + - name: Push changes + run: | + git config --global user.name github-actions + git config --global user.email github-actions@github.com + git commit -a -m "Update buildVars" + git push origin HEAD:main + - id: import_gpg + if: ${{ inputs.signAddOn }} + name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v5 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.PASSPHRASE }} + - if: ${{ inputs.signAddOn }} + name: Sign add-on + run: gpg --detach-sign *.nvda-addon + - name: Calculate sha256 + run: sha256sum *.nvda-addon >> sha256.txt + - name: Create tag + run: | + git tag ${{ inputs.version }} + git push origin ${{ inputs.version }} + - name: Release + uses: ncipollo/release-action@v1 + with: + tag: ${{ inputs.version }} + artifacts: "*.nvda-addon,*.sig,publicKey.asc,sha256.txt" + generateReleaseNotes: true + prerelease: ${{ inputs.prerelease }} diff --git a/.gitignore b/.gitignore index 9f98b832..acb5dcd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,15 @@ -target +addon/doc/*.css +addon/doc/en/ +addon/globalPlugins/MathCAT/libmathcat_py.pyd Cargo.lock -NVDA-addon/addon/globalPlugins/MathCAT/Rules/ -NVDA-addon/addon/globalPlugins/MathCAT/libmathcat.pyd -Example/*.pyd - +target/ +*_docHandler.py +*.html +manifest.ini +*.mo +*.pot +*.py[co] +*.nvda-addon +.sconsign.dblite +/[0-9]*.[0-9]*.[0-9]*.json +.venv/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..d5c872f7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,96 @@ + +# https://pre-commit.ci/ +# Configuration for Continuous Integration service +ci: + skip: [pyrightLocal] + autoupdate_schedule: monthly + autoupdate_commit_msg: "Pre-commit auto-update" + autofix_commit_msg: "Pre-commit auto-fix" + submodules: true + +default_language_version: + python: python3.11 + +repos: +- repo: https://github.com/pre-commit-ci/pre-commit-ci-config + rev: v1.6.1 + hooks: + - id: check-pre-commit-ci-config + +- repo: meta + hooks: + # ensures that exclude directives apply to any file in the repository. + - id: check-useless-excludes + # ensures that the configured hooks apply to at least one file in the repository. + - id: check-hooks-apply + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + # Prevents commits to certain branches + - id: no-commit-to-branch + args: ["--branch", "main"] + # Checks that large files have not been added. Default cut-off for "large" files is 500kb. + - id: check-added-large-files + # Checks python syntax + - id: check-ast + # Checks for filenames that will conflict on case insensitive filesystems (the majority of Windows filesystems, most of the time) + - id: check-case-conflict + # Checks for artifacts from resolving merge conflicts. + - id: check-merge-conflict + # Checks Python files for debug statements, such as python's breakpoint function, or those inserted by some IDEs. + - id: debug-statements + # Removes trailing whitespace. + - id: trailing-whitespace + types_or: [python, batch, markdown, toml, yaml, rust] + # Ensures all files end in 1 (and only 1) newline. + - id: end-of-file-fixer + types_or: [python, batch, markdown, toml, yaml, rust] + # Removes the UTF-8 BOM from files that have it. + # See https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/codingStandards.md#encoding + - id: fix-byte-order-marker + types_or: [python, batch, markdown, toml, yaml, rust] + # Validates TOML files. + - id: check-toml + # Validates YAML files. + - id: check-yaml + # Validates XML files. + # Ensures that links to lines in files under version control point to a particular commit. + - id: check-vcs-permalinks + # Avoids using reserved Windows filenames. + - id: check-illegal-windows-names + +- repo: https://github.com/asottile/add-trailing-comma + rev: v3.1.0 + hooks: + # Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables, + # if a trailing comma is added. + # This adds a trailing comma to args/iterable items in case it was missed. + - id: add-trailing-comma + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Matches Ruff version in pyproject. + rev: v0.8.1 + hooks: + - id: ruff + name: lint with ruff + args: [ --fix ] + - id: ruff-format + name: format with ruff + +- repo: https://github.com/RobertCraigie/pyright-python + rev: v1.1.394 + hooks: + - id: pyright + alias: pyrightLocal + name: Check types with pyright + +- repo: https://github.com/RobertCraigie/pyright-python + rev: v1.1.396 + hooks: + - id: pyright + alias: pyrightCI + name: Check types with pyright + # use nodejs version of pyright and install pyproject.toml for CI + additional_dependencies: [".", "pyright[nodejs]"] + stages: [manual] # Only run from CI manually diff --git a/.vs/MathCATForPython/v15/.suo b/.vs/MathCATForPython/v15/.suo new file mode 100644 index 00000000..fb297c10 Binary files /dev/null and b/.vs/MathCATForPython/v15/.suo differ diff --git a/.vscode/settings.json b/.vscode/settings.json index 7c80e60c..89f0f706 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,7 @@ "bil-Malti", "Bislama", "bizaad", + "Bokmål", "Bosanski", "Català", "Česky", @@ -34,8 +35,11 @@ "Diné bizaad", "Dorerin", "Dorerin Naoero", + "eigh", "English", "Español", + "espeak", + "ESPEAK", "Esperanto", "Ɛʋɛ", "Faka", @@ -90,6 +94,7 @@ "Magyar", "Majel", "Malagasy", + "mathcat", "mathml", "Melayu", "Moldovenească", @@ -141,6 +146,7 @@ "Srpskohrvatski / Српскохрватски", "SSML", "staticline", + "subexprs", "Suomi", "surrogatepass", "Svenska", @@ -228,11 +234,19 @@ "中文", "日本語" ], - "python.linting.pylintEnabled": true, + "git.ignoreLimitWarning": true, + "python.analysis.diagnosticMode": "workspace", + "python.languageServer": "Pylance", + + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + "python.linting.enabled": true, - "python.linting.mypyEnabled": false, + "python.linting.ruffEnabled": true, + "python.linting.ruffPath": "ruff", + + "python.linting.pylintEnabled": false, "python.linting.flake8Enabled": false, - "python.analysis.diagnosticSeverityOverrides": { - "reportMissingImports": "none" - }, -} \ No newline at end of file + "python.linting.mypyEnabled": false, + + "cmake.configureOnOpen": false, +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..db0d7d8f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,922 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "MathCatForPython" +version = "0.7.6-beta.1" +dependencies = [ + "mathcat", + "pyo3", + "zip", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bzip2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "jiff" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "mathcat" +version = "0.7.6-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6da2158ce7343f2f728b41780a1a52b6a2a701860259755638821e99f6fab9" +dependencies = [ + "bitflags", + "cfg-if", + "dirs", + "env_logger", + "error-chain", + "fastrand", + "lazy_static", + "log", + "phf", + "radix_fmt", + "regex", + "roman-numerals-rs", + "strum", + "strum_macros", + "sxd-document", + "sxd-xpath", + "unicode-script", + "yaml-rust", + "zip", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "peresil" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f658886ed52e196e850cfbbfddab9eaa7f6d90dd0929e264c31e5cec07e09e57" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf" +dependencies = [ + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb" +dependencies = [ + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_fmt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "roman-numerals-rs" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85cd47a33a4510b1424fe796498e174c6a9cf94e606460ef022a19f3e4ff85e" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sxd-document" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d82f37be9faf1b10a82c4bd492b74f698e40082f0f40de38ab275f31d42078" +dependencies = [ + "peresil", + "typed-arena", +] + +[[package]] +name = "sxd-xpath" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea" +dependencies = [ + "peresil", + "quick-error", + "sxd-document", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typed-arena" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zip" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" +dependencies = [ + "arbitrary", + "bzip2", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index dcef63db..1a2d204b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,11 @@ [package] name = "MathCatForPython" -version = "0.2.6" +version = "0.7.6-beta.9" authors = ["Neil Soiffer "] -edition = "2018" +edition = "2024" +resolver = "2" # allows different build dependency features + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -19,19 +21,19 @@ name = "libmathcat_py" crate-type = ["cdylib"] [dependencies.mathcat] -# version = "0.2.6" -# for testing MathCAT without having to publish a new version (change two occurences) -path = "../MathCAT/" +version = "=0.7.6-beta.9" +# for testing MathCAT without having to publish a new version (change two occurrences) +# path = "../MathCAT/" [dependencies.pyo3] -version = "0.15.1" +version = "0.27" features = ["extension-module", "abi3"] [build-dependencies] -zip = { version = "0.6.2", default-features = false, features = ["deflate"] } -# mathcat = "0.2.6" -# for testing MathCAT without having to publish a new version (change two occurences) -mathcat = {path = "../MathCAT/"} +zip = { version = "8.5", default-features = false, features = ["bzip2"] } +mathcat = {version = "=0.7.6-beta.9", features = ["include-zip"]} # for building, we want the zip files so we can include them separately +# mathcat = { path = "../MathCAT/", features = ["include-zip"]} # for building, we want the zip files so we can include them separately + [profile.release] diff --git a/Example/test.py b/Example/test.py index ec5f562a..4e9e6609 100644 --- a/Example/test.py +++ b/Example/test.py @@ -7,69 +7,112 @@ import os +import sys +import libmathcat_py as libmathcat # import shutil # if os.path.exists("libmathcat_py.pyd"): # os.remove("libmathcat_py.pyd") # shutil.copy("..\\target\\i686-pc-windows-msvc\\release\\libmathcat_py.dll", "libmathcat.pyd") -import libmathcat - -def SetMathCATPreferences(): - try: - libmathcat.SetRulesDir( - # this assumes the Rules dir is in the same dir a the library. Modify as needed - os.path.join( os.path.dirname(os.path.abspath(__file__)), "Rules") - ) - except Exception as e: - print("problem with finding the MathCAT rules") - - try: - libmathcat.SetPreference("TTS", "none") - libmathcat.SetPreference("Language", "en") # Also "id" and "vi" - libmathcat.SetPreference("SpeechStyle", "SimpleSpeak") # Also "ClearSpeak" - libmathcat.SetPreference("Verbosity", "Verbose") # also terse "Terse"/"Medium" - libmathcat.SetPreference("CapitalLetters_UseWord", "true") # if "true", X => "cap x" - except Exception as e: - print("problem with setting a preference") - -def SetMathMLForMathCAT(mathml: str): - try: - libmathcat.SetMathML(mathml) - except Exception as e: - print("problem with SetMathML") - -def GetSpeech(): - try: - return libmathcat.GetSpokenText() - except Exception as e: - return "problem with getting speech for MathML" - - - -SetMathCATPreferences() # you only need to this once -print("Using MathCAT version '{}'".format(libmathcat.GetVersion())) - -mathml = " 1 X " -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech: '{}'".format(mathml, GetSpeech())) - -mathml = "xy" -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech: '{}'".format(mathml, GetSpeech())) - -mathml = " x 3 " -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech: '{}'".format(mathml, GetSpeech())) - -mathml = " x T " -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech: '{}'".format(mathml, GetSpeech())) - -mathml = "!" -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech: '{}'".format(mathml, GetSpeech())) - -mathml = "(73)" -SetMathMLForMathCAT(mathml) -print("MathML: {}\nSpeech (no inference): '{}'".format(mathml, GetSpeech())) + +def setMathCATPreferences(): + try: + libmathcat.SetRulesDir( + # this assumes the Rules dir is in the same dir a the library. Modify as needed + os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules"), + ) + except Exception as e: + sys.exit(f"problem with finding the MathCAT rules: {e}") + + try: + libmathcat.SetPreference("TTS", "none") + libmathcat.SetPreference("Language", "en") # Also "id" and "vi" + libmathcat.SetPreference("SpeechStyle", "SimpleSpeak") # Also "ClearSpeak" + libmathcat.SetPreference("Verbosity", "Verbose") # also terse "Terse"/"Medium" + libmathcat.SetPreference("CapitalLetters_UseWord", "true") # if "true", X => "cap x" + libmathcat.SetPreference("BrailleCode", "Nemeth") + except Exception as e: + sys.exit(f"problem with setting a preference: {e}") + + +def setMathMLForMathCAT(mathml: str): + try: + libmathcat.SetMathML(mathml) + except Exception as e: + sys.exit(f"problem with setMathML: {e}") + + +def getSpeech(): + try: + return libmathcat.GetSpokenText() + except Exception as e: + sys.exit(f"problem with getting speech for MathML: {e}") + + +def getBraille(): + try: + return libmathcat.GetBraille("") + except Exception as e: + sys.exit(f"problem with getting braille for MathML: {e}") + + +def test(): + setMathCATPreferences() # you only need to this once + print("Using MathCAT version '{}'".format(libmathcat.GetVersion())) + + mathml = " 1 X " + setMathMLForMathCAT(mathml) + + languages = libmathcat.GetSupportedLanguages() + if "en" not in languages: + sys.exit(f"Supported languages does not include 'en': {languages}") + speech_styles = libmathcat.GetSupportedSpeechStyles("en") + if "SimpleSpeak" not in speech_styles or "ClearSpeak" not in speech_styles: + sys.exit(f"Supported speech styles does not include 'SimpleSpeak' and/or 'ClearSpeak': {speech_styles}") + braille_codes = libmathcat.GetSupportedBrailleCodes() + if "UEB" not in braille_codes or "Nemeth" not in braille_codes: + sys.exit(f"Supported languages does not include 'UEB' and/or 'Nemeth': {braille_codes}") + speech = getSpeech() + if speech != "1 over cap x": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + braille = getBraille() + if braille != "⠹⠂⠌⠠⠭⠼": + sys.exit(f"MathML: {mathml}\nBraille: '{braille}'") + + mathml = "xy" + setMathMLForMathCAT(mathml) + speech = getSpeech() + if speech != "x cross product y": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + + mathml = " x 3 " + setMathMLForMathCAT(mathml) + speech = getSpeech() + if speech != "x cubed": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + + mathml = " x T " + setMathMLForMathCAT(mathml) + speech = getSpeech() + if speech != "x transpose": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + + mathml = "!" + setMathMLForMathCAT(mathml) + speech = getSpeech() + if speech != "x factorial": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + + mathml = "\ + (73)" + setMathMLForMathCAT(mathml) + speech = getSpeech() + if speech != "7 choose 3": + sys.exit(f"MathML: {mathml}\nSpeech: '{speech}'") + + print("Test was successful!") + + +test() +sys.exit(0) diff --git a/NVDA-addon/.gitignore b/NVDA-addon/.gitignore deleted file mode 100644 index 0be8af1c..00000000 --- a/NVDA-addon/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -addon/doc/*.css -addon/doc/en/ -*_docHandler.py -*.html -manifest.ini -*.mo -*.pot -*.py[co] -*.nvda-addon -.sconsign.dblite -/[0-9]*.[0-9]*.[0-9]*.json diff --git a/NVDA-addon/.pre-commit-config.yaml b/NVDA-addon/.pre-commit-config.yaml new file mode 100644 index 00000000..dd7a9d69 --- /dev/null +++ b/NVDA-addon/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-ast + - id: check-case-conflict + - id: check-yaml diff --git a/NVDA-addon/0.2.1.json b/NVDA-addon/0.2.1.json new file mode 100644 index 00000000..9c120f94 --- /dev/null +++ b/NVDA-addon/0.2.1.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "\n\t\tMathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to languages other than English are in progress.\n\t\t", + "sha256": "5ce5852776e1ee2e94afb01898e88f9c1c403838601f282cf152c57413f34aee", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.2.5", + "addonVersionNumber": { + "major": 0, + "minor": 2, + "patch": 5 + }, + "minNVDAVersion": { + "major": 2018, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} diff --git a/NVDA-addon/0.2.2.json b/NVDA-addon/0.2.2.json new file mode 100644 index 00000000..905aeea6 --- /dev/null +++ b/NVDA-addon/0.2.2.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "\n\t\tMathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to languages other than English are in progress.\n\t\t", + "sha256": "017a2817b54de419eef2dcac125e67210941b87e65556ba83670fea69f59bb2c", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.2.5", + "addonVersionNumber": { + "major": 0, + "minor": 2, + "patch": 5 + }, + "minNVDAVersion": { + "major": 2018, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} diff --git a/NVDA-addon/0.2.5.json b/NVDA-addon/0.2.5.json new file mode 100644 index 00000000..5ab2de4c --- /dev/null +++ b/NVDA-addon/0.2.5.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "\n\t\tMathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to languages other than English are in progress.\n\t\t", + "sha256": "04b7c28990b83d0a1880bc4f93a2ead1c1d3bd9dd91da4e626e0e1b1d566c11c", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.2.5", + "addonVersionNumber": { + "major": 0, + "minor": 2, + "patch": 5 + }, + "minNVDAVersion": { + "major": 2018, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/0.2.6.json b/NVDA-addon/0.2.6.json new file mode 100644 index 00000000..c3af3e7a --- /dev/null +++ b/NVDA-addon/0.2.6.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to languages other than English are in progress.", + "sha256": "b277935590bec4b6b78a99824c890f2a9114e69fdd6050c1ed1af828f1c2d8d4", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.2.6", + "addonVersionNumber": { + "major": 0, + "minor": 2, + "patch": 6 + }, + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/0.3.0.json b/NVDA-addon/0.3.0.json new file mode 100644 index 00000000..3d148eb0 --- /dev/null +++ b/NVDA-addon/0.3.0.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to Indonesian, Spanish, and Vietnamese exist and other translations are in progress.", + "sha256": "8be828b0b4ec48500e1dba517d467f63e7af121b6c0b805c2b31fae920e3fa32", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.3.0", + "addonVersionNumber": { + "major": 0, + "minor": 3, + "patch": 0 + }, + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/0.3.1.json b/NVDA-addon/0.3.1.json new file mode 100644 index 00000000..60bfaf00 --- /dev/null +++ b/NVDA-addon/0.3.1.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to Indonesian, Spanish, and Vietnamese exist and other translations are in progress.", + "sha256": "04747f219c34e9bd8716fa92d18f9e84ab71c09e0603960ecb84cb581f5428bd", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.3.1", + "addonVersionNumber": { + "major": 0, + "minor": 3, + "patch": 1 + }, + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/0.3.2.json b/NVDA-addon/0.3.2.json new file mode 100644 index 00000000..cfe1d622 --- /dev/null +++ b/NVDA-addon/0.3.2.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to Indonesian, Spanish, and Vietnamese exist and other translations are in progress.", + "sha256": "e4cc3984c1c29ea9cc0aee092ad1b1562fff3cf5a024b4e52c2470bcfca1b2cd", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.3.2", + "addonVersionNumber": { + "major": 0, + "minor": 3, + "patch": 2 + }, + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/0.3.3.json b/NVDA-addon/0.3.3.json new file mode 100644 index 00000000..868f3f19 --- /dev/null +++ b/NVDA-addon/0.3.3.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to Indonesian, Spanish, and Vietnamese exist and other translations are in progress.", + "sha256": "d48761ace19e2d6de3b2ef0e7e31c5d8a4ba275e364f9c32f72036e49b67ec08", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.3.3", + "addonVersionNumber": { + "major": 0, + "minor": 3, + "patch": 3 + }, + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/NVDA-addon/MathCAT-0.2.0.json b/NVDA-addon/MathCAT-0.2.0.json new file mode 100644 index 00000000..6247af25 --- /dev/null +++ b/NVDA-addon/MathCAT-0.2.0.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "\n\t\tMathCAT is a replacement for MathPlayer which has been discontinued.\n\t\tIt provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n\t\tThe speech quality is not quite as good as MathPlayer's speech yet,\n\t\tbut the braille support is much better and includes both Nemeth and UEB Technical.\n\t\tTranslations to languages other than English are in progress.\n\t\t", + "sha256": "86b80eebde3e30c2b9005a1b7e8c1adff4197eb172d93fde51196c813e6c7440", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "0.2.5", + "addonVersionNumber": { + "major": 0, + "minor": 2, + "patch": 5 + }, + "minNVDAVersion": { + "major": 2018, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2023, + "minor": 1, + "patch": 0 + }, + "channel": "stable", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} diff --git a/NVDA-addon/README.md.bak b/NVDA-addon/README.md.bak new file mode 100644 index 00000000..e09b173e --- /dev/null +++ b/NVDA-addon/README.md.bak @@ -0,0 +1,28 @@ +# MathCAT + +* Author: Neil Soiffer +* NVDA compatibility: 2018.1 or later (untested in earlier versions) +* Download [stable version][1] + +MathCAT is designed to eventually replace MathPlayer because MathPlayer is no longer supported. MathCAT generates speech and braille from MathML. The speech for math produced by MathCAT is enhanced with prosody so that it sounds more natural. The speech can be navigated in three modes using the same commands as MathPlayer. In addition, the navigation node is indicated on a braille display. Both Nemeth and UEB technical are supported. + +MathCAT adds a settings menu to NVDA's preferences menu. In the settings menu, numerous options in MathCAT can be set to control the speech, navigation, and braille. + +For full user documentation, please see the [MathCAT User Documentation](https://nsoiffer.github.io/MathCAT/users.html). For information on the MathCAT project in general, see the main [MathCAT Documentation page](https://nsoiffer.github.io/MathCAT). + + +Who should use MathCAT: + +* Those who need high quality Nemeth braille (MathPlayer's Nemeth is based on liblouis' Nemeth generation which has a number of significant bugs that are technically difficult to fix). +* Those who need UEB technical braille +* Those who want to try out the latest technology and are willing to help by reporting bugs +* Those who use Eloquence as a voice + +Who should NOT use MathCAT: + +* Anyone who uses MathPlayer with a non-English language (translations exist for Indonesian and Vietnamese; translations will be coming in the future) +* Anyone who uses MathPlayer with a non-Nemeth/non-UEB braille output (contact me if you want to help out with a braille translation) +* Anyone who uses MathPlayer to read Chemical Formulas (that will hopefully show up in the next non-bug release) +* Anyone who prefers Access8Math to MathPlayer (for speech or other features) + +MathCAT's rules for speech are not yet as extensive as MathPlayer's rules -- that may be another reason to stick with MathPlayer. MathCAT is being used as a testbed for ideas for MathML 4 that allow authors to express their intent so that ambiguous notations can be spoken correctly and not guessed at. I have held off on adding too many rules since the architecture of MathCAT is centered around using and inferring author intent and these are not fully settled yet. \ No newline at end of file diff --git a/NVDA-addon/_template_addon_release.json b/NVDA-addon/_template_addon_release.json new file mode 100644 index 00000000..c6d3a5fe --- /dev/null +++ b/NVDA-addon/_template_addon_release.json @@ -0,0 +1,29 @@ +{ + "addonId": "easyAddonTech.XYZ", + "addonVersionNumber": { + "major": 21, + "minor": 6, + "patch": 0 + }, + "addonVersionName": "21.06", + "displayName": "My addon", + "publisher": "easyAddonTech", + "description": "Makes doing XYZ easier", + "homepage": "https://github.com/nvaccess/addon-datastore", + "minNVDAVersion": { + "major": 2019, + "minor": 3, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2020, + "minor": 4, + "patch": 0 + }, + "channel": "beta", + "URL": "https://github.com/nvaccess/addon-datastore/releases/download/v0.1.0/myAddon.nvda-addon", + "sha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82", + "sourceURL": "https://github.com/nvaccess/addon-datastore/", + "license": "GPL v2", + "licenseURL": "https://github.com/nvaccess/addon-datastore/license.MD" +} diff --git a/NVDA-addon/addon/.github/workflows/build_addon.yml b/NVDA-addon/addon/.github/workflows/build_addon.yml new file mode 100644 index 00000000..9b97cd9f --- /dev/null +++ b/NVDA-addon/addon/.github/workflows/build_addon.yml @@ -0,0 +1,64 @@ +name: build addon + +on: + push: + tags: ["*"] + # To build on main/master branch, uncomment the following line: + # branches: [ main , master ] + + pull_request: + branches: [ main, master ] + + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - run: echo -e "pre-commit\nscons\nmarkdown">requirements.txt + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + cache: 'pip' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r requirements.txt + sudo apt-get update -y + sudo apt-get install -y gettext + + - name: Code checks + run: export SKIP=no-commit-to-branch; pre-commit run --all + + - name: building addon + run: scons + + - uses: actions/upload-artifact@v3 + with: + name: packaged_addon + path: ./*.nvda-addon + + upload_release: + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/') }} + needs: ["build"] + steps: + - uses: actions/checkout@v3 + - name: download releases files + uses: actions/download-artifact@v3 + - name: Display structure of downloaded files + run: ls -R + + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: packaged_addon/*.nvda-addon + fail_on_unmatched_files: true + prerelease: ${{ contains(github.ref, '-') }} diff --git a/NVDA-addon/addon/doc/en/readme.md b/NVDA-addon/addon/doc/en/readme.md new file mode 100644 index 00000000..0cd0000e --- /dev/null +++ b/NVDA-addon/addon/doc/en/readme.md @@ -0,0 +1,61 @@ +# MathCAT + +* Author: Neil Soiffer +* NVDA compatibility: 2018.1 or later (untested in earlier versions) +* Download [stable version][1] + +MathCAT is designed to eventually replace MathPlayer because MathPlayer is no longer supported. MathCAT generates speech and braille from MathML. The speech for math produced by MathCAT is enhanced with prosody so that it sounds more natural. The speech can be navigated in three modes using the same commands as MathPlayer. In addition, the navigation node is indicated on a braille display. Both Nemeth and UEB technical are supported. + +MathCAT has a number of configuration options that control speech, navigation, and braille. +Many of these can be set in the MathCAT settings dialog (found NVDA Preferences menu). +For more information on these settings, see the [MathCAT documentation](https://nsoiffer.github.io/MathCAT/users.html). +The documentation includes a link to [a table listing all of the navigation commands in MathCAT](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Note: MathCAT is a general library for generating speech and braille from MathML. It is used by other AT projects besides NVDA. For information on the MathCAT project in general, see the main [MathCAT Documentation page](https://nsoiffer.github.io/MathCAT). + + +Who should use MathCAT: + +* Those who need high quality Nemeth braille (MathPlayer's Nemeth is based on liblouis' Nemeth generation which has a number of significant bugs that are technically difficult to fix). +* Those who need UEB technical braille +* Those who want to try out the latest technology and are willing to help by reporting bugs +* Those who use Eloquence as a voice + +Who should NOT use MathCAT: + +* Anyone who uses MathPlayer with a non-English language (translations exist for Indonesian and Vietnamese; translations will be coming in the future) +* Anyone who uses MathPlayer with a non-Nemeth/non-UEB braille output (contact me if you want to help out with a braille translation) +* Anyone who prefers Access8Math to MathPlayer (for speech or other features) + +MathCAT's rules for speech are not yet as extensive as MathPlayer's rules -- that may be another reason to stick with MathPlayer. MathCAT is being used as a testbed for ideas for MathML 4 that allow authors to express their intent so that ambiguous notations can be spoken correctly and not guessed at. I have held off on adding too many rules since the architecture of MathCAT is centered around using and inferring author intent and these are not fully settled yet. + +## MathCAT Update Log + +### Version 0.2 +* Lots of bug fixes +* Improvements to speech +* A preference setting to control the duration of pausing (works with changes to relative speech rate for math) +* Support to recognize chemistry notation and speak it appropriately +* Translations to Indonesian and Vietnamese + + +### Version 0.2.5 +* More improvements chemistry +* Fixes for Nemeth: +* * Added "omission" rules +* * Added some rules for English Language Indicators +* * Added more cases where the Mulitpurpose indicator is needed +* * Fixes related to Nemeth and punctuation + + +### Version 0.3.3 +This release has a number of bug fixes in it. The major new features and bug fixes are: +* Added Spanish Translation (thanks to Noelia Ruiz and María Allo Roldán) +* Modified navigation so that it starts zoomed in one level +* Added cntrl+alt+arrow as a way to navigate tabular structures. These keys should be more memorable because they are used for table navigation in NVDA. +* Worked around NVDA bug for eSpeak voices that caused them to slow down when the relative MathRate was set to be slower than the text speech rate. +* Worked around a OneCore voice problem so that they will speak the long 'a' sound. + +There are lots of small tweaks to the speech and some bug fixes for both Nemeth and UEB. + +Note: there is now an option to get Vietnam's braille standard as braille output. This is still a work in progress and is too buggy to be used other than for testing. I expect the next MathCAT release will contain a reliable implementation. diff --git a/NVDA-addon/addon/doc/id/readme.md b/NVDA-addon/addon/doc/id/readme.md new file mode 100644 index 00000000..1b14b811 --- /dev/null +++ b/NVDA-addon/addon/doc/id/readme.md @@ -0,0 +1,27 @@ +# MathCAT + +- Penulis: Neil Soiffer +- Kompatibilitas NVDA: 2018.1 atau lebih baru (belum diuji di versi sebelumnya) +- Unduh [versi stabil][1] + +MathCAT dirancang untuk menggantikan MathPlayer karena MathPlayer tidak lagi didukung. MathCAT menghasilkan ucapan dan braille dari MathML. Ucapan untuk matematika yang dihasilkan oleh MathCAT ditingkatkan dengan prosodi sehingga terdengar lebih alami. Ucapan dapat dinavigasi dalam tiga mode menggunakan perintah yang sama seperti MathPlayer. Selain itu, simpul navigasi ditunjukkan pada tampilan braille. Mendukung braille Nemeth dan UEB. + +MathCAT menambahkan menu pengaturan ke menu preferensi NVDA. Di menu pengaturan, banyak opsi di MathCAT dapat diatur untuk mengontrol ucapan, navigasi, dan braille. + +Untuk dokumentasi pengguna secara lengkap, silakan lihat [Dokumentasi Pengguna MathCAT](https://nsoiffer.github.io/MathCAT/users.html). Untuk informasi tentang proyek MathCAT secara umum, lihat [Dokumentasi MathCAT](https://nsoiffer.github.io/MathCAT). + +Siapa yang boleh menggunakan MathCAT: + +- Mereka yang membutuhkan Nemeth braille (Nemeth MathPlayer didasarkan pada generasi Nemeth liblouis yang memiliki sejumlah bug signifikan yang secara teknis sulit untuk diperbaiki). +- Mereka yang membutuhkan braille UEB +- Mereka yang ingin mencoba teknologi terbaru dan bersedia membantu dengan melaporkan bug +- Mereka yang menggunakan Eloquence sebagai suara + +Siapa yang TIDAK boleh menggunakan MathCAT: + +- Siapa pun yang menggunakan MathPlayer dengan bahasa non-Inggris (terjemahan akan tersedia di masa mendatang) +- Siapa pun yang menggunakan MathPlayer dengan keluaran braille non-Nemeth/non-UEB (hubungi saya jika Anda ingin membantu dengan terjemahan braille) +- Siapa pun yang menggunakan MathPlayer untuk membaca Rumus Kimia (yang diharapkan akan muncul di rilis non-bug berikutnya) +- Siapa saja yang lebih memilih Access8Math daripada MathPlayer (untuk ucapan atau fitur lainnya) + +Aturan MathCAT untuk pengucapan belum seluas aturan MathPlayer -- itu mungkin alasan lain untuk tetap menggunakan MathPlayer. MathCAT digunakan sebagai testbed untuk ide-ide untuk MathML 4 yang memungkinkan penulis untuk mengekspresikan maksud mereka sehingga notasi ambigu dapat diucapkan dengan benar dan tidak ditebak. Saya telah menunda menambahkan terlalu banyak aturan karena arsitektur MathCAT berpusat di sekitar penggunaan dan menyimpulkan maksud penulis dan ini belum sepenuhnya diselesaikan. diff --git a/NVDA-addon/addon/doc/style.css b/NVDA-addon/addon/doc/style.css new file mode 100644 index 00000000..90f99d14 --- /dev/null +++ b/NVDA-addon/addon/doc/style.css @@ -0,0 +1,26 @@ +@charset "utf-8"; +body { +font-family : Verdana, Arial, Helvetica, Sans-serif; +line-height: 1.2em; +} +h1, h2 {text-align: center} +dt { +font-weight : bold; +float : left; +width: 10%; +clear: left +} +dd { +margin : 0 0 0.4em 0; +float : left; +width: 90%; +display: block; +} +p { clear : both; +} +a { text-decoration : underline; +} +:active { +text-decoration : none; +} +a:focus, a:hover {outline: solid} diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/MathCAT.py b/NVDA-addon/addon/globalPlugins/MathCAT/MathCAT.py deleted file mode 100644 index 17504d0c..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/MathCAT.py +++ /dev/null @@ -1,371 +0,0 @@ -# MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML -# The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued. -# Author: Neil Soiffer -# Copyright: this file is copyright GPL2 -# The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license -# and also (obviously) requires external speech engines and braille drivers. -# The plugin also requires the use of a small python dll: python3.dll -# python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved" - - -# Note: this code is a lot of cut/paste from other code and very likely could be substantially improved/cleaned. -import braille # we generate braille -import globalVars -from keyboardHandler import KeyboardInputGesture # navigation key strokes -from logHandler import log # logging -import mathPres # math plugin stuff -from os import path # set rule dir path -import re # regexp patter match -import speech # speech commands -import config # look up caps setting -import ui # copy message -from scriptHandler import script # copy MathML via ctrl-c -from synthDriverHandler import getSynth # speech engine param setting -import winUser # clipboard manipulation -import gettext -_ = gettext.gettext -from ctypes import windll # register clipboard formats -from typing import Any, Optional - -from . import libmathcat - -# speech/SSML processing borrowed from NVDA's mathPres/mathPlayer.py -from speech.commands import ( - BeepCommand, - PitchCommand, - VolumeCommand, - RateCommand, - LangChangeCommand, - BreakCommand, - CharacterModeCommand, - PhonemeCommand, -) - -RE_MP_SPEECH = re.compile( - # Break. - r" ?" - # Pronunciation of characters. - r"|(?P[^<]+) ?" - # Specific pronunciation. - r"|(?P[^ <]+) ?" - # Prosody. - r"| ?" - r"|(?P) ?" - r"| ?" # hack for beeps - # Other tags, which we don't care about. - r"|<[^>]+> ?" - # Actual content. - r"|(?P[^<]+)") - -PROSODY_COMMANDS = { - "pitch": PitchCommand, - "volume": VolumeCommand, - "rate": RateCommand, -} - -def ConvertSSMLTextForNVDA(text:str, language:str=""): - # MathCAT's default rate is 180 wpm. - # Assume that 0% is 80 wpm and 100% is 450 wpm and scale accordingly. - # log.info("Speech str: '{}'".format(text)) - synth = getSynth() - wpm = synth._percentToParam(synth.rate, 80, 450) - breakMulti = 180.0 / wpm - synthConfig = config.conf["speech"][synth.name] - supported_commands = synth.supportedCommands - use_break = BreakCommand in supported_commands - use_pitch = PitchCommand in supported_commands - use_rate = RateCommand in supported_commands - use_volume = VolumeCommand in supported_commands - use_phoneme = PhonemeCommand in supported_commands - use_character = CharacterModeCommand in supported_commands - out = [] - if language: - out.append(LangChangeCommand(language)) - resetProsody = [] - for m in RE_MP_SPEECH.finditer(text): - if m.lastgroup == "break": - if use_break: - out.append(BreakCommand(time=int(int(m.group("break")) * breakMulti))) - elif m.lastgroup == "char": - ch = m.group("char") - if use_character: - out.extend((CharacterModeCommand(True), ch, CharacterModeCommand(False))) - else: - out.extend((" ", ch, " ")) - elif m.lastgroup == "beep": - out.append(BeepCommand(2000, 50)) - elif m.lastgroup == "pitch": - if use_pitch: - out.append(PitchCommand(multiplier=int(m.group(m.lastgroup)))) - resetProsody.append(PitchCommand) - elif m.lastgroup in PROSODY_COMMANDS: - command = PROSODY_COMMANDS[m.lastgroup] - if command in supported_commands: - out.append(command(multiplier=int(m.group(m.lastgroup)) / 100.0)) - resetProsody.append(command) - elif m.lastgroup == "prosodyReset": - # for command in resetProsody: # only supported commands were added, so no need to check - command = resetProsody.pop() - out.append(command(multiplier=1)) - elif m.lastgroup == "phonemeText": - if use_phoneme: - out.append(PhonemeCommand(m.group("ipa"), text=m.group("phonemeText"))) - else: - out.append(m.group("phonemeText")) - elif m.lastgroup == "content": - # MathCAT puts out spaces between words, the speak command seems to want to glom the strings together at times, - # so we need to add individual " "s to the output - out.extend((" ", m.group(0), " ")) - if language: - out.append(LangChangeCommand(None)) - # log.info("Speech commands: '{}'".format(out)) - return out - -class MathCATInteraction(mathPres.MathInteractionNVDAObject): - # Put MathML on the clipboard using the two formats below (defined by MathML spec) - # We use both formats because some apps may only use one or the other - # Note: filed https://github.com/nvaccess/nvda/issues/13240 to make this usable outside of MathCAT - CF_MathML = windll.user32.RegisterClipboardFormatW("MathML") - CF_MathML_Presentation = windll.user32.RegisterClipboardFormatW("MathML Presentation") - # log.info("2**** MathCAT registering data formats: CF_MathML %x, CF_MathML_Presentation %x" % (CF_MathML, CF_MathML_Presentation)) - - def __init__(self, provider=None, mathMl: Optional[str]=None): - super(MathCATInteraction, self).__init__(provider=provider, mathMl=mathMl) - provider._setSpeechLanguage(mathMl) - self.init_mathml = mathMl - try: - libmathcat.SetMathML(mathMl) - except Exception as e: - speech.speakMessage(_("Illegal MathML found: see NVDA error log for details")) - log.error(e) - - def reportFocus(self): - super(MathCATInteraction, self).reportFocus() - try: - speech.speak(ConvertSSMLTextForNVDA(libmathcat.GetSpokenText(), - self.provider._language)) - except Exception as e: - log.error(e) - speech.speakMessage(_("Error in speaking math: see NVDA error log for details")) - - - def getBrailleRegions(self, review: bool = False): - # log.info("***MathCAT start getBrailleRegions") - yield braille.NVDAObjectRegion(self, appendText=" ") - region = braille.Region() - region.focusToHardLeft = True - # libmathcat.SetBrailleWidth(braille.handler.displaySize) - try: - region.rawText = libmathcat.GetBraille("") - except Exception as e: - log.error(e) - speech.speakMessage(_("Error in brailling math: see NVDA error log for details")) - region.rawText = "" - - # log.info("***MathCAT end getBrailleRegions ***") - yield region - - def getScript(self, gesture: KeyboardInputGesture): - # Pass most keys to MathCAT. Pretty ugly. - if isinstance(gesture, KeyboardInputGesture) and "NVDA" not in gesture.modifierNames and ( - gesture.mainKeyName in { - "leftArrow", "rightArrow", "upArrow", "downArrow", - "home", "end", - "space", "backspace", "enter", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - } - # or len(gesture.mainKeyName) == 1 - ): - return self.script_navigate - return super().getScript(gesture) - - def script_navigate(self, gesture: KeyboardInputGesture): - # log.info("***MathCAT script_navigate") - try: - if gesture != None: - modNames = gesture.modifierNames - text = libmathcat.DoNavigateKeyPress(gesture.vkCode, - "shift" in modNames, "control" in modNames, "alt" in modNames, False) - speech.speak(ConvertSSMLTextForNVDA(text, self.provider._language)) - - # update the braille to reflect the nav position (might be excess code, but it works) - nav_node = libmathcat.GetNavigationMathMLId() - region = braille.Region() - region.rawText = libmathcat.GetBraille(nav_node[0]) - region.focusToHardLeft = True - region.update() - braille.handler.buffer.regions.append(region) - braille.handler.buffer.focus(region) - braille.handler.buffer.update() - braille.handler.update() - except Exception as e: - log.error(e) - speech.speakMessage(_("Error in navigating math: see NVDA error log for details")) - - - _startsWithMath = re.compile("\\s*?" # copy will fix up name spacing - elif self.init_mathml != '': - mathml = self.init_mathml - self._copyToClipAsMathML(mathml) - ui.message(_("copy")) - except Exception as e: - log.error(e) - speech.speakMessage(_("unable to copy math: see NVDA error log for details")) - - - # not a perfect match sequence, but should capture normal MathML - # not a perfect match sequence, but should capture normal MathML - _mathTagHasNameSpace = re.compile("") - _hasAddedId = re.compile(" id='[^'].+' data-id-added='true'") - _hasDataAttr = re.compile(" data-[^=]+='[^']*'") - def _wrapMathMLForClipBoard(self, text: str) -> str: - # cleanup the MathML a little - text = re.sub(self._hasAddedId, "", text) - mathml_with_ns = re.sub(self._hasDataAttr, "", text) - if not re.match(self._mathTagHasNameSpace, mathml_with_ns): - mathml_with_ns = mathml_with_ns.replace('math', "math xmlns='http://www.w3.org/1998/Math/MathML'", 1) - return mathml_with_ns - - def _copyToClipAsMathML(self, text: str, notify: Optional[bool] = False) -> bool: - """Copies the given text to the windows clipboard. - @returns: True if it succeeds, False otherwise. - @param text: the text which will be copied to the clipboard - @param notify: whether to emit a confirmation message - """ - # copied from api.py and modified to use CF_MathML_Presentation - if not isinstance(text, str) or len(text) == 0: - return False - from api import getClipData - import gui - - try: - with winUser.openClipboard(gui.mainFrame.Handle): - winUser.emptyClipboard() - text = self._wrapMathMLForClipBoard(text) - self._setClipboardData(self.CF_MathML, '' + text) - self._setClipboardData(self.CF_MathML_Presentation, '' + text) - self._setClipboardData(winUser.CF_UNICODETEXT, text) - got = getClipData() - except OSError: - if notify: - ui.reportTextCopiedToClipboard() # No argument reports a failure. - return False - if got == text: - if notify: - ui.reportTextCopiedToClipboard(text) - return True - if notify: - ui.reportTextCopiedToClipboard() # No argument reports a failure. - return False - - def _setClipboardData(self, format, data: str): - # Need to support MathML Presentation, so this copied from winUser.py and the first two lines are commented out - # For now only unicode is a supported format - # if format!=CF_UNICODETEXT: - # raise ValueError("Unsupported format") - from textUtils import WCHAR_ENCODING - from ctypes import c_wchar, WinError - import winKernel - text = data - bufLen = len(text.encode(WCHAR_ENCODING, errors="surrogatepass")) + 2 - # Allocate global memory - h=winKernel.HGLOBAL.alloc(winKernel.GMEM_MOVEABLE, bufLen) - # Acquire a lock to the global memory receiving a local memory address - with h.lock() as addr: - # Write the text into the allocated memory - buf=(c_wchar*bufLen).from_address(addr) - buf.value=text - # Set the clipboard data with the global memory - if not windll.user32.SetClipboardData(format,h): - raise WinError() - # NULL the global memory handle so that it is not freed at the end of scope as the clipboard now has it. - h.forget() - -class MathCAT(mathPres.MathPresentationProvider): - def __init__(self): - # super(MathCAT, self).__init__(*args, **kwargs) - - try: - # IMPORTANT -- SetRulesDir must be the first call to libmathcat - rules_dir = path.join( path.dirname(path.abspath(__file__)), "Rules") - log.info("MathCAT Rules dir: %s" % rules_dir) - libmathcat.SetRulesDir(rules_dir) - libmathcat.SetPreference("TTS", "SSML") - - except Exception as e: - log.error(e) - speech.speakMessage(_("MathCAT initialization failed: see NVDA error log for details")) - - - def getSpeechForMathMl(self, mathml: str): - self._setSpeechLanguage(mathml) - try: - libmathcat.SetMathML(mathml) - except Exception as e: - log.error(e) - speech.speakMessage(_("Illegal MathML found: see NVDA error log for details")) - libmathcat.SetMathML("") # set it to something - try: - synth = getSynth() - synthConfig = config.conf["speech"][synth.name] - supported_commands = synth.supportedCommands - # Set preferences for capital letters - libmathcat.SetPreference("CapitalLetters_Beep", "true" if synthConfig["beepForCapitals"] else "false") - libmathcat.SetPreference("CapitalLetters_UseWord", "true" if synthConfig["sayCapForCapitals"] else "false") - if PitchCommand in supported_commands: - libmathcat.SetPreference("CapitalLetters_Pitch", str(synthConfig["capPitchChange"])) - if self._add_sounds(): - return [BeepCommand(800,25)] + ConvertSSMLTextForNVDA(libmathcat.GetSpokenText()) + [BeepCommand(600,15)] - else: - return ConvertSSMLTextForNVDA(libmathcat.GetSpokenText()) - - except Exception as e: - log.error(e) - speech.speakMessage(_("Error in speaking math: see NVDA error log for details")) - return [""] - - def _add_sounds(self): - try: - return libmathcat.GetPreference("SpeechSound") != "None" - except: - return False - - def getBrailleForMathMl(self, mathml: str): - # log.info("***MathCAT getBrailleForMathMl") - try: - libmathcat.SetMathML(mathml) - except Exception as e: - log.error(e) - speech.speakMessage(_("Illegal MathML found: see NVDA error log for details")) - libmathcat.SetMathML("") # set it to something - try: - return libmathcat.GetBraille("") - except Exception as e: - log.error(e) - speech.speakMessage(_("Error in brailling math: see NVDA error log for details")) - return "" - - - def interactWithMathMl(self, mathml: str): - MathCATInteraction(provider=self, mathMl=mathml).setFocus() - MathCATInteraction(provider=self, mathMl=mathml).script_navigate(None) - - def _setSpeechLanguage(self, mathml: str): - # NVDA inserts its notion of the current language into the math tag, so we can't use it - # see nvda\source\mathPres\mathPlayer.py for original version of this code - # lang = mathPres.getLanguageFromMath(mathml) - - # it might have changed, so can't just set it in init() - self._language = libmathcat.GetPreference("Language") diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/MathCATPreferences.py b/NVDA-addon/addon/globalPlugins/MathCAT/MathCATPreferences.py deleted file mode 100644 index e9cc4c47..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/MathCATPreferences.py +++ /dev/null @@ -1,549 +0,0 @@ -import math -import wx -from . import MathCATgui -from . import yaml -import os -import glob -import sys -import webbrowser -import gettext -_ = gettext.gettext - -from logHandler import log # logging -from typing import Any, Dict, Union - -# two constants to scale "PauseFactor" -# these work out so that a slider that goes [0,14] has value ~100 at 7 and ~1000 at 14 -PAUSE_FACTOR_SCALE = 9.5 -PAUSE_FACTOR_LOG_BASE = 1.4 - -# initialize the user preferences tuples -user_preferences: Dict[str, Dict[str, Union[int, str, bool]]] = {} -#Speech_Language is derived from the folder structure -Speech_Impairment = ("LearningDisability", "Blindness", "LowVision") -#Speech_SpeechStyle is derived from the yaml files under the selected language -Speech_Verbosity = ("Terse", "Medium", "Verbose") -Speech_SubjectArea = ("General") -Speech_Chemistry = ("SpellOut", "Off") -Navigation_NavMode = ("Enhanced", "Simple", "Character") -#Navigation_ResetNavMode is boolean -#Navigation_OverView is boolean -Navigation_NavVerbosity = ("Terse", "Medium", "Verbose") -#Navigation_AutoZoomOut is boolean -Braille_BrailleNavHighlight = ("Off", "FirstChar", "EndPoints", "All") -Braille_BrailleCode = ("Nemeth", "UEB") - -class UserInterface(MathCATgui.MathCATPreferencesDialog): - def __init__(self,parent): - #initialize parent class - MathCATgui.MathCATPreferencesDialog.__init__(self,parent) - - #load the logo into the dialog - full_path_to_logo = os.path.expanduser('~')+"\\AppData\\Roaming\\nvda\\addons\\mathCAT\\globalPlugins\\MathCAT\\logo.png" - if os.path.exists(full_path_to_logo): - self.m_bitmapLogo.SetBitmap(wx.Bitmap(full_path_to_logo)) - - # load in the system values followed by the user prefs (if any) - UserInterface.load_default_preferences() - UserInterface.load_user_preferences() - UserInterface.validate_user_preferences() - - if "MathCATPreferencesLastCategory" in user_preferences: - #set the categories selection to what we used on last run - self.m_listBoxPreferencesTopic.SetSelection(user_preferences["MathCATPreferencesLastCategory"]) - #show the appropriate dialogue page - self.m_simplebookPanelsCategories.SetSelection(self.m_listBoxPreferencesTopic.GetSelection()) - else: - #set the categories selection to the first item - self.m_listBoxPreferencesTopic.SetSelection(0) - user_preferences["MathCATPreferencesLastCategory"]="0" - #populate the languages - UserInterface.GetLanguages(self) - #set the ui items to match the preferences - UserInterface.set_ui_values(self) - - @staticmethod - def path_to_languages_folder(): - #the user preferences file is stored at: MathCAT\Rules\Languages - return os.path.expanduser('~')+"\\AppData\\Roaming\\nvda\\addons\\mathCAT\\globalPlugins\\MathCAT\\Rules\\Languages" - - @staticmethod - def LanguagesDict(): - languages = { - "aa": "Afar", - "ab": "Аҧсуа", - "af": "Afrikaans", - "ak": "Akana", - "an": "Aragonés", - "ar": "العربية", - "as": "অসমীয়া", - "av": "Авар", - "ay": "Aymar", - "az": "Azərbaycanca / آذربايجان", - "ba": "Башҡорт", - "be": "Беларуская", - "bg": "Български", - "bh": "भोजपुरी", - "bi": "Bislama", - "bm": "Bahamanian", - "bn": "বাংলা", - "bo": "བོད་ཡིག / Bod skad", - "bs": "Bosanski", - "ca": "Català", - "ce": "Нохчийн", - "ch": "Chamoru", - "co": "Corsu", - "cr": "Nehiyaw", - "cs": "Česky", - "cu": "словѣньскъ / slověnĭskŭ", - "cv": "Чăваш", - "cy": "Cymraeg", - "da": "Dansk", - "de": "Deutsch", - "dv": "ދިވެހިބަސް", - "dz": "རྫོང་ཁ", - "ee": "Ɛʋɛ", - "en": "English", - "eo": "Esperanto", - "es": "Español", - "fa": "فارسی", - "fi": "Suomi", - "fj": "Na Vosa Vakaviti", - "fo": "Føroyskt", - "fr": "Français", - "ur": "Furlan", - "fy": "Frysk", - "ga": "Gaeilge", - "gd": "Gàidhlig", - "gl": "Galego", - "gn": "Avañe'ẽ", - "gu": "ગુજરાતી", - "gv": "Gaelg", - "ha": "هَوُسَ", - "he": "עברית", - "hi": "हिन्दी", - "ho": "Hiri Motu", - "hr": "Hrvatski", - "ht": "Krèyol ayisyen", - "hu": "Magyar", - "hy": "Հայերեն", - "hz": "Otsiherero", - "ia": "Interlingua", - "id": "Bahasa Indonesia", - "ig": "Igbo", - "ii": "ꆇꉙ / 四川彝语", - "ik": "Iñupiak", - "io": "Ido", - "is": "Íslenska", - "iu": "ᐃᓄᒃᑎᑐᑦ", - "ja": "日本語", - "jv": "Basa Jawa", - "ka": "ქართული", - "kg": "KiKongo", - "ki": "Gĩkũyũ", - "kj": "Kuanyama", - "kk": "Қазақша", - "km": "ភាសាខ្មែរ", - "kn": "ಕನ್ನಡ", - "ko": "한국어", - "ks": "कॉशुर / کٲش", - "ku": "Kurdî", - "kv": "Коми", - "kw": "Kernewek", - "ky": "Kırgızca / Кыргызча", - "la": "Latina", - "lb": "Lëtzebuergesch", - "lg": "Luganda", - "li": "Limburgs", - "ln": "Lingála", - "lo": "ລາວ / Pha xa lao", - "lt": "Lietuvių", - "lv": "Latviešu", - "mg": "Malagasy", - "mh": "Kajin Majel / Ebon", - "mk": "Македонски", - "ml": "മലയാളം", - "mn": "Монгол", - "mo": "Moldovenească", - "ms": "Bahasa Melayu", - "mt": "bil-Malti", - "my": "Myanmasa", - "na": "Dorerin Naoero", - "ne": "नेपाली", - "ng": "Oshiwambo", - "nl": "Nederlands", - "nn": "Norsk (nynorsk)", - "nr": "isiNdebele", - "nv": "Diné bizaad", - "ny": "Chi-Chewa", - "oc": "Occitan", - "oj": "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin", - "om": "Oromoo", - "os": "Иронау", - "pa": "ਪੰਜਾਬੀ / پنجابی", - "pi": "Pāli / पाऴि", - "pl": "Polski", - "ps": "پښتو", - "pt": "Português", - "qu": "Runa Simi", - "rm": "Rumantsch", - "ro": "Română", - "ru": "Русский", - "rw": "Kinyarwandi", - "sa": "संस्कृतम्", - "sc": "Sardu", - "sd": "सिंधी / سنڌي", - "se": "Davvisámegiella", - "sg": "Sängö", - "sh": "Srpskohrvatski / Српскохрватски", - "si": "සිංහල", - "sk": "Slovenčina", - "sl": "Slovenščina", - "sm": "Gagana Samoa", - "sn": "chiShona", - "so": "Soomaaliga", - "sq": "Shqip", - "sr": "Српски", - "ss": "SiSwati", - "st": "Sesotho", - "su": "Basa Sunda", - "sv": "Svenska", - "sw": "Kiswahili", - "ta": "தமிழ்", - "tg": "Тоҷикӣ", - "th": "ไทย / Phasa Thai", - "ti": "ትግርኛ", - "tk": "Туркмен / تركمن", - "tl": "Tagalog", - "to": "Lea Faka-Tonga", - "tr": "Türkçe", - "ts": "Xitsonga", - "tt": "Tatarça", - "tw": "Twi", - "ty": "Reo Mā`ohi", - "ug": "Uyƣurqə / ئۇيغۇرچە", - "uk": "Українська", - "ur": "اردو", - "uz": "Ўзбек", - "ve": "Tshivenḓa", - "vi": "Tiếng Việt", - "vo": "Volapük", - "wa": "Walon", - "wo": "Wollof", - "xh": "isiXhosa", - "yi": "ייִדיש", - "yo": "Yorùbá", - "za": "Cuengh / Tôô / 壮语", - "zh": "中文", - "zu": "isiZulu" - } - return languages - - def GetLanguages(self): - # initialise the language list - languages_dict = UserInterface.LanguagesDict() - #clear the language names in the dialog - self.m_choiceLanguage.Clear() - #populate the available language names in the dialog - for f in os.listdir(UserInterface.path_to_languages_folder()): - if os.path.isdir(UserInterface.path_to_languages_folder()+"\\"+f): - if languages_dict.get(f, 'missing') == 'missing': - self.m_choiceLanguage.Append(f + " (" + f + ")") - else: - self.m_choiceLanguage.Append(languages_dict[f] + " (" + f + ")") - - def GetLanguageCode(self): - lang_selection = self.m_choiceLanguage.GetStringSelection() - lang_code = lang_selection[lang_selection.find("(")+1 : lang_selection.find(")")] - return lang_code - - def GetSpeechStyles(self, this_SpeechStyle: str): - #clear the SpeechStyle choices - self.m_choiceSpeechStyle.Clear() - #get the currently selected language code - this_language_code = UserInterface.GetLanguageCode(self) - - this_path = os.path.expanduser('~')+"\\AppData\\Roaming\\nvda\\addons\\MathCAT\\globalPlugins\\MathCAT\\Rules\\Languages\\"+this_language_code+"\\*_Rules.yaml" - #populate the m_choiceSpeechStyle choices - for f in glob.glob(this_path): - fname = os.path.basename(f) - self.m_choiceSpeechStyle.Append((fname[:fname.find("_Rules.yaml")])) - try: - #set the SpeechStyle to the same as previous - self.m_choiceSpeechStyle.SetStringSelection(this_SpeechStyle) - except: - #that didn't work, choose the first in the list - self.m_choiceSpeechStyle.SetSelection(0) - - def set_ui_values(self): - #set the UI elements to the ones read from the preference file(s) - try: - self.m_choiceImpairment.SetSelection(Speech_Impairment.index(user_preferences["Speech"]["Impairment"])) - try: - lang_pref = user_preferences["Speech"]["Language"] - i = 0 - while "(" + lang_pref + ")" not in self.m_choiceLanguage.GetString(i): - i = i + 1 - if i == self.m_choiceLanguage.GetCount(): - break - if "(" + lang_pref + ")" in self.m_choiceLanguage.GetString(i): - self.m_choiceLanguage.SetSelection(i) - else: - self.m_choiceLanguage.SetSelection(0) - except: - #the language in the settings file is not in the folder structure, something went wrong, set to the first in the list - self.m_choiceLanguage.SetSelection(0) - try: - #now get the available SpeechStyles from the folder structure and set to the preference setting is possible - self.GetSpeechStyles(user_preferences["Speech"]["SpeechStyle"]) - except: - self.m_choiceSpeechStyle.Append("Error when setting SpeechStyle for " + self.m_choiceLanguage.GetStringSelection()) - #set the rest of the UI elements - self.m_choiceSpeechAmount.SetSelection(Speech_Verbosity.index(user_preferences["Speech"]["Verbosity"])) - self.m_sliderRelativeSpeed.SetValue(user_preferences["Speech"]["MathRate"]) - pause_factor = 0 if user_preferences["Speech"]["PauseFactor"]<=1 else round(math.log(user_preferences["Speech"]["PauseFactor"]/PAUSE_FACTOR_SCALE, PAUSE_FACTOR_LOG_BASE)) - self.m_sliderPauseFactor.SetValue(pause_factor) - self.m_checkBoxSpeechSound.SetValue(user_preferences["Speech"]["SpeechSound"] == "Beep") - self.m_choiceSpeechForChemical.SetSelection(Speech_Chemistry.index(user_preferences["Speech"]["Chemistry"])) - self.m_choiceNavigationMode.SetSelection(Navigation_NavMode.index(user_preferences["Navigation"]["NavMode"])) - self.m_checkBoxResetNavigationMode.SetValue(user_preferences["Navigation"]["ResetNavMode"]) - self.m_choiceSpeechAmountNavigation.SetSelection(Navigation_NavVerbosity.index(user_preferences["Navigation"]["NavVerbosity"])) - if user_preferences["Navigation"]["Overview"]: - self.m_choiceNavigationSpeech.SetSelection(1) - else: - self.m_choiceNavigationSpeech.SetSelection(0) - self.m_checkBoxResetNavigationSpeech.SetValue(user_preferences["Navigation"]["ResetOverview"]) - self.m_checkBoxAutomaticZoom.SetValue(user_preferences["Navigation"]["AutoZoomOut"]) - self.m_choiceBrailleHighlights.SetSelection(Braille_BrailleNavHighlight.index(user_preferences["Braille"]["BrailleNavHighlight"])) - self.m_choiceBrailleMathCode.SetSelection(Braille_BrailleCode.index(user_preferences["Braille"]["BrailleCode"])) - except KeyError as err: - print('Key not found') - - def get_ui_values(self): - global user_preferences - # read the values from the UI and update the user preferences dictionary - user_preferences["Speech"]["Impairment"] = Speech_Impairment[self.m_choiceImpairment.GetSelection()] - user_preferences["Speech"]["Language"] = self.GetLanguageCode() - user_preferences["Speech"]["SpeechStyle"] = self.m_choiceSpeechStyle.GetStringSelection() - user_preferences["Speech"]["Verbosity"] = Speech_Verbosity[self.m_choiceSpeechAmount.GetSelection()] - user_preferences["Speech"]["MathRate"] = self.m_sliderRelativeSpeed.GetValue() - pf_slider = self.m_sliderPauseFactor.GetValue() - pause_factor = 0 if pf_slider==0 else round(PAUSE_FACTOR_SCALE *math.pow(PAUSE_FACTOR_LOG_BASE, pf_slider)) # avoid log(0) - user_preferences["Speech"]["PauseFactor"] = pause_factor - if self.m_checkBoxSpeechSound.GetValue(): - user_preferences["Speech"]["SpeechSound"] = "Beep" - else: - user_preferences["Speech"]["SpeechSound"] = "None" - user_preferences["Speech"]["Chemistry"] = Speech_Chemistry[self.m_choiceSpeechForChemical.GetSelection()] - user_preferences["Navigation"]["NavMode"] = Navigation_NavMode[self.m_choiceNavigationMode.GetSelection()] - user_preferences["Navigation"]["ResetNavMode"] = self.m_checkBoxResetNavigationMode.GetValue() - user_preferences["Navigation"]["NavVerbosity"] = Navigation_NavVerbosity[self.m_choiceSpeechAmountNavigation.GetSelection()] - user_preferences["Navigation"]["Overview"] = self.m_choiceNavigationSpeech.GetSelection() != 0 - user_preferences["Navigation"]["ResetOverview"] = self.m_checkBoxResetNavigationSpeech.GetValue() - user_preferences["Navigation"]["AutoZoomOut"] = self.m_checkBoxAutomaticZoom.GetValue() - user_preferences["Braille"]["BrailleNavHighlight"] = Braille_BrailleNavHighlight[self.m_choiceBrailleHighlights.GetSelection()] - user_preferences["Braille"]["BrailleCode"] = Braille_BrailleCode[self.m_choiceBrailleMathCode.GetSelection()] - user_preferences["MathCATPreferencesLastCategory"] = self.m_listBoxPreferencesTopic.GetSelection() - - @staticmethod - def path_to_default_preferences(): - #the default preferences file is: C:\Users\AppData\Roaming\\nvda\\addons\\MathCAT\\globalPlugins\\MathCAT\\Rules\\prefs.yaml - return os.path.expanduser('~')+"\\AppData\\Roaming\\nvda\\addons\\MathCAT\\globalPlugins\\MathCAT\\Rules\\prefs.yaml" - - @staticmethod - def path_to_user_preferences_folder(): - #the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml - return os.path.expanduser('~')+"\\AppData\\Roaming\\MathCAT" - - @staticmethod - def path_to_user_preferences(): - #the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml - return UserInterface.path_to_user_preferences_folder() + "\\prefs.yaml" - - @staticmethod - def load_default_preferences(): - global user_preferences - #load default preferences into the user preferences data structure (overwrites existing) - if os.path.exists(UserInterface.path_to_default_preferences()): - with open(UserInterface.path_to_default_preferences(), encoding='utf-8') as f: - user_preferences = yaml.load(f, Loader=yaml.FullLoader) - - @staticmethod - def load_user_preferences(): - global user_preferences - #merge user file values into the user preferences data structure - if os.path.exists(UserInterface.path_to_user_preferences()): - with open(UserInterface.path_to_user_preferences(), encoding='utf-8') as f: - # merge with the default preferences, overwriting with the user's values - user_preferences.update(yaml.load(f, Loader=yaml.FullLoader)) - - @staticmethod - def validate(key1: str, key2: str, valid_values: list, default_value: Union[str, int, bool]): - global user_preferences - try: - if valid_values == None: - #any value is valid - if user_preferences[key1][key2] != "": - return - if (type(valid_values[0]) == int) and (type(valid_values[1]) == int): - #any value between lower and upper bounds is valid - if (user_preferences[key1][key2] >= valid_values[0]) and (user_preferences[key1][key2] <= valid_values[1]): - return - else: - #any value in the list is valid - if user_preferences[key1][key2] in valid_values: - return - except: - #the preferences entry does not exist - pass - if not key1 in user_preferences: - user_preferences[key1] = {key2: default_value} - else: - user_preferences[key1][key2] = default_value - - @staticmethod - def validate_user_preferences(): - #check each user preference value to ensure it is present and valid, set default value if not - # Speech: - #Impairment: Blindness # LearningDisability, LowVision, Blindness - UserInterface.validate("Speech", "Impairment", ["LearningDisability", "LowVision", "Blindness"], "Blindness") - # Language: en # any known language code and sub-code -- could be en-uk, etc - UserInterface.validate("Speech", "Language", None, "en") - # Verbosity: Medium # Terse, Medium, Verbose - UserInterface.validate("Speech", "Verbosity", ["Terse", "Medium", "Verbose"], "Medium") - # MathRate: 100 # Change from text speech rate (%) - UserInterface.validate("Speech", "MathRate", [0,200], 100) - # PauseFactor: 100 # TBC - UserInterface.validate("Speech", "PauseFactor", [0,1000], 100) - # SpeechSound: None # make a sound when starting/ending math speech -- None, Beep - UserInterface.validate("Speech", "SpeechSound", ["None", "Beep"], "None") - # SpeechStyle: ClearSpeak # Any known speech style (falls back to ClearSpeak) - UserInterface.validate("Speech", "SpeechStyle", None, "ClearSpeak") - # SubjectArea: General # FIX: still working on this - UserInterface.validate("Speech", "SubjectArea", None, "General") - # Chemistry: SpellOut # SpellOut (H 2 0), AsCompound (Water), Off (H sub 2 O) - UserInterface.validate("Speech", "Chemistry", ["SpellOut", "Off"], "SpellOut") - #Navigation: - # NavMode: Enhanced # Enhanced, Simple, Character - UserInterface.validate("Navigation", "NavMode", ["Enhanced", "Simple", "Character"], "Enhanced") - # ResetNavMode: false # remember previous value and use it - UserInterface.validate("Navigation", "ResetNavMode", [False, True], False) - # Overview: false # speak the expression or give a description/overview - UserInterface.validate("Navigation", "Overview", [False, True] ,False) - # ResetOverview: true # remember previous value and use it - UserInterface.validate("Navigation", "ResetOverview", [False, True], True) - # NavVerbosity: Medium # Terse, Medium, Full (words to say for nav command) - UserInterface.validate("Navigation", "NavVerbosity", ["Terse", "Medium", "Full"], "Medium") - # AutoZoomOut: true # Auto zoom out of 2D exprs (use shift-arrow to force zoom out if unchecked) - UserInterface.validate("Navigation", "AutoZoomOut", [False, True], True) - #Braille: - # BrailleNavHighlight: EndPoints # Highlight with dots 7 & 8 the current nav node -- values are Off, FirstChar, EndPoints, All - UserInterface.validate("Braille", "BrailleNavHighlight", ["Off", "FirstChar", "EndPoints", "All"], "EndPoints") - # BrailleCode: "Nemeth" # Any supported braille code (currently Nemeth, UEB) - UserInterface.validate("Braille", "BrailleCode", ["Nemeth", "UEB"], "Nemeth") - - @staticmethod - def write_user_preferences(): - if not os.path.exists(UserInterface.path_to_user_preferences_folder()): - #create a folder for the user preferences - os.mkdir(UserInterface.path_to_user_preferences_folder()) - with open(UserInterface.path_to_user_preferences(), 'w', encoding="utf-8") as f: - #write values to the user preferences file, NOT the default - yaml.dump(user_preferences, stream=f, allow_unicode=True) - - def OnRelativeSpeedChanged( self, event ): - from .MathCAT import ConvertSSMLTextForNVDA - from speech import speak - rate = self.m_sliderRelativeSpeed.GetValue() - text = _(u"the square root of x squared plus y squared").replace("XXX", str(rate), 1) - speak( ConvertSSMLTextForNVDA(text) ) - - def OnPauseFactorChanged( self, event ): - from .MathCAT import ConvertSSMLTextForNVDA - from speech import speak - rate = self.m_sliderRelativeSpeed.GetValue() - pf_slider = self.m_sliderPauseFactor.GetValue() - pause_factor = 0 if pf_slider==0 else round(PAUSE_FACTOR_SCALE *math.pow(PAUSE_FACTOR_LOG_BASE, pf_slider)) - text = _(f"the fraction with numerator x to the n -th power plus 1 and denominator x to the n -th power minus 1 end fraction ") - speak( ConvertSSMLTextForNVDA(text) ) - - def OnClickOK(self,event): - UserInterface.get_ui_values(self) - UserInterface.write_user_preferences() - self.Destroy() - - def OnClickCancel(self,event): - self.Destroy() - - def OnClickApply(self,event): - UserInterface.get_ui_values(self) - UserInterface.write_user_preferences() - - def OnClickReset(self,event): - UserInterface.load_default_preferences() - UserInterface.validate_user_preferences() - UserInterface.set_ui_values(self) - - def OnClickHelp(self,event): - webbrowser.open('https://nsoiffer.github.io/MathCAT/users.html') - - def OnListBoxCategories(self,event): - #the category changed, now show the appropriate dialogue page - self.m_simplebookPanelsCategories.SetSelection(self.m_listBoxPreferencesTopic.GetSelection()) - - def OnLanguage(self,event): - #the language changed, get the SpeechStyles for the new language - UserInterface.GetSpeechStyles(self, self.m_choiceSpeechStyle.GetSelection()) - - def MathCATPreferencesDialogOnCharHook(self,event: wx.KeyEvent): - #designed choice is that Enter is the same as clicking OK, and Escape is the same as clicking Cancel - keyCode = event.GetKeyCode() - if keyCode == wx.WXK_ESCAPE: - UserInterface.OnClickCancel(self,event) - return - if keyCode == wx.WXK_RETURN: - UserInterface.OnClickOK(self,event) - if keyCode == wx.WXK_TAB: - if event.GetModifiers() == wx.MOD_CONTROL: - #cycle the category forward - new_category = self.m_listBoxPreferencesTopic.GetSelection() + 1 - if new_category == 3: - new_category = 0 - self.m_listBoxPreferencesTopic.SetSelection(new_category) - #update the ui to show the new page - UserInterface.OnListBoxCategories(self,event) - #set the focus into the category list box - self.m_listBoxPreferencesTopic.SetFocus() - #jump out so the tab key is not processed - return - if event.GetModifiers() == wx.MOD_CONTROL|wx.MOD_SHIFT: - #cycle the category back - new_category = self.m_listBoxPreferencesTopic.GetSelection() - 1 - if new_category == -1: - new_category = 2 - self.m_listBoxPreferencesTopic.SetSelection(new_category) - #update the ui to show the new page - UserInterface.OnListBoxCategories(self,event) - #update the ui to show the new page - self.m_listBoxPreferencesTopic.SetFocus() - #jump out so the tab key is not processed - return - if (event.GetModifiers() == wx.MOD_NONE) and (MathCATgui.MathCATPreferencesDialog.FindFocus() == self.m_listBoxPreferencesTopic): - if self.m_listBoxPreferencesTopic.GetSelection() == 0: - self.m_choiceImpairment.SetFocus() - elif self.m_listBoxPreferencesTopic.GetSelection() == 1: - self.m_choiceNavigationMode.SetFocus() - elif self.m_listBoxPreferencesTopic.GetSelection() == 2: - self.m_choiceBrailleMathCode.SetFocus() - return - if (event.GetModifiers() == wx.MOD_SHIFT) and (MathCATgui.MathCATPreferencesDialog.FindFocus() == self.m_buttonOK): - if self.m_listBoxPreferencesTopic.GetSelection() == 0: - self.m_choiceSpeechForChemical.SetFocus() - elif self.m_listBoxPreferencesTopic.GetSelection() == 1: - self.m_choiceSpeechAmountNavigation.SetFocus() - elif self.m_listBoxPreferencesTopic.GetSelection() == 2: - self.m_choiceBrailleHighlights.SetFocus() - return - #continue handling keyboard event - event.Skip() - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/MathCATgui.py b/NVDA-addon/addon/globalPlugins/MathCAT/MathCATgui.py deleted file mode 100644 index 077cffb9..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/MathCATgui.py +++ /dev/null @@ -1,390 +0,0 @@ -# -*- coding: utf-8 -*- - -########################################################################### -## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) -## http://www.wxformbuilder.org/ -## -## PLEASE DO *NOT* EDIT THIS FILE! -########################################################################### - -import wx -# import wx.xrc - -import gettext -_ = gettext.gettext - -########################################################################### -## Class MathCATPreferencesDialog -########################################################################### - -class MathCATPreferencesDialog ( wx.Dialog ): - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"MathCAT Preferences"), pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.DEFAULT_DIALOG_STYLE ) - - self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) - - gbSizerMathCATPreferences = wx.GridBagSizer( 0, 0 ) - gbSizerMathCATPreferences.SetFlexibleDirection( wx.BOTH ) - gbSizerMathCATPreferences.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) - - self.m_panelCategories = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - bSizerCategories = wx.BoxSizer( wx.VERTICAL ) - - self.m_staticTextCategories = wx.StaticText( self.m_panelCategories, wx.ID_ANY, _(u"Categories:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextCategories.Wrap( -1 ) - - bSizerCategories.Add( self.m_staticTextCategories, 0, wx.ALL, 5 ) - - m_listBoxPreferencesTopicChoices = [ _(u"Speech"), _(u"Navigation"), _(u"Braille") ] - self.m_listBoxPreferencesTopic = wx.ListBox( self.m_panelCategories, wx.ID_ANY, wx.Point( -1,-1 ), wx.Size( -1,-1 ), m_listBoxPreferencesTopicChoices, wx.LB_NO_SB|wx.LB_SINGLE ) - bSizerCategories.Add( self.m_listBoxPreferencesTopic, 0, wx.ALL, 5 ) - - - bSizerCategories.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - self.m_bitmapLogo = wx.StaticBitmap( self.m_panelCategories, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.Size( 126,85 ), 0 ) - bSizerCategories.Add( self.m_bitmapLogo, 0, wx.ALL, 5 ) - - - self.m_panelCategories.SetSizer( bSizerCategories ) - self.m_panelCategories.Layout() - bSizerCategories.Fit( self.m_panelCategories ) - gbSizerMathCATPreferences.Add( self.m_panelCategories, wx.GBPosition( 0, 0 ), wx.GBSpan( 1, 1 ), wx.EXPAND |wx.ALL, 5 ) - - self.m_simplebookPanelsCategories = wx.Simplebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_panelSpeech = wx.Panel( self.m_simplebookPanelsCategories, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.BORDER_SIMPLE|wx.TAB_TRAVERSAL ) - bSizerSpeech = wx.BoxSizer( wx.VERTICAL ) - - bSizerImpairment = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextImpairment = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Generate speech for:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextImpairment.Wrap( -1 ) - - bSizerImpairment.Add( self.m_staticTextImpairment, 0, wx.ALL, 5 ) - - m_choiceImpairmentChoices = [ _(u"Learning disabilities"), _(u"Blindness"), _(u"Low vision") ] - self.m_choiceImpairment = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceImpairmentChoices, 0 ) - self.m_choiceImpairment.SetSelection( 1 ) - bSizerImpairment.Add( self.m_choiceImpairment, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerImpairment, 1, wx.EXPAND, 5 ) - - bSizerLanguage = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextLanguage = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Language:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextLanguage.Wrap( -1 ) - - bSizerLanguage.Add( self.m_staticTextLanguage, 0, wx.ALL, 5 ) - - m_choiceLanguageChoices = [ _(u"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") ] - self.m_choiceLanguage = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceLanguageChoices, 0 ) - self.m_choiceLanguage.SetSelection( 0 ) - bSizerLanguage.Add( self.m_choiceLanguage, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerLanguage, 1, wx.EXPAND, 5 ) - - bSizerSpeechStyle = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextSpeechStyle = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Speech style:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextSpeechStyle.Wrap( -1 ) - - bSizerSpeechStyle.Add( self.m_staticTextSpeechStyle, 0, wx.ALL, 5 ) - - m_choiceSpeechStyleChoices = [ _(u"xxxxxxxxxxxxxxxx") ] - self.m_choiceSpeechStyle = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceSpeechStyleChoices, 0 ) - self.m_choiceSpeechStyle.SetSelection( 0 ) - bSizerSpeechStyle.Add( self.m_choiceSpeechStyle, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerSpeechStyle, 1, wx.EXPAND, 5 ) - - bSizer71 = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextSpeechAmount = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Speech amount:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextSpeechAmount.Wrap( -1 ) - - bSizer71.Add( self.m_staticTextSpeechAmount, 0, wx.ALL, 5 ) - - m_choiceSpeechAmountChoices = [ _(u"Terse"), _(u"Medium"), _(u"Verbose") ] - self.m_choiceSpeechAmount = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceSpeechAmountChoices, 0 ) - self.m_choiceSpeechAmount.SetSelection( 0 ) - bSizer71.Add( self.m_choiceSpeechAmount, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizer71, 1, wx.EXPAND, 5 ) - - bSizerRelativeSpeed = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextRelativeSpeed = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Relative speech rate:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextRelativeSpeed.Wrap( -1 ) - - bSizerRelativeSpeed.Add( self.m_staticTextRelativeSpeed, 0, wx.ALL, 5 ) - - self.m_sliderRelativeSpeed = wx.Slider( self.m_panelSpeech, wx.ID_ANY, 100, 20, 200, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) - bSizerRelativeSpeed.Add( self.m_sliderRelativeSpeed, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerRelativeSpeed, 1, wx.EXPAND, 5 ) - - bSizerPauseFactor = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticPauseFactor = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Pause factor:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticPauseFactor.Wrap( -1 ) - - bSizerPauseFactor.Add( self.m_staticPauseFactor, 0, wx.ALL, 5 ) - - self.m_sliderPauseFactor = wx.Slider( self.m_panelSpeech, wx.ID_ANY, 7, 0, 14, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) - bSizerPauseFactor.Add( self.m_sliderPauseFactor, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerPauseFactor, 1, wx.EXPAND, 5 ) - - bSizerSpeechSound = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_checkBoxSpeechSound = wx.CheckBox( self.m_panelSpeech, wx.ID_ANY, _(u"Make a sound when starting/ending math speech"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerSpeechSound.Add( self.m_checkBoxSpeechSound, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerSpeechSound, 1, wx.EXPAND, 5 ) - - bSizerSubjectArea = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextSubjectArea = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Subject area to be used when it cannot be determined automatically:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextSubjectArea.Wrap( -1 ) - - bSizerSubjectArea.Add( self.m_staticTextSubjectArea, 0, wx.ALL, 5 ) - - m_choiceSubjectAreaChoices = [ _(u"General") ] - self.m_choiceSubjectArea = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceSubjectAreaChoices, 0 ) - self.m_choiceSubjectArea.SetSelection( 0 ) - bSizerSubjectArea.Add( self.m_choiceSubjectArea, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerSubjectArea, 1, wx.EXPAND, 5 ) - - bSizerSpeechForChemical = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextSpeechForChemical = wx.StaticText( self.m_panelSpeech, wx.ID_ANY, _(u"Speech for chemical formulas:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextSpeechForChemical.Wrap( -1 ) - - bSizerSpeechForChemical.Add( self.m_staticTextSpeechForChemical, 0, wx.ALL, 5 ) - - m_choiceSpeechForChemicalChoices = [ _(u"Spell it out (H 2 O)"), _(u"Off (H sub 2 O)") ] - self.m_choiceSpeechForChemical = wx.Choice( self.m_panelSpeech, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceSpeechForChemicalChoices, 0 ) - self.m_choiceSpeechForChemical.SetSelection( 0 ) - bSizerSpeechForChemical.Add( self.m_choiceSpeechForChemical, 0, wx.ALL, 5 ) - - - bSizerSpeech.Add( bSizerSpeechForChemical, 1, wx.EXPAND, 5 ) - - - self.m_panelSpeech.SetSizer( bSizerSpeech ) - self.m_panelSpeech.Layout() - bSizerSpeech.Fit( self.m_panelSpeech ) - self.m_simplebookPanelsCategories.AddPage( self.m_panelSpeech, _(u"a page"), False ) - self.m_panelNavigation = wx.Panel( self.m_simplebookPanelsCategories, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.BORDER_SIMPLE|wx.TAB_TRAVERSAL ) - bSizerNavigation = wx.BoxSizer( wx.VERTICAL ) - - sbSizerNavigationMode = wx.StaticBoxSizer( wx.StaticBox( self.m_panelNavigation, wx.ID_ANY, _(u"Navigation mode to use when beginning to navigate an equation:") ), wx.VERTICAL ) - - m_choiceNavigationModeChoices = [ _(u"Enhanced"), _(u"Simple"), _(u"Character") ] - self.m_choiceNavigationMode = wx.Choice( sbSizerNavigationMode.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceNavigationModeChoices, 0 ) - self.m_choiceNavigationMode.SetSelection( 1 ) - sbSizerNavigationMode.Add( self.m_choiceNavigationMode, 0, wx.ALL, 5 ) - - self.m_checkBoxResetNavigationMode = wx.CheckBox( sbSizerNavigationMode.GetStaticBox(), wx.ID_ANY, _(u"Reset navigation mode on entry to an expression"), wx.DefaultPosition, wx.DefaultSize, 0 ) - sbSizerNavigationMode.Add( self.m_checkBoxResetNavigationMode, 0, wx.ALL, 5 ) - - - bSizerNavigation.Add( sbSizerNavigationMode, 1, wx.EXPAND, 5 ) - - sbSizerNavigationSpeech = wx.StaticBoxSizer( wx.StaticBox( self.m_panelNavigation, wx.ID_ANY, _(u"Navigation speech to use when beginning to navigate an equation:") ), wx.VERTICAL ) - - m_choiceNavigationSpeechChoices = [ _(u"Speak"), _(u"Describe/overview") ] - self.m_choiceNavigationSpeech = wx.Choice( sbSizerNavigationSpeech.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceNavigationSpeechChoices, 0 ) - self.m_choiceNavigationSpeech.SetSelection( 1 ) - sbSizerNavigationSpeech.Add( self.m_choiceNavigationSpeech, 0, wx.ALL, 5 ) - - self.m_checkBoxResetNavigationSpeech = wx.CheckBox( sbSizerNavigationSpeech.GetStaticBox(), wx.ID_ANY, _(u"Reset navigation speech on entry to an expression"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_checkBoxResetNavigationSpeech.SetValue(True) - sbSizerNavigationSpeech.Add( self.m_checkBoxResetNavigationSpeech, 0, wx.ALL, 5 ) - - - bSizerNavigation.Add( sbSizerNavigationSpeech, 1, wx.EXPAND, 5 ) - - bSizerNavigationZoom = wx.BoxSizer( wx.VERTICAL ) - - self.m_checkBoxAutomaticZoom = wx.CheckBox( self.m_panelNavigation, wx.ID_ANY, _(u"Automatic zoom out of 2D notations"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerNavigationZoom.Add( self.m_checkBoxAutomaticZoom, 0, wx.ALL, 5 ) - - bSizerSpeechAmountNavigation = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextSpeechAmountNavigation = wx.StaticText( self.m_panelNavigation, wx.ID_ANY, _(u"Speech amount for navigation:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextSpeechAmountNavigation.Wrap( -1 ) - - bSizerSpeechAmountNavigation.Add( self.m_staticTextSpeechAmountNavigation, 0, wx.ALL, 5 ) - - m_choiceSpeechAmountNavigationChoices = [ _(u"Terse"), _(u"Medium"), _(u"Verbose") ] - self.m_choiceSpeechAmountNavigation = wx.Choice( self.m_panelNavigation, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceSpeechAmountNavigationChoices, 0 ) - self.m_choiceSpeechAmountNavigation.SetSelection( 0 ) - bSizerSpeechAmountNavigation.Add( self.m_choiceSpeechAmountNavigation, 0, wx.ALL, 5 ) - - - bSizerNavigationZoom.Add( bSizerSpeechAmountNavigation, 1, wx.EXPAND, 5 ) - - - bSizerNavigation.Add( bSizerNavigationZoom, 1, wx.EXPAND, 5 ) - - - self.m_panelNavigation.SetSizer( bSizerNavigation ) - self.m_panelNavigation.Layout() - bSizerNavigation.Fit( self.m_panelNavigation ) - self.m_simplebookPanelsCategories.AddPage( self.m_panelNavigation, _(u"a page"), False ) - self.m_panelBraille = wx.Panel( self.m_simplebookPanelsCategories, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.BORDER_SIMPLE|wx.TAB_TRAVERSAL ) - bSizerBraille = wx.BoxSizer( wx.VERTICAL ) - - bSizerBrailleMathCode = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextBrailleMathCode = wx.StaticText( self.m_panelBraille, wx.ID_ANY, _(u"Braille math code for refreshable displays:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextBrailleMathCode.Wrap( -1 ) - - bSizerBrailleMathCode.Add( self.m_staticTextBrailleMathCode, 0, wx.ALL, 5 ) - - m_choiceBrailleMathCodeChoices = [ _(u"Nemeth"), _(u"UEB") ] - self.m_choiceBrailleMathCode = wx.Choice( self.m_panelBraille, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceBrailleMathCodeChoices, 0 ) - self.m_choiceBrailleMathCode.SetSelection( 1 ) - bSizerBrailleMathCode.Add( self.m_choiceBrailleMathCode, 0, wx.ALL, 5 ) - - - bSizerBraille.Add( bSizerBrailleMathCode, 1, wx.EXPAND, 5 ) - - bSizerBrailleHighlights = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_staticTextBrailleHighlights = wx.StaticText( self.m_panelBraille, wx.ID_ANY, _(u"Highlight with dots 7 && 8 the current nav node:"), wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTextBrailleHighlights.Wrap( -1 ) - - bSizerBrailleHighlights.Add( self.m_staticTextBrailleHighlights, 0, wx.ALL, 5 ) - - m_choiceBrailleHighlightsChoices = [ _(u"Off"), _(u"First character"), _(u"Endpoints"), _(u"All") ] - self.m_choiceBrailleHighlights = wx.Choice( self.m_panelBraille, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choiceBrailleHighlightsChoices, 0 ) - self.m_choiceBrailleHighlights.SetSelection( 1 ) - bSizerBrailleHighlights.Add( self.m_choiceBrailleHighlights, 0, wx.ALL, 5 ) - - - bSizerBraille.Add( bSizerBrailleHighlights, 1, wx.EXPAND, 5 ) - - - bSizerBraille.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - - bSizerBraille.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - - bSizerBraille.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - - bSizerBraille.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - - bSizerBraille.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - - self.m_panelBraille.SetSizer( bSizerBraille ) - self.m_panelBraille.Layout() - bSizerBraille.Fit( self.m_panelBraille ) - self.m_simplebookPanelsCategories.AddPage( self.m_panelBraille, _(u"a page"), False ) - - gbSizerMathCATPreferences.Add( self.m_simplebookPanelsCategories, wx.GBPosition( 0, 1 ), wx.GBSpan( 1, 1 ), wx.EXPAND |wx.ALL, 10 ) - - self.m_staticlineAboveButtons = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - gbSizerMathCATPreferences.Add( self.m_staticlineAboveButtons, wx.GBPosition( 1, 0 ), wx.GBSpan( 1, 2 ), wx.EXPAND |wx.ALL, 5 ) - - self.m_panelButtons = wx.Panel( self, wx.ID_ANY, wx.Point( -1,-1 ), wx.DefaultSize, 0 ) - bSizerButtons = wx.BoxSizer( wx.HORIZONTAL ) - - - bSizerButtons.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - - self.m_buttonOK = wx.Button( self.m_panelButtons, wx.ID_ANY, _(u"OK"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerButtons.Add( self.m_buttonOK, 0, wx.ALL, 5 ) - - self.m_buttonCancel = wx.Button( self.m_panelButtons, wx.ID_ANY, _(u"Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerButtons.Add( self.m_buttonCancel, 0, wx.ALL, 5 ) - - self.m_buttonApply = wx.Button( self.m_panelButtons, wx.ID_ANY, _(u"Apply"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerButtons.Add( self.m_buttonApply, 0, wx.ALL, 5 ) - - self.m_buttonReset = wx.Button( self.m_panelButtons, wx.ID_ANY, _(u"Reset to defaults"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerButtons.Add( self.m_buttonReset, 0, wx.ALL, 5 ) - - self.m_buttonHelp = wx.Button( self.m_panelButtons, wx.ID_ANY, _(u"Help"), wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizerButtons.Add( self.m_buttonHelp, 0, wx.ALL, 5 ) - - - self.m_panelButtons.SetSizer( bSizerButtons ) - self.m_panelButtons.Layout() - bSizerButtons.Fit( self.m_panelButtons ) - gbSizerMathCATPreferences.Add( self.m_panelButtons, wx.GBPosition( 2, 1 ), wx.GBSpan( 1, 2 ), wx.EXPAND |wx.ALL, 5 ) - - - self.SetSizer( gbSizerMathCATPreferences ) - self.Layout() - gbSizerMathCATPreferences.Fit( self ) - - self.Centre( wx.BOTH ) - - # Connect Events - self.Bind( wx.EVT_CHAR_HOOK, self.MathCATPreferencesDialogOnCharHook ) - self.Bind( wx.EVT_KEY_UP, self.MathCATPreferencesDialogOnKeyUp ) - self.m_listBoxPreferencesTopic.Bind( wx.EVT_LISTBOX, self.OnListBoxCategories ) - self.m_choiceLanguage.Bind( wx.EVT_CHOICE, self.OnLanguage ) - self.m_sliderRelativeSpeed.Bind( wx.EVT_SCROLL_CHANGED, self.OnRelativeSpeedChanged ) - self.m_sliderPauseFactor.Bind( wx.EVT_SCROLL_CHANGED, self.OnPauseFactorChanged ) - self.m_buttonOK.Bind( wx.EVT_BUTTON, self.OnClickOK ) - self.m_buttonCancel.Bind( wx.EVT_BUTTON, self.OnClickCancel ) - self.m_buttonApply.Bind( wx.EVT_BUTTON, self.OnClickApply ) - self.m_buttonReset.Bind( wx.EVT_BUTTON, self.OnClickReset ) - self.m_buttonHelp.Bind( wx.EVT_BUTTON, self.OnClickHelp ) - - def __del__( self ): - pass - - - # Virtual event handlers, override them in your derived class - def MathCATPreferencesDialogOnCharHook( self, event ): - event.Skip() - - def MathCATPreferencesDialogOnKeyUp( self, event ): - event.Skip() - - def OnListBoxCategories( self, event ): - event.Skip() - - def OnLanguage( self, event ): - event.Skip() - - def OnRelativeSpeedChanged( self, event ): - event.Skip() - - def OnPauseFactorChanged( self, event ): - event.Skip() - - def OnClickOK( self, event ): - event.Skip() - - def OnClickCancel( self, event ): - event.Skip() - - def OnClickApply( self, event ): - event.Skip() - - def OnClickReset( self, event ): - event.Skip() - - def OnClickHelp( self, event ): - event.Skip() - - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/__init__.py b/NVDA-addon/addon/globalPlugins/MathCAT/__init__.py deleted file mode 100644 index 64bd8129..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML -# The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued. -# Author: Neil Soiffer -# Copyright: this file is copyright GPL2 -# The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license -# and also (obviously) requires external speech engines and braille drivers. -# The plugin also requires the use of a small python dll: python3.dll -# python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved" - - -import globalPlugins # we are a global plugin -import globalPluginHandler # we are a global plugin -import globalVars -from logHandler import log # logging -import mathPres # math plugin stuff -from gui import mainFrame -import wx - -from .MathCAT import MathCAT -from .MathCATPreferences import UserInterface - -mathPres.registerProvider(MathCAT(), speech=True, braille=True, interaction=True) - -class GlobalPlugin(globalPluginHandler.GlobalPlugin): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # MathCAT.__init__(self) - self.add_MathCAT_menu() - - def add_MathCAT_menu(self): - if not globalVars.appArgs.secure: - self.preferencesMenu = mainFrame.sysTrayIcon.preferencesMenu - self.settings = self.preferencesMenu.Append(wx.ID_ANY, _("&MathCAT Settings...")) - mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.on_settings, self.settings) - - def on_settings(self, evt): - mainFrame._popupSettingsDialog(UserInterface) - - def terminate(self): - try: - if not globalVars.appArgs.secure: - self.preferencesMenu.Remove(self.settings) - except (AttributeError, RuntimeError): - pass - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/libmathcat.pyd b/NVDA-addon/addon/globalPlugins/MathCAT/libmathcat.pyd new file mode 100644 index 00000000..3b3ef982 Binary files /dev/null and b/NVDA-addon/addon/globalPlugins/MathCAT/libmathcat.pyd differ diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/_yaml.cp37-win32.pyd b/NVDA-addon/addon/globalPlugins/MathCAT/yaml/_yaml.cp37-win32.pyd deleted file mode 100644 index a03984a1..00000000 Binary files a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/_yaml.cp37-win32.pyd and /dev/null differ diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/cyaml.py b/NVDA-addon/addon/globalPlugins/MathCAT/yaml/cyaml.py deleted file mode 100644 index 0c213458..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/cyaml.py +++ /dev/null @@ -1,101 +0,0 @@ - -__all__ = [ - 'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader', - 'CBaseDumper', 'CSafeDumper', 'CDumper' -] - -from yaml._yaml import CParser, CEmitter - -from .constructor import * - -from .serializer import * -from .representer import * - -from .resolver import * - -class CBaseLoader(CParser, BaseConstructor, BaseResolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class CSafeLoader(CParser, SafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class CFullLoader(CParser, FullConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - FullConstructor.__init__(self) - Resolver.__init__(self) - -class CUnsafeLoader(CParser, UnsafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - UnsafeConstructor.__init__(self) - Resolver.__init__(self) - -class CLoader(CParser, Constructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - Constructor.__init__(self) - Resolver.__init__(self) - -class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class CSafeDumper(CEmitter, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class CDumper(CEmitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/dumper.py b/NVDA-addon/addon/globalPlugins/MathCAT/yaml/dumper.py deleted file mode 100644 index 6aadba55..00000000 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/dumper.py +++ /dev/null @@ -1,62 +0,0 @@ - -__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] - -from .emitter import * -from .serializer import * -from .representer import * -from .resolver import * - -class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class Dumper(Emitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - diff --git a/NVDA-addon/buildVars.py b/NVDA-addon/buildVars.py.bak similarity index 74% rename from NVDA-addon/buildVars.py rename to NVDA-addon/buildVars.py.bak index d7e42e92..73ae47e6 100644 --- a/NVDA-addon/buildVars.py +++ b/NVDA-addon/buildVars.py.bak @@ -16,42 +16,37 @@ def _(arg): # Add-on information variables addon_info = { # add-on Name/identifier, internal for NVDA - "addon_name": "MathCAT", + "addon_name": "addonTemplate", # Add-on summary, usually the user visible name of the addon. # Translators: Summary for this add-on # to be shown on installation and add-on information found in Add-ons Manager. - "addon_summary": _("MathCAT: speech and braille from MathML"), + "addon_summary": _("Add-on user visible name"), # Add-on description # Translators: Long description to be shown for this add-on on add-on information from add-ons manager - "addon_description": _(""" - MathCAT is a replacement for MathPlayer which has been discontinued. - It provides speech and braille support, and also supports MathPlayer's three modes of navigation. - The speech quality is not quite as good as MathPlayer's speech yet, - but the braille support is much better and includes both Nemeth and UEB Technical. - Translations to languages other than English are in progress. - """), + "addon_description": _("""Description for the add-on. +It can span multiple lines."""), # version - "addon_version": "0.2.6", + "addon_version": "x.y", # Author(s) - "addon_author": "Neil Soiffer ", + "addon_author": "name ", # URL for the add-on documentation support - "addon_url": "https://nsoiffer.github.io/MathCAT/", + "addon_url": None, # URL for the add-on repository where the source code can be found - "addon_sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "addon_sourceURL": None, # Documentation file name "addon_docFileName": "readme.html", # Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional) - "addon_minimumNVDAVersion": "2019.3", + "addon_minimumNVDAVersion": None, # Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version) - "addon_lastTestedNVDAVersion": "2023.1", + "addon_lastTestedNVDAVersion": None, # Add-on update channel (default is None, denoting stable releases, # and for development releases, use "dev".) # Do not change unless you know what you are doing! "addon_updateChannel": None, # Add-on license such as GPL 2 - "addon_license": "MIT and GPL 2", + "addon_license": None, # URL for the license document the ad-on is licensed under - "addon_licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE", + "addon_licenseURL": None, } # Define the python files that are the sources of your add-on. @@ -62,7 +57,7 @@ def _(arg): # pythonSources = ["addon/globalPlugins/*.py"] # For more information on SCons Glob expressions please take a look at: # https://scons.org/doc/production/HTML/scons-user/apd.html -pythonSources = ["addon/globalPlugins/mathcat.py"] +pythonSources = [] # Files that contain strings for translation. Usually your python sources i18nSources = pythonSources + ["buildVars.py"] diff --git a/NVDA-addon/doc/id/readme.md b/NVDA-addon/doc/id/readme.md new file mode 100644 index 00000000..1b14b811 --- /dev/null +++ b/NVDA-addon/doc/id/readme.md @@ -0,0 +1,27 @@ +# MathCAT + +- Penulis: Neil Soiffer +- Kompatibilitas NVDA: 2018.1 atau lebih baru (belum diuji di versi sebelumnya) +- Unduh [versi stabil][1] + +MathCAT dirancang untuk menggantikan MathPlayer karena MathPlayer tidak lagi didukung. MathCAT menghasilkan ucapan dan braille dari MathML. Ucapan untuk matematika yang dihasilkan oleh MathCAT ditingkatkan dengan prosodi sehingga terdengar lebih alami. Ucapan dapat dinavigasi dalam tiga mode menggunakan perintah yang sama seperti MathPlayer. Selain itu, simpul navigasi ditunjukkan pada tampilan braille. Mendukung braille Nemeth dan UEB. + +MathCAT menambahkan menu pengaturan ke menu preferensi NVDA. Di menu pengaturan, banyak opsi di MathCAT dapat diatur untuk mengontrol ucapan, navigasi, dan braille. + +Untuk dokumentasi pengguna secara lengkap, silakan lihat [Dokumentasi Pengguna MathCAT](https://nsoiffer.github.io/MathCAT/users.html). Untuk informasi tentang proyek MathCAT secara umum, lihat [Dokumentasi MathCAT](https://nsoiffer.github.io/MathCAT). + +Siapa yang boleh menggunakan MathCAT: + +- Mereka yang membutuhkan Nemeth braille (Nemeth MathPlayer didasarkan pada generasi Nemeth liblouis yang memiliki sejumlah bug signifikan yang secara teknis sulit untuk diperbaiki). +- Mereka yang membutuhkan braille UEB +- Mereka yang ingin mencoba teknologi terbaru dan bersedia membantu dengan melaporkan bug +- Mereka yang menggunakan Eloquence sebagai suara + +Siapa yang TIDAK boleh menggunakan MathCAT: + +- Siapa pun yang menggunakan MathPlayer dengan bahasa non-Inggris (terjemahan akan tersedia di masa mendatang) +- Siapa pun yang menggunakan MathPlayer dengan keluaran braille non-Nemeth/non-UEB (hubungi saya jika Anda ingin membantu dengan terjemahan braille) +- Siapa pun yang menggunakan MathPlayer untuk membaca Rumus Kimia (yang diharapkan akan muncul di rilis non-bug berikutnya) +- Siapa saja yang lebih memilih Access8Math daripada MathPlayer (untuk ucapan atau fitur lainnya) + +Aturan MathCAT untuk pengucapan belum seluas aturan MathPlayer -- itu mungkin alasan lain untuk tetap menggunakan MathPlayer. MathCAT digunakan sebagai testbed untuk ide-ide untuk MathML 4 yang memungkinkan penulis untuk mengekspresikan maksud mereka sehingga notasi ambigu dapat diucapkan dengan benar dan tidak ditebak. Saya telah menunda menambahkan terlalu banyak aturan karena arsitektur MathCAT berpusat di sekitar penggunaan dan menyimpulkan maksud penulis dan ini belum sepenuhnya diselesaikan. diff --git a/NVDA-addon/doc/style.css b/NVDA-addon/doc/style.css new file mode 100644 index 00000000..aac17be3 --- /dev/null +++ b/NVDA-addon/doc/style.css @@ -0,0 +1,26 @@ +@charset "utf-8"; +body { +font-family : Verdana, Arial, Helvetica, Sans-serif; +line-height: 1.2em; +} +h1, h2 {text-align: center} +dt { +font-weight : bold; +float : left; +width: 10%; +clear: left +} +dd { +margin : 0 0 0.4em 0; +float : left; +width: 90%; +display: block; +} +p { clear : both; +} +a { text-decoration : underline; +} +:active { +text-decoration : none; +} +a:focus, a:hover {outline: solid} diff --git a/NVDA-addon/sconstruct b/NVDA-addon/sconstruct.bak similarity index 69% rename from NVDA-addon/sconstruct rename to NVDA-addon/sconstruct.bak index 8aa914b5..ab61654e 100644 --- a/NVDA-addon/sconstruct +++ b/NVDA-addon/sconstruct.bak @@ -1,5 +1,5 @@ # NVDA add-on template SCONSTRUCT file -# Copyright (C) 2012-2023 Rui Batista, Noelia Martinez, Joseph Lee +# Copyright (C) 2012-2021 Rui Batista, Noelia Martinez, Joseph Lee # This file is covered by the GNU General Public License. # See the file COPYING.txt for more details. @@ -75,21 +75,8 @@ def mdTool(env): env['BUILDERS']['markdown'] = mdBuilder -def validateVersionNumber(key, val, env): - # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. - # Ignore all this if version number is not specified, in which case json generator will validate this info. - if val == "0.0.0": - return - versionNumber = val.split(".") - if len(versionNumber) < 3: - raise ValueError("versionNumber must have three parts (major.minor.patch)") - if not all([part.isnumeric() for part in versionNumber]): - raise ValueError("versionNumber (major.minor.patch) must be integers") - - vars = Variables() vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) -vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0", validateVersionNumber) vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) @@ -100,9 +87,7 @@ if env["dev"]: import datetime buildDate = datetime.datetime.now() year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) - versionTimestamp = "".join([year, month.zfill(2), day.zfill(2)]) - env["addon_version"] = f"{versionTimestamp}-dev" - env["versionNumber"] = f"{versionTimestamp}.0.0" + env["addon_version"] = "".join([year, month.zfill(2), day.zfill(2), "-dev"]) env["channel"] = "dev" elif env["version"] is not None: env["addon_version"] = env["version"] @@ -170,85 +155,9 @@ def createAddonBundleFromPath(path, dest): absPath = os.path.join(dir, filename) if pathInBundle not in buildVars.excludedFiles: z.write(absPath, pathInBundle) - createAddonStoreJson(dest) return dest -def createAddonStoreJson(bundle): - """Creates add-on store JSON file from an add-on package and manifest data.""" - import json - import hashlib - # Set different json file names and version number properties based on version number parsing results. - if env["versionNumber"] == "0.0.0": - env["versionNumber"] = buildVars.addon_info["addon_version"] - versionNumberParsed = env["versionNumber"].split(".") - if all([part.isnumeric() for part in versionNumberParsed]): - if len(versionNumberParsed) == 1: - versionNumberParsed += ["0", "0"] - elif len(versionNumberParsed) == 2: - versionNumberParsed.append("0") - else: - versionNumberParsed = [] - if len(versionNumberParsed): - major, minor, patch = [int(part) for part in versionNumberParsed] - jsonFilename = f'{major}.{minor}.{patch}.json' - else: - jsonFilename = f'{buildVars.addon_info["addon_version"]}.json' - major, minor, patch = 0, 0, 0 - print('Generating % s' % jsonFilename) - sha256 = hashlib.sha256() - with open(bundle, "rb") as f: - for byte_block in iter(lambda: f.read(65536), b""): - sha256.update(byte_block) - hashValue = sha256.hexdigest() - try: - minimumNVDAVersion = buildVars.addon_info["addon_minimumNVDAVersion"].split(".") - except AttributeError: - minimumNVDAVersion = [0, 0, 0] - minMajor, minMinor = minimumNVDAVersion[:2] - minPatch = minimumNVDAVersion[-1] if len(minimumNVDAVersion) == 3 else "0" - try: - lastTestedNVDAVersion = buildVars.addon_info["addon_lastTestedNVDAVersion"].split(".") - except AttributeError: - lastTestedNVDAVersion = [0, 0, 0] - lastTestedMajor, lastTestedMinor = lastTestedNVDAVersion[:2] - lastTestedPatch = lastTestedNVDAVersion[-1] if len(lastTestedNVDAVersion) == 3 else "0" - channel = buildVars.addon_info["addon_updateChannel"] - if channel is None: - channel = "stable" - addonStoreEntry = { - "addonId": buildVars.addon_info["addon_name"], - "displayName": buildVars.addon_info["addon_summary"], - "URL": "", - "description": buildVars.addon_info["addon_description"], - "sha256": hashValue, - "homepage": buildVars.addon_info["addon_url"], - "addonVersionName": buildVars.addon_info["addon_version"], - "addonVersionNumber": { - "major": major, - "minor": minor, - "patch": patch - }, - "minNVDAVersion": { - "major": int(minMajor), - "minor": int(minMinor), - "patch": int(minPatch) - }, - "lastTestedVersion": { - "major": int(lastTestedMajor), - "minor": int(lastTestedMinor), - "patch": int(lastTestedPatch) - }, - "channel": channel, - "publisher": "", - "sourceURL": buildVars.addon_info["addon_sourceURL"], - "license": buildVars.addon_info["addon_license"], - "licenseURL": buildVars.addon_info["addon_licenseURL"], - } - with open(jsonFilename, "w") as addonStoreJson: - json.dump(addonStoreEntry, addonStoreJson, indent="\t") - - def generateManifest(source, dest): addon_info = buildVars.addon_info with codecs.open(source, "r", "utf-8") as f: diff --git a/NVDA-addon/test.py b/NVDA-addon/test.py deleted file mode 100644 index 3f81a5be..00000000 --- a/NVDA-addon/test.py +++ /dev/null @@ -1,570 +0,0 @@ -"""create and manipulate C data types in Python""" - -import os as _os, sys as _sys -import types as _types - -__version__ = "1.1.0" - -from _ctypes import Union, Structure, Array -from _ctypes import _Pointer -from _ctypes import CFuncPtr as _CFuncPtr -from _ctypes import __version__ as _ctypes_version -from _ctypes import RTLD_LOCAL, RTLD_GLOBAL -from _ctypes import ArgumentError - -from struct import calcsize as _calcsize - -if __version__ != _ctypes_version: - raise Exception("Version number mismatch", __version__, _ctypes_version) - -if _os.name == "nt": - from _ctypes import FormatError - -DEFAULT_MODE = RTLD_LOCAL -if _os.name == "posix" and _sys.platform == "darwin": - # On OS X 10.3, we use RTLD_GLOBAL as default mode - # because RTLD_LOCAL does not work at least on some - # libraries. OS X 10.3 is Darwin 7, so we check for - # that. - - if int(_os.uname().release.split('.')[0]) < 8: - DEFAULT_MODE = RTLD_GLOBAL - -from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ - FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \ - FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \ - FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR - -# WINOLEAPI -> HRESULT -# WINOLEAPI_(type) -# -# STDMETHODCALLTYPE -# -# STDMETHOD(name) -# STDMETHOD_(type, name) -# -# STDAPICALLTYPE - -def create_string_buffer(init, size=None): - """create_string_buffer(aBytes) -> character array - create_string_buffer(anInteger) -> character array - create_string_buffer(aBytes, anInteger) -> character array - """ - if isinstance(init, bytes): - if size is None: - size = len(init)+1 - _sys.audit("ctypes.create_string_buffer", init, size) - buftype = c_char * size - buf = buftype() - buf.value = init - return buf - elif isinstance(init, int): - _sys.audit("ctypes.create_string_buffer", None, init) - buftype = c_char * init - buf = buftype() - return buf - raise TypeError(init) - -def c_buffer(init, size=None): -## "deprecated, use create_string_buffer instead" -## import warnings -## warnings.warn("c_buffer is deprecated, use create_string_buffer instead", -## DeprecationWarning, stacklevel=2) - return create_string_buffer(init, size) - -_c_functype_cache = {} -def CFUNCTYPE(restype, *argtypes, **kw): - """CFUNCTYPE(restype, *argtypes, - use_errno=False, use_last_error=False) -> function prototype. - - restype: the result type - argtypes: a sequence specifying the argument types - - The function prototype can be called in different ways to create a - callable object: - - prototype(integer address) -> foreign function - prototype(callable) -> create and return a C callable function from callable - prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method - prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal - prototype((function name, dll object)[, paramflags]) -> foreign function exported by name - """ - flags = _FUNCFLAG_CDECL - if kw.pop("use_errno", False): - flags |= _FUNCFLAG_USE_ERRNO - if kw.pop("use_last_error", False): - flags |= _FUNCFLAG_USE_LASTERROR - if kw: - raise ValueError("unexpected keyword argument(s) %s" % kw.keys()) - try: - return _c_functype_cache[(restype, argtypes, flags)] - except KeyError: - class CFunctionType(_CFuncPtr): - _argtypes_ = argtypes - _restype_ = restype - _flags_ = flags - _c_functype_cache[(restype, argtypes, flags)] = CFunctionType - return CFunctionType - -if _os.name == "nt": - from _ctypes import LoadLibrary as _dlopen - from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL - - _win_functype_cache = {} - def WINFUNCTYPE(restype, *argtypes, **kw): - # docstring set later (very similar to CFUNCTYPE.__doc__) - flags = _FUNCFLAG_STDCALL - if kw.pop("use_errno", False): - flags |= _FUNCFLAG_USE_ERRNO - if kw.pop("use_last_error", False): - flags |= _FUNCFLAG_USE_LASTERROR - if kw: - raise ValueError("unexpected keyword argument(s) %s" % kw.keys()) - try: - return _win_functype_cache[(restype, argtypes, flags)] - except KeyError: - class WinFunctionType(_CFuncPtr): - _argtypes_ = argtypes - _restype_ = restype - _flags_ = flags - _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType - return WinFunctionType - if WINFUNCTYPE.__doc__: - WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") - -elif _os.name == "posix": - from _ctypes import dlopen as _dlopen - -from _ctypes import sizeof, byref, addressof, alignment, resize -from _ctypes import get_errno, set_errno -from _ctypes import _SimpleCData - -def _check_size(typ, typecode=None): - # Check if sizeof(ctypes_type) against struct.calcsize. This - # should protect somewhat against a misconfigured libffi. - from struct import calcsize - if typecode is None: - # Most _type_ codes are the same as used in struct - typecode = typ._type_ - actual, required = sizeof(typ), calcsize(typecode) - if actual != required: - raise SystemError("sizeof(%s) wrong: %d instead of %d" % \ - (typ, actual, required)) - -class py_object(_SimpleCData): - _type_ = "O" - def __repr__(self): - try: - return super().__repr__() - except ValueError: - return "%s()" % type(self).__name__ -_check_size(py_object, "P") - -class c_short(_SimpleCData): - _type_ = "h" -_check_size(c_short) - -class c_ushort(_SimpleCData): - _type_ = "H" -_check_size(c_ushort) - -class c_long(_SimpleCData): - _type_ = "l" -_check_size(c_long) - -class c_ulong(_SimpleCData): - _type_ = "L" -_check_size(c_ulong) - -if _calcsize("i") == _calcsize("l"): - # if int and long have the same size, make c_int an alias for c_long - c_int = c_long - c_uint = c_ulong -else: - class c_int(_SimpleCData): - _type_ = "i" - _check_size(c_int) - - class c_uint(_SimpleCData): - _type_ = "I" - _check_size(c_uint) - -class c_float(_SimpleCData): - _type_ = "f" -_check_size(c_float) - -class c_double(_SimpleCData): - _type_ = "d" -_check_size(c_double) - -class c_longdouble(_SimpleCData): - _type_ = "g" -if sizeof(c_longdouble) == sizeof(c_double): - c_longdouble = c_double - -if _calcsize("l") == _calcsize("q"): - # if long and long long have the same size, make c_longlong an alias for c_long - c_longlong = c_long - c_ulonglong = c_ulong -else: - class c_longlong(_SimpleCData): - _type_ = "q" - _check_size(c_longlong) - - class c_ulonglong(_SimpleCData): - _type_ = "Q" - ## def from_param(cls, val): - ## return ('d', float(val), val) - ## from_param = classmethod(from_param) - _check_size(c_ulonglong) - -class c_ubyte(_SimpleCData): - _type_ = "B" -c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte -# backward compatibility: -##c_uchar = c_ubyte -_check_size(c_ubyte) - -class c_byte(_SimpleCData): - _type_ = "b" -c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte -_check_size(c_byte) - -class c_char(_SimpleCData): - _type_ = "c" -c_char.__ctype_le__ = c_char.__ctype_be__ = c_char -_check_size(c_char) - -class c_char_p(_SimpleCData): - _type_ = "z" - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value) -_check_size(c_char_p, "P") - -class c_void_p(_SimpleCData): - _type_ = "P" -c_voidp = c_void_p # backwards compatibility (to a bug) -_check_size(c_void_p) - -class c_bool(_SimpleCData): - _type_ = "?" - -from _ctypes import POINTER, pointer, _pointer_type_cache - -class c_wchar_p(_SimpleCData): - _type_ = "Z" - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value) - -class c_wchar(_SimpleCData): - _type_ = "u" - -def _reset_cache(): - _pointer_type_cache.clear() - _c_functype_cache.clear() - if _os.name == "nt": - _win_functype_cache.clear() - # _SimpleCData.c_wchar_p_from_param - POINTER(c_wchar).from_param = c_wchar_p.from_param - # _SimpleCData.c_char_p_from_param - POINTER(c_char).from_param = c_char_p.from_param - _pointer_type_cache[None] = c_void_p - -def create_unicode_buffer(init, size=None): - """create_unicode_buffer(aString) -> character array - create_unicode_buffer(anInteger) -> character array - create_unicode_buffer(aString, anInteger) -> character array - """ - if isinstance(init, str): - if size is None: - if sizeof(c_wchar) == 2: - # UTF-16 requires a surrogate pair (2 wchar_t) for non-BMP - # characters (outside [U+0000; U+FFFF] range). +1 for trailing - # NUL character. - size = sum(2 if ord(c) > 0xFFFF else 1 for c in init) + 1 - else: - # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for - # trailing NUL character. - size = len(init) + 1 - _sys.audit("ctypes.create_unicode_buffer", init, size) - buftype = c_wchar * size - buf = buftype() - buf.value = init - return buf - elif isinstance(init, int): - _sys.audit("ctypes.create_unicode_buffer", None, init) - buftype = c_wchar * init - buf = buftype() - return buf - raise TypeError(init) - - -# XXX Deprecated -def SetPointerType(pointer, cls): - if _pointer_type_cache.get(cls, None) is not None: - raise RuntimeError("This type already exists in the cache") - if id(pointer) not in _pointer_type_cache: - raise RuntimeError("What's this???") - pointer.set_type(cls) - _pointer_type_cache[cls] = pointer - del _pointer_type_cache[id(pointer)] - -# XXX Deprecated -def ARRAY(typ, len): - return typ * len - -################################################################ - - -class CDLL(object): - """An instance of this class represents a loaded dll/shared - library, exporting functions using the standard C calling - convention (named 'cdecl' on Windows). - - The exported functions can be accessed as attributes, or by - indexing with the function name. Examples: - - .qsort -> callable object - ['qsort'] -> callable object - - Calling the functions releases the Python GIL during the call and - reacquires it afterwards. - """ - _func_flags_ = _FUNCFLAG_CDECL - _func_restype_ = c_int - # default values for repr - _name = '' - _handle = 0 - _FuncPtr = None - - def __init__(self, name, mode=DEFAULT_MODE, handle=None, - use_errno=False, - use_last_error=False, - winmode=None): - self._name = name - flags = self._func_flags_ - if use_errno: - flags |= _FUNCFLAG_USE_ERRNO - if use_last_error: - flags |= _FUNCFLAG_USE_LASTERROR - if _sys.platform.startswith("aix"): - """When the name contains ".a(" and ends with ")", - e.g., "libFOO.a(libFOO.so)" - this is taken to be an - archive(member) syntax for dlopen(), and the mode is adjusted. - Otherwise, name is presented to dlopen() as a file argument. - """ - if name and name.endswith(")") and ".a(" in name: - mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW ) - if _os.name == "nt": - if winmode is not None: - mode = winmode - else: - import nt - mode = 0x00001000 - if '/' in name or '\\' in name: - self._name = nt._getfullpathname(self._name) - mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR - - class _FuncPtr(_CFuncPtr): - _flags_ = flags - _restype_ = self._func_restype_ - self._FuncPtr = _FuncPtr - - if handle is None: - self._handle = _dlopen(self._name, mode) - else: - self._handle = handle - - def __repr__(self): - return "<%s '%s', handle %x at %#x>" % \ - (self.__class__.__name__, self._name, - (self._handle & (_sys.maxsize*2 + 1)), - id(self) & (_sys.maxsize*2 + 1)) - - def __getattr__(self, name): - if name.startswith('__') and name.endswith('__'): - raise AttributeError(name) - func = self.__getitem__(name) - setattr(self, name, func) - return func - - def __getitem__(self, name_or_ordinal): - func = self._FuncPtr((name_or_ordinal, self)) - if not isinstance(name_or_ordinal, int): - func.__name__ = name_or_ordinal - return func - -class PyDLL(CDLL): - """This class represents the Python library itself. It allows - accessing Python API functions. The GIL is not released, and - Python exceptions are handled correctly. - """ - _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI - -if _os.name == "nt": - - class WinDLL(CDLL): - """This class represents a dll exporting functions using the - Windows stdcall calling convention. - """ - _func_flags_ = _FUNCFLAG_STDCALL - - # XXX Hm, what about HRESULT as normal parameter? - # Mustn't it derive from c_long then? - from _ctypes import _check_HRESULT, _SimpleCData - class HRESULT(_SimpleCData): - _type_ = "l" - # _check_retval_ is called with the function's result when it - # is used as restype. It checks for the FAILED bit, and - # raises an OSError if it is set. - # - # The _check_retval_ method is implemented in C, so that the - # method definition itself is not included in the traceback - # when it raises an error - that is what we want (and Python - # doesn't have a way to raise an exception in the caller's - # frame). - _check_retval_ = _check_HRESULT - - class OleDLL(CDLL): - """This class represents a dll exporting functions using the - Windows stdcall calling convention, and returning HRESULT. - HRESULT error values are automatically raised as OSError - exceptions. - """ - _func_flags_ = _FUNCFLAG_STDCALL - _func_restype_ = HRESULT - -class LibraryLoader(object): - def __init__(self, dlltype): - self._dlltype = dlltype - - def __getattr__(self, name): - if name[0] == '_': - raise AttributeError(name) - dll = self._dlltype(name) - setattr(self, name, dll) - return dll - - def __getitem__(self, name): - return getattr(self, name) - - def LoadLibrary(self, name): - return self._dlltype(name) - - # __class_getitem__ = classmethod(_types.GenericAlias) - -cdll = LibraryLoader(CDLL) -pydll = LibraryLoader(PyDLL) - -if _os.name == "nt": - pythonapi = PyDLL("python dll", None, _sys.dllhandle) -elif _sys.platform == "cygwin": - pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) -else: - pythonapi = PyDLL(None) - - -if _os.name == "nt": - windll = LibraryLoader(WinDLL) - oledll = LibraryLoader(OleDLL) - - GetLastError = windll.kernel32.GetLastError - from _ctypes import get_last_error, set_last_error - - def WinError(code=None, descr=None): - if code is None: - code = GetLastError() - if descr is None: - descr = FormatError(code).strip() - return OSError(None, descr, None, code) - -if sizeof(c_uint) == sizeof(c_void_p): - c_size_t = c_uint - c_ssize_t = c_int -elif sizeof(c_ulong) == sizeof(c_void_p): - c_size_t = c_ulong - c_ssize_t = c_long -elif sizeof(c_ulonglong) == sizeof(c_void_p): - c_size_t = c_ulonglong - c_ssize_t = c_longlong - -# functions - -from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr - -## void *memmove(void *, const void *, size_t); -memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) - -## void *memset(void *, int, size_t) -memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) - -def PYFUNCTYPE(restype, *argtypes): - class CFunctionType(_CFuncPtr): - _argtypes_ = argtypes - _restype_ = restype - _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI - return CFunctionType - -_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) -def cast(obj, typ): - return _cast(obj, obj, typ) - -_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) -def string_at(ptr, size=-1): - """string_at(addr[, size]) -> string - - Return the string at addr.""" - return _string_at(ptr, size) - -try: - from _ctypes import _wstring_at_addr -except ImportError: - pass -else: - _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) - def wstring_at(ptr, size=-1): - """wstring_at(addr[, size]) -> string - - Return the string at addr.""" - return _wstring_at(ptr, size) - - -if _os.name == "nt": # COM stuff - def DllGetClassObject(rclsid, riid, ppv): - try: - ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*']) - except ImportError: - return -2147221231 # CLASS_E_CLASSNOTAVAILABLE - else: - return ccom.DllGetClassObject(rclsid, riid, ppv) - - def DllCanUnloadNow(): - try: - ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*']) - except ImportError: - return 0 # S_OK - return ccom.DllCanUnloadNow() - -from ctypes._endian import BigEndianStructure, LittleEndianStructure - -# Fill in specifically-sized types -c_int8 = c_byte -c_uint8 = c_ubyte -for kind in [c_short, c_int, c_long, c_longlong]: - if sizeof(kind) == 2: c_int16 = kind - elif sizeof(kind) == 4: c_int32 = kind - elif sizeof(kind) == 8: c_int64 = kind -for kind in [c_ushort, c_uint, c_ulong, c_ulonglong]: - if sizeof(kind) == 2: c_uint16 = kind - elif sizeof(kind) == 4: c_uint32 = kind - elif sizeof(kind) == 8: c_uint64 = kind -del(kind) - -_reset_cache() - -print( "type of SetClipboardData: %s" % str(windll.user32.SetClipboardData)) -print( "type of RegisterClipboardFormat: %s" % str(windll.user32.RegisterClipboardFormatA)) -CF_MathML = windll.user32.RegisterClipboardFormatW("MathML") -CF_MathML_Presentation = windll.user32.RegisterClipboardFormatW("MathML Presentation") -print("MathCAT registering data formats: CF_MathML %x, CF_MathML_Presentation %x" % - (CF_MathML, CF_MathML_Presentation)) diff --git a/NVDA-addon/README b/README similarity index 100% rename from NVDA-addon/README rename to README diff --git a/NVDA-addon/README.install b/README.install similarity index 100% rename from NVDA-addon/README.install rename to README.install diff --git a/README.md b/README.md deleted file mode 100644 index 025e4a6e..00000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# MathCATForPython -A Python Interface and NVDA plugin to MathCAT - -See the [MathCAT repo](https://github.com/NSoiffer/MathCAT) for more information. diff --git a/addon/doc/da/readme.md b/addon/doc/da/readme.md new file mode 100644 index 00000000..8417527a --- /dev/null +++ b/addon/doc/da/readme.md @@ -0,0 +1,168 @@ +# MathCAT # + +* Forfatter: Neil Soiffer +* NVDA compatibility: 2018.1 or later (untested in earlier versions) +* Download [stabil version][1] + +MathCAT er designet til at erstatte MathPlayer, fordi MathPlayer ikke +længere understøttes. MathCAT genererer tale og punkt fra MathML. Talen til +matematik produceret af MathCAT er forbedret med prosodi, så den lyder mere +naturlig. Talen kan navigeres i tre tilstande ved hjælp af de samme +kommandoer som MathPlayer. Derudover er navigationsknuden vist på et +punktdisplay. Både Nemeth og UEB teknisk understøttes. + +MathCAT har en række konfigurationsmuligheder, der styrer tale, navigation +og punkt. Mange af disse kan indstilles i MathCAT-indstillingsdialogen +(findes i meNVDA-menuen>Opsætning). For mere information om disse +indstillinger, læs +[MathCAT-dokumentationen](https://nsoiffer.github.io/MathCAT/users.html). +Dokumentationen indeholder et link til [en tabel med alle +navigationskommandoer i +MathCAT](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Bemærk: MathCAT er et generelt bibliotek til generering af tale og punkt fra +MathML. Dette bruges af andre lignende projekter udover NVDA. For +information om MathCAT-projektet, se +[MathCAT-dokumentationssiden](https://nsoiffer.github.io/MathCAT). + + +Hvem bør bruge MathCAT: + +* Dem, der har brug for Nemeth-braille af høj kvalitet (MathPlayers Nemeth + er baseret på liblouis' Nemeth-generation, som har en række væsentlige + fejl, som er teknisk svære at rette). +* Dem, der har brug for UEB teknisk braille +* Dem, der ønsker at prøve den nyeste teknologi og er villige til at hjælpe + ved at rapportere fejl +* Dem, der bruger Eloquence som stemme + +Hvem bør IKKE bruge MathCAT: + +* Enhver, der bruger MathPlayer med et andet sprog end engelsk + (oversættelser findes til indonesisk og vietnamesisk; oversættelser er + sandsynlige i fremtiden) +* Enhver, der bruger MathPlayer med et ikke-Nemeth/ikke-UEB brailleoutput + (kontakt mig, hvis du vil hjælpe med en brailleoversættelse) +* Enhver, der foretrækker Access8Math frem for MathPlayer (til tale eller + andre funktioner) + +MathCATs regler for tale er endnu ikke så omfattende som MathPlayers regler +- det kan være endnu en grund til at holde sig til MathPlayer. MathCAT +bliver brugt som et testbed for ideer til MathML 4, der gør det muligt for +forfattere at udtrykke deres hensigt, så tvetydige notationer kan læses +korrekt og ikke gættes på. Jeg har holdt op med at tilføje for mange regler, +da arkitekturen i MathCAT er centreret omkring brug og udledning af +forfatterens hensigt, og disse er ikke helt afklarede endnu. + +## MathCAT Update Log + +### Version 0.5.0 +* Added German LaTeX braille code. Unlike other braille codes, this + generates ASCII chars and uses the current braille output table to + translate the characters to braille. +* Added (expermental) ASCIIMath braille code. Like the LaTeX braille code, + this generates ASCII chars and uses the current braille output table to + translate the characters to braille. +* Added "CopyAs" preference that supports copying as MathML, LaTeX, or + ASCIIMath using cntl+C when focused on MathML (as before). The currently + focused node is copied. Note: this is only listed in the prefs.yaml file + and is not exposed (yet) in the MathCAT Preferences dialog. + +### Version 0.4.2 +* Fixed language switching when voice changes and MathCAT language is "Auto" +* Added more checks for $Impairments to improve reading when it is not set + for those who are blind +* Nemeth: fix for "~" when it isn't part of an mrow +* UEB: character additions, "~" spacing fix if prefix, xor fix, +* MathML cleanup for accented vowels (mainly for Vietnamese) +* Major rewrite of preference reading/updating code with big speedup -- + added `CheckRuleFiles` pref to control which files are checked for updates +* Added two new interface calls -- enables setting the navigaton location + from the braille cursor (not part of MathCAT addon yet) + +### Version 0.3.11 +* Upgraded to python 3.11 and verified working with NVDA 2024.1 +* Fix bugs in Vietnamese braille and also in Speech, mostly for chemistry. +* Fix broken braille when braille code and dependent language don't match + (specifically Vietnam braille and Vietnamese speech) +* Fix whitespace bug in HTML inside of tokens +* Improve roman numeral detection + +### Version 0.3.9 +* Added Traditional Chinese translation (thanks to Hon-Jang Yang) +* Fixed bug with navigating into the base of a scripted expression that has + parenthesis +* Significantly changed the way whitespace is handled. This mainly affects + braille output (spaces and "omission" detection). +* Improved recognition of chemistry +* UEB braille fixes that came up from adding chemistry examples +* UEB fixes for adding auxillary parenthesis in some cases + +### Version 0.3.8 +Braille: + +* Dialog has been internationalized for several languages (many thanks to + the translators!) +* Initial implementation of CMU -- the braille code used in Spanish and + Portuguese speaking countries +* Fix some UEB bugs and added some characters for UEB +* Significant improvements to Vietnamese braille + +Other fixes: + +* Change relative rate dialog slider to have a maximum value of 100% (now + only allows setting slower rates). Also, added step sizes so it is easier + to raise/lower the rate significantly. +* Fix eSpeak bug that sometimes cut off speech when the relative rate was + changed +* Improvements to Vietnamese speech +* Fixed bug with OneCore voices saying "a" +* Fixed some navigation bugs when `AutoZoomOut` is False (not the default) +* Fix updating around language changes and some other dialog changes so they + take effect immediately upon clicking "Apply" or "OK". +* Added an "Use Voice's Language" option so that out of the box, MathCAT + will speak in the right language (if there is a translation) +* Several improvements for cleaning up poor MathML code + +### Version 0.3.3 +Denne udgivelse indeholder en række fejlrettelser. De vigtigste nye +funktioner og fejlrettelser er: + +* Added Spanish Translation (thanks to Noelia Ruiz and María Allo Roldán) +* Ændret navigation, så den begynder at zoome i ét niveau +* Tilføjet cntrl+alt+pil som en måde at navigere i tabelstrukturer på. Disse + taster burde være mere mindeværdige, fordi de bruges til tabelnavigation i + NVDA. +* Virkede uden om NVDA-fejl for eSpeak-stemmer, der fik dem til at sænke + farten, når den relative MathRate var indstillet til at være langsommere + end teksttalehastigheden. +* Arbejdet med et OneCore-stemmeproblem, så de vil sige den lange 'a'-lyd. + +Der er masser af små justeringer af talen og nogle fejlrettelser til både +Nemeth og UEB. + +Bemærk: der er nu en mulighed for at få Vietnams braillestandard som +brailleoutput. Dette er stadig et igangværende arbejde og er for buggy til +at blive brugt andet end til test. Jeg forventer, at den næste +MathCAT-udgivelse vil indeholde en pålidelig implementering. + +### Version 0.2.5 +* Flere forbedringer kemi +* Rettelser til Nemeth: + + * Added "omission" rules + * Added some rules for English Language Indicators + * Added more cases where the Mulitpurpose indicator is needed + * Fixes related to Nemeth and punctuation + +### Version 0.2 +* Masser af fejlrettelser +* Forbedringer af tale +* En præferenceindstilling til at kontrollere varigheden af pause (fungerer + med ændringer af relativ talehastighed for matematik) +* Støtte til at genkende keminotation og tale den korrekt +* Oversættelser til indonesisk og vietnamesisk + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/de/readme.md b/addon/doc/de/readme.md new file mode 100644 index 00000000..1c95edbd --- /dev/null +++ b/addon/doc/de/readme.md @@ -0,0 +1,260 @@ +# MathCAT # + +* Autor: Neil Soiffer +* NVDA-Kompatibilität: 2018.1 oder neuer (nicht getestet in älteren + Versionen) +* [Stabile Version herunterladen][1] + +MathCAT wurde entwickelt, um MathPlayer zu ersetzen, da der MathPlayer nicht +mehr unterstützt wird. MathCAT generiert Ausgaben über die Sprachausgabe und +in Braille aus MathML. Die von MathCAT erzeugte Sprache für Mathematik wird +durch Prosodie verbessert, so dass sie natürlicher klingt. Die Sprache kann +in drei Modi mit denselben Befehlen wie MathPlayer navigiert werden. Darüber +hinaus wird der Navigationsknoten auf einer Braillezeile angezeigt. Sowohl +Nemeth- als auch UEB-Technik werden unterstützt. + +MathCAT verfügt über eine Reihe von Konfigurationsoptionen, die die +Sprachausgabe, Navigation und die Braille-Ausgabe steuern. Viele dieser +Optionen können in den MathCAT-Einstellungen vorgenommen werden (zu finden +im NVDA-Menü). Weitere Informationen zu diesen Einstellungen finden Sie in +der +[MathCAT-Dokumentation](https://nsoiffer.github.io/MathCAT/users.html). Die +Dokumentation enthält einen Link zu [einer Tabelle mit allen +Navigationsbefehlen in +MathCAT](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Hinweis: MathCAT ist eine allgemeine Bibliothek zur Erzeugung von Sprache +und Braille aus MathML. Sie wird neben NVDA auch von anderen AT-Projekten +verwendet. Informationen über das MathCAT-Projekt im Allgemeinen finden Sie +auf der [Dokumentationsseite für +MathCAT](https://nsoiffer.github.io/MathCAT). + + +Wer sollte MathCAT benutzen: + +* Diejenigen, die eine hohe Qualität der Nemeth-Brailleschrift benötigen + (die Nemeth-Schrift von MathPlayer basiert auf der Nemeth-Schrift von + liblouis, die eine Reihe schwerwiegender Fehler aufweist, die technisch + schwer zu beheben sind). +* Diejenigen, die technische Braille-Schrift UEB, CMU + (Spanisch/Portugiesisch), deutsches LaTeX, ASCIIMath oder vietnamesische + Braille-Schrift benötigen +* Diejenigen, die die neueste Technologie ausprobieren wollen und bereit + sind, durch die Meldung von Fehlern zu helfen +* Diejenigen, die eine Eloquence-Stimme benutzen + +Wer sollte MathCAT NICHT benutzen: + +* Alle, die den MathPlayer mit einer Sprache verwendet, die noch nicht von + MathCAT unterstützt wird (Übersetzungen gibt es für Chinesisch + (traditionell), Spanisch, Indonesisch und Vietnamesisch; Übersetzungen + werden in Zukunft folgen) und wer mit den unterstützten Sprachen (noch) + nicht vertraut ist. +* Alle, die den Access8Math dem MathPlayer vorzieht (wegen der Sprache oder + anderer Funktionen) + +Die Sprachregeln von MathCAT sind noch nicht so umfangreich wie die von +MathPlayer - ein weiterer Grund, bei MathPlayer zu bleiben. MathCAT wird als +Testumgebung für Ideen für MathML 4 verwendet, mit denen die Autoren +mehrdeutige Notationen korregieren können und diese dann nicht mehr erraten +werden müssen. Ich habe mich noch etwas zurückgehalten, zu viele Regeln +hinzuzufügen, da sich die Architektur von MathCAT auf die Verwendung und +Ableitung von Autorenabsichten konzentriert und diese noch nicht vollständig +geklärt sind. + +## Änderungsprotokoll für MathCAT + +### Version 0.6.3 + +* Alle Sprach- und Braille-Regeldateien werden in ein Verzeichnis gepackt + und bei Bedarf entpackt. + + * Dies spart derzeit ~5 MB, wenn Rules.zip entpackt wird, und wird noch + mehr sparen, wenn mehr Sprachen und Braille-Codes hinzugefügt werden. + * Dies ist eine Vorbereitung für die Integration von MathCAT in NVDA 2024.3 + +* Neue Einstellung `DecimalSeparator` hinzugefügt. + + * Der Standardwert ist `Auto`, andere Werte sind ".", "," und "Custom". Die + ersten drei Werte legen `DecimalSeparators` und `BlockSeparators` fest. + * Der Wert von `Auto` setzt diese Einstellungen auf der Grundlage des + Wertes der Voreinstellung "Sprache". Für einige Sprachen, wie + z.B. Spanisch, wird `,` in einigen Ländern und `.` in anderen + verwendet. In diesem Fall ist es am besten, die Sprache so einzustellen, + dass sie auch den Ländercode enthält (z. B. `es-es` oder `es-mx`), um + sicherzustellen, dass der richtige Wert verwendet wird. + +* Schwedisch wurde zu den unterstützten Sprachen hinzugefügt. +* Es wurden weitere Unicode-Zeichen hinzugefügt, um sowohl alle + Unicode-Zeichen, die als "Sm" gekennzeichnet sind, als auch diejenigen mit + einer mathematischen Klasse (mit Ausnahme der Klassen Alphabetic und + Glyph) in den Unicode-Standard aufzunehmen. +* Nachdem ich die Funktionsweise der Voreinstellungen in einer früheren + Version geändert hatte, hatte ich vergessen, `MathRate` und `PauseFactor` + in Zahlen und nicht in Strings zu ändern. +* Fehler in den Braille-Regeln behoben (verpasste Änderung von früher), wo + ein drittes Argument hätte angegeben werden müssen, um zu sagen, dass in + den _Braille_ `definitions.yaml`-Dateien und nicht in den Sprachdateien + nachgeschaut werden soll, wenn der Wert einer Definition gesucht wird. +* Die Verwendung der Datei `definitions.yaml` wurde bereinigt. +* Einige Fehler in der MathML-Bereinigung für "," Dezimaltrennzeichen wurden + behoben. +* Ich habe einen Fehler in der Braille-Hervorhebung gefunden, wenn nichts + hervorgehoben ist (vielleicht passiert das nie, weshalb ich es in der + Praxis nicht gesehen habe?) +* Der "Beschreibungs"-Modus wurde so korrigiert, dass er funktioniert - er + ist immer noch sehr minimal und wahrscheinlich noch nicht nützlich +* Minimale unterstützte Version neu festgelegt + +### Version 0.5.6 +* Ein Dialogfeld für Kopieren als... zum MathCAT-Dialog hinzugefügt (im + Bereich "Navigation"). +* Ein Fehler wurde behoben, bei dem die Sprache beim Wechsel des Sprachstils + auf Englisch zurückgesetzt wurde. +* Fehler bei der Navigation und in Braille behoben +* Einige Probleme mit den ASCIIMath-Abständen wurden behoben. +* Verbesserte Erkennung von chemischen Formeln +* MathCAT wurde auf die neue BANA-Nemeth-Chemie-Spezifikation aktualisiert + (immer noch nur einzeilig und Änderungen der Schriftart/Schriftart für + Sonderfälle werden nicht berücksichtigt) +* Behebung eines Absturzes, wenn Nicht-ASCII-Ziffern (z. B. fette Ziffern) + in Zahlen verwendet werden +* Keine kursiven Indikatoren in Braille-Codes verwenden, wenn die + mathematischen alphanumerischen kursiven Zeichen verwendet werden. +* Einige andere kleinere Fehlerbehebungen, die nicht von Benutzern gemeldet + wurden + +### Version 0.5.0 +* Deutscher LaTeX-Braille-Code hinzugefügt. Im Gegensatz zu anderen + Braille-Codes erzeugt dieser ASCII-Zeichen und verwendet die aktuelle + Braille-Ausgabetabelle, um die Zeichen in Braille zu übersetzen. +* Ein (experimenteller) ASCIIMath-Braille-Code wurde hinzugefügt. Wie der + LaTeX-Braille-Code erzeugt dieser ASCII-Zeichen und verwendet die aktuelle + Braille-Ausgabetabelle, um die Zeichen in Braille zu übersetzen. +* Neue Einstellung "Kopieren als", die das Kopieren als MathML, LaTeX oder + ASCIIMath mit cntl+C unterstützt, wenn der Fokus auf MathML liegt (wie + zuvor). Der aktuell fokussierte Knoten wird kopiert. Hinweis: Diese + Einstellung wird nur in der prefs.yaml-Datei aufgeführt und ist (noch) + nicht im Dialogfeld für die MathCAT-Einstellungen sichtbar. + +### Version 0.4.2 +* Sprachumschaltung behoben, wenn die Stimme wechselt und die + MathCAT-Sprache auf "Auto" eingestellt ist +* Weitere Überprüfungen für $Impairments hinzugefügt, um das Lesen zu + verbessern, wenn es für Blinde nicht gesetzt ist +* Nemeth: Korrektur für "~", wenn es nicht Teil eines Mrows ist +* UEB: Zeichen hinzugefügt, "~"-Abstand korrigiert, wenn Präfix, xor + korrigiert, +* MathML-Bereinigung für akzentuierte Vokale (hauptsächlich für + Vietnamesisch) +* Umfassende Überarbeitung des Codes zum Lesen und Aktualisieren von + Präferenzen mit großer Geschwindigkeitssteigerung -- Hinzufügen der + Präferenz `CheckRuleFiles`, um zu kontrollieren, welche Dateien auf + Aktualisierungen geprüft werden +* Zwei neue Interface-Aufrufe hinzugefügt -- ermöglicht das Setzen der + Navigationsposition des Braille-Cursors (noch nicht Teil von MathCAT) + +### Version 0.3.11 +* Aktualisiert auf Python 3.11 und Überprüfung, ob es mit NVDA 2024.1 + kompatibel ist +* Behebung von Fehlern in Braille in Vietnamesisch und auch in der + Sprachausgabe, vor allem für Chemie. +* Fehlerhafte Braille-Ausgaben korrigiert, wenn Braille-Code und abhängige + Sprache nicht übereinstimmen (insbesondere vietnamesische Braille-Schrift + und vietnamesische Sprache) +* Whitespace-Fehler in HTML innerhalb von Token behoben +* Verbesserung der Erkennung römischer Ziffern + + +### Version 0.3.9 +* Übersetzung für traditionelles Chinesisch hinzugefügt (Dank an Hon-Jang + Yang) +* Fehler beim Navigieren in die Basis eines geskripteten Ausdrucks mit + Klammern behoben +* Die Art und Weise, wie Leerzeichen behandelt werden, wurde erheblich + geändert. Dies betrifft vor allem die Braille-Ausgabe (Leerzeichen und + Erkennung von "Auslassungen"). +* Verbesserte Erkennung von chemischen Formeln +* UEB-Braille-Korrekturen, die sich aus dem Hinzufügen von Chemie-Beispielen + ergeben haben +* UEB-Korrekturen für das Hinzufügen von Hilfsklammern in einigen Fällen + + +### Version 0.3.8 + +Braille: + +* Das Dialogfeld wurde für mehrere Sprachen internationalisiert (vielen Dank + an die Übersetzer!) +* Erstmalige Einführung von CMU - dem Braille-Code, der in spanisch- und + portugiesischsprachigen Ländern verwendet wird +* Behebung einiger UEB-Fehler und Hinzufügen einiger UEB-Zeichen +* Signifikante Verbesserungen in Braille für Vietnamesisch + +Weitere Korrekturen: + +* Der Schieberegler des Dialogs "Relative Rate" hat nun einen Maximalwert + von 100 % (jetzt können nur noch langsamere Raten eingestellt + werden). Außerdem wurden Schrittgrößen hinzugefügt, damit es einfacher + ist, die Rate deutlich zu erhöhen/verringern. +* Fehler mit der eSpeak behoben, der manchmal die Sprache abschnitt, wenn + die relative Rate geändert wurde +* Verbesserungen der vietnamesischen Sprache +* Fehler bei den OneCore-Stimmen behoben, die "a" sagen +* Einige Navigationsfehler behoben, wenn "AutoZoomOut" auf "False" gesetzt + ist (nicht der Standard) +* Die Aktualisierung von Änderungen bei den Sprachen und einigen anderen + Änderungen bei Dialogfeldern wurde korrigiert, so dass sie sofort wirksam + werden, wenn Sie auf "Übernehmen" oder "OK" klicken. +* Die Option "Sprache der Stimme verwenden" wurde hinzugefügt, so dass + MathCAT von Anfang an in der richtigen Sprache spricht (wenn es eine + Übersetzung gibt) +* Mehrere Verbesserungen zur Bereinigung von schlechtem MathML-Code + +### Version 0.3.3 +Diese Version enthält eine Reihe von Fehlerkorrekturen. Die wichtigsten +neuen Funktionen und Fehlerkorrekturen sind: + +* Spanische Übersetzung hinzugefügt (Dank an Noelia Ruiz und María Allo + Roldán) +* Die Navigation wurde so geändert, dass sie um eine Ebene vergrößert + startet. +* Strg+Alt+Pfeiltasten als Möglichkeit zur Navigation in tabellarischen + Strukturen hinzugefügt. Diese Tasten sollten einprägsamer sein, da sie für + die Tabellennavigation in NVDA verwendet werden. +* Ein NVDA-Fehler für eSpeak-Stimmen wurde behoben, der dazu führte, dass + sie langsamer wurden, wenn die relative MathRate langsamer als die + Sprachgeschwindigkeit im Text eingestellt war. +* Ein Problem mit Onecore-Stimmen korrigiert, damit sie auch ein langen + A-Laut sprechen. + +Es gibt viele kleine Verbesserungen für die Sprachausgabe und einige +Fehlerkorrekturen sowohl für Nemeth als auch für UEB. + +Hinweis: Es gibt jetzt eine Option, um den vietnamesischen Braille-Standard +als Braille-Ausgabe zu erhalten. Diese ist noch in Arbeit und +fehleranfällig, um außer zum Testen verwendet zu werden. Die nächste +MathCAT-Version wird eine zuverlässige Implementierung enthalten. + +### Version 0.2.5 +* Weitere Verbesserungen in Chemie +* Korrekturen für Nemeth: + + * Regeln für "Auslassungen" hinzugefügt + * Einige Regeln für englische Sprachindikatoren hinzugefügt + * Weitere Fälle, in denen der Mehrzweck-Indikator benötigt wird, wurden + hinzugefügt + * Korrekturen im Zusammenhang mit Nemeth und Zeichensetzung + +### Version 0.2 +* Viele Fehlerkorrekturen +* Verbesserungen für die Sprachausgabe +* Eine Einstellung zur Steuerung der Dauer von Pausen (funktioniert mit + Änderungen der relativen Sprechgeschwindigkeit für Mathematik) +* Unterstützung bei der Erkennung der chemischen Notation und deren + korrekten Aussprache +* Übersetzungen ins Indonesische und Vietnamesische + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/es/readme.md b/addon/doc/es/readme.md new file mode 100644 index 00000000..0fff3723 --- /dev/null +++ b/addon/doc/es/readme.md @@ -0,0 +1,245 @@ +# MathCAT # + +* Autor: Neil Soiffer +* Compatibilidad con NVDA: 2018.1 o posterior (no probado en versiones + anteriores) +* Descargar [versión estable][1] + +MathCat está diseñado para sustituir eventualmente a MathPlayer, ya que este +último ya no está soportado. MathCat genera voz y braille desde MathML. La +voz producida por MathCat para las matemáticas se mejora con entonación para +que suene más natural. Se puede navegar por la voz con tres modos usando las +mismas órdenes que en MathPlayer. Además, el nodo de navegación se indica en +la pantalla Braille. Se soportan tanto Nemeth como UEB técnico. + +MathCat tiene varias opciones de configuración que controlan la voz, la +navegación y el braille. Muchas de ellas pueden configurarse en el diálogo +de opciones de MathCat (en el menú Preferencias de NVDA). Para más +información sobre estos ajustes, consulta la [documentación de +MathCat](https://nsoiffer.github.io/MathCAT/users.html). La documentación +incluye un enlace a [una tabla que enumera todas las órdenes de navegación +de MathCat](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Nota: MathCat es una biblioteca general para generar voz y braille a partir +de MathML. Es usada por otros proyectos de tecnologías de asistencia además +de NVDA. Para información general sobre el proyecto MathCat, consulta la +[página principal de documentación de +MathCat](https://nsoiffer.github.io/MathCAT). + + +Quién debería usar MathCat: + +* Quien necesite Braille Nemeth de alta calidad (el Nemeth de MathPlayer se + basa en la generación Nemeth de Liblouis, que tiene una importante + cantidad de fallos técnicamente complicados de corregir). +* Aquellos que necesiten braille técnico UEB, CMU (español/portugués), LaTeX + alemán, ASCIIMath, o braille vietnamita +* Quien quiera probar nuevas tecnologías y esté dispuesto a ayudar + informando de fallos +* Quien use Eloquence como voz + +Quién no debería usar MathCat: + +* Cualquiera que use Math Player en idiomas distintos al inglés (existen + traducciones a chino (tradicional), indonesio, vietnamita y español; las + traducciones se irán haciendo en el futuro) y no se sienten cómodos con la + voz en uno de los idiomas soportados. +* Cualquiera que prefiera Access8Math a MathPlayer (por la voz u otras + funciones) + +Las reglas del habla de MathCat no son todavía tan extensas como las de +MathPlayer -- esa puede ser otra razón para seguir con MathPlayer. MathCat +se está usando como un banco de pruebas para MathML 4, que permite a los +autores expresar su intención de tal forma que las notaciones ambiguas se +verbalicen correctamente sin tener que adivinarlas. Me he esperado antes de +añadir muchas reglas, ya que la arquitectura de MathCat se centra en usar e +inferir la intención del autor, y esto aún no está completamente +establecido. + +## Registro de actualización de MathCat + +### Versión 0.6.3 + +* Todos los archivos de idioma y reglas braille se comprimen por carpeta y + se descomprimen a petición. + + * Se ahorran unos 5 mb cuando rules.zip está descomprimido, y ahorrará aún + más según se añadan más idiomas y códigos braille. + * Esto se hace como preparación para incluir MathCat en NVDA 2024.3 + +* Se añade una nueva preferencia de "separador decimal". + + * El valor por defecto es `Automático`, siendo otros valores ".", ",", y + "personalizado". Los primeros tres valores establecen `DecimalSeparators` + y `BlockSeparators`. + * `Automático` establece estas preferencias en función de la preferencia + `language`. En algunos idiomas, como el español, se usa ',' en algunos + países y '.' en otros. En este caso, es mejor configurar el idioma para + que incluya también el código del país (por ejemplo, `es-es` or `es-mx`) + para garantizar que se usa el valor correcto. + +* Se añade el sueco a los idiomas soportados. +* Se añaden más caracteres Unicode para incluir todos los caracteres Unicode + marcados como "Sm" y aquellos con una mathclass (excepto las clases + Alphabetic y Glyph) en el estándar Unicode. +* Tras cambiar el funcionamiento de las preferencias en una versión + anterior, olvidé cambiar `MathRate` y `PauseFactor` para que sean números + en vez de cadenas. +* Corregido un fallo en las reglas braille (un cambio anterior que se + perdió) por el que se debería haber dado un tercer argumento para decir + que se mire en los archivos `definitions.yaml` _Braille_ y no en los de + voz al buscar el valor de una definición. +* Se limpia el uso de `definitions.yaml`. +* Se corrigen algunos fallos en la limpieza de MathML para los separadores + decimales ",". +* Se ha encontrado un fallo en el resaltado braille cuando no hay nada + resaltado (tal vez nunca se dio, ¿por eso no lo vi en la práctica?) +* Corregido el modo "describir" para que funcione. Todavía está al mínimo y + probablemente aún no es útil +* Se corrige la versión mínima soportada + +### Versión 0.5.6 +* Se ha añadido Copiar como... al diálogo de MathCat (en el panel + "Navegación") +* Se corrige un fallo por el que el idioma volvía a ser inglés al cambiar + los estilos de habla. +* Se corrige un fallo con la navegación y el braille +* Se corrigen algunos problemas de espaciado de Asciimath. +* Mejorado el reconocimiento de química +* Se actualiza MathCat a la nueva especificación química BANA Nemeth + (todavía sólo una sola línea y no se manejan los casos especiales de + cambio de estilo/fuente) +* Se corrige un error fatal cuando se usan dígitos no ASCII en números (por + ejemplo, dígitos en negrita) +* No se usan indicadores de cursiva en los códigos braille cuando se usan + los caracteres de cursiva alfanuméricos de matemáticas +* Algunas pequeñas correcciones de las que no informaron los usuarios + +### Versión 0.5.0 +* Se añade código braille LaTeX en alemán. Al contrario que otros códigos + braille, este genera caracteres ASCII y usa la tabla de salida braille + actual para transcribirlos. +* Se añade código braille ASCIIMath (experimental). Al igual que el código + braille LaTeX, genera caracteres ASCII y usa la tabla braille de salida + actual para transcribirlos. +* Se añade la preferencia "CopyAs", que soporta copiar como MathML, LaTeX o + ASCIIMath usando control+c cuando el foco está sobre contenido MathML + (como antes). El nodo que tiene el foco se copia. Nota: esto sólo se lista + en el archivo prefs.yaml y no se expone en el diálogo de preferencias de + MathCat (todavía). + +### Versión 0.4.2 +* Se corrige el cambio de idioma cuando los cambios de voz y el idioma de + MathCat se configuran en "Automático" +* Se añaden más comprobaciones de impedimentos para mejorar la lectura + cuando no está configurada para personas ciegas +* Nemeth: corrección de "~" cuando no es parte de un mrow +* UEB: se añaden caracteres, corrección de espaciado en "~", prefijo if, + prefijo xor, +* Limpieza de MathML en vocales acentuadas (principalmente en vietnamita) +* Reescritura importante del código que lee y actualiza las preferencias con + mucha más celeridad. Se añade la preferencia `CheckRuleFiles` para + controlar para qué archivos se buscan actualizaciones +* Se añaden dos nuevas llamadas de interfaz que permiten configurar la + posición de navegación a partir del cursor braille (todavía no forman + parte del complemento MathCat) + +### Versión 0.3.11 +* Se actualiza a Python 3.11 y se comprueba el funcionamiento en NVDA 2024.1 +* Se corrigen fallos en el braille y la voz vietnamitas, en su mayoría de + química. +* Se corrige un fallo en braille cuando el código braille y el idioma + dependiente no coinciden (concretamente, braille vietnamita y voz + vietnamita) +* Se corrige un fallo con los espacios en blanco en HTML dentro de los + tokens +* Se mejora la detección de números romanos + + +### Versión 0.3.9 +* Se añade traducción al chino tradicional (gracias a Hon-Jang Yang) +* Se corrige un fallo con la navegación en la base de una expresión escrita + que tiene paréntesis +* Ha cambiado significativamente el manejo de los espacios en + blanco. Principalmente afecta a la salida braille (espacios y detección de + "omisión"). +* Mejorado el reconocimiento de química +* Correcciones en el braille UEB que aparecieron al añadir ejemplos de + química +* Correcciones en UEB al añadir paréntesis auxiliares en algunos casos + + +### Versión 0.3.8 + +Braille: + +* Se ha internacionalizado el diálogo en varios idiomas (¡Muchas gracias a + los traductores!) +* Implementación inicial de CMU, código braille usado en los países de habla + hispana y portuguesa +* Se corrigen algunos fallos de UEB y se añaden algunos caracteres para UEB +* Mejoras importantes al braille vietnamita + +Otras correcciones: + +* Se cambia el deslizador del diálogo de velocidad relativa para que tenga + un valor máximo del 100% (ahora sólo permite configurar velocidades + menores). También se añaden pasos de tamaño para que sea más fácil + aumentar o disminuir la velocidad significativamente. +* Se corrige un problema con Espeak que hacía que la voz se entrecortara al + cambiar su velocidad relativa +* Mejoras en la voz vietnamita +* Corregido un fallo con las voces OneCore, que decían "a" +* Se corrigen algunos fallos de navegación cuando `AutoZoomOut` es falso (no + el que hay por defecto) +* Se corrigen algunos cambios en los diálogos y en el idioma para que tengan + efecto inmediato cuando se pulse "Aplicar" o "Aceptar". +* Se añade una opción "Usar idioma de la voz" para que MathCat hable en el + idioma correcto desde el principio (si hay traducción) +* Varias mejoras al limpiar código MathML pobre + +### Versión 0.3.3 +Esta versión contiene bastantes correcciones de fallos. Las principales +funciones nuevas y correcciones son: + +* Se ha añadido traducción al español (gracias a Noelia Ruiz y María Allo + Roldán) +* Se modifica la navegación, de tal forma que ahora comienza ampliada en un + nivel +* Se añade control+alt+flechas como mecanismo para navegar por estructuras + tabulares. Estas teclas deberían ser más fáciles de memorizar, ya que se + usan para navegar por tablas en NVDA. +* Corregido un fallo con las voces de Espeak que provocaba que se + ralentizaran cuando la velocidad relativa matemática se configuraba para + ser más lenta que la velocidad de verbalización del texto. +* Solucionado un problema con las voces OneCore, que deberían poder + verbalizar el sonido de la 'a' larga. + +Hay montones de pequeños retoques en la voz y algunos fallos corregidos en +Nemeth y UEB. + +Nota: ahora hay una opción para obtener braille estándar vietnamita en la +salida braille. Este trabajo todavía está en progreso y tiene demasiados +fallos como para usarlo para otra cosa que no sean pruebas. Espero que la +próxima versión de MathCat contenga una implementación fiable. + +### Versión 0.2.5 +* Más mejoras en química +* Correcciones en Nemeth: + + * Añadidas reglas de "omisión" + * Añadidas algunas reglas para los indicadores en inglés + * Añadidos más casos donde es necesario el indicador Multipropósito + * Correcciones relacionadas con Nemeth y la puntuación + +### Versión 0.2 +* Muchos fallos corregidos +* Mejoras en la voz +* Una opción en las preferencias para controlar la duración de las pausas + (funciona con los cambios relativos de velocidad de la voz en matemáticas) +* Soporte para reconocer notación química y verbalizarla adecuadamente +* Traducciones al indonesio y al vietnamita + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/fi/readme.md b/addon/doc/fi/readme.md new file mode 100644 index 00000000..6fdebfc5 --- /dev/null +++ b/addon/doc/fi/readme.md @@ -0,0 +1,246 @@ +# MathCAT # + +* Tekijä: Neil Soiffer +* Yhteensopivuus: NVDA 2018.1 tai uudempi (ei testattu aiemmissa versioissa) +* Lataa [vakaa versio][1] + +MathCAT is designed to eventually replace MathPlayer because MathPlayer is +no longer supported. MathCAT generates speech and braille from MathML. The +speech for math produced by MathCAT is enhanced with prosody so that it +sounds more natural. The speech can be navigated in three modes using the +same commands as MathPlayer. In addition, the navigation node is indicated +on a braille display. Both Nemeth and UEB technical are supported. + +MathCATissa on useita asetusvaihtoehtoja, jotka ohjaavat puhetta, +navigointia ja pistekirjoitusta. Monia näistä voidaan määrittää MathCATin +asetusvalintaikkunassa (löytyy NVDA:n Asetukset-valikosta). Lisätietoja saat +[MathCATin +dokumentaatiosta](https://nsoiffer.github.io/MathCAT/users.html). +Dokumentaatiossa on linkki [taulukkoon, jossa luetellaan kaikki MathCATin +navigointikomennot](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +MathCAT on yleiskirjasto, joka tuottaa puhetta ja pistekirjoitusta +MathML-muodosta. Sitä käytetään NVDA:n lisäksi myös muissa +apuvälineteknologisissa projekteissa. Yleistä tietoa MathCAT-projektista on +[MathCATin dokumentaatiosivulla](https://nsoiffer.github.io/MathCAT). + + +Kenen tulisi käyttää MathCATia: + +* Käyttäjien, jotka tarvitsevat korkealaatuista Nemeth-pistekirjoitusta + (MathPlayerin Nemeth perustuu liblouisin Nemeth-sukupolveen, jossa on + useita merkittäviä bugeja, joita on teknisesti vaikea korjata). +* Those who need UEB technical braille, CMU (Spanish/Portuguese), German + LaTeX, ASCIIMath, or Vietnamese braille +* Käyttäjien, jotka haluavat kokeilla uusinta teknologiaa ja ovat valmiita + auttamaan ilmoittamalla bugeista +* Käyttäjien, jotka käyttävät puhesyntetisaattorina Eloquencea + +Kenen EI tulisi käyttää MathCATia: + +* Anyone who uses MathPlayer with a language that is not yet supported by + MathCAT (translations exist for Chinese (Traditional), Spanish, Indonesian + and Vietnamese; translations will be coming in the future) and are not + comfortable with speech in one of the supported languages. +* Kaikkien, jotka pitävät enemmän Access8Mathista kuin MathPlayerista + (puheen tai muiden ominaisuuksien vuoksi). + +MathCATin puhesäännöt eivät ole vielä yhtä kattavia kuin MathPlayerissa +(tämä voi olla toinen syy MathPlayerissa pysymiseen). MathCATia käytetään +MathML 4 -ideoiden testausalustana, jonka avulla tekijät voivat ilmaista +tarkoituksensa, jotta moniselitteiset merkinnät voidaan puhua oikein eikä +arvailla. Olen viivytellyt liian monien sääntöjen lisäämistä, koska +MathCATin arkkitehtuuri keskittyy käyttöön ja tekijän tarkoituksen +päättelemiseen, eikä niitä ole vielä täysin ratkaistu. + +## MathCATin päivitysloki + +### Version 0.6.3 + +* All the language and braille Rule files are zipped up per directory and + unzipped on demand. + + * This currently saves ~5mb when Rules.zip is unzipped, and will save even + more as more languages and braille codes are added. + * This is in preparation for MathCAT being built into NVDA 2024.3 + +* Added new preference `DecimalSeparator`. + + * The default value is `Auto`, with other values being ".", ",", and + "Custom". The first three values set `DecimalSeparators` and + `BlockSeparators`. + * `Auto` sets those preferences based on the value of the `Language` + pref. For some language such as Spanish, `,` is used in some countries + and `.` is used in others. In this case, it is best to set the language + to also include the country code (e.g, `es-es` or `es-mx`) to ensure the + right value is used. + +* Added Swedish to supported languages. +* Added more Unicode chars to include both all Unicode chars marked as "Sm" + and those with a mathclass (except Alphabetic and Glyph classes) in the + Unicode standard. +* After changing how prefs work in a previous version, I forgot to change + `MathRate` and `PauseFactor` to be numbers, not strings. +* Fixed bug in the braille Rules (missed change from earlier) where a third + argument should have been given to say to look in the _Braille_ + `definitions.yaml` files and not the speech ones when looking up the value + of a definition. +* Cleaned up use of `definitions.yaml`. +* Fixed some bugs in the MathML cleanup for "," decimal separators. +* Found a bug in braille highlighting when nothing is highlighted (maybe + never happens which is why I didn't see it in practice?) +* Fixed "Describe" mode so that it works -- it is still very minimal and + probably not useful yet +* Fixed minimum supported version + +### Version 0.5.6 +* Added Copy As... to the MathCAT dialog (in the "Navagation" pane). +* Fixed a bug where the language reverted to English when changing speech + styles. +* Fixed a bug with navigation and braille +* Fixed some Asciimath spacing problems. +* Improved chemistry recognition +* Updated MathCAT to new BANA Nemeth chemistry spec (still only single line + and special case style/font changes not handled) +* Fix a crash when non-ASCII digits (e.g., bold digits) are used in numbers +* Don't use italic indicators in braille codes when the math alphanumeric + italic chars are used +* Some other smaller bug fixes that weren't reported by users + +### Versio 0.5.0 +* Lisätty saksalainen LaTeX-pistekirjoitusmerkistö. Toisin kuin muut + pistekirjoitusmerkistöt, tämä tuottaa ASCII-merkkejä ja käyttää + senhetkistä pistetulostustaulukkoa merkkien kääntämiseen + pistekirjoitukseksi. +* Lisätty kokeellinen ASCIIMath-pistekirjoitusmerkistö. Kuten + LaTeX-merkistö, tämäkin tuottaa ASCII-merkkejä ja käyttää senhetkistä + pistetulostustaulukkoa merkkien kääntämiseen pistekirjoitukseksi. +* Lisätty "CopyAs"-asetus, joka tukee kopiointia MathML-, LaTeX- tai + ASCIIMath-muodossa Ctrl+C-näppäinkomentoa käyttäen kohdistuksen ollessa + MathML:ssä (kuten aiemmin). Aktiivisena oleva solmu kopioidaan. Huom: + Asetus on toistaiseksi muutettavissa vain prefs.yaml-tiedostossa eikä sitä + vielä näytetä MathCATin asetusvalintaikkunassa. + +### Versio 0.4.2 +* Korjattu kielen vaihtaminen kun puheääni vaihtuu ja MathCATin kielenä on + "Automaattinen" +* Lisätty tarkistuksia $Impairments-muuttujalle lukemisen parantamiseksi + silloin, kun se on määritetty muita kuin sokeita varten. +* Nemeth: Korjaus "~":lle, kun se ei ole osa mrow:ta +* UEB: Merkkejä lisätty, "~":n välilyönnin korjaus jos etuliitteenä, + xor-korjaus +* MathML:n siistiminen korostetuille vokaaleille (pääasiassa + vietnamilaisille). +* Suuri osa asetusten luku- ja päivityskoodista kirjoitettu uudestaan, mikä + nopeutti koodia huomattavasti – lisätty ``CheckRuleFiles``-asetus + säätelemään, mitkä tiedostot tarkistetaan päivitysten varalta +* Lisätty kaksi uutta rajapintakutsua: mahdollistaa navigointisijainnin + asettamisen pistekohdistimesta (ei vielä osa MathCAT-lisäosaa) + +### Versio 0.3.11 +* Päivitetty python versioksi 3.11 ja varmistettu toimivuus NVDA 2024.1:n + kanssa +* Korjattu pääasiassa kemiaan liittyviä bugeja vietnaminkielisessä + pistekirjoituksessa ja puheessa. +* Korjattu rikkoutuva pistekirjoitus, kun pistemerkistö ja siihen liittyvä + kieli eivät täsmää (erityisesti Vietnamilainen pistekirjoitus ja puhe) +* Korjattu tyhjän tilan bugi HTML:n sisällä olevissa merkeissä +* Paranneltu roomalaisten numeroiden tunnistusta + + +### Versio 0.3.9 +* Lisätty perinteisen kiinan käännös (kiitos Hon-Jang Yangille) +* Korjattu bugi navigoitaessa skriptatun lausekkeen sulkeita sisältävään + perusosaan +* Muutettu merkittävästi tapaa, jolla tyhjätilaa käsitellään. Tämä vaikuttaa + pääasiassa pistekirjoitustulosteeseen (välilyönnit ja "jättämisen" + havaitseminen). +* Kemiallisten merkintöjen tunnistusta paranneltu +* Tehty UEB-pistekirjoituksen korjauksia, joiden tarve ilmeni kemiallisten + kaavojen esimerkkejä lisättäessä +* Korjauksia UEB:lle apusulkeita lisättäessä joissakin tapauksissa + + +### Versio 0.3.8 + +Pistekirjoitus: + +* Valintaikkuna on käännetty useille kielille (kiitokset kääntäjille!) +* Alustava toteutus Espanjassa ja useissa portugalinkielisissä maissa + käytettävälle CMU-pistekirjoitusmerkistölle +* Korjattu UEB:n bugeja ja lisätty merkkejä +* Merkittäviä parannuksia vietnamilaiseen pistekirjoitukseen + +Muita korjauksia: + +* Muutettu suhteellisen nopeuden liukusäädintä siten, että sen enimmäisarvo + on 100 % (sallii nyt vain hitaampien nopeuksien asettamisen). Lisätty myös + askelkokoja, jotta nopeuden merkittävä nostaminen/laskeminen olisi + helpompaa. +* Korjattu eSpeakin bugi, joka katkaisi toisinaan puheen, kun suhteellista + nopeutta muutettiin +* Parannuksia vietnaminkieliseen puheeseen +* Korjattu OneCore-äänien bugi, joka sai ne sanomaan "a" +* Korjattu navigoinnin bugeja, joita esiintyi kun automaattinen + loitontaminen ei ole käytössä (ei oletuksena) +* Korjattu kielen vaihtamisen ja muutaman muun valintaikkunan muutosten + päivittämistä siten, että muutokset tulevat voimaan heti Käytä- tai + OK-painiketta painettaessa. +* Lisätty "Käytä äänen kieltä" -asetus, jotta MathCAT puhuu oletusarvoisesti + oikealla kielellä (jos käännös on saatavilla) +* Useita parannuksia huonon MathML-koodin siistimiseksi + +### Versio 0.3.3 +Tähän versioon on tehty useita bugikorjauksia. Merkittävimpiä uusia +ominaisuuksia ja bugikorjauksia ovat: + +* Lisätty espanjankielinen käännös (kiitos Noelia Ruizille ja María Allo + Roldánille) +* Muutettu navigointia siten, että se alkaa zoomattuna yhden tason lähemmäs +* Lisätty Ctrl+Alt+Nuolinäppäimet taulukkomaisissa rakenteissa + navigoimiseen. Näiden näppäinyhdistelmien pitäisi olla helpommin + muistettavat, koska niitä käytetään NVDA:ssa taulukkonavigointiin. +* Korjattu eSpeak-äänien NVDA-bugi, joka aiheutti niiden hidastumisen, kun + suhteellinen matematiikkapuheen nopeus oli asetettu tekstipuheen nopeutta + hitaammaksi. +* Tähän on tehty useita bugikorjauksia. Merkittävimmät uudet ominaisuudet ja + bugikorjaukset ovat: * Lisätty espanjankielinen käännös (kiitos Noelia + Ruizille ja María Allo Roldánille) * Muutettu navigointia siten, että se + alkaa zoomattuna yhden tason lähemmäs * Lisätty Ctrl+Alt+Nuolinäppäimet + taulukkomaisissa rakenteissa navigoimiseen. Näiden näppäinyhdistelmien + pitäisi olla helpommin muistettavat, koska niitä käytetään NVDA:ssa + taulukkonavigointiin. * Korjattu eSpeak-äänien NVDA-bugi, joka aiheutti + niiden hidastumisen, kun suhteellinen matematiikkapuheen nopeus oli + asetettu tekstipuheen nopeutta hitaammaksi. * Korjattu OneCore-ääniä + koskeva ongelma, jotta ne lausuisivat pitkän a-äänteen. + +Paljon pieniä hienosäätöjä puheeseen ja bugikorjauksia sekä Nemethiin että +UEB:hen. + +Huom: Nyt on käytettävissä vaihtoehto, jolla saa vietnamilaisen +pistekirjoitusstandardin pistekirjoituksen tuottamiseen. Tämä on edelleen +keskeneräinen ja liian virhealtis käytettäväksi muuhun kuin +testaamiseen. Seuraavassa MathCATin versiossa pitäisi jo olla luotettava +toteutus. + +### Versio 0.2.5 +* Lisää parannuksia kemiallisiin merkintöihin +* Korjauksia Nemeth-merkintöihin: + + * Added "omission" rules + * Added some rules for English Language Indicators + * Added more cases where the Mulitpurpose indicator is needed + * Fixes related to Nemeth and punctuation + +### Versio 0.2 +* Paljon bugikorjauksia +* Parannuksia puheeseen +* Asetus tauon keston säätämiseen (toimii matematiikkapuheen suhteellisen + nopeuden muutosten kanssa) +* Tuki kemiallisten merkintöjen tunnistamiselle ja niiden asianmukaiselle + puhumiselle +* Käännökset indonesiaksi ja vietnamiksi + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/fr/readme.md b/addon/doc/fr/readme.md new file mode 100644 index 00000000..74ae189b --- /dev/null +++ b/addon/doc/fr/readme.md @@ -0,0 +1,258 @@ +# MathCAT # + +* Auteur : Neil Soiffer +* Compatibilité NVDA : 2018.1 ou version ultérieure (non testée dans les + versions antérieures) +* Télécharger [version stable][1] + +MathCAT est conçu pour finalement remplacer MathPlayer, car ce dernier n'est +plus pris en charge. MathCAT génère la parole et le braille à partir de +MathML. La parole produite par MathCAT pour les mathématiques est améliorée +avec intonation afin qu'elle semble plus naturelle. Vous pouvez naviguer +dans la parole de trois façons en utilisant les mêmes commandes que dans +MathPlayer. De plus, le nœud de navigation est indiqué sur l'afficheur +braille. les codes Nemeth et UEB technique sont pris en charge. + +MathCAT a un certain nombre d'options de configuration qui contrôlent la +parole, la navigation et le braille. Beaucoup d'entre eux peuvent être +définis dans la boîte de dialogue des paramètres MathCAT (qui se trouve dans +le menu Préférences de NVDA). Pour plus d'informations sur ces paramètres, +consultez la [documentation +MathCAT](https://nsoiffer.github.io/mathcat/users.html). La documentation +comprend un lien vers [une table répertoriant toutes les commandes de +navigation dans +MathCAT](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Remarque : MathCAT est une bibliothèque générale pour générer la parole et +le braille à partir de MathML. Il est utilisé par d'autres dans des projets +en plus de NVDA. Pour plus d'informations sur le projet MathCAT en général, +consultez la principale [page de documentation de +MathCAT](https://nsoiffer.github.io/MathCAT). + + +Qui devrait utiliser MathCAT : + +* Ceux qui ont besoin de Nemeth Braille de haute qualité (Nemeth de + MathPlayer est basé sur la génération Nemeth de liblouis qui a un certain + nombre de bogues importants qui sont techniquement difficiles à corriger). +* Ceux qui ont besoin du braille technique UEB, de CMU (espagnol/portugais), + du LaTeX allemand, d'ASCIIMath ou du braille vietnamien +* Ceux qui veulent essayer les dernières technologies et sont prêts à aider + en signalant des bogues +* Ceux qui utilisent Eloquence comme voix + +Qui ne devrait pas utiliser MathCAT : + +* Toute personne utilisant MathPlayer avec une langue pas encore prise en + charge par Mathcat (des traductions existent pour le chinois + (traditionnel), l'espagnol, l'indonésien et le vietnamien; d'autres + traductions doivent arriver dans un futur proche) et qui ne se sent pas à + l'aise avec la parole dans les langues prises en charge. +* Quiconque préfère Access8Math à MathPlayer (pour la parole ou d'autres + fonctionnalités) + +Les règles de la parole de MathCAT ne sont pas encore aussi étendues que +celles de MathPlayer - cela peut être une autre raison de continuer avec +MathPlayer. MathCAT est utilisé comme banque de test pour MathML 4, ce qui +permet aux auteurs d'exprimer leur intention afin que les notations ambiguës +soient verbalisées correctement sans avoir à les deviner. J'ai attendu avant +d'ajouter de nombreuses règles, car l'architecture de MathCAT se concentre +sur l'utilisation et la déduction de l'intention de l'auteur, et ce n'est +pas encore complètement établi. + +## Mise à jour du Journal de MathCAT + +### Version 0.6.3 + +* Tous les fichiers de règles de langue et de braille sont zippés par + répertoire et décompressés à la demande. + + * Cela économise actuellement environ 5 Mo lorsque les règles.zip sont + décompressées et économisera encore plus lorsque plus de langues et de + codes braille seront ajoutés. + * Ceci est en préparation pour l'intégration de Mathcat dans NVDA 2024.3 + +* Ajout d'une nouvelle préférence `Decimalseparator`. + + * La valeur par défaut est `Auto`, les autres valeurs étant ".", "," et + "Custom". Les trois premières valeurs définissent `Decimalseparators` et + `Blockseparators`. + * `Auto` définit ces préférences en fonction de la valeur de pref + `Language`. Pour certaines langue comme l'espagnol, `,`,est utilisée dans + certains pays et `.` est utilisée dans d'autres. Dans ce cas, il est + préférable de définir la langue pour inclure également le code du pays + (par exemple, `es-es` ou `es-mx`) pour s'assurer que la bonne valeur est + utilisée. + +* Ajout du suédois aux langues prises en charge. +* Ajout de nouveaux caractères Unicode, incluant tous ceux marqués comme + "Sm" ainsi que ceux ayant une classe mathématique (à l'exception des + classes Alphabetic et Glyph) dans le standard Unicode. +* Après avoir changé le fonctionnement des préfes dans une version + précédente, j'ai oublié de modifier `Mathrate` et `Pausefactor` afin + qu'ils soient des nombres, pas des chaînes. +* Correction d'un bug dans les règles braille (modification oubliée + précédemment) où un troisième argument aurait dû être fourni pour indiquer + de rechercher dans les fichiers _Braille_ `definitions.yaml` et non dans + ceux de la parole lors de la recherche de la valeur d'une définition. +* Nettoyage de l'utilisation de `definitions.yaml`. +* Correction de bugs dans le nettoyage du MathML pour les séparateurs + décimaux ",". +* Correction d’un bug dans la mise en évidence braille lorsque rien n’est + mis en évidence (ce cas ne se produit peut-être jamais en pratique, ce qui + expliquerait pourquoi il n’avait pas été détecté). +* Correction du mode "Décrire" pour qu'il fonctionne -- il est encore très + minimaliste et probablement pas encore utile +* Correction de la version minimale prise en charge + +### Version 0.5.6 +* Ajout de Copier en tant que... au dialogue MathCAT (dans le volet + "Navigation"). +* Correction d'un bug où la langue retournait à l'anglais lors de la + modification des styles de parole. +* Correction d'un bug relatif à la navigation et au braille +* Correction de problèmes d'espacement Asciimath. +* Reconnaissance améliorée de la chimie +* Mise à jour de Mathcat pour les nouvelles spécifications de chimie Bana + Nemeth (toujours seulement pour les lignes simples, cas spéciaux de + changement de style ou de police non pris en charge) +* Correction d'un plantage lorsque des chiffres non ASCII (par exemple, des + chiffres en gras) sont utilisés dans les nombres +* Pas d'utilisation d'indicateurs italiques dans les codes braille lorsque + les caractères italiques alphanumériques mathématiques sont utilisés +* Quelques autres corrections de bogues plus petites qui n'ont pas été + rapportées par les utilisateurs + +### Version 0.5.0 +* Ajout du code braille LaTeX allemand. Contrairement à d'autres codes + braille, celui-ci génère des caractères ASCII et utilise la table de + sortie braille actuelle pour traduire les caractères en braille. +* Ajout du code braille ASCIIMath (expérimental). Comme le code braille + LaTeX, celui-ci génère des caractères ASCII et utilise la table de sortie + braille actuelle pour traduire les caractères en braille. +* Ajout de la préférence "CopyAs" qui prend en charge la copie au format + MathML, LaTeX ou ASCIIMath en utilisant contrôle+C lorsque le focus sur du + MathML (comme auparavant). Le nœud ayant le focus est copié. Remarque : + cette option n'est listée que dans le fichier prefs.yaml et n'est pas + (encore) exposé dans la boîte de dialogue Préférences MathCAT. + +### Version 0.4.2 +* Correctif pour le changement de langue lorsque la voix change et que la + langue MathCAT est "Auto" +* Ajout de plus de contrôles de $Impairments afin d'améliorer la lecture + lorsqu'elle n'est pas définie pour les personnes aveugles +* Nemeth : correction de "~" lorsqu'il ne fait pas partie d'un mrow +* UEB : ajouts de caractères, correction de l'espacement de "~" avec un + préfixe, correction de xor, +* Nettoyage MathML pour les voyelles accentuées (principalement pour le + vietnamien) +* Réécriture majeure du code de lecture/mise à jour des préférences avec une + grande accélération -- ajout de la préférence `CheckRuleFiles` pour + contrôler quels fichiers sont vérifiés pour les mises à jour +* Ajout de deux nouveaux appels d'interface -- permet de définir + l'emplacement de navigation à partir du curseur braille (ne fait pas + encore partie de l'extension MathCAT) + +### Version 0.3.11 +* Mise à niveau vers python 3.11 et vérification du fonctionnement avec NVDA + 2024.1 +* Correction de bugs dans le braille vietnamien et également dans la parole, + principalement pour la chimie. +* Correction du dysfonctionnement du braille lorsque le code braille et la + langue dépendante ne correspondent pas (en particulier le braille + vietnamien et la parole vietnamienne) +* Correction d'un bug d'espacement en HTML à l'intérieur des élements +* Amélioration de la détection des chiffres romains + + +### Version 0.3.9 +* Ajout de la traduction en chinois traditionnel (remerciements à Hon-Jang + Yang) +* Correction d'un bug lors de la navigation dans la base d'une expression + scriptée comportant des parenthèses +* Modification significative de la façon dont les espaces sont gérés. Cela + affecte principalement la sortie braille (détection des espaces et des + "omissions"). +* Reconnaissance améliorée de la chimie +* Corrections du braille UEB résultant de l'ajout d'exemples de chimie +* Corrections UEB pour l'ajout de parenthèses auxiliaires dans certains cas + + +### Version 0.3.8 + +Braille : + +* Les dialogues ont été internationalisé pour plusieurs langues (un grand + merci aux traducteurs !) +* Implémentation initiale du CMU -- le code braille utilisé dans les pays + hispanophones et lusophones +* Correction de quelques bugs de l'UEB et ajout de quelques caractères pour + l'UEB +* Améliorations significatives du braille Vietnamien + +Autres correctifs : + +* Modifiez le curseur de la boîte de dialogue de débit relatif pour avoir + une valeur maximale de 100 % (permet désormais uniquement de définir des + débits plus lents). En outre, des tailles de pas ont été ajoutées pour + qu'il soit plus facile d'augmenter/diminuer le débit de manière + significative. +* Correction d'un bug d'eSpeak qui coupait parfois la parole lorsque le + débit relatif était modifié +* Améliorations de la parole en Vietnamien +* Correction d'un bug avec les voix OneCore disant "a" +* Correction de quelques bugs de navigation lorsque `AutoZoomOut` est False + (pas la valeur par défaut) +* Correction de la mise à jour autour des changements de langue et de + certains autres changements de boîte de dialogue afin qu'ils prennent + effet immédiatement après avoir cliqué sur "Appliquer" ou "OK". +* Ajout d'une option "Utiliser la langue de la voix" afin que MathCAT parle + immédiatement dans la bonne langue (s'il existe une traduction) +* Plusieurs améliorations de nettoyage de mauvais code MathML + +### Version 0.3.3 +Cette version contient un certain nombre de corrections de bogues. Les +nouvelles fonctionnalités et correctifs de bogues sont : + +* Ajout de la traduction en espagnol (grâce à Noelia Ruiz et María Allo + Roldán) +* Navigation modifiée afin qu'elle démarre le zoom sur un niveau +* Ajout de ctrl+alt+flèche comme moyen de naviguer sur les structures + tabulaires. Ces touches doivent être plus mémorables car elles sont + utilisées pour la navigation de tableau dans NVDA. +* Travaillé autour du bogue de NVDA pour les voix eSpeak qui les ont amenés + à ralentir lorsque le relatif MathRate a été mis plus lentement que le + débit de la parole de texte. +* Travaillé autour d'un problème de la voix OneCore afin qu'ils parlent le + son long "a". + +Il y a beaucoup de petits ajustements dans la parole et quelques corrections +de bogues pour Nemeth et UEB. + +Remarque : il y a maintenant une option pour obtenir la norme en braille +Vietnamien comme sortie braille. Il s'agit toujours d'un travail en cours et +il y a trop de bogues pour être utilisé autrement que pour les tests. Je +m'attends à ce que la prochaine version de MathCAT contienne une +implémentation fiable. + +### Version 0.2.5 +* Plus d'améliorations chimique +* Correction pour Nemeth : + + * Ajout de règles "omission" + * Ajout de quelques règles pour les indicateurs de langue anglaise + * Ajout de plus de cas où l'indicateur polyvalent est nécessaire + * Correctifs liés au code Nemeth et à la ponctuation + +### Version 0.2 +* Beaucoup de correctifs de bogues +* Améliorations de la parole +* Un paramètre de préférence pour contrôler la durée de la pause (fonctionne + avec le changement du débit relatif de la parole pour les mathématiques) +* Prise en charge de la reconnaissance de la notation chimique et à la + verbaliser de manière appropriée +* Traductions vers indonésien et vietnamien + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/ru/readme.md b/addon/doc/ru/readme.md new file mode 100644 index 00000000..b6e01753 --- /dev/null +++ b/addon/doc/ru/readme.md @@ -0,0 +1,246 @@ +# MathCAT # + +* Автор: Neil Soiffer +* Совместимость с NVDA: 2018.1 или выше (не проверялось в более ранних + версиях) +* Загрузить [стабильную версию][1] + +MathCat предназначен для замены в конечном итоге MathPlayer, поскольку +MathPlayer больше не поддерживается. MathCat генерирует речь и Брайль с +помощью MathML. Речь для math, созданная MathCat, дополнена просодией, чтобы +она звучала более естественно. Навигация по речи может осуществляться в трех +режимах с использованием тех же команд, что и в MathPlayer. Кроме того, +навигационный узел отображается на брайлевском дисплее. Поддерживаются +технические решения Nemeth и UEB. + +В MathCat есть несколько параметров конфигурации, которые управляют речью, +навигацией и Брайлем. Многие из них можно настроить в диалоге настроек +MathCat (находится в меню настроек NVDA). Дополнительную информацию об этих +настройках смотрите в [документации по +MathCat](https://nsoiffer.github.io/MathCAT/users.html). Документация +содержит ссылку на +[таблицу](https://nsoiffer.github.io/MathCAT/nav-commands.html), в которой +перечислены все навигационные команды в MathCat. + +Примечание: MathCat - это общая библиотека для генерации речи и шрифта +Брайля на основе MathML. Она используется в других проектах AT, помимо +NVDA. Информацию о проекте MathCat в целом смотрите на главной [странице +документации MathCat](https://nsoiffer.github.io/MathCAT). + + +Кто должен использовать MathCat: + +* Те, кому нужен высококачественный шрифт Брайля Nemeth (Nemeth от + MathPlayer основан на поколении Nemeth от liblouis, в котором есть ряд + существенных ошибок, которые технически трудно исправить). +* Те, кому нужен технический шрифт Брайля UEB, CMU + (испанский/португальский), немецкий LaTeX, AsciiMath или вьетнамский шрифт + Брайля +* Те, кто хочет опробовать новейшие технологии и готов помочь, сообщая об + ошибках +* Те, кто использует Eloquence в качестве голоса + +Кому НЕ следует использовать MathCat: + +* Любому, кто использует MathPlayer на языке, который еще не поддерживается + MathCat (существуют переводы для китайского (традиционного), испанского, + индонезийского и вьетнамского языков; переводы появятся в будущем), и кому + не нравится речь на одном из поддерживаемых языков. +* Любому, кто предпочитает Access8Math для MathPlayer (для речи или других + функций) + +Правила MathCat для речи еще не так обширны, как правила MathPlayer - это +может быть еще одной причиной придерживаться MathPlayer. MathCat +используется в качестве испытательного стенда для идей для MathML 4, которые +позволяют авторам выражать свои намерения так, чтобы двусмысленные +обозначения можно было произносить правильно, а не угадывать. Я воздержался +от добавления слишком большого количества правил, поскольку архитектура +MathCat сосредоточена на использовании и выводе намерений автора, и они еще +не полностью определены. + +## Журнал обновлений MathCat + +### Версия 0.6.3 + +* Все языковые файлы и файлы правил шрифта Брайля заархивированы по + каталогам и распаковываются по требованию. + + * В настоящее время это экономит ~5 Мб при распаковке Rules.zip и будет + экономить еще больше по мере добавления новых языков и кодов Брайля. + * Это делается в рамках подготовки к внедрению MathCat в NVDA 2024.3 + +* Добавлена новая настройка "DecimalSeparator`. + + * Значением по умолчанию является "Auto", другими значениями являются ".", + "," и "Custom". Первые три значения задают "DecimalSeparators" и + `BlockSeparators`. + * `Автоматически` устанавливает эти настройки на основе значения параметра + `Язык`. Для некоторых языков, таких как испанский, в одних странах + используется `,`, а в других - `.`. В этом случае лучше всего указать + язык, в котором также указан код страны (например, `es-es` или `es-mx`), + чтобы убедиться, что используется правильное значение. + +* Добавлен шведский язык в число поддерживаемых языков. +* Добавлено больше символов Unicode, чтобы включить в стандарт Unicode как + все символы Unicode, помеченные как "Sm", так и те, которые содержат + mathclass (за исключением классов Alphabetic и Glyph). +* После изменения способа работы префиксов в предыдущей версии я забыл + изменить "MathRate" и "PauseFactor" на числа, а не на строки. +* Исправлена ошибка в правилах Брайля (пропущенное изменение по сравнению с + предыдущими версиями), из-за которой при поиске значения определения + должен был быть указан третий аргумент, указывающий на необходимость + поиска в файлах _Braille_ `definitions.yaml`, а не в речевых файлах. +* Исправлено использование "definitions.yaml`. +* Исправлены некоторые ошибки в очистке MathML для десятичных разделителей + ",". +* Обнаружил ошибку в выделении шрифтом Брайля, когда ничего не выделяется + (возможно, этого никогда не происходит, поэтому я не видел этого на + практике?) +* Исправлен режим "Описания", чтобы он работал - он по-прежнему очень + минимален и, вероятно, пока бесполезен +* Исправлена минимальная поддерживаемая версия + +### Версия 0.5.6 +* Добавлено копировать как... в диалоговое окно MathCat (на панели + "Навигация"). +* Исправлена ошибка, из-за которой язык возвращался к английскому при смене + стиля речи. +* Исправлена ошибка с навигацией и Брайлем +* Исправлены некоторые проблемы с интервалом между символами Asciimath. +* Улучшено распознавание химического состава +* Обновлен MathCat до новой спецификации химии BANA Nemeth (по-прежнему не + обработаны изменения стиля/шрифта только в одной строке и в специальном + регистре) +* Исправлена ошибка, возникавшая при использовании в числах цифр, отличных + от ASCII (например, выделенных жирным шрифтом) +* Не использовать курсивные указатели в кодах Брайля, когда используются + математические буквенно-цифровые символы, выделенные курсивом +* Некоторые другие мелкие исправления ошибок, о которых пользователи не + сообщали + +### Версия 0.5.0 +* Добавлен немецкий шрифт Брайля LaTeX. В отличие от других шрифтов Брайля, + этот код генерирует символы ASCII и использует текущую таблицу вывода + шрифта Брайля для перевода символов в шрифт Брайля. +* Добавлен (экспериментально) код азбуки Брайля AsciiMath. Как и в коде + Брайлевского шрифта LaTeX, он генерирует символы ASCII и использует + текущую таблицу вывода Брайля для перевода символов в шрифт Брайля. +* Добавлена опция "CopyAs", которая поддерживает копирование в форматах + MathML, LaTeX или AsciiMath с использованием cntl +C при фокусировке на + MathML (как и раньше). Копируется текущий узел в фокусе. Примечание: это + указано только в настройках.файл yaml и не отображается (пока) в диалоге + настроек MathCat. + +### Версия 0.4.2 +* Исправлено переключение языка при изменении голоса и выборе языка MathCat + в качестве "Автоматического" +* Добавлены дополнительные проверки на наличие нарушений чтения, чтобы + улучшить чтение, когда оно не настроено для слепых +* Nemeth: исправлена ошибка с "~", когда она не является частью mrow +* UEB: добавление символов, исправление интервала "~" в префиксе if, + исправление xor, +* Очистка MathML для гласных с ударением (в основном для вьетнамцев) +* Кардинальная переработка предпочтительного кода чтения / обновления с + большим ускорением - добавлен параметр "CheckRuleFiles" для контроля того, + какие файлы проверяются на наличие обновлений +* Добавлены два новых вызова интерфейса - позволяет устанавливать + местоположение навигатора с помощью Брайлевского курсора (пока не является + частью дополнения MathCat) + +### Версия 0.3.11 +* Обновлен до python 3.11 и проверен на работоспособность с NVDA 2024.1 +* Исправлены ошибки во вьетнамском шрифте Брайля, а также в речи, в основном + по химии. +* Исправлены ошибки в наборе шрифта Брайля, когда код шрифта Брайля и + зависимый язык не совпадают (в частности, вьетнамский шрифт Брайля и + вьетнамская речь) +* Исправлена ошибка с пробелами в HTML внутри токенов +* Улучшено распознавание римских цифр + + +### Версия 0.3.9 +* Добавлен традиционный китайский перевод (спасибо Hon-Jang Yang) +* Исправлена ошибка с переходом к основанию скриптового выражения, + содержащего круглые скобки +* Существенно изменён способ обработки пробелов. В основном это влияет на + вывод по Брайлю (пробелы и обнаружение пропусков). +* Улучшено распознавание химии +* UEB исправляет ошибки, связанные с добавлением примеров по химии +* UEB исправляет ошибки при добавлении вспомогательных скобок в некоторых + случаях + + +### Версия 0.3.8 + +Брайль: + +* Диалог был интернационализирован для нескольких языков (большое спасибо + переводчикам!) +* Первоначальное внедрение CMU - кода Брайля, используемого в испаноязычных + и португалоязычных странах +* Исправлены некоторые ошибки в UEB и добавлены некоторые символы для UEB +* Значительные улучшения во вьетнамском шрифте Брайля + +Другие исправления: + +* Изменён ползунок диалога относительной скорости на максимальное значение + 100% (теперь можно устанавливать только более низкие скорости). Кроме + того, добавлены размеры шага, чтобы было проще значительно повышать / + понижать скорость. +* Исправлена ошибка eSpeak, из-за которой иногда прерывалась речь при + изменении относительной скорости +* Улучшения во вьетнамской речи +* Исправлена ошибка, из-за которой голоса OneCore произносили "a" +* Исправлены некоторые ошибки навигации, когда значение `AutoZoomOut` было + ложным (не по умолчанию) +* Исправлены изменения, связанные с изменением языка и некоторыми другими + изменениями в диалоге, чтобы они вступали в силу немедленно после нажатия + кнопки "Применить" или "ОК". +* Добавлена опция "Использовать язык озвучки", чтобы MathCat сразу говорил + на нужном языке (если есть перевод). +* Несколько улучшений для очистки некачественного кода MathML + +### Версия 0.3.3 +В этом выпуске исправлен ряд ошибок. Основными новыми функциями и +исправлениями ошибок являются: + +* Добавлен перевод на испанский (спасибо Noelia Ruiz и María Allo Roldán) +* Изменена навигация таким образом, что она начинает увеличиваться на один + уровень +* Добавлены клавиши cntrl+alt+стрелка для навигации по табличным + структурам. Эти клавиши должны быть более запоминающимися, поскольку они + используются для навигации по таблицам в NVDA. +* Устранена ошибка NVDA для голосовых сообщений eSpeak, из-за которой они + замедлялись, когда относительная математическая скорость была установлена + ниже скорости текстовой речи. +* Мы решили проблему с голосами OneCore, чтобы они произносили длинный звук + 'a'. + +There are lots of small tweaks to the speech and some bug fixes for both +Nemeth and UEB. + +Note: there is now an option to get Vietnam's braille standard as braille +output. This is still a work in progress and is too buggy to be used other +than for testing. I expect the next MathCAT release will contain a reliable +implementation. + +### Версия 0.2.5 +* Больше улучшений химии +* Fixes for Nemeth: + + * Добавлены правила "пропуска" + * Добавлены некоторые правила для индикаторов английского языка + * Добавлено больше случаев, когда требуется многоцелевой индикатор + * Исправления, связанные с Nemeth и пунктуацией + +### Версия 0.2 +* Множество исправлений ошибок +* Улучшения в речи +* Предпочтительная настройка для управления длительностью паузы (работает с + изменениями относительной скорости речи для математики) +* Поддержка распознавания химических знаков и правильного их произношения +* Переводы на индонезийский и вьетнамский языки + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/tr/readme.md b/addon/doc/tr/readme.md new file mode 100644 index 00000000..da8ecbd3 --- /dev/null +++ b/addon/doc/tr/readme.md @@ -0,0 +1,237 @@ +# MathCAT # + +* Yazar: Neil Soiffer +* NVDA uyumluluğu: 2018.1 veya sonrası (önceki sürümlerde denenmemiştir) +* [Kararlı sürümü indir][1] + +MathCAT, MathPlayer artık desteklenmediğinden, sonunda MathPlayer'ın yerini +alacak şekilde tasarlanmıştır. MathCAT, MathML'den konuşma ve braille +üretir. MathCAT tarafından matematik için üretilen konuşma, daha doğal +görünmesi için prozodi ile zenginleştirilmiştir. Konuşmada MathPlayer ile +aynı komutlar kullanılarak üç modda gezinilebilir. Ayrıca gezinme düğümü bir +braille ekranında gösterilir. Hem Nemeth hem de UEB teknik +desteklenmektedir. + +MathCAT konuşma, Gezinme ve braille'i kontrol eden bir dizi yapılandırma +seçeneğine sahiptir. Bunların çoğu MathCAT ayarlar iletişim kutusunda +ayarlanabilir (NVDA Tercihler menüsünde bulunur). Bu ayarlar hakkında daha +fazla bilgi için [MathCAT belgelerine] +(https://nsoiffer.github.io/MathCAT/users.html) bakın. Belgeler +[MathCAT'teki tüm gezinme komutlarını listeleyen bir +tablo](https://nsoiffer.github.io/MathCAT/nav-commands.html) için bir +bağlantı içerir. + +Not: MathCAT, MathML'den konuşma ve braille oluşturmaya yönelik genel bir +kitaplıktır. NVDA dışında diğer AT projeleri tarafından kullanılır. Genel +olarak MathCAT projesi hakkında bilgi için ana [MathCAT Dokümantasyon +sayfasına](https://nsoiffer.github.io/MathCAT) bakın. + + +MathCAT'ı kimler kullanmalı: + +* Yüksek kaliteli Nemeth braille'e ihtiyaç duyanlar (MathPlayer'ın Nemeth'i, + teknik olarak düzeltilmesi zor olan bir dizi önemli hataya sahip olan + liblouis'in Nemeth nesline dayanmaktadır). +* UEB teknik braille, CMU (İspanyolca/Portekizce), Almanca LaTeX, ASCIIMath + veya Vietnamca braille'e ihtiyaç duyanlar +* En son teknolojiyi denemek isteyenler ve hataları bildirerek yardım etmeye + istekli olanlar +* Eloquence sesini kullananlar + +MathCAT'ı kimler KULLANMAMALIDIR: + +* MathPlayer'ı henüz MathCAT tarafından desteklenmeyen bir dille kullanan + (Çince (Geleneksel), İspanyolca, Endonezce ve Vietnamca için çeviriler + mevcuttur; çeviriler gelecekte gelecektir) ve desteklenen dillerden + birinde konuşma konusunda rahat olmayan herkes. +* Access8Math'i MathPlayer'a tercih eden herkes (konuşma veya diğer + özellikler için) + +MathCAT'in konuşma kuralları henüz MathPlayer'ın kuralları kadar kapsamlı +değil -- bu, MathPlayer'a bağlı kalmanın başka bir nedeni olabilir. MathCAT, +yazarların niyetlerini ifade etmelerine izin veren MathML 4 fikirleri için +bir test ortamı olarak kullanılıyor, böylece belirsiz gösterimler doğru bir +şekilde konuşulabilir ve tahmin edilemez. MathCAT'in mimarisi, yazar +niyetini kullanma ve çıkarım yapma etrafında toplandığından ve bunlar henüz +tam olarak çözülmediğinden, çok fazla kural eklemeyi erteledim. + +## MathCAT Güncelleme Günlüğü + +### Sürüm 0.6.3 + +* Tüm dil ve Braille Kural dosyaları dizin bazında sıkıştırılır ve talep + üzerine açılır. + + * Bu, şu anda Rules.zip dosyası açıldığında yaklaşık 5 MB tasarruf + sağlar. Daha fazla dil ve braille kodu eklendikçe daha da fazla tasarruf + sağlayacaktır. + * Bu, MathCAT'in NVDA 2024.3'e dahili olarak eklenmesine hazırlık + niteliğindedir + +* Yeni tercih 'Ondalık Ayırıcı' eklendi. + + * Varsayılan değer "Otomatik" olup diğer değerler ".", "," ve + "Özel"dir. İlk üç değer `Ondalık Ayırıcılar` ve `Blok Ayırıcılar`ı + ayarlar. + * 'Otomatik', bu tercihleri ​​'Dil' tercihinin değerine göre + ayarlar. İspanyolca gibi bazı diller için bazı ülkelerde `,`, bazılarında + ise `.` kullanılır. Bu durumda, doğru değerin kullanıldığından emin olmak + için dili ülke kodunu da içerecek şekilde ayarlamak (ör. "es-es" veya + "es-mx") en iyisidir. + +* Desteklenen dillere İsveççe eklendi. +* Unicode standardına hem "Sm" olarak işaretlenen tüm Unicode karakterlerini + hem de matematik sınıfına sahip olanları (Alfabetik ve Glif sınıfları + hariç) dahil etmek için daha fazla Unicode karakteri eklendi. +* Önceki bir sürümde tercihlerin çalışma şeklini değiştirdikten sonra, + 'MathRate' ve 'PauseFactor'ı dize olarak değil sayı olarak değiştirmeyi + unuttum. +* Bir tanımın değerini ararken konuşma dosyalarına değil, _Braille_ + `definitions.yaml` dosyalarına bakmak için üçüncü bir argümanın + verilmesinin gerektiği Braille Kurallarında (önceki değişikliklerde + kaçırılan) hata düzeltildi. +* 'definitions.yaml' kullanımı temizlendi. +* ``, ondalık ayırıcılar için MathML temizliğindeki bazı hatalar düzeltildi. +* Hiçbir şey vurgulanmadığında braille vurgulamada bir hata buldum (belki de + hiçbir zaman gerçekleşmez, bu yüzden bunu pratikte görmedim?) +* "Açıklama" modu çalışacak şekilde düzeltildi; hala çok az düzeyde ve + muhtemelen henüz kullanışlı değil +* Desteklenen minimum sürüm düzeltildi + +### Sürüm 0.5.6 +* MathCAT iletişim kutusuna ("Gezinme" bölmesinde) Farklı + Kopyala... eklendi. +* Konuşma stillerini değiştirirken dilin İngilizceye dönmesine neden olan + bir hata düzeltildi. +* Gezinme ve braille ile ilgili bir hata düzeltildi +* Bazı Asciimath aralık sorunları düzeltildi. +* Geliştirilmiş kimya tanıma +* MathCAT yeni BANA Nemeth kimya spesifikasyonuna güncellendi (hala yalnızca + tek satır ve özel durum stili/yazı tipi değişiklikleri işlenmedi) +* Sayılarda ASCII olmayan rakamlar (ör. kalın rakamlar) kullanıldığında + oluşan çökme düzeltildi +* Matematik alfanümerik italik karakterler kullanıldığında, braille + kodlarında italik göstergeler kullanmayın +* Kullanıcılar tarafından bildirilmeyen diğer bazı küçük hata düzeltmeleri + +### Sürüm 0.5.0 +* Almanca LaTeX braille kodu eklendi. Diğer braille kodlarından farklı + olarak bu, ASCII karakterleri oluşturur ve karakterleri braille'e çevirmek + için mevcut braille çıktı tablosunu kullanır. +* ASCIIMath braille kodu eklendi. (Deneysel) LaTeX braille kodu gibi, bu da + ASCII karakterleri oluşturur ve karakterleri braille'e çevirmek için + mevcut braille çıktı tablosunu kullanır. +* MathML'ye odaklanıldığında (daha önce olduğu gibi) CTRL+C kullanılarak + MathML, LaTeX veya ASCIIMath olarak kopyalamayı destekleyen "Farklı + Kopyala" tercihi eklendi. O anda odaklanılan düğüm kopyalanır. Not: Bu + yalnızca prefs.yaml dosyasında listelenir ve MathCAT Tercihleri ​​iletişim + kutusunda (henüz) gösterilmez. + +### Sürüm 0.4.2 +* Ses değiştiğinde ve MathCAT dili "Otomatik" olduğunda dil değişimi + düzeltildi +* Görme engelliler için ayarlanmadığında okumayı iyileştirmek amacıyla + $Impairments için daha fazla kontrol eklendi +* Nemeth: Bir mrowun parçası olmadığında "~" için düzeltme +* UEB: karakter eklemeleri, önek ise "~" boşluk düzeltmesi, xor düzeltmesi, +* Aksanlı ünlüler için MathML temizliği (özellikle Vietnamca için) +* Tercih okuma/güncelleme kodunun büyük bir hızla yeniden yazılması - hangi + dosyaların güncellemeler için kontrol edildiğini denetlemek için + "CheckRuleFiles" tercihi eklendi +* İki yeni arayüz çağrısı eklendi - gezinme konumunun braille imlecinden + ayarlanmasını sağlar (henüz MathCAT eklentisinin bir parçası değil) + +### Sürüm 0.3.11 +* Python 3.11'e yükseltildi ve NVDA 2024.1 ile çalıştığı doğrulandı +* Vietnamca braille alfabesindeki ve ayrıca Konuşmadaki, çoğunlukla kimyaya + yönelik hatalar düzeltildi. +* Braille kodu ve bağımlı dil eşleşmediğinde bozuk braille düzeltildi + (özellikle Vietnam braille ve Vietnamca konuşma) +* Belirteçlerin içindeki HTML'de bulunan boşluk hatası düzeltildi +* Roma rakamı algılaması geliştirildi + + +### Sürüm 0.3.9 +* Geleneksel Çince çevirisi eklendi (Hon-Jang Yang sayesinde) +* Parantez içeren kodlanmış bir ifadenin tabanına gitmeyle ilgili hata + düzeltildi +* Boşlukların işlenme şekli önemli ölçüde değiştirildi. Bu esas olarak + braille çıktısını etkiler (boşluklar ve "ihmal" tespiti). +* Kimyanın daha iyi tanınması +* Kimya örneklerinin eklenmesiyle ortaya çıkan UEB braille düzeltmeleri +* Bazı durumlarda yardımcı parantez eklemeye yönelik UEB düzeltmeleri + + +### Sürüm 0.3.8 + +Braille: + +* Diyalog birçok dilde uluslararası hale getirildi (çevirmenlere çok + teşekkürler!) +* CMU'nun ilk uygulaması - İspanyolca ve Portekizce konuşulan ülkelerde + kullanılan braille kodu +* Bazı UEB hataları düzeltildi ve UEB için bazı karakterler eklendi +* Vietnamca braille alfabesinde önemli iyileştirmeler + +Diğer düzeltmeler: + +* Göreli hız iletişim kutusunun kaydırıcısı maksimum %100 değerine sahip + olacak şekilde değiştirildi (artık yalnızca daha yavaş hızların + ayarlanmasına izin veriyor). Ayrıca, hızı önemli ölçüde artırmak/düşürmek + daha kolay olacak şekilde adım boyutları eklendi. +* Göreceli hız değiştirildiğinde bazen konuşmayı kesen eSpeak hatası + düzeltildi +* Vietnamca konuşmada iyileştirmeler +* OneCore seslerinin "a" demesiyle ilgili hata düzeltildi +* 'Otomatik Yakınlaştırma' Yanlış olduğunda (varsayılan değil) bazı gezinme + hataları düzeltildi +* Dil değişiklikleri ve diğer bazı iletişim kutusu değişiklikleriyle ilgili + güncellemeler düzeltildi; böylece bunların "Uygula" veya "Tamam" + tıklandığında hemen etkili olması sağlandı. +* MathCAT'in kutudan çıkar çıkmaz doğru dilde konuşması için "Sesin Dilini + Kullan" seçeneği eklendi (bir çeviri varsa) +* Zayıf MathML kodunu temizlemeye yönelik çeşitli iyileştirmeler + +### Sürüm 0.3.3 +Bu sürümde bir dizi hata düzeltmesi var. Başlıca yeni özellikler ve hata +düzeltmeleri şunlardır: + +* İspanyolca Çeviri eklendi (Noelia Ruiz ve Maria Allo Roldan'a teşekkürler) +* Gezinme bir seviye yakınlaştırılmış olarak başlayacak şekilde değiştirildi +* Tablo yapılarında gezinmenin bir yolu olarak ctrl+alt+ok eklendi. Bu + tuşlar, NVDA'da tablolarda gezinmek için kullanıldıkları için daha akılda + kalıcı olmalıdır. +* Göreli Matematik Hızı metin konuşma hızından daha yavaş olarak + ayarlandığında, eSpeak seslerinin yavaşlamasına neden olan NVDA hatası + giderildi. +* Uzun 'a' sesini konuşabilmeleri için OneCore ses problemi üzerinde + çalıştık. + +Hem Nemeth hem de UEB için konuşmada pek çok küçük ayar ve bazı hata +düzeltmeleri var. + +Not: Artık Vietnam'ın braille standardını braille çıktısı olarak alma +seçeneği var. Bu hala devam eden bir çalışma ve test dışında +kullanılamayacak kadar hatalı. Bir sonraki MathCAT sürümünün güvenilir bir +uygulama içermesini bekliyorum. + +### Sürüm 0.2.5 +* Kimya alanında daha fazla iyileştirme +* Nemeth için Düzeltmeler: + + * "İhmal" kuralları eklendi + * İngilizce Dil Göstergeleri için bazı kurallar eklendi + * Çok amaçlı göstergenin gerekli olduğu daha fazla durum eklendi + * Nemeth ve noktalama işaretleriyle ilgili düzeltmeler + +### Sürüm 0.2 +* Çok sayıda hata düzeltmesi +* Konuşma iyileştirmeleri +* Duraklatma süresini kontrol etmek için bir tercih ayarı (matematik için + göreli konuşma hızındaki değişikliklerle çalışır) +* Kimya gösterimini tanıma ve uygun şekilde konuşma desteği +* Endonezce ve Vietnamca çeviriler + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/doc/zh_CN/readme.md b/addon/doc/zh_CN/readme.md new file mode 100644 index 00000000..d535382d --- /dev/null +++ b/addon/doc/zh_CN/readme.md @@ -0,0 +1,184 @@ +# MathCAT # + +* 作者: 尼尔·索伊弗尔(Neil Soiffer) +* NVDA 兼容性: 2018.1 或更高版本(未在早期版本中测试) +* 下载 [稳定版][1] + +MathCAT is designed to eventually replace MathPlayer because MathPlayer is +no longer supported. MathCAT generates speech and braille from MathML. The +speech for math produced by MathCAT is enhanced with prosody so that it +sounds more natural. The speech can be navigated in three modes using the +same commands as MathPlayer. In addition, the navigation node is indicated +on a braille display. Both Nemeth and UEB technical are supported. + +MathCAT 有许多控制语音、导航和盲文的配置选项。其中许多可以在 MathCAT 设置对话框中设置 (可从 NVDA +首选项菜单中找到)。有关这些设置的更多信息,请参阅 [MathCAT +文档](https://nsoiffer.github.io/MathCAT/users.html)。该文档包含指向 [MathCAT +导航命令表](https://nsoiffer.github.io/MathCAT/nav-commands.html) 的链接。 + +注意: MathCAT 是一个用于从 MathML 生成语音和盲文的通用库。除了 NVDA 之外,其他 AT (辅助工具)项目也在使用。有关 +MathCAT 项目的常规信息,请参阅 [MathCAT 文档主页](https://nsoiffer.github.io/MathCAT)。 + + +谁应该使用 MathCAT: + +* 那些需要高质量 Nemeth 码盲文的人 (MathPlayer 的 Nemeth 码基于 liblouis 的 Nemeth + 码一代,有许多技术上难以修复的重大错误)。 +* Those who need UEB technical braille, CMU (Spanish/Portuguese), German + LaTeX, ASCIIMath, or Vietnamese braille +* 那些想要尝试最新技术并愿意通过报告 Bug 来提供帮助的人 +* 那些用 Eloquence 合成器的人 + +谁不应该使用 MathCAT: + +* Anyone who uses MathPlayer with a language that is not yet supported by + MathCAT (translations exist for Chinese (Traditional), Spanish, Indonesian + and Vietnamese; translations will be coming in the future) and are not + comfortable with speech in one of the supported languages. +* 任何喜欢 Access8Math 而不是 MathPlayer 的人 (需要使用语音或其他更多功能的人) + +MathCAT 的语音规则还没有 MathPlayer 的规则那么全面——这可能是坚持使用 MathPlayer 的另一个原因。MathCAT 被用作 +MathML 4 思想的测试平台,它允许作者表达他们的意图,以便能够正确地说出不明确的符号,而不是猜测。我没有添加太多规则,因为 MathCAT +的体系结构以使用和推断作者意图为中心,这些还没有完全解决。 + +## MathCAT 更新日志 + +### Version 0.6.3 + +* All the language and braille Rule files are zipped up per directory and + unzipped on demand. + + * This currently saves ~5mb when Rules.zip is unzipped, and will save even + more as more languages and braille codes are added. + * This is in preparation for MathCAT being built into NVDA 2024.3 + +* Added new preference `DecimalSeparator`. + + * The default value is `Auto`, with other values being ".", ",", and + "Custom". The first three values set `DecimalSeparators` and + `BlockSeparators`. + * `Auto` sets those preferences based on the value of the `Language` + pref. For some language such as Spanish, `,` is used in some countries + and `.` is used in others. In this case, it is best to set the language + to also include the country code (e.g, `es-es` or `es-mx`) to ensure the + right value is used. + +* Added Swedish to supported languages. +* Added more Unicode chars to include both all Unicode chars marked as "Sm" + and those with a mathclass (except Alphabetic and Glyph classes) in the + Unicode standard. +* After changing how prefs work in a previous version, I forgot to change + `MathRate` and `PauseFactor` to be numbers, not strings. +* Fixed bug in the braille Rules (missed change from earlier) where a third + argument should have been given to say to look in the _Braille_ + `definitions.yaml` files and not the speech ones when looking up the value + of a definition. +* Cleaned up use of `definitions.yaml`. +* Fixed some bugs in the MathML cleanup for "," decimal separators. +* Found a bug in braille highlighting when nothing is highlighted (maybe + never happens which is why I didn't see it in practice?) +* Fixed "Describe" mode so that it works -- it is still very minimal and + probably not useful yet +* Fixed minimum supported version + +### Version 0.5.6 +* Added Copy As... to the MathCAT dialog (in the "Navagation" pane). +* Fixed a bug where the language reverted to English when changing speech + styles. +* Fixed a bug with navigation and braille +* Fixed some Asciimath spacing problems. +* Improved chemistry recognition +* Updated MathCAT to new BANA Nemeth chemistry spec (still only single line + and special case style/font changes not handled) +* Fix a crash when non-ASCII digits (e.g., bold digits) are used in numbers +* Don't use italic indicators in braille codes when the math alphanumeric + italic chars are used +* Some other smaller bug fixes that weren't reported by users + +### 版本 0.5.0 +* 添加了德语 LaTeX 盲文代码。与其他盲文代码不同,这会生成 ASCII 字符,并使用当前盲文输出表将字符转换为盲文。 +* 增加了(实验) ASCII Math 盲文码。与 LaTeX 盲文代码一样,它生成 ASCII 字符,并使用当前盲文输出表将字符转换为盲文。 +* 添加了`CopyAs`首选项,支持在关注 MathML 时使用 ctrl+C 复制为 MathML、LaTeX 或 ASCII + Math(如前所述)。将复制当前关注的节点。注意:这仅列在 prefs.yaml 文件中,并且尚未在 “MathCAT首选项” 对话框中公开。 + +### 版本 0.4.2 +* 修复了语音变化且 MathCAT 语言为“自动”时的语言切换问题 +* 添加了更多 $Impairments 检查,以在未为盲人设置时提高阅读能力 +* Nemeth:修复了“~”不是 mrow 的一部分时的问题 +* UEB: character additions, "~" spacing fix if prefix, xor fix, +* 对重音元音的 MathML 清理(主要针对越南语) +* 首选项读取/更新代码的重大重写,速度大大加快--添加了`CheckRuleFiles`前缀以控制检查哪些文件进行更新 +* Added two new interface calls -- enables setting the navigaton location + from the braille cursor (not part of MathCAT addon yet) + +### 版本 0.3.11 +* 升级到 python 3.11,并验证可以与 NVDA 2024.1 一起工作 +* 修复越南语盲文和语音中的错误,主要用于化学。 +* 修复当盲文代码和相关语言不匹配时停止工作的问题(特别是越南盲文和越南语语音) +* 修复了 HTML 标记内部的空白错误 +* 改进了罗马数字检测 + + +### 版本 0.3.9 +* 增加了繁体中文翻译(感谢 Hon-Jang Yang) +* Fixed bug with navigating into the base of a scripted expression that has + parenthesis +* 显著改变了空白的处理方式。这主要影响盲文输出(空白和“省略”检测)。 +* 完善了对化学的识别 +* 添加化学示例后产生的UEB盲文修复 +* UEB 修复了在某些情况下添加辅助括号的问题 + + +### 版本 0.3.8 + +盲文: + +* 对话已经国际化(非常感谢翻译者们!) +* CMU 的初步实施——CMU 盲文码在西班牙和几个葡萄牙语国家使用 +* 修复了一些 UEB 错误,并为 UEB 添加了一些字符 +* 越南盲文的重大改进 + +其他修复: + +* 更改相对速率对话框滑块,使其最大值为 100% (现在只允许设置较慢的语速)。此外,增加了步长,因此更容易显著提高/降低语速。 +* 修复了当相对速率改变时有时会中断语音的 espeak 错误 +* 越南语语音的改进 +* 修正了 OneCore 语音说 “a” 的错误 +* 修复了`AutoZoomOut`为 False (非默认值) 时的一些导航错误 +* 修复了围绕语言更改和其他一些对话框更改的更新,以便它们在单击“应用”或“确认”后立即生效。 +* 添加了“使用语音的语言”选项,这样 MathCAT 就可以使用正确的语言(如果有翻译的话) +* 有关清理 MathML 码的几个改进 + +### 版本 0.3.3 +此版本修复了许多错误。主要的新功能和错误修复如下: + +* 添加了西班牙语翻译 (感谢 Noelia Ruiz 和 María Allo Roldán) +* 修改导航以便于在一个级别内开始放大 +* 添加了 ctrl+alt+箭头键用于导航表格结构。这些按键应该很容易记忆,因为它们与 NVDA 在使用的导航表格按键很类似。 +* 解决了 NVDA 的 eSpeak 语音的错误,当相对数学语速设置为低于文本语速时,该错误会导致语音减慢。 +* 解决了 OneCore 的语音问题,以便于它们能说出长 “a” 音。 + +语音有一些小的调整, Nemeth 码和 UEB 有一些错误修复。 + +注: 现在可以选择将越南的盲文标准作为盲文输出。这仍然是一项正在进行的工作,并且存在太多的 bug,不能用于测试之外的其他用途。我预计下一个 +MathCAT 版本将包含一个可靠的实现。 + +### 版本 0.2.5 +* 进一步化学改进 +* 对 Nemeth 码的修复: + + * Added "omission" rules + * Added some rules for English Language Indicators + * Added more cases where the Mulitpurpose indicator is needed + * Fixes related to Nemeth and punctuation + +### 版本 0.2 +* 大量错误修复 +* 改善语音 +* 一个首选项用于控制暂停持续时间 (适用于数学中相对语速的更改) +* 支持识别并是当说出化学符号 +* 印尼语和越南语翻译 + +[[!tag dev stable]] + +[1]: https://www.nvaccess.org/addonStore/legacy?file=mathcat diff --git a/addon/globalPlugins/MathCAT/MathCAT.py b/addon/globalPlugins/MathCAT/MathCAT.py new file mode 100644 index 00000000..62760fcc --- /dev/null +++ b/addon/globalPlugins/MathCAT/MathCAT.py @@ -0,0 +1,734 @@ +# -*- coding: UTF-8 -*- + +"""MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML. +The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued.""" +# Author: Neil Soiffer +# Copyright: this file is copyright GPL2 +# The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license +# and also (obviously) requires external speech engines and braille drivers. +# The plugin also requires the use of a small python dll: python3.dll +# python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved" + +# Note: this code is a lot of cut/paste from other code and very likely could be substantially improved/cleaned. +import braille # we generate braille +import mathPres # math plugin stuff +import re # regexp patter match +import speech # speech commands +import config # look up caps setting +import ui # copy message +import winUser # clipboard manipulation +import gettext +import addonHandler +import winKernel +import gui + +from . import libmathcat_py as libmathcat +from typing import Type +from collections.abc import Generator, Callable +from keyboardHandler import KeyboardInputGesture # navigation key strokes +from logHandler import log # logging +from os import path # set rule dir path +from scriptHandler import script # copy MathML via ctrl-c +from synthDriverHandler import ( + getSynth, + SynthDriver, +) +from ctypes import windll # register clipboard formats +from speech import getCurrentLanguage +from speech.types import SpeechSequence + +# speech/SSML processing borrowed from NVDA's mathPres/mathPlayer.py +from speech.commands import ( + BeepCommand, + PitchCommand, + VolumeCommand, + RateCommand, + LangChangeCommand, + BreakCommand, + CharacterModeCommand, + PhonemeCommand, + IndexCommand, + BaseProsodyCommand, + SpeechCommand, + SynthCommand, +) + +from textUtils import WCHAR_ENCODING +from ctypes import c_wchar, WinError, Array +from api import getClipData +from synthDrivers import _espeak + +_ = gettext.gettext + +addonHandler.initTranslation() + +RE_MATHML_SPEECH: re.Pattern = re.compile( + # Break. + r" ?" + # Pronunciation of characters. + r"|(?P[^<]+) ?" + # Specific pronunciation. + r"|(?P[^ <]+) ?" + # Prosody. + r"| ?" + r"|(?P) ?" + r"| ?" # hack for beeps + # Other tags, which we don't care about. + r"|<[^>]+> ?" + # Actual content. + r"|(?P[^<]+)", +) + +PROSODY_COMMANDS: dict[str, BaseProsodyCommand] = { + "pitch": PitchCommand, + "volume": VolumeCommand, + "rate": RateCommand, +} +RE_MATH_LANG: re.Pattern = re.compile(r"""""") + +# try to get around espeak bug where voice slows down (for other voices, just a waste of time) +# we use a global that gets set at a time when the rate is probably good (SetMathML) +_synthesizerRate: int | None = None + + +def getLanguageToUse(mathMl: str = "") -> str: + """Get the language specified in a math tag if the language pref is Auto, else the language preference. + + :param mathMl: The MathML string to examine for language. Defaults to an empty string. + :returns: The language string to use. + """ + mathCATLanguageSetting: str = "Auto" + try: + # ignore regional differences if the MathCAT language setting doesn't have it. + mathCATLanguageSetting = libmathcat.GetPreference("Language") + except Exception as e: + log.exception(e) + + # log.info(f"getLanguageToUse: {mathCATLanguageSetting}") + if mathCATLanguageSetting != "Auto": + return mathCATLanguageSetting + + languageMatch: re.Match | None = RE_MATH_LANG.search(mathMl) + language: str = ( + languageMatch.group(2) if languageMatch else getCurrentLanguage() + ) # seems to be current voice's language + language = language.lower().replace("_", "-") + if language == "cmn": + language = "zh-cmn" + elif language == "yue": + language = "zh-yue" + return language + + +def convertSSMLTextForNVDA(text: str) -> list[str | SpeechCommand]: + """ + Change the SSML in the text into NVDA's command structure. + The environment is examined to determine whether a language switch is needed. + + :param text: The SSML text to convert. + :returns: A list of strings and SpeechCommand objects. + """ + # MathCAT's default rate is 180 wpm. + # Assume that 0% is 80 wpm and 100% is 450 wpm and scale accordingly. + # log.info(f"\nSpeech str: '{text}'") + + # find MathCAT's language setting and store it away (could be "Auto") + # if MathCAT's setting doesn't match NVDA's language setting, change the language that is used + mathCATLanguageSetting: str = "en" # set in case GetPreference fails + try: + mathCATLanguageSetting = libmathcat.GetPreference("Language") + except Exception as e: + log.exception(e) + language: str = getLanguageToUse() + nvdaLanguage: str = getCurrentLanguage().replace("_", "-") + # log.info(f"mathCATLanguageSetting={mathCATLanguageSetting}, lang={language}, NVDA={nvdaLanguage}") + + _monkeyPatchESpeak() + + synth: SynthDriver = getSynth() + # I tried the engines on a 180 word excerpt. The speeds do not change linearly and differ a bit between engines + # At "50" espeak finished in 46 sec, sapi in 75 sec, and one core in 70; at '100' one core was much slower than the others + wpm: int = max(10, 2 * getSynth()._get_rate()) + breakMulti: float = 180.0 / wpm + supportedCommands: set[Type["SynthCommand"]] = synth.supportedCommands + useBreak: bool = BreakCommand in supportedCommands + usePitch: bool = PitchCommand in supportedCommands + # use_rate = RateCommand in supported_commands + # use_volume = VolumeCommand in supported_commands + usePhoneme: bool = PhonemeCommand in supportedCommands + # as of 7/23, oneCore voices do not implement the CharacterModeCommand despite it being in supported_commands + useCharacter: bool = CharacterModeCommand in supportedCommands and synth.name != "oneCore" + out: list[str | SpeechCommand] = [] + if mathCATLanguageSetting != language: + # log.info(f"Setting language to {language}") + try: + libmathcat.SetPreference("Language", language) + except Exception as e: + log.exception(e) + language = mathCATLanguageSetting # didn't set the language + if language != nvdaLanguage: + out.append(LangChangeCommand(language)) + + resetProsody: list[Type["BaseProsodyCommand"]] = [] + # log.info(f"\ntext: {text}") + for m in RE_MATHML_SPEECH.finditer(text): + if m.lastgroup == "break": + if useBreak: + out.append(BreakCommand(time=int(int(m.group("break")) * breakMulti))) + elif m.lastgroup == "char": + ch: str = m.group("char") + if useCharacter: + out.extend((CharacterModeCommand(True), ch, CharacterModeCommand(False))) + else: + out.extend((" ", "eigh" if ch == "a" and language.startswith("en") else ch, " ")) + elif m.lastgroup == "beep": + out.append(BeepCommand(2000, 50)) + elif m.lastgroup == "pitch": + if usePitch: + out.append(PitchCommand(multiplier=int(m.group(m.lastgroup)))) + resetProsody.append(PitchCommand) + elif m.lastgroup in PROSODY_COMMANDS: + command: Type["BaseProsodyCommand"] = PROSODY_COMMANDS[m.lastgroup] + if command in supportedCommands: + out.append(command(multiplier=int(m.group(m.lastgroup)) / 100.0)) + resetProsody.append(command) + elif m.lastgroup == "prosodyReset": + # for command in resetProsody: # only supported commands were added, so no need to check + command: Type["BaseProsodyCommand"] = resetProsody.pop() + out.append(command(multiplier=1)) + elif m.lastgroup == "phonemeText": + if usePhoneme: + out.append(PhonemeCommand(m.group("ipa"), text=m.group("phonemeText"))) + else: + out.append(m.group("phonemeText")) + elif m.lastgroup == "content": + # MathCAT puts out spaces between words, the speak command seems to want to glom the strings together at times, + # so we need to add individual " "s to the output + out.extend((" ", m.group(0), " ")) + # there is a bug in MS Word that concats the math and the next character outside of math, so we add a space + out.append(" ") + + if mathCATLanguageSetting != language: + # restore the old value (probably "Auto") + try: + libmathcat.SetPreference("Language", mathCATLanguageSetting) + except Exception as e: + log.exception(e) + if language != nvdaLanguage: + out.append(LangChangeCommand(None)) + # log.info(f"Speech commands: '{out}'") + return out + + +class MathCATInteraction(mathPres.MathInteractionNVDAObject): + """An NVDA object used to interact with MathML.""" + + # Put MathML or other formats on the clipboard. + # MathML is put on the clipboard using the two formats below (defined by MathML spec) + # We use both formats because some apps may only use one or the other + # Note: filed https://github.com/nvaccess/nvda/issues/13240 to make this usable outside of MathCAT + CF_MathML: int = windll.user32.RegisterClipboardFormatW("MathML") + CF_MathML_Presentation: int = windll.user32.RegisterClipboardFormatW( + "MathML Presentation", + ) + # log.info("2**** MathCAT registering data formats: + # CF_MathML %x, CF_MathML_Presentation %x" % (CF_MathML, CF_MathML_Presentation)) + + def __init__( + self, + provider: mathPres.MathPresentationProvider | None = None, + mathMl: str | None = None, + ): + """Initialize the MathCATInteraction object. + + :param provider: Optional presentation provider. + :param mathMl: Optional initial MathML string. + """ + super(MathCATInteraction, self).__init__(provider=provider, mathMl=mathMl) + if mathMl is None: + self.initMathML = "" + else: + self.initMathML = mathMl + + def reportFocus(self) -> None: + """Calls MathCAT's ZoomIn command and speaks the resulting text.""" + super(MathCATInteraction, self).reportFocus() + # try to get around espeak bug where voice slows down + if _synthesizerRate and getSynth().name == "espeak": + getSynth()._set_rate(_synthesizerRate) + try: + text: str = libmathcat.DoNavigateCommand("ZoomIn") + speech.speak(convertSSMLTextForNVDA(text)) + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in starting navigation of math: see NVDA error log for details")) + finally: + # try to get around espeak bug where voice slows down + if _synthesizerRate and getSynth().name == "espeak": + # log.info(f'reportFocus: reset to {_synthesizer_rate}') + getSynth()._set_rate(_synthesizerRate) + + def getBrailleRegions( + self, + review: bool = False, + ) -> Generator[braille.Region, None, None]: + """Yields braille.Region objects for this MathCATInteraction object.""" + # log.info("***MathCAT start getBrailleRegions") + yield braille.NVDAObjectRegion(self, appendText=" ") + region: braille.Region = braille.Region() + region.focusToHardLeft = True + # libmathcat.SetBrailleWidth(braille.handler.displaySize) + try: + region.rawText = libmathcat.GetBraille("") + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in brailling math: see NVDA error log for details")) + region.rawText = "" + + # log.info("***MathCAT end getBrailleRegions ***") + yield region + + def getScript( + self, + gesture: KeyboardInputGesture, + ) -> Callable[[KeyboardInputGesture], None] | None: + """ + Returns the script function bound to the given gesture. + + :param gesture: A KeyboardInputGesture sent to this object. + :returns: The script bound to that gesture. + """ + if ( + isinstance(gesture, KeyboardInputGesture) + and "NVDA" not in gesture.modifierNames + and gesture.mainKeyName + in { + "leftArrow", + "rightArrow", + "upArrow", + "downArrow", + "home", + "end", + "space", + "backspace", + "enter", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + } + # or len(gesture.mainKeyName) == 1 + ): + return self.script_navigate + else: + return super().getScript(gesture) + + def script_navigate(self, gesture: KeyboardInputGesture) -> None: + """Performs the specified navigation command. + + :param gesture: They keyboard command which specified the navigation command to perform. + """ + try: + # try to get around espeak bug where voice slows down + if _synthesizerRate and getSynth().name == "espeak": + getSynth()._set_rate(_synthesizerRate) + if gesture is not None: # == None when initial focus -- handled in reportFocus() + modNames: list[str] = gesture.modifierNames + text = libmathcat.DoNavigateKeyPress( + gesture.vkCode, + "shift" in modNames, + "control" in modNames, + "alt" in modNames, + False, + ) + # log.info(f"Navigate speech for {gesture.vkCode}/(s={'shift' in modNames}, c={'control' in modNames}): '{text}'") + speech.speak(convertSSMLTextForNVDA(text)) + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in navigating math: see NVDA error log for details")) + finally: + # try to get around espeak bug where voice slows down + if _synthesizerRate and getSynth().name == "espeak": + # log.info(f'script_navigate: reset to {_synthesizer_rate}') + getSynth()._set_rate(_synthesizerRate) + + if not braille.handler.enabled: + return + + try: + # update the braille to reflect the nav position (might be excess code, but it works) + navNode: tuple[str, int] = libmathcat.GetNavigationMathMLId() + brailleChars = libmathcat.GetBraille(navNode[0]) + # log.info(f'braille display = {config.conf["braille"]["display"]}, braille_chars: {braille_chars}') + region: braille.Region = braille.Region() + region.rawText = brailleChars + region.focusToHardLeft = True + region.update() + braille.handler.buffer.regions.append(region) + braille.handler.buffer.focus(region) + braille.handler.buffer.update() + braille.handler.update() + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in brailling math: see NVDA error log for details")) + + _startsWithMath: re.Pattern = re.compile("\\s*? None: + """Copies the raw data to the clipboard, either as MathML, ASCII math, or LaTeX, depending on user preferences. + + :param gesture: The gesture which activated this script. + """ + try: + copyAs: str = "mathml" # value used even if "CopyAs" pref is invalid + textToCopy: str = "" + try: + copyAs = libmathcat.GetPreference("CopyAs").lower() + except Exception as e: + log.exception(f"Not able to get 'CopyAs' preference: {e}") + if copyAs == "asciimath" or copyAs == "latex": + # save the old braille code, set the new one, get the braille, then reset the code + savedBrailleCode: str = libmathcat.GetPreference("BrailleCode") + libmathcat.SetPreference("BrailleCode", "LaTeX" if copyAs == "latex" else "ASCIIMath") + textToCopy = libmathcat.GetNavigationBraille() + libmathcat.SetPreference("BrailleCode", savedBrailleCode) + if copyAs == "asciimath": + copyAs = "ASCIIMath" # speaks better in at least some voices + else: + mathml: str = libmathcat.GetNavigationMathML()[0] + if not re.match(self._startsWithMath, mathml): + mathml = "\n" + mathml + "" # copy will fix up name spacing + elif self.initMathML != "": + mathml = self.initMathML + if copyAs == "speech": + # save the old MathML, set the navigation MathML as MathMl, get the speech, then reset the MathML + savedMathML: str = self.initMathML + savedTTS: str = libmathcat.GetPreference("TTS") + if savedMathML == "": # shouldn't happen + raise Exception("Internal error -- MathML not set for copy") + libmathcat.SetPreference("TTS", "None") + libmathcat.SetMathML(mathml) + # get the speech text and collapse the whitespace + textToCopy = " ".join(libmathcat.GetSpokenText().split()) + libmathcat.SetPreference("TTS", savedTTS) + libmathcat.SetMathML(savedMathML) + else: + textToCopy = self._wrapMathMLForClipBoard(mathml) + + self._copyToClipAsMathML(textToCopy, copyAs == "mathml") + # Translators: copy to clipboard + ui.message(_("copy as ") + copyAs) + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("unable to copy math: see NVDA error log for details")) + + # not a perfect match sequence, but should capture normal MathML + # not a perfect match sequence, but should capture normal MathML + _mathTagHasNameSpace: re.Pattern = re.compile("") + _hasAddedId: re.Pattern = re.compile(" id='[^'].+' data-id-added='true'") + _hasDataAttr: re.Pattern = re.compile(" data-[^=]+='[^']*'") + + def _wrapMathMLForClipBoard(self, text: str) -> str: + """Cleanup the MathML a little.""" + text = re.sub(self._hasAddedId, "", text) + mathMLWithNS: str = re.sub(self._hasDataAttr, "", text) + if not re.match(self._mathTagHasNameSpace, mathMLWithNS): + mathMLWithNS = mathMLWithNS.replace( + "math", + "math xmlns='http://www.w3.org/1998/Math/MathML'", + 1, + ) + return mathMLWithNS + + def _copyToClipAsMathML( + self, + text: str, + isMathML: bool, + notify: bool | None = False, + ) -> bool: + """Copies the given text to the windows clipboard. + + :param text: the text which will be copied to the clipboard. + :param notify: whether to emit a confirmation message. + :returns: True if it succeeds, False otherwise. + """ + # copied from api.py and modified to use CF_MathML_Presentation + if not isinstance(text, str) or len(text) == 0: + return False + + try: + with winUser.openClipboard(gui.mainFrame.Handle): + winUser.emptyClipboard() + if isMathML: + self._setClipboardData(self.CF_MathML, '' + text) + self._setClipboardData(self.CF_MathML_Presentation, '' + text) + self._setClipboardData(winUser.CF_UNICODETEXT, text) + got: str = getClipData() + except OSError: + if notify: + ui.reportTextCopiedToClipboard() # No argument reports a failure. + return False + if got == text: + if notify: + ui.reportTextCopiedToClipboard(text) + return True + if notify: + ui.reportTextCopiedToClipboard() # No argument reports a failure. + return False + + def _setClipboardData(self, format: int, data: str) -> None: + """Sets the clipboard data to the given data with the specified format. + + :param format: The format for the clipboard data. + This is an integer format code returned by windll.user32.RegisterClipboardFormatW. + :param data: The data to set on the clipboard. + """ + # Need to support MathML Presentation, so this copied from winUser.py and the first two lines are commented out + # For now only unicode is a supported format + # if format!=CF_UNICODETEXT: + # raise ValueError("Unsupported format") + text: str = data + bufLen: int = len(text.encode(WCHAR_ENCODING, errors="surrogatepass")) + 2 + # Allocate global memory + h: winKernel.HGLOBAL = winKernel.HGLOBAL.alloc(winKernel.GMEM_MOVEABLE, bufLen) + # Acquire a lock to the global memory receiving a local memory address + with h.lock() as addr: + # Write the text into the allocated memory + buf: Array[c_wchar] = (c_wchar * bufLen).from_address(addr) + buf.value = text + # Set the clipboard data with the global memory + if not windll.user32.SetClipboardData(format, h): + raise WinError() + # NULL the global memory handle so that it is not freed at the end of scope as the clipboard now has it. + h.forget() + + +class MathCAT(mathPres.MathPresentationProvider): + def __init__(self): + """Initializes MathCAT, loading the rules specified in the rules directory.""" + # super(MathCAT, self).__init__(*args, **kwargs) + + try: + # IMPORTANT -- SetRulesDir must be the first call to libmathcat besides GetVersion() + rulesDir: str = path.join(path.dirname(path.abspath(__file__)), "Rules") + log.info(f"MathCAT {libmathcat.GetVersion()} installed. Using rules dir: {rulesDir}") + libmathcat.SetRulesDir(rulesDir) + libmathcat.SetPreference("TTS", "SSML") + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("MathCAT initialization failed: see NVDA error log for details")) + + def getSpeechForMathMl( + self, + mathml: str, + ) -> list[str | SpeechCommand]: + """Outputs a MathML string as speech. + + :param mathml: A MathML string. + :returns: A list of speech commands and strings representing the given MathML. + """ + global _synthesizerRate + synth: SynthDriver = getSynth() + synthConfig = config.conf["speech"][synth.name] + if synth.name == "espeak": + _synthesizerRate = synthConfig["rate"] + # log.info(f'_synthesizer_rate={_synthesizer_rate}, get_rate()={getSynth()._get_rate()}') + getSynth()._set_rate(_synthesizerRate) + # log.info(f'..............get_rate()={getSynth()._get_rate()}, name={synth.name}') + try: + # need to set Language before the MathML for DecimalSeparator canonicalization + language: str = getLanguageToUse(mathml) + # MathCAT should probably be extended to accept "extlang" tagging, but it uses lang-region tagging at the moment + libmathcat.SetPreference("Language", language) + libmathcat.SetMathML(mathml) + except Exception as e: + log.exception(e) + log.exception(f"MathML is {mathml}") + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Illegal MathML found: see NVDA error log for details")) + libmathcat.SetMathML("") # set it to something + try: + say_fontstyle_change: bool = config.conf["documentFormatting"]["reportHighlight"] + libmathcat.SetPreference( + "IgnoreBold", + "false" if say_fontstyle_change else "true", + ) + supportedCommands: set[Type["SynthCommand"]] = synth.supportedCommands + # Set preferences for capital letters + libmathcat.SetPreference( + "CapitalLetters_Beep", + "true" if synthConfig["beepForCapitals"] else "false", + ) + libmathcat.SetPreference( + "CapitalLetters_UseWord", + "true" if synthConfig["sayCapForCapitals"] else "false", + ) + # log.info(f"Speech text: {libmathcat.GetSpokenText()}") + if PitchCommand in supportedCommands: + libmathcat.SetPreference("CapitalLetters_Pitch", str(synthConfig["capPitchChange"])) + if self._addSounds(): + return ( + [BeepCommand(800, 25)] + + convertSSMLTextForNVDA(libmathcat.GetSpokenText()) + + [BeepCommand(600, 15)] + ) + else: + return convertSSMLTextForNVDA(libmathcat.GetSpokenText()) + + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in speaking math: see NVDA error log for details")) + return [""] + finally: + # try to get around espeak bug where voice slows down + if _synthesizerRate and getSynth().name == "espeak": + # log.info(f'getSpeechForMathMl: reset to {_synthesizer_rate}') + getSynth()._set_rate(_synthesizerRate) + + def _addSounds(self) -> bool: + """Queries the user preferences to determine whether or not sounds should be added. + + :returns: True if MathCAT's `SpeechSound` preference is set, and False otherwise. + """ + try: + return libmathcat.GetPreference("SpeechSound") != "None" + except Exception as e: + log.exception(f"MathCAT: An exception occurred in _add_sounds: {e}") + return False + + def getBrailleForMathMl(self, mathml: str) -> str: + """Gets the braille representation of a given MathML string by calling MathCAT's GetBraille function. + + :param mathml: A MathML string. + :returns: A braille string representing the input MathML. + """ + # log.info("***MathCAT getBrailleForMathMl") + try: + libmathcat.SetMathML(mathml) + except Exception as e: + log.exception(e) + log.exception(f"MathML is {mathml}") + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Illegal MathML found: see NVDA error log for details")) + libmathcat.SetMathML("") # set it to something + try: + return libmathcat.GetBraille("") + except Exception as e: + log.exception(e) + # Translators: this message directs users to look in the log file + speech.speakMessage(_("Error in brailling math: see NVDA error log for details")) + return "" + + def interactWithMathMl(self, mathml: str) -> None: + """Interact with a MathML string, creating a MathCATInteraction object. + + :param mathml: The MathML representing the math to interact with. + """ + MathCATInteraction(provider=self, mathMl=mathml).setFocus() + MathCATInteraction(provider=self, mathMl=mathml).script_navigate(None) + + +CACHED_SYNTH: SynthDriver | None = None + + +def _monkeyPatchESpeak() -> None: + """Patches an eSpeak bug where the voice slows down.""" + global CACHED_SYNTH + currentSynth: SynthDriver = getSynth() + if currentSynth.name != "espeak" or CACHED_SYNTH == currentSynth: + return # already patched + + CACHED_SYNTH = currentSynth + currentSynth.speak = patchedSpeak.__get__(currentSynth, type(currentSynth)) + + +def patchedSpeak(self, speechSequence: SpeechSequence) -> None: # noqa: C901 + # log.info(f"\npatched_speak input: {speechSequence}") + textList: list[str] = [] + langChanged = False + prosody: dict[str, int] = {} + # We output malformed XML, as we might close an outer tag after opening an inner one; e.g. + # . + # However, eSpeak doesn't seem to mind. + for item in speechSequence: + if isinstance(item, str): + textList.append(self._processText(item)) + elif isinstance(item, IndexCommand): + textList.append('' % item.index) + elif isinstance(item, CharacterModeCommand): + textList.append('' if item.state else "") + elif isinstance(item, LangChangeCommand): + langChangeXML = self._handleLangChangeCommand(item, langChanged) + textList.append(langChangeXML) + langChanged = True + elif isinstance(item, BreakCommand): + textList.append(f'') + elif isinstance(item, RateCommand): + if item.multiplier == 1: + textList.append("") + else: + textList.append(f"") + elif type(item) in self.PROSODY_ATTRS: + if prosody: + # Close previous prosody tag. + textList.append('') # hack added for cutoff speech (issue #55) + textList.append("") + attr = self.PROSODY_ATTRS[type(item)] + if item.multiplier == 1: + # Returning to normal. + try: + del prosody[attr] + except KeyError: + pass + else: + prosody[attr] = int(item.multiplier * 100) + if not prosody: + continue + textList.append("") + elif isinstance(item, PhonemeCommand): + # We can't use str.translate because we want to reject unknown characters. + try: + phonemes: str = "".join([self.IPA_TO_ESPEAK[char] for char in item.ipa]) + # There needs to be a space after the phoneme command. + # Otherwise, eSpeak will announce a subsequent SSML tag instead of processing it. + textList.append("[[%s]] " % phonemes) + except KeyError: + log.debugWarning("Unknown character in IPA string: %s" % item.ipa) + if item.text: + textList.append(self._processText(item.text)) + else: + log.exception("Unknown speech: %s" % item) + # Close any open tags. + if langChanged: + textList.append("") + if prosody: + textList.append("") + text = "".join(textList) + # log.info(f"monkey-patched text={text}") + oldRate: int = getSynth()._get_rate() + _espeak.speak(text) + # try to get around espeak bug where voice slows down + getSynth()._set_rate(oldRate) diff --git a/addon/globalPlugins/MathCAT/MathCATPreferences.py b/addon/globalPlugins/MathCAT/MathCATPreferences.py new file mode 100644 index 00000000..0012ccb0 --- /dev/null +++ b/addon/globalPlugins/MathCAT/MathCATPreferences.py @@ -0,0 +1,1015 @@ +# -*- coding: UTF-8 -*- + +import math +import wx +from . import MathCATgui +from . import yaml +import os +import glob +import webbrowser +import gettext +import addonHandler +from logHandler import log # logging +from collections.abc import Callable +from .MathCAT import convertSSMLTextForNVDA +from speech import speak +from zipfile import ZipFile + +addonHandler.initTranslation() +_ = gettext.gettext + +# two constants to scale "PauseFactor" +# these work out so that a slider that goes [0,14] has value ~100 at 7 and ~1000 at 14 +PAUSE_FACTOR_SCALE: float = 9.5 +PAUSE_FACTOR_LOG_BASE: float = 1.4 + +# initialize the user preferences tuples +userPreferences: dict[str, dict[str, int | str | bool]] = {} +# Speech_Language is derived from the folder structures +Speech_DecimalSeparator = ("Auto", ".", ",", "Custom") +Speech_Impairment = ("LearningDisability", "Blindness", "LowVision") +# Speech_SpeechStyle is derived from the yaml files under the selected language +Speech_Verbosity = ("Terse", "Medium", "Verbose") +Speech_SubjectArea = "General" +Speech_Chemistry = ("SpellOut", "Off") +Navigation_NavMode = ("Enhanced", "Simple", "Character") +# Navigation_ResetNavMode is boolean +# Navigation_OverView is boolean +Navigation_NavVerbosity = ("Terse", "Medium", "Verbose") +# Navigation_AutoZoomOut is boolean +Navigation_CopyAs = ("MathML", "LaTeX", "ASCIIMath", "Speech") +Braille_BrailleNavHighlight = ("Off", "FirstChar", "EndPoints", "All") + + +class UserInterface(MathCATgui.MathCATPreferencesDialog): + """UI class for the MathCAT Preferences Dialog. + + Initializes and manages user preferences, including language, speech, braille, + and navigation settings. Extends MathCATgui.MathCATPreferencesDialog. + """ + + def __init__(self, parent: wx.Window | None): + """Initialize the preferences dialog. + + Sets up the UI, loads preferences, applies defaults and saved settings, + and restores the previous UI state. + + :param parent: The parent window for the dialog. + """ + # initialize parent class + MathCATgui.MathCATPreferencesDialog.__init__(self, parent) + + # load the logo into the dialog + fullPathToLogo: str = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo.png") + if os.path.exists(fullPathToLogo): + self._bitmapLogo.SetBitmap(wx.Bitmap(fullPathToLogo)) + + # load in the system values followed by the user prefs (if any) + UserInterface.loadDefaultPreferences() + UserInterface.loadUserPreferences() + + # hack for "CopyAs" because its location in the prefs is not yet fixed + if "CopyAs" not in userPreferences["Navigation"]: + userPreferences["Navigation"]["CopyAs"] = ( + userPreferences["Other"]["CopyAs"] if "CopyAs" in userPreferences["Other"] else "MathML" + ) + UserInterface.validateUserPreferences() + + if "NVDAAddOn" in userPreferences: + # set the categories selection to what we used on last run + self._listBoxPreferencesTopic.SetSelection(userPreferences["NVDAAddOn"]["LastCategory"]) + # show the appropriate dialogue page + self._simplebookPanelsCategories.SetSelection(self._listBoxPreferencesTopic.GetSelection()) + else: + # set the categories selection to the first item + self._listBoxPreferencesTopic.SetSelection(0) + userPreferences["NVDAAddOn"] = {"LastCategory": "0"} + # populate the languages and braille codes + UserInterface.getLanguages(self) + UserInterface.getBrailleCodes(self) + # set the ui items to match the preferences + UserInterface.setUIValues(self) + + @staticmethod + def pathToLanguagesFolder() -> str: + r"""Returns the full path to the Languages rules folder. + + The language rules are stored in: + MathCAT\Rules\Languages, relative to the location of this file. + + :return: Absolute path to the Languages folder as a string. + """ + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "Languages") + + @staticmethod + def pathToBrailleFolder() -> str: + r"""Returns the full path to the Braille rules folder. + + The Braille rules are stored in: + MathCAT\Rules\Braille, relative to the location of this file. + + :return: Absolute path to the Braille folder as a string. + """ + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "Braille") + + @staticmethod + def languagesDict() -> dict[str, str]: + """Returns a dictionary mapping language codes to their corresponding language names. + + This dictionary includes standard language codes, as well as regional variants such as + 'en-GB', 'zh-HANT', and others. + + :return: A dictionary where the key is the language code (e.g., 'en', 'fr', 'zh-HANS') + and the value is the language name (e.g. 'English', 'Français', 'Chinese, Simplified'). + """ + languages = { + "aa": "Afar", + "ab": "Аҧсуа", + "af": "Afrikaans", + "ak": "Akana", + "an": "Aragonés", + "ar": "العربية", + "as": "অসমীয়া", + "av": "Авар", + "ay": "Aymar", + "az": "Azərbaycanca / آذربايجان", + "ba": "Башҡорт", + "be": "Беларуская", + "bg": "Български", + "bh": "भोजपुरी", + "bi": "Bislama", + "bm": "Bahamanian", + "bn": "বাংলা", + "bo": "བོད་ཡིག / Bod skad", + "bs": "Bosanski", + "ca": "Català", + "ce": "Нохчийн", + "ch": "Chamoru", + "co": "Corsu", + "cr": "Nehiyaw", + "cs": "Česky", + "cu": "словѣньскъ / slověnĭskŭ", + "cv": "Чăваш", + "cy": "Cymraeg", + "da": "Dansk", + "de": "Deutsch", + "dv": "ދިވެހިބަސް", + "dz": "རྫོང་ཁ", + "ee": "Ɛʋɛ", + "el": "Ελληνικά", + "en": "English", + "en-GB": "English, United Kingdom", + "en-US": "English, United States", + "eo": "Esperanto", + "es": "Español", + "fa": "فارسی", + "fi": "Suomi", + "fj": "Na Vosa Vakaviti", + "fo": "Føroyskt", + "fr": "Français", + "fy": "Frysk", + "ga": "Gaeilge", + "gd": "Gàidhlig", + "gl": "Galego", + "gn": "Avañe'ẽ", + "gu": "ગુજરાતી", + "gv": "Gaelg", + "ha": "هَوُسَ", + "he": "עברית", + "hi": "हिन्दी", + "ho": "Hiri Motu", + "hr": "Hrvatski", + "ht": "Krèyol ayisyen", + "hu": "Magyar", + "hy": "Հայերեն", + "hz": "Otsiherero", + "ia": "Interlingua", + "id": "Bahasa Indonesia", + "ig": "Igbo", + "ii": "ꆇꉙ / 四川彝语", + "ik": "Iñupiak", + "io": "Ido", + "is": "Íslenska", + "it": "Italiano", + "iu": "ᐃᓄᒃᑎᑐᑦ", + "ja": "日本語", + "jv": "Basa Jawa", + "ka": "ქართული", + "kg": "KiKongo", + "ki": "Gĩkũyũ", + "kj": "Kuanyama", + "kk": "Қазақша", + "km": "ភាសាខ្មែរ", + "kn": "ಕನ್ನಡ", + "ko": "한국어", + "ks": "कॉशुर / کٲش", + "ku": "Kurdî", + "kv": "Коми", + "kw": "Kernewek", + "ky": "Kırgızca / Кыргызча", + "la": "Latina", + "lb": "Lëtzebuergesch", + "lg": "Luganda", + "li": "Limburgs", + "ln": "Lingála", + "lo": "ລາວ / Pha xa lao", + "lt": "Lietuvių", + "lv": "Latviešu", + "mg": "Malagasy", + "mh": "Kajin Majel / Ebon", + "mk": "Македонски", + "ml": "മലയാളം", + "mn": "Монгол", + "mo": "Moldovenească", + "ms": "Bahasa Melayu", + "mt": "bil-Malti", + "my": "Myanmasa", + "na": "Dorerin Naoero", + "nb": "Norsk, bokmål", + "ne": "नेपाली", + "ng": "Oshiwambo", + "nl": "Nederlands", + "nn": "Norsk, nynorsk", + "nr": "isiNdebele", + "nv": "Diné bizaad", + "ny": "Chi-Chewa", + "oc": "Occitan", + "oj": "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin", + "om": "Oromoo", + "os": "Иронау", + "pa": "ਪੰਜਾਬੀ / پنجابی", + "pi": "Pāli / पाऴि", + "pl": "Polski", + "ps": "پښتو", + "pt": "Português", + "qu": "Runa Simi", + "rm": "Rumantsch", + "ro": "Română", + "ru": "Русский", + "rw": "Kinyarwandi", + "sa": "संस्कृतम्", + "sc": "Sardu", + "sd": "सिंधी / سنڌي", + "se": "Davvisámegiella", + "sg": "Sängö", + "sh": "Srpskohrvatski / Српскохрватски", + "si": "සිංහල", + "sk": "Slovenčina", + "sl": "Slovenščina", + "sm": "Gagana Samoa", + "sn": "chiShona", + "so": "Soomaaliga", + "sq": "Shqip", + "sr": "Српски", + "ss": "SiSwati", + "st": "Sesotho", + "su": "Basa Sunda", + "sv": "Svenska", + "sw": "Kiswahili", + "ta": "தமிழ்", + "tg": "Тоҷикӣ", + "th": "ไทย / Phasa Thai", + "ti": "ትግርኛ", + "tk": "Туркмен / تركمن", + "tl": "Tagalog", + "to": "Lea Faka-Tonga", + "tr": "Türkçe", + "ts": "Xitsonga", + "tt": "Tatarça", + "tw": "Twi", + "ty": "Reo Mā`ohi", + "ug": "Uyƣurqə / ئۇيغۇرچە", + "uk": "Українська", + "ur": "اردو", + "uz": "Ўзбек", + "ve": "Tshivenḓa", + "vi": "Tiếng Việt", + "vo": "Volapük", + "wa": "Walon", + "wo": "Wollof", + "xh": "isiXhosa", + "yi": "ייִדיש", + "yo": "Yorùbá", + "za": "Cuengh / Tôô / 壮语", + "zh": "中文", + "zh-HANS": "Chinese, Simplified", + "zh-HANT": "Chinese, Traditional", + "zh-TW": "Chinese, Traditional, Taiwan", + "zu": "isiZulu", + } + return languages + + def getRulesFiles( + self, + pathToDir: str, + processSubDirs: Callable[[str, str], list[str]] | None, + ) -> list[str]: + """ + Get the rule files from a directory, optionally processing subdirectories. + + Searches for files ending with '_Rules.yaml' in the specified directory. + If no rule files are found, attempts to find them inside a corresponding ZIP archive, + including checking any subdirectories inside the ZIP. + + :param pathToDir: Path to the directory to search for rule files. + :param processSubDirs: Optional callable to process subdirectories. It should take the subdirectory name + and the language code as arguments, returning a list of rule filenames found in that subdirectory. + :return: A list of rule file names found either directly in the directory or inside the ZIP archive. + """ + language: str = os.path.basename(pathToDir) + ruleFiles: list[str] = [ + os.path.basename(file) for file in glob.glob(os.path.join(pathToDir, "*_Rules.yaml")) + ] + for dir in os.listdir(pathToDir): + if os.path.isdir(os.path.join(pathToDir, dir)): + if processSubDirs: + ruleFiles.extend(processSubDirs(dir, language)) + + if len(ruleFiles) == 0: + # look in the .zip file for the style files, including regional subdirs -- it might not have been unzipped + try: + zip_file: ZipFile = ZipFile(f"{pathToDir}\\{language}.zip", "r") + for file in zip_file.namelist(): + if file.endswith("_Rules.yaml"): + ruleFiles.append(file) + elif zip_file.getinfo(file).is_dir() and processSubDirs: + ruleFiles.extend(processSubDirs(dir, language)) + except Exception as e: + log.debugWarning(f"MathCAT Dialog: didn't find zip file {zip_file}. Error: {e}") + + return ruleFiles + + def getLanguages(self) -> None: + """Populate the language choice dropdown with available languages and their regional variants. + + This method scans the language folders and adds entries for each language and its + regional dialects. Language folders use ISO 639-1 codes and regional variants use ISO 3166-1 alpha-2 codes. + + It also adds a special "Use Voice's Language (Auto)" option at the top. + """ + + def addRegionalLanguages(subDir: str, language: str) -> list[str]: + # the language variants are in folders named using ISO 3166-1 alpha-2 + # codes https://en.wikipedia.org/wiki/ISO_3166-2 + # check if there are language variants in the language folder + if subDir != "SharedRules": + languagesDict: dict[str, str] = UserInterface.languagesDict() + # add to the listbox the text for this language variant together with the code + regionalCode: str = language + "-" + subDir.upper() + if languagesDict.get(regionalCode, "missing") != "missing": + self._choiceLanguage.Append(f"{languagesDict[regionalCode]} ({language}-{subDir})") + elif languagesDict.get(language, "missing") != "missing": + self._choiceLanguage.Append(f"{languagesDict[language]} ({regionalCode})") + else: + self._choiceLanguage.Append(f"{language} ({regionalCode})") + return [os.path.basename(file) for file in glob.glob(os.path.join(subDir, "*_Rules.yaml"))] + return [] + + # initialise the language list + languagesDict: dict[str, str] = UserInterface.languagesDict() + # clear the language names in the dialog + self._choiceLanguage.Clear() + # Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog + # "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style + self._choiceLanguage.Append(_("Use Voice's Language (Auto)")) + # populate the available language names in the dialog + # the implemented languages are in folders named using the relevant ISO 639-1 + # code https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + languageDir: str = UserInterface.pathToLanguagesFolder() + for language in os.listdir(languageDir): + pathToLanguageDir: str = os.path.join(UserInterface.pathToLanguagesFolder(), language) + if os.path.isdir(pathToLanguageDir): + # only add this language if there is a xxx_Rules.yaml file + if len(self.getRulesFiles(pathToLanguageDir, addRegionalLanguages)) > 0: + # add to the listbox the text for this language together with the code + if languagesDict.get(language, "missing") != "missing": + self._choiceLanguage.Append(languagesDict[language] + " (" + language + ")") + else: + self._choiceLanguage.Append(language + " (" + language + ")") + + def getLanguageCode(self) -> str: + """Extract the language code from the selected language string in the UI. + + The selected language string is expected to contain the language code in parentheses, + for example: "English (en)". + + :return: The language code extracted from the selection. + """ + langSelection: str = self._choiceLanguage.GetStringSelection() + langCode: str = langSelection[langSelection.find("(") + 1 : langSelection.find(")")] + return langCode + + def getSpeechStyles(self, thisSpeechStyle: str) -> None: + """Get all the speech styles for the current language. + This sets the SpeechStyles dialog entry. + + :param thisSpeechStyle: The speech style to set or highlight in the dialog. + """ + from speech import getCurrentLanguage + + def getSpeechStyleFromDirectory(dir: str, lang: str) -> list[str]: + r"""Get the speech styles from any regional dialog, from the main language, dir and if there isn't from the zip file. + The 'lang', if it has a region dialect, is of the form 'en\uk' + The returned list is sorted alphabetically + + :param dir: The directory path to search for speech styles. + :param lang: Language code which may include a regional dialect (e.g., 'en\uk'). + :return: A list of speech styles sorted alphabetically. + """ + # start with the regional dialect, then add on any (unique) styles in the main dir + mainLang: str = lang.split("\\")[0] # does the right thing even if there is no regional directory + allStyleFiles: list[str] = [] + if lang.find("\\") >= 0: + allStyleFiles: list[str] = [ + os.path.basename(name) for name in glob.glob(dir + lang + "\\*_Rules.yaml") + ] + allStyleFiles.extend( + [os.path.basename(name) for name in glob.glob(dir + mainLang + "\\*_Rules.yaml")], + ) + allStyleFiles = list(set(allStyleFiles)) # make them unique + if len(allStyleFiles) == 0: + # look in the .zip file for the style files -- this will have regional variants, but also have that dir + try: + zipFilePath: str = dir + mainLang + "\\" + mainLang + ".zip" + zipFile: ZipFile = ZipFile(zipFilePath, "r") # file might not exist + allStyleFiles = [ + name.split("/")[-1] for name in zipFile.namelist() if name.endswith("_Rules.yaml") + ] + except Exception as e: + log.debugWarning(f"MathCAT Dialog: didn't find zip file {zipFile}. Error: {e}") + allStyleFiles.sort() + return allStyleFiles + + # clear the SpeechStyle choices + self._choiceSpeechStyle.Clear() + # get the currently selected language code + languageCode: str = UserInterface.getLanguageCode(self) + + if languageCode == "Auto": + # list the speech styles for the current voice rather than have none listed + languageCode = getCurrentLanguage().lower().replace("_", "-") + languageCode = languageCode.replace("-", "\\") + + languagePath = UserInterface.pathToLanguagesFolder() + "\\" + # log.info(f"languagePath={languagePath}") + # populate the m_choiceSpeechStyle choices + allStyleFiles = [ + # remove "_Rules.yaml" from the list + name[: name.find("_Rules.yaml")] + for name in getSpeechStyleFromDirectory(languagePath, languageCode) + ] + # There isn't a LiteralSpeak rules file since it has no language-specific rules. We add it at the end. + # Translators: at the moment, do NOT translate this string as some code specifically looks for this name. + allStyleFiles.append("LiteralSpeak") + for name in allStyleFiles: + self._choiceSpeechStyle.Append((name)) + try: + # set the SpeechStyle to the same as previous + self._choiceSpeechStyle.SetStringSelection( + thisSpeechStyle if thisSpeechStyle in allStyleFiles else allStyleFiles[0], + ) + except Exception as e: + log.exception( + f"MathCAT: An exception occurred in GetSpeechStyles evaluating set SetStringSelection: {e}", + ) + # that didn't work, choose the first in the list + self._choiceSpeechStyle.SetSelection(0) + + def getBrailleCodes(self) -> None: + """Initializes and populates the braille code choice control with available braille codes. + + Scans the braille codes folder for valid directories containing rules files, and adds them + to the braille code dropdown in the dialog. + """ + # initialise the braille code list + self._choiceBrailleMathCode.Clear() + # populate the available braille codes in the dialog + # the dir names are used, not the rule file names because the dir names have to be unique + pathToBrailleFolder: str = UserInterface.pathToBrailleFolder() + for brailleCode in os.listdir(pathToBrailleFolder): + pathToBrailleCode: str = os.path.join(pathToBrailleFolder, brailleCode) + if os.path.isdir(pathToBrailleCode): + if len(self.getRulesFiles(pathToBrailleCode, None)) > 0: + self._choiceBrailleMathCode.Append(brailleCode) + + def setUIValues(self) -> None: + """Sets the UI elements based on the values read from the user preferences. + + Attempts to match preference values to UI controls; falls back to defaults if values are invalid + or missing. + """ + try: + self._choiceImpairment.SetSelection( + Speech_Impairment.index(userPreferences["Speech"]["Impairment"]), + ) + try: + langPref: str = userPreferences["Speech"]["Language"] + self._choiceLanguage.SetSelection(0) + i: int = 1 # no need to test i == 0 + while i < self._choiceLanguage.GetCount(): + if f"({langPref})" in self._choiceLanguage.GetString(i): + self._choiceLanguage.SetSelection(i) + break + i += 1 + except Exception as e: + log.exception( + f"MathCAT: An exception occurred in setUIValues ('{userPreferences['Speech']['Language']}'): {e}", + ) + # the language in the settings file is not in the folder structure, something went wrong, + # set to the first in the list + self._choiceLanguage.SetSelection(0) + try: + # now get the available SpeechStyles from the folder structure and set to the preference setting is possible + self.getSpeechStyles(str(userPreferences["Speech"]["SpeechStyle"])) + except Exception as e: + log.exception(f"MathCAT: An exception occurred in set_ui_values (getting SpeechStyle): {e}") + self._choiceSpeechStyle.Append( + "Error when setting SpeechStyle for " + self._choiceLanguage.GetStringSelection(), + ) + # set the rest of the UI elements + self._choiceDecimalSeparator.SetSelection( + Speech_DecimalSeparator.index(userPreferences["Other"]["DecimalSeparator"]), + ) + self._choiceSpeechAmount.SetSelection( + Speech_Verbosity.index(userPreferences["Speech"]["Verbosity"]), + ) + self._sliderRelativeSpeed.SetValue(userPreferences["Speech"]["MathRate"]) + pause_factor = ( + 0 + if int(userPreferences["Speech"]["PauseFactor"]) <= 1 + else round( + math.log( + int(userPreferences["Speech"]["PauseFactor"]) / PAUSE_FACTOR_SCALE, + PAUSE_FACTOR_LOG_BASE, + ), + ) + ) + self._sliderPauseFactor.SetValue(pause_factor) + self._checkBoxSpeechSound.SetValue(userPreferences["Speech"]["SpeechSound"] == "Beep") + self._choiceSpeechForChemical.SetSelection( + Speech_Chemistry.index(userPreferences["Speech"]["Chemistry"]), + ) + + self._choiceNavigationMode.SetSelection( + Navigation_NavMode.index(userPreferences["Navigation"]["NavMode"]), + ) + self._checkBoxResetNavigationMode.SetValue(userPreferences["Navigation"]["ResetNavMode"]) + self._choiceSpeechAmountNavigation.SetSelection( + Navigation_NavVerbosity.index(userPreferences["Navigation"]["NavVerbosity"]), + ) + if userPreferences["Navigation"]["Overview"]: + self._choiceNavigationSpeech.SetSelection(1) + else: + self._choiceNavigationSpeech.SetSelection(0) + self._checkBoxResetNavigationSpeech.SetValue(userPreferences["Navigation"]["ResetOverview"]) + self._checkBoxAutomaticZoom.SetValue(userPreferences["Navigation"]["AutoZoomOut"]) + self._choiceCopyAs.SetSelection( + Navigation_CopyAs.index(userPreferences["Navigation"]["CopyAs"]), + ) + + self._choiceBrailleHighlights.SetSelection( + Braille_BrailleNavHighlight.index(userPreferences["Braille"]["BrailleNavHighlight"]), + ) + try: + braillePref: str = userPreferences["Braille"]["BrailleCode"] + i = 0 + while braillePref != self._choiceBrailleMathCode.GetString(i): + i = i + 1 + if i == self._choiceBrailleMathCode.GetCount(): + break + if braillePref == self._choiceBrailleMathCode.GetString(i): + self._choiceBrailleMathCode.SetSelection(i) + else: + self._choiceBrailleMathCode.SetSelection(0) + except Exception as e: + log.exception(f"MathCAT: An exception occurred while trying to set the Braille code: {e}") + # the braille code in the settings file is not in the folder structure, something went wrong, + # set to the first in the list + self._choiceBrailleMathCode.SetSelection(0) + except KeyError as err: + print("Key not found", err) + + def getUIValues(self) -> None: + """Reads the current values from the UI controls and updates the user preferences accordingly.""" + global userPreferences + # read the values from the UI and update the user preferences dictionary + userPreferences["Speech"]["Impairment"] = Speech_Impairment[self._choiceImpairment.GetSelection()] + userPreferences["Speech"]["Language"] = self.getLanguageCode() + userPreferences["Other"]["DecimalSeparator"] = Speech_DecimalSeparator[ + self._choiceDecimalSeparator.GetSelection() + ] + userPreferences["Speech"]["SpeechStyle"] = self._choiceSpeechStyle.GetStringSelection() + userPreferences["Speech"]["Verbosity"] = Speech_Verbosity[self._choiceSpeechAmount.GetSelection()] + userPreferences["Speech"]["MathRate"] = self._sliderRelativeSpeed.GetValue() + pfSlider: int = self._sliderPauseFactor.GetValue() + pauseFactor: int = ( + 0 if pfSlider == 0 else round(PAUSE_FACTOR_SCALE * math.pow(PAUSE_FACTOR_LOG_BASE, pfSlider)) + ) # avoid log(0) + userPreferences["Speech"]["PauseFactor"] = pauseFactor + if self._checkBoxSpeechSound.GetValue(): + userPreferences["Speech"]["SpeechSound"] = "Beep" + else: + userPreferences["Speech"]["SpeechSound"] = "None" + userPreferences["Speech"]["Chemistry"] = Speech_Chemistry[ + self._choiceSpeechForChemical.GetSelection() + ] + userPreferences["Navigation"]["NavMode"] = Navigation_NavMode[ + self._choiceNavigationMode.GetSelection() + ] + userPreferences["Navigation"]["ResetNavMode"] = self._checkBoxResetNavigationMode.GetValue() + userPreferences["Navigation"]["NavVerbosity"] = Navigation_NavVerbosity[ + self._choiceSpeechAmountNavigation.GetSelection() + ] + userPreferences["Navigation"]["Overview"] = self._choiceNavigationSpeech.GetSelection() != 0 + userPreferences["Navigation"]["ResetOverview"] = self._checkBoxResetNavigationSpeech.GetValue() + userPreferences["Navigation"]["AutoZoomOut"] = self._checkBoxAutomaticZoom.GetValue() + userPreferences["Navigation"]["CopyAs"] = Navigation_CopyAs[self._choiceCopyAs.GetSelection()] + + userPreferences["Braille"]["BrailleNavHighlight"] = Braille_BrailleNavHighlight[ + self._choiceBrailleHighlights.GetSelection() + ] + userPreferences["Braille"]["BrailleCode"] = self._choiceBrailleMathCode.GetStringSelection() + if "NVDAAddOn" not in userPreferences: + userPreferences["NVDAAddOn"] = {"LastCategory": "0"} + userPreferences["NVDAAddOn"]["LastCategory"] = self._listBoxPreferencesTopic.GetSelection() + + @staticmethod + def pathToDefaultPreferences() -> str: + """Returns the full path to the default preferences file.""" + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "prefs.yaml") + + @staticmethod + def pathToUserPreferencesFolder() -> str: + """Returns the path to the folder where user preferences are stored.""" + # the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml + return os.path.join(os.path.expandvars("%APPDATA%"), "MathCAT") + + @staticmethod + def pathToUserPreferences() -> str: + """Returns the full path to the user preferences file.""" + # the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml + return os.path.join(UserInterface.pathToUserPreferencesFolder(), "prefs.yaml") + + @staticmethod + def loadDefaultPreferences() -> None: + """Loads the default preferences, overwriting any existing user preferences.""" + global userPreferences + # load default preferences into the user preferences data structure (overwrites existing) + if os.path.exists(UserInterface.pathToDefaultPreferences()): + with open( + UserInterface.pathToDefaultPreferences(), + encoding="utf-8", + ) as f: + userPreferences = yaml.load(f, Loader=yaml.FullLoader) + + @staticmethod + def loadUserPreferences() -> None: + """Loads user preferences from a file and merges them into the current preferences. + + If the user preferences file exists, its values overwrite the defaults. + """ + global userPreferences + # merge user file values into the user preferences data structure + if os.path.exists(UserInterface.pathToUserPreferences()): + with open(UserInterface.pathToUserPreferences(), encoding="utf-8") as f: + # merge with the default preferences, overwriting with the user's values + userPreferences.update(yaml.load(f, Loader=yaml.FullLoader)) + + @staticmethod + def validate( + key1: str, + key2: str, + validValues: list[str | bool], + defaultValue: str | bool, + ) -> None: + """Validates that a preference value is in a list of valid options or non-empty if no list is given. + + If the value is missing or invalid, sets it to the default. + + :param key1: The first-level key in the preferences dictionary. + :param key2: The second-level key in the preferences dictionary. + :param validValues: A list of valid values; if empty, any non-empty value is valid. + :param defaultValue: The default value to set if validation fails. + """ + global userPreferences + try: + if validValues == []: + # any value is valid + if userPreferences[key1][key2] != "": + return + + else: + # any value in the list is valid + if userPreferences[key1][key2] in validValues: + return + except Exception as e: + log.exception(f"MathCAT: An exception occurred in validate: {e}") + # the preferences entry does not exist + if key1 not in userPreferences: + userPreferences[key1] = {key2: defaultValue} + else: + userPreferences[key1][key2] = defaultValue + + @staticmethod + def validateInt( + key1: str, + key2: str, + validValues: list[int], + defaultValue: int, + ) -> None: + """Validates that an integer preference is within a specified range. + + If the value is missing or out of bounds, sets it to the default. + + :param key1: The first-level key in the preferences dictionary. + :param key2: The second-level key in the preferences dictionary. + :param validValues: A list with two integers [min, max] representing valid bounds. + :param defaultValue: The default value to set if validation fails. + """ + global userPreferences + try: + # any value between lower and upper bounds is valid + if ( + int(userPreferences[key1][key2]) >= validValues[0] + and int(userPreferences[key1][key2]) <= validValues[1] + ): + return + except Exception as e: + log.exception(f"MathCAT: An exception occurred in validateInt: {e}") + # the preferences entry does not exist + if key1 not in userPreferences: + userPreferences[key1] = {key2: defaultValue} + else: + userPreferences[key1][key2] = defaultValue + + @staticmethod + def validateUserPreferences(): + """Validates all user preferences, ensuring each is present and valid. + + If a preference is missing or invalid, it is reset to its default value. + Validation covers speech, navigation, and braille settings. + """ + # Speech: + # Impairment: Blindness # LearningDisability, LowVision, Blindness + UserInterface.validate( + "Speech", + "Impairment", + ["LearningDisability", "LowVision", "Blindness"], + "Blindness", + ) + # Language: en # any known language code and sub-code -- could be en-uk, etc + UserInterface.validate("Speech", "Language", [], "en") + # Verbosity: Medium # Terse, Medium, Verbose + UserInterface.validate("Speech", "Verbosity", ["Terse", "Medium", "Verbose"], "Medium") + # MathRate: 100 # Change from text speech rate (%) + UserInterface.validateInt("Speech", "MathRate", [0, 200], 100) + # PauseFactor: 100 # TBC + UserInterface.validateInt("Speech", "PauseFactor", [0, 1000], 100) + # SpeechSound: None # make a sound when starting/ending math speech -- None, Beep + UserInterface.validate("Speech", "SpeechSound", ["None", "Beep"], "None") + # SpeechStyle: ClearSpeak # Any known speech style (falls back to ClearSpeak) + UserInterface.validate("Speech", "SpeechStyle", [], "ClearSpeak") + # SubjectArea: General # FIX: still working on this + UserInterface.validate("Speech", "SubjectArea", [], "General") + # Chemistry: SpellOut # SpellOut (H 2 0), AsCompound (Water), Off (H sub 2 O) + UserInterface.validate("Speech", "Chemistry", ["SpellOut", "Off"], "SpellOut") + # Navigation: + # NavMode: Enhanced # Enhanced, Simple, Character + UserInterface.validate("Navigation", "NavMode", ["Enhanced", "Simple", "Character"], "Enhanced") + # ResetNavMode: false # remember previous value and use it + UserInterface.validate("Navigation", "ResetNavMode", [False, True], False) + # Overview: false # speak the expression or give a description/overview + UserInterface.validate("Navigation", "Overview", [False, True], False) + # ResetOverview: true # remember previous value and use it + UserInterface.validate("Navigation", "ResetOverview", [False, True], True) + # NavVerbosity: Medium # Terse, Medium, Full (words to say for nav command) + UserInterface.validate("Navigation", "NavVerbosity", ["Terse", "Medium", "Full"], "Medium") + # AutoZoomOut: true # Auto zoom out of 2D exprs (use shift-arrow to force zoom out if unchecked) + UserInterface.validate("Navigation", "AutoZoomOut", [False, True], True) + # CopyAs: MathML # MathML, LaTeX, ASCIIMath, Speech + UserInterface.validate("Navigation", "CopyAs", ["MathML", "LaTeX", "ASCIIMath", "Speech"], "MathML") + # Braille: + # BrailleNavHighlight: EndPoints + # Highlight with dots 7 & 8 the current nav node -- values are Off, FirstChar, EndPoints, All + UserInterface.validate( + "Braille", + "BrailleNavHighlight", + ["Off", "FirstChar", "EndPoints", "All"], + "EndPoints", + ) + # BrailleCode: "Nemeth" # Any supported braille code (currently Nemeth, UEB, CMU, Vietnam) + UserInterface.validate("Braille", "BrailleCode", [], "Nemeth") + + @staticmethod + def writeUserPreferences() -> None: + """Writes the current user preferences to a file and updates special settings. + + Sets the language preference through the native library, ensures the preferences + folder exists, and saves the preferences to disk. + """ + # Language is special because it is set elsewhere by SetPreference which overrides the user_prefs -- so set it here + from . import libmathcat_py as libmathcat + + try: + libmathcat.SetPreference("Language", userPreferences["Speech"]["Language"]) + except Exception as e: + log.exception( + f'Error in trying to set MathCAT "Language" preference to "{userPreferences["Speech"]["Language"]}": {e}', + ) + if not os.path.exists(UserInterface.pathToUserPreferencesFolder()): + # create a folder for the user preferences + os.mkdir(UserInterface.pathToUserPreferencesFolder()) + with open(UserInterface.pathToUserPreferences(), "w", encoding="utf-8") as f: + # write values to the user preferences file, NOT the default + yaml.dump(userPreferences, stream=f, allow_unicode=True) + + def onRelativeSpeedChanged(self, event: wx.ScrollEvent) -> None: + """Handles changes to the relative speed slider and updates speech output. + + Adjusts the speech rate based on the slider value and speaks a test phrase + with the updated rate. + + :param event: The scroll event triggered by adjusting the relative speed slider. + """ + rate: int = self._sliderRelativeSpeed.GetValue() + # Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" + text: str = _("the square root of x squared plus y squared").replace( + "XXX", + str(rate), + 1, + ) + speak(convertSSMLTextForNVDA(text)) + + def onPauseFactorChanged(self, event: wx.ScrollEvent) -> None: + """Handles changes to the pause factor slider and updates speech output accordingly. + + Calculates the pause durations based on the slider value, constructs an SSML string + with adjusted prosody and breaks, and sends it for speech synthesis. + + :param event: The scroll event triggered by adjusting the pause factor slider. + """ + rate: int = self._sliderRelativeSpeed.GetValue() + pfSlider = self._sliderPauseFactor.GetValue() + pauseFactor = ( + 0 if pfSlider == 0 else round(PAUSE_FACTOR_SCALE * math.pow(PAUSE_FACTOR_LOG_BASE, pfSlider)) + ) + text: str = _( + # Translators: this is a test string that is spoken. Only translate "the fraction with numerator" + # and other parts NOT inside '<.../>', + "the fraction with numerator \ + x to the \ + n -th\ + power plus 1\ + and denominator \ + x to the \ + n -thpower\ + minus 1\ + end fraction ", + ).format( + rate=rate, + pause_factor_128=128 * pauseFactor // 100, + pause_factor_150=150 * pauseFactor // 100, + pause_factor_300=300 * pauseFactor // 100, + pause_factor_600=600 * pauseFactor // 100, + ) + speak(convertSSMLTextForNVDA(text)) + + def onClickOK(self, event: wx.CommandEvent) -> None: + """Saves current preferences and closes the dialog. + + Retrieves values from the UI, writes them to the preferences, and then closes the window. + + :param event: The event triggered by clicking the OK button. + """ + UserInterface.getUIValues(self) + UserInterface.writeUserPreferences() + self.Destroy() + + def onClickCancel(self, event: wx.CommandEvent) -> None: + """Closes the preferences dialog without saving changes. + + :param event: The event triggered by clicking the Cancel button. + """ + self.Destroy() + + def onClickApply(self, event: wx.CommandEvent) -> None: + """Applies the current UI settings to the user preferences. + + Retrieves values from the UI and writes them to the preferences configuration. + + :param event: The event triggered by clicking the Apply button. + """ + UserInterface.getUIValues(self) + UserInterface.writeUserPreferences() + + def onClickReset(self, event: wx.CommandEvent) -> None: + """Resets preferences to their default values. + + Loads the default preferences, validates them, and updates the UI accordingly. + + :param event: The event triggered by clicking the Reset button. + """ + UserInterface.loadDefaultPreferences() + UserInterface.validateUserPreferences() + UserInterface.setUIValues(self) + + def onClickHelp(self, event: wx.CommandEvent) -> None: + """Opens the MathCAT user guide in the default web browser. + + Triggered when the Help button is clicked. + + :param event: The event triggered by clicking the Help button. + """ + webbrowser.open("https://nsoiffer.github.io/MathCAT/users.html") + + def onListBoxCategories(self, event: wx.CommandEvent) -> None: + """Handles category selection changes in the preferences list box. + + Updates the displayed panel in the dialog to match the newly selected category. + + :param event: The event triggered by selecting a different category. + """ + self._simplebookPanelsCategories.SetSelection(self._listBoxPreferencesTopic.GetSelection()) + + def onLanguage(self, event: wx.CommandEvent) -> None: + """Handles the event when the user changes the selected language. + + Retrieves and updates the available speech styles for the newly selected language + in the preferences dialog. + + :param event: The event triggered by changing the language selection. + """ + UserInterface.getSpeechStyles(self, self._choiceSpeechStyle.GetStringSelection()) + + def mathCATPreferencesDialogOnCharHook(self, event: wx.KeyEvent) -> None: + """Handles character key events within the MathCAT Preferences dialog. + + This method interprets specific key presses to mimic button clicks or + navigate within the preferences dialog: + + - escape: Triggers the Cancel button functionality. + - enter: Triggers the OK button functionality. + - ctrl+tab: Cycles forward through the preference categories. + - ctrl+shift+tab: Cycles backward through the preference categories. + - tab: Moves focus to the first control in the currently selected category, + if the category list has focus. + - shift+tab: Moves focus to the second row of controls, + if the OK button has focus. + + If none of these keys are matched, the event is skipped to allow default processing. + + :param event: The keyboard event to handle. + """ + keyCode: int = event.GetKeyCode() + if keyCode == wx.WXK_ESCAPE: + UserInterface.onClickCancel(self, event) + return + if keyCode == wx.WXK_RETURN: + UserInterface.onClickOK(self, event) + if keyCode == wx.WXK_TAB: + if event.GetModifiers() == wx.MOD_CONTROL: + # cycle the category forward + newCategory: int = self._listBoxPreferencesTopic.GetSelection() + 1 + if newCategory == 3: + newCategory = 0 + self._listBoxPreferencesTopic.SetSelection(newCategory) + # update the ui to show the new page + UserInterface.onListBoxCategories(self, event) + # set the focus into the category list box + self._listBoxPreferencesTopic.SetFocus() + # jump out so the tab key is not processed + return + if event.GetModifiers() == wx.MOD_CONTROL | wx.MOD_SHIFT: + # cycle the category back + newCategory: int = self._listBoxPreferencesTopic.GetSelection() - 1 + if newCategory == -1: + newCategory = 2 + self._listBoxPreferencesTopic.SetSelection(newCategory) + # update the ui to show the new page + UserInterface.onListBoxCategories(self, event) + # update the ui to show the new page + self._listBoxPreferencesTopic.SetFocus() + # jump out so the tab key is not processed + return + if event.GetModifiers() == wx.MOD_NONE and ( + MathCATgui.MathCATPreferencesDialog.FindFocus() == self._listBoxPreferencesTopic + ): + if self._listBoxPreferencesTopic.GetSelection() == 0: + self._choiceImpairment.SetFocus() + elif self._listBoxPreferencesTopic.GetSelection() == 1: + self._choiceNavigationMode.SetFocus() + elif self._listBoxPreferencesTopic.GetSelection() == 2: + self._choiceBrailleMathCode.SetFocus() + return + if (event.GetModifiers() == wx.MOD_SHIFT) and ( + MathCATgui.MathCATPreferencesDialog.FindFocus() == self._buttonOK + ): + if self._listBoxPreferencesTopic.GetSelection() == 0: + self._choiceSpeechForChemical.SetFocus() + elif self._listBoxPreferencesTopic.GetSelection() == 1: + self._choiceSpeechAmountNavigation.SetFocus() + elif self._listBoxPreferencesTopic.GetSelection() == 2: + self._choiceBrailleHighlights.SetFocus() + return + # continue handling keyboard event + event.Skip() diff --git a/addon/globalPlugins/MathCAT/MathCATgui.py b/addon/globalPlugins/MathCAT/MathCATgui.py new file mode 100644 index 00000000..fdf06b81 --- /dev/null +++ b/addon/globalPlugins/MathCAT/MathCATgui.py @@ -0,0 +1,883 @@ +import wx + +# import wx.xrc +import gettext +import addonHandler + +_ = gettext.gettext +addonHandler.initTranslation() + +########################################################################### +# Class MathCATPreferencesDialog +########################################################################### + + +class MathCATPreferencesDialog(wx.Dialog): + """Main dialog window for configuring MathCAT preferences. + + This base class sets up the layout and controls. + """ + + def __init__(self, parent: wx.Window | None): + """Initialize the preferences dialog. + + :param parent: The parent window for this dialog. + """ + wx.Dialog.__init__( + self, + parent, + id=wx.ID_ANY, + # Translators: title for MathCAT preferences dialog + title=_("MathCAT Preferences"), + pos=wx.DefaultPosition, + size=wx.Size(-1, -1), + style=wx.DEFAULT_DIALOG_STYLE, + ) + + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) + + gbSizerMathCATPreferences: wx.GridBagSizer = wx.GridBagSizer(0, 0) + gbSizerMathCATPreferences.SetFlexibleDirection(wx.BOTH) + gbSizerMathCATPreferences.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + + self._panelCategories: wx.Panel = wx.Panel( + self, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + wx.TAB_TRAVERSAL, + ) + bSizerCategories: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) + + self._staticTextCategories: wx.StaticText = wx.StaticText( + self._panelCategories, + wx.ID_ANY, + # Translators: A heading that labels three navigation pane tab names in the MathCAT dialog + _("Categories:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextCategories.Wrap(-1) + + bSizerCategories.Add(self._staticTextCategories, 0, wx.ALL, 5) + + listBoxPreferencesTopicChoices: list[str] = [ + # Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" + _("Speech"), + # Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" + _("Navigation"), + # Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" + _("Braille"), + ] + self._listBoxPreferencesTopic: wx.ListBox = wx.ListBox( + self._panelCategories, + wx.ID_ANY, + wx.Point(-1, -1), + wx.Size(-1, -1), + listBoxPreferencesTopicChoices, + wx.LB_NO_SB | wx.LB_SINGLE, + ) + bSizerCategories.Add(self._listBoxPreferencesTopic, 0, wx.ALL, 5) + + bSizerCategories.Add((0, 0), 1, wx.EXPAND, 5) + + self._bitmapLogo: wx.StaticBitmap = wx.StaticBitmap( + self._panelCategories, + wx.ID_ANY, + wx.NullBitmap, + wx.DefaultPosition, + wx.Size(126, 85), + 0, + ) + bSizerCategories.Add(self._bitmapLogo, 0, wx.ALL, 5) + + self._panelCategories.SetSizer(bSizerCategories) + self._panelCategories.Layout() + bSizerCategories.Fit(self._panelCategories) + gbSizerMathCATPreferences.Add( + self._panelCategories, + wx.GBPosition(0, 0), + wx.GBSpan(1, 1), + wx.EXPAND | wx.ALL, + 5, + ) + + self._simplebookPanelsCategories: wx.Simplebook = wx.Simplebook( + self, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._panelSpeech: wx.Panel = wx.Panel( + self._simplebookPanelsCategories, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + wx.BORDER_SIMPLE | wx.TAB_TRAVERSAL, + ) + bSizerSpeech: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) + + bSizerImpairment: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextImpairment: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: this is the text label for whom to target the speech for (options are below) + _("Generate speech for:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextImpairment.Wrap(-1) + + bSizerImpairment.Add(self._staticTextImpairment, 0, wx.ALL, 5) + + impairmentChoices: list[str] = [ + # Translators: these are the categories of impairments that MathCAT supports + # Translators: Learning disabilities includes dyslexia and ADHD + _("Learning disabilities"), + # Translators: target people who are blind + _("Blindness"), + # Translators: target people who have low vision + _("Low vision"), + ] + self._choiceImpairment: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + impairmentChoices, + 0, + ) + self._choiceImpairment.SetSelection(1) + bSizerImpairment.Add(self._choiceImpairment, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerImpairment, 1, wx.EXPAND, 5) + + bSizerLanguage: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextLanguage: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down allowing users to choose the speech language for math + _("Language:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextLanguage.Wrap(-1) + + bSizerLanguage.Add(self._staticTextLanguage, 0, wx.ALL, 5) + + languageChoices: list[str] = ["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] + self._choiceLanguage: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + languageChoices, + 0, + ) + self._choiceLanguage.SetSelection(0) + bSizerLanguage.Add(self._choiceLanguage, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerLanguage, 1, wx.EXPAND, 5) + + bSizerDecimalSeparator: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextDecimalSeparator: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down to specify what character to use in numbers as the decimal separator + _("Decimal separator for numbers:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextDecimalSeparator.Wrap(-1) + + bSizerDecimalSeparator.Add(self._staticTextDecimalSeparator, 0, wx.ALL, 5) + + # Translators: options for decimal separator. + decimalSeparatorChoices: list[str] = [ + # Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language + _("Auto"), + # options for decimal separator -- use "." (and use ", " for block separators) + ("."), + # options for decimal separator -- use "," (and use ". " for block separators) + (","), + # Translators: options for decimal separator -- "Custom" = user sets it + # Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it + _("Custom"), + ] + self._choiceDecimalSeparator: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + decimalSeparatorChoices, + 0, + ) + self._choiceDecimalSeparator.SetSelection(0) + bSizerDecimalSeparator.Add(self._choiceDecimalSeparator, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerDecimalSeparator, 1, wx.EXPAND, 5) + + bSizerSpeechStyle: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextSpeechStyle: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math + _("Speech style:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextSpeechStyle.Wrap(-1) + + bSizerSpeechStyle.Add(self._staticTextSpeechStyle, 0, wx.ALL, 5) + + speechStyleChoices: list[str] = ["xxxxxxxxxxxxxxxx"] + self._choiceSpeechStyle: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + speechStyleChoices, + 0, + ) + self._choiceSpeechStyle.SetSelection(0) + bSizerSpeechStyle.Add(self._choiceSpeechStyle, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerSpeechStyle, 1, wx.EXPAND, 5) + + bSizerSpeechAmount: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextSpeechAmount: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down to specify how verbose/terse the speech should be + _("Speech verbosity:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextSpeechAmount.Wrap(-1) + + bSizerSpeechAmount.Add(self._staticTextSpeechAmount, 0, wx.ALL, 5) + + # Translators: options for speech verbosity. + speechAmountChoices: list[str] = [ + # Translators: options for speech verbosity -- "terse" = use less words + _("Terse"), + # Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words + _("Medium"), + # Translators: options for speech verbosity -- "verbose" = use more words + _("Verbose"), + ] + self._choiceSpeechAmount: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + speechAmountChoices, + 0, + ) + self._choiceSpeechAmount.SetSelection(0) + bSizerSpeechAmount.Add(self._choiceSpeechAmount, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerSpeechAmount, 1, wx.EXPAND, 5) + + bSizerRelativeSpeed: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextRelativeSpeed: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math + _("Relative speech rate:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextRelativeSpeed.Wrap(-1) + + bSizerRelativeSpeed.Add(self._staticTextRelativeSpeed, 0, wx.ALL, 5) + + self._sliderRelativeSpeed: wx.Slider = wx.Slider( + self._panelSpeech, + wx.ID_ANY, + 100, + 10, + 100, + wx.DefaultPosition, + wx.DefaultSize, + wx.SL_HORIZONTAL, + ) + self._sliderRelativeSpeed.SetLineSize(9) + bSizerRelativeSpeed.Add(self._sliderRelativeSpeed, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerRelativeSpeed, 1, wx.EXPAND, 5) + + bSizerPauseFactor: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticPauseFactor: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech + _("Pause factor:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticPauseFactor.Wrap(-1) + + bSizerPauseFactor.Add(self._staticPauseFactor, 0, wx.ALL, 5) + + self._sliderPauseFactor: wx.Slider = wx.Slider( + self._panelSpeech, + wx.ID_ANY, + 7, + 0, + 14, + wx.DefaultPosition, + wx.DefaultSize, + wx.SL_HORIZONTAL, + ) + bSizerPauseFactor.Add(self._sliderPauseFactor, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerPauseFactor, 1, wx.EXPAND, 5) + + bSizerSpeechSound: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._checkBoxSpeechSound: wx.CheckBox = wx.CheckBox( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for check box controling a beep sound + _("Make a sound when starting/ending math speech"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerSpeechSound.Add(self._checkBoxSpeechSound, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerSpeechSound, 1, wx.EXPAND, 5) + + bSizerSubjectArea: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextSubjectArea: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) + _("Subject area to be used when it cannot be determined automatically:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextSubjectArea.Wrap(-1) + + bSizerSubjectArea.Add(self._staticTextSubjectArea, 0, wx.ALL, 5) + + # Translators: a generic (non-specific) math subject area + subjectAreaChoices: list[str] = [_("General")] + self._choiceSubjectArea: wx.Choice = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + subjectAreaChoices, + 0, + ) + self._choiceSubjectArea.SetSelection(0) + bSizerSubjectArea.Add(self._choiceSubjectArea, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerSubjectArea, 1, wx.EXPAND, 5) + + bSizerSpeechForChemical: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextSpeechForChemical: wx.StaticText = wx.StaticText( + self._panelSpeech, + wx.ID_ANY, + # Translators: label for pull down to specify how verbose/terse the speech should be + _("Speech for chemical formulas:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextSpeechForChemical.Wrap(-1) + + bSizerSpeechForChemical.Add(self._staticTextSpeechForChemical, 0, wx.ALL, 5) + + speechForChemicalChoices: list[str] = [ + # Translators: values for chemistry options with example speech in parenthesis + _("Spell it out (H 2 O)"), + # Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) + _("Off (H sub 2 O)"), + ] + self._choiceSpeechForChemical = wx.Choice( + self._panelSpeech, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + speechForChemicalChoices, + 0, + ) + self._choiceSpeechForChemical.SetSelection(0) + bSizerSpeechForChemical.Add(self._choiceSpeechForChemical, 0, wx.ALL, 5) + + bSizerSpeech.Add(bSizerSpeechForChemical, 1, wx.EXPAND, 5) + + self._panelSpeech.SetSizer(bSizerSpeech) + self._panelSpeech.Layout() + bSizerSpeech.Fit(self._panelSpeech) + self._simplebookPanelsCategories.AddPage(self._panelSpeech, "a page", False) + self._panelNavigation: wx.Panel = wx.Panel( + self._simplebookPanelsCategories, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + wx.BORDER_SIMPLE | wx.TAB_TRAVERSAL, + ) + bSizerNavigation: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) + + sbSizerNavigationMode: wx.StaticBoxSizer = wx.StaticBoxSizer( + wx.StaticBox( + self._panelNavigation, + wx.ID_ANY, + # Translators: label for pull down to specify one of three modes use to navigate math expressions + _("Navigation mode to use when beginning to navigate an equation:"), + ), + wx.VERTICAL, + ) + + navigationModeChoices: list[str] = [ + # Translators: names of different modes of navigation. "Enhanced" mode understands math structure + _("Enhanced"), + # Translators: "Simple" walks by character expect for things like fractions, roots, and scripts + _("Simple"), + # Translators: "Character" moves around by character, automatically moving into fractions, etc + _("Character"), + ] + self._choiceNavigationMode: wx.Choice = wx.Choice( + sbSizerNavigationMode.GetStaticBox(), + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + navigationModeChoices, + 0, + ) + self._choiceNavigationMode.SetSelection(1) + sbSizerNavigationMode.Add(self._choiceNavigationMode, 0, wx.ALL, 5) + + self._checkBoxResetNavigationMode: wx.CheckBox = wx.CheckBox( + sbSizerNavigationMode.GetStaticBox(), + wx.ID_ANY, + # Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved + _("Reset navigation mode on entry to an expression"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + sbSizerNavigationMode.Add(self._checkBoxResetNavigationMode, 0, wx.ALL, 5) + + bSizerNavigation.Add(sbSizerNavigationMode, 1, wx.EXPAND, 5) + + sbSizerNavigationSpeech: wx.StaticBoxSizer = wx.StaticBoxSizer( + wx.StaticBox( + self._panelNavigation, + wx.ID_ANY, + # Translators: label for pull down to specify whether the expression is spoken or described (an overview) + _("Navigation speech to use when beginning to navigate an equation:"), + ), + wx.VERTICAL, + ) + + # Translators: either "Speak" the expression or give a description (overview) of the expression + navigationSpeechChoices: list[str] = [ + # Translators: "Speak" the expression after moving to it + _("Speak"), + # Translators: "Describe" the expression after moving to it ("overview is a synonym") + _("Describe/overview"), + ] + self._choiceNavigationSpeech: wx.Choice = wx.Choice( + sbSizerNavigationSpeech.GetStaticBox(), + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + navigationSpeechChoices, + 0, + ) + self._choiceNavigationSpeech.SetSelection(1) + sbSizerNavigationSpeech.Add(self._choiceNavigationSpeech, 0, wx.ALL, 5) + + self._checkBoxResetNavigationSpeech: wx.CheckBox = wx.CheckBox( + sbSizerNavigationSpeech.GetStaticBox(), + wx.ID_ANY, + # Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored + _("Reset navigation speech on entry to an expression"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._checkBoxResetNavigationSpeech.SetValue(True) + sbSizerNavigationSpeech.Add(self._checkBoxResetNavigationSpeech, 0, wx.ALL, 5) + + bSizerNavigation.Add(sbSizerNavigationSpeech, 1, wx.EXPAND, 5) + + bSizerNavigationZoom: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) + + self._checkBoxAutomaticZoom: wx.CheckBox = wx.CheckBox( + self._panelNavigation, + wx.ID_ANY, + # Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., + # or whether you have to manually back out of the fraction, etc. + _("Automatic zoom out of 2D notations"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerNavigationZoom.Add(self._checkBoxAutomaticZoom, 0, wx.ALL, 5) + + bSizerSpeechAmountNavigation: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextSpeechAmountNavigation: wx.StaticText = wx.StaticText( + self._panelNavigation, + wx.ID_ANY, + # Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands + _("Speech amount for navigation:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextSpeechAmountNavigation.Wrap(-1) + + bSizerSpeechAmountNavigation.Add(self._staticTextSpeechAmountNavigation, 0, wx.ALL, 5) + + # Translators: options for navigation verbosity. + speechAmountNavigationChoices: list[str] = [ + # Translators: options for navigation verbosity -- "terse" = use less words + _("Terse"), + # Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words + _("Medium"), + # Translators: options for navigation verbosity -- "verbose" = use more words + _("Verbose"), + ] + self._choiceSpeechAmountNavigation: wx.Choice = wx.Choice( + self._panelNavigation, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + speechAmountNavigationChoices, + 0, + ) + self._choiceSpeechAmountNavigation.SetSelection(0) + bSizerSpeechAmountNavigation.Add(self._choiceSpeechAmountNavigation, 0, wx.ALL, 5) + + bSizerNavigationZoom.Add(bSizerSpeechAmountNavigation, 1, wx.EXPAND, 5) + + bSizerNavigation.Add(bSizerNavigationZoom, 1, wx.EXPAND, 5) + + bSizerCopyAs: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextCopyMathAs: wx.StaticText = wx.StaticText( + self._panelNavigation, + wx.ID_ANY, + # Translators: label for pull down to specify how math will be copied to the clipboard + _("Copy math as:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextCopyMathAs.Wrap(-1) + + bSizerCopyAs.Add(self._staticTextCopyMathAs, 0, wx.ALL, 5) + + # Translators: options for copy math as. + copyAsChoices: list[str] = [ + # Translators: options for Copy expression to clipboard as -- "MathML" + _("MathML"), + # Translators: options for Copy expression to clipboard as -- "LaTeX" + _("LaTeX"), + # Translators: options for Copy expression to clipboard as -- "ASCIIMath" + _("ASCIIMath"), + # Translators: options for Copy expression to clipboard as -- speech text + _("Speech"), + ] + self._choiceCopyAs: wx.Choice = wx.Choice( + self._panelNavigation, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + copyAsChoices, + 0, + ) + self._choiceCopyAs.SetSelection(0) + bSizerCopyAs.Add(self._choiceCopyAs, 0, wx.ALL, 5) + + bSizerNavigation.Add(bSizerCopyAs, 1, wx.EXPAND, 5) + + self._panelNavigation.SetSizer(bSizerNavigation) + self._panelNavigation.Layout() + bSizerNavigation.Fit(self._panelNavigation) + self._simplebookPanelsCategories.AddPage( + self._panelNavigation, + "a page", + False, + ) + self._panelBraille: wx.Panel = wx.Panel( + self._simplebookPanelsCategories, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + wx.BORDER_SIMPLE | wx.TAB_TRAVERSAL, + ) + bSizerBraille = wx.BoxSizer(wx.VERTICAL) + + bSizerBrailleMathCode: wx.BoxSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextBrailleMathCode: wx.StaticText = wx.StaticText( + self._panelBraille, + wx.ID_ANY, + # Translators: label for pull down to specify which braille code to use + _("Braille math code for refreshable displays:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextBrailleMathCode.Wrap(-1) + + bSizerBrailleMathCode.Add(self._staticTextBrailleMathCode, 0, wx.ALL, 5) + brailleMathCodeChoices: list[str] = ["xxxxxxxxxxx"] + self._choiceBrailleMathCode: wx.Choice = wx.Choice( + self._panelBraille, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + brailleMathCodeChoices, + 0, + ) + self._choiceBrailleMathCode.SetSelection(1) + bSizerBrailleMathCode.Add(self._choiceBrailleMathCode, 0, wx.ALL, 5) + + bSizerBraille.Add(bSizerBrailleMathCode, 1, wx.EXPAND, 5) + + bSizerBrailleHighlights = wx.BoxSizer(wx.HORIZONTAL) + + self._staticTextBrailleHighlights: wx.StaticText = wx.StaticText( + self._panelBraille, + wx.ID_ANY, + # Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs + _("Highlight with dots 7 && 8 the current nav node:"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + self._staticTextBrailleHighlights.Wrap(-1) + + bSizerBrailleHighlights.Add(self._staticTextBrailleHighlights, 0, wx.ALL, 5) + + brailleHighlightsChoices: list[str] = [ + # Translators: options for using dots 7 and 8: + # Translators: "off" -- don't highlight + _("Off"), + # Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 + _("First character"), + # Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 + _("Endpoints"), + # Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 + _("All"), + ] + self._choiceBrailleHighlights: wx.Choice = wx.Choice( + self._panelBraille, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + brailleHighlightsChoices, + 0, + ) + self._choiceBrailleHighlights.SetSelection(1) + bSizerBrailleHighlights.Add(self._choiceBrailleHighlights, 0, wx.ALL, 5) + + bSizerBraille.Add(bSizerBrailleHighlights, 1, wx.EXPAND, 5) + + bSizerBraille.Add((0, 0), 1, wx.EXPAND, 5) + + bSizerBraille.Add((0, 0), 1, wx.EXPAND, 5) + + bSizerBraille.Add((0, 0), 1, wx.EXPAND, 5) + + bSizerBraille.Add((0, 0), 1, wx.EXPAND, 5) + + bSizerBraille.Add((0, 0), 1, wx.EXPAND, 5) + + self._panelBraille.SetSizer(bSizerBraille) + self._panelBraille.Layout() + bSizerBraille.Fit(self._panelBraille) + self._simplebookPanelsCategories.AddPage(self._panelBraille, "a page", False) + + gbSizerMathCATPreferences.Add( + self._simplebookPanelsCategories, + wx.GBPosition(0, 1), + wx.GBSpan(1, 1), + wx.EXPAND | wx.ALL, + 10, + ) + + self._staticlineAboveButtons: wx.StaticLine = wx.StaticLine( + self, + wx.ID_ANY, + wx.DefaultPosition, + wx.DefaultSize, + wx.LI_HORIZONTAL, + ) + gbSizerMathCATPreferences.Add( + self._staticlineAboveButtons, + wx.GBPosition(1, 0), + wx.GBSpan(1, 2), + wx.EXPAND | wx.ALL, + 5, + ) + + self._panelButtons: wx.Panel = wx.Panel(self, wx.ID_ANY, wx.Point(-1, -1), wx.DefaultSize, 0) + bSizerButtons = wx.BoxSizer(wx.HORIZONTAL) + + bSizerButtons.Add((0, 0), 1, wx.EXPAND, 5) + + self._buttonOK: wx.Button = wx.Button( + self._panelButtons, + wx.ID_ANY, + # Translators: dialog "ok" button + _("OK"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerButtons.Add(self._buttonOK, 0, wx.ALL, 5) + + self._buttonCancel: wx.Button = wx.Button( + self._panelButtons, + wx.ID_ANY, + # Translators: dialog "cancel" button + _("Cancel"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerButtons.Add(self._buttonCancel, 0, wx.ALL, 5) + + self._buttonApply: wx.Button = wx.Button( + self._panelButtons, + wx.ID_ANY, + # Translators: dialog "apply" button + _("Apply"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerButtons.Add(self._buttonApply, 0, wx.ALL, 5) + + self._buttonReset: wx.Button = wx.Button( + self._panelButtons, + wx.ID_ANY, + # Translators: button to reset all the preferences to their default values + _("Reset to defaults"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerButtons.Add(self._buttonReset, 0, wx.ALL, 5) + + self._buttonHelp: wx.Button = wx.Button( + self._panelButtons, + wx.ID_ANY, + # Translators: button to bring up a help page + _("Help"), + wx.DefaultPosition, + wx.DefaultSize, + 0, + ) + bSizerButtons.Add(self._buttonHelp, 0, wx.ALL, 5) + + self._panelButtons.SetSizer(bSizerButtons) + self._panelButtons.Layout() + bSizerButtons.Fit(self._panelButtons) + gbSizerMathCATPreferences.Add( + self._panelButtons, + wx.GBPosition(2, 1), + wx.GBSpan(1, 2), + wx.EXPAND | wx.ALL, + 5, + ) + + self.SetSizer(gbSizerMathCATPreferences) + self.Layout() + gbSizerMathCATPreferences.Fit(self) + + self.Centre(wx.BOTH) + + # Connect Events + self.Bind(wx.EVT_CHAR_HOOK, self.mathCATPreferencesDialogOnCharHook) + self.Bind(wx.EVT_KEY_UP, self.mathCATPreferencesDialogOnKeyUp) + self._listBoxPreferencesTopic.Bind(wx.EVT_LISTBOX, self.onListBoxCategories) + self._choiceLanguage.Bind(wx.EVT_CHOICE, self.onLanguage) + self._sliderRelativeSpeed.Bind( + wx.EVT_SCROLL_CHANGED, + self.onRelativeSpeedChanged, + ) + self._sliderPauseFactor.Bind(wx.EVT_SCROLL_CHANGED, self.onPauseFactorChanged) + self._buttonOK.Bind(wx.EVT_BUTTON, self.onClickOK) + self._buttonCancel.Bind(wx.EVT_BUTTON, self.onClickCancel) + self._buttonApply.Bind(wx.EVT_BUTTON, self.onClickApply) + self._buttonReset.Bind(wx.EVT_BUTTON, self.onClickReset) + self._buttonHelp.Bind(wx.EVT_BUTTON, self.onClickHelp) + + def __del__(self): + """Destructor placeholder; override if cleanup is needed.""" + pass + + # Virtual event handlers, override them in your derived class + def mathCATPreferencesDialogOnCharHook(self, event: wx.KeyEvent) -> None: + """Handle character input events; override in subclass as needed.""" + event.Skip() + + def mathCATPreferencesDialogOnKeyUp(self, event: wx.KeyEvent) -> None: + """Handle key release events; override in subclass as needed.""" + event.Skip() + + def onListBoxCategories(self, event: wx.CommandEvent) -> None: + """Handle selection events in the categories list box; override in subclass as needed.""" + event.Skip() + + def onLanguage(self, event: wx.CommandEvent) -> None: + """Handle language selection; override in subclass as needed.""" + event.Skip() + + def onRelativeSpeedChanged(self, event: wx.ScrollEvent) -> None: + """Handle change in relative speed; override in subclass as needed.""" + event.Skip() + + def onPauseFactorChanged(self, event: wx.ScrollEvent) -> None: + """Handle change in pause factor; override in subclass as needed.""" + event.Skip() + + def onClickOK(self, event: wx.CommandEvent) -> None: + """Handle OK button click; override in subclass as needed.""" + event.Skip() + + def onClickCancel(self, event: wx.CommandEvent) -> None: + """Handle Cancel button click; override in subclass as needed.""" + event.Skip() + + def onClickApply(self, event: wx.CommandEvent) -> None: + """Handle Apply button click; override in subclass as needed.""" + event.Skip() + + def onClickReset(self, event: wx.CommandEvent) -> None: + """Handle Reset button click; override in subclass as needed.""" + event.Skip() + + def onClickHelp(self, event: wx.CommandEvent) -> None: + """Handle Help button click; override in subclass as needed.""" + event.Skip() diff --git a/addon/globalPlugins/MathCAT/__init__.py b/addon/globalPlugins/MathCAT/__init__.py new file mode 100644 index 00000000..4bc4da43 --- /dev/null +++ b/addon/globalPlugins/MathCAT/__init__.py @@ -0,0 +1,71 @@ +# -*- coding: UTF-8 -*- + +""" +MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML. +The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued. +Author: Neil Soiffer +Copyright: this file is copyright GPL2 + The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license + and also (obviously) requires external speech engines and braille drivers. + The plugin also requires the use of a small python dll: python3.dll + python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved +""" + +import globalPluginHandler # we are a global plugin +import globalVars +import mathPres # math plugin stuff +import wx +import addonHandler +from gui import mainFrame +from .MathCAT import MathCAT +from .MathCATPreferences import UserInterface + +# Import the _ function for translation +_ = wx.GetTranslation +addonHandler.initTranslation() +mathPres.registerProvider(MathCAT(), speech=True, braille=True, interaction=True) + + +class GlobalPlugin(globalPluginHandler.GlobalPlugin): + """ + Global plugin for the MathCAT add-on. + """ + + def __init__(self, *args, **kwargs): + """ + Initialize the Global Plugin and add the MathCAT menu. + + :param args: Additional positional arguments. + :param kwargs: Additional keyword arguments. + """ + super().__init__(*args, **kwargs) + # MathCAT.__init__(self) + self.addMathCATMenu() + + def addMathCATMenu(self) -> None: + """ + Adds the MathCAT settings menu to the NVDA preferences. + """ + if not globalVars.appArgs.secure: + self.preferencesMenu = mainFrame.sysTrayIcon.preferencesMenu + # Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog + self.settings = self.preferencesMenu.Append(wx.ID_ANY, _("&MathCAT Settings...")) + mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onSettings, self.settings) + + def onSettings(self, evt: wx.CommandEvent) -> None: + """ + Opens the MathCAT preferences dialog. + + :param evt: The event that triggered this action. + """ + mainFrame.popupSettingsDialog(UserInterface) + + def terminate(self) -> None: + """ + Cleans up by removing the MathCAT menu item upon termination. + """ + try: + if not globalVars.appArgs.secure: + self.preferencesMenu.Remove(self.settings) + except (AttributeError, RuntimeError): + pass diff --git a/addon/globalPlugins/MathCAT/libmathcat_py.pyd b/addon/globalPlugins/MathCAT/libmathcat_py.pyd new file mode 100644 index 00000000..df703f63 Binary files /dev/null and b/addon/globalPlugins/MathCAT/libmathcat_py.pyd differ diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/logo.png b/addon/globalPlugins/MathCAT/logo.png similarity index 100% rename from NVDA-addon/addon/globalPlugins/MathCAT/logo.png rename to addon/globalPlugins/MathCAT/logo.png diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/python3.dll b/addon/globalPlugins/MathCAT/python3.dll similarity index 100% rename from NVDA-addon/addon/globalPlugins/MathCAT/python3.dll rename to addon/globalPlugins/MathCAT/python3.dll diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/__init__.py b/addon/globalPlugins/MathCAT/yaml/__init__.py similarity index 85% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/__init__.py rename to addon/globalPlugins/MathCAT/yaml/__init__.py index 465041dc..87271d0a 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/__init__.py +++ b/addon/globalPlugins/MathCAT/yaml/__init__.py @@ -8,7 +8,7 @@ from .loader import * from .dumper import * -__version__ = '6.0' +__version__ = '6.0.2' try: from .cyaml import * __with_libyaml__ = True @@ -154,9 +154,11 @@ def unsafe_load_all(stream): """ return load_all(stream, UnsafeLoader) -def emit(events, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): +def emit( + events, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, +): """ Emit YAML parsing events into a stream. If stream is None, return the produced string instead. @@ -165,8 +167,10 @@ def emit(events, stream=None, Dumper=Dumper, if stream is None: stream = io.StringIO() getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) + dumper = Dumper( + stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + ) try: for event in events: dumper.emit(event) @@ -175,11 +179,13 @@ def emit(events, stream=None, Dumper=Dumper, if getvalue: return getvalue() -def serialize_all(nodes, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): +def serialize_all( + nodes, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, +): """ Serialize a sequence of representation trees into a YAML stream. If stream is None, return the produced string instead. @@ -191,10 +197,12 @@ def serialize_all(nodes, stream=None, Dumper=Dumper, else: stream = io.BytesIO() getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) + dumper = Dumper( + stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end, + ) try: dumper.open() for node in nodes: @@ -212,12 +220,14 @@ def serialize(node, stream=None, Dumper=Dumper, **kwds): """ return serialize_all([node], stream, Dumper=Dumper, **kwds) -def dump_all(documents, stream=None, Dumper=Dumper, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): +def dump_all( + documents, stream=None, Dumper=Dumper, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, +): """ Serialize a sequence of Python objects into a YAML stream. If stream is None, return the produced string instead. @@ -229,12 +239,14 @@ def dump_all(documents, stream=None, Dumper=Dumper, else: stream = io.BytesIO() getvalue = stream.getvalue - dumper = Dumper(stream, default_style=default_style, - default_flow_style=default_flow_style, - canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys) + dumper = Dumper( + stream, default_style=default_style, + default_flow_style=default_flow_style, + canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys, + ) try: dumper.open() for data in documents: @@ -268,8 +280,10 @@ def safe_dump(data, stream=None, **kwds): """ return dump_all([data], stream, Dumper=SafeDumper, **kwds) -def add_implicit_resolver(tag, regexp, first=None, - Loader=None, Dumper=Dumper): +def add_implicit_resolver( + tag, regexp, first=None, + Loader=None, Dumper=Dumper, +): """ Add an implicit scalar detector. If an implicit scalar value matches the given regexp, @@ -385,6 +399,7 @@ def to_yaml(cls, dumper, data): """ Convert a Python object to a representation node. """ - return dumper.represent_yaml_object(cls.yaml_tag, data, cls, - flow_style=cls.yaml_flow_style) - + return dumper.represent_yaml_object( + cls.yaml_tag, data, cls, + flow_style=cls.yaml_flow_style, + ) diff --git a/addon/globalPlugins/MathCAT/yaml/_yaml.cp312-win32.pyd b/addon/globalPlugins/MathCAT/yaml/_yaml.cp312-win32.pyd new file mode 100644 index 00000000..d8e2cc57 Binary files /dev/null and b/addon/globalPlugins/MathCAT/yaml/_yaml.cp312-win32.pyd differ diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/composer.py b/addon/globalPlugins/MathCAT/yaml/composer.py similarity index 79% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/composer.py rename to addon/globalPlugins/MathCAT/yaml/composer.py index 6d15cb40..1f389f41 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/composer.py +++ b/addon/globalPlugins/MathCAT/yaml/composer.py @@ -38,9 +38,11 @@ def get_single_node(self): # Ensure that the stream contains no more documents. if not self.check_event(StreamEndEvent): event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) + raise ComposerError( + "expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark, + ) # Drop the STREAM-END event. self.get_event() @@ -65,16 +67,20 @@ def compose_node(self, parent, index): event = self.get_event() anchor = event.anchor if anchor not in self.anchors: - raise ComposerError(None, None, "found undefined alias %r" - % anchor, event.start_mark) + raise ComposerError( + None, None, "found undefined alias %r" + % anchor, event.start_mark, + ) return self.anchors[anchor] event = self.peek_event() anchor = event.anchor if anchor is not None: if anchor in self.anchors: - raise ComposerError("found duplicate anchor %r; first occurrence" - % anchor, self.anchors[anchor].start_mark, - "second occurrence", event.start_mark) + raise ComposerError( + "found duplicate anchor %r; first occurrence" + % anchor, self.anchors[anchor].start_mark, + "second occurrence", event.start_mark, + ) self.descend_resolver(parent, index) if self.check_event(ScalarEvent): node = self.compose_scalar_node(anchor) @@ -90,8 +96,10 @@ def compose_scalar_node(self, anchor): tag = event.tag if tag is None or tag == '!': tag = self.resolve(ScalarNode, event.value, event.implicit) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) + node = ScalarNode( + tag, event.value, + event.start_mark, event.end_mark, style=event.style, + ) if anchor is not None: self.anchors[anchor] = node return node @@ -101,9 +109,11 @@ def compose_sequence_node(self, anchor): tag = start_event.tag if tag is None or tag == '!': tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) + node = SequenceNode( + tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style, + ) if anchor is not None: self.anchors[anchor] = node index = 0 @@ -119,9 +129,11 @@ def compose_mapping_node(self, anchor): tag = start_event.tag if tag is None or tag == '!': tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) + node = MappingNode( + tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style, + ) if anchor is not None: self.anchors[anchor] = node while not self.check_event(MappingEndEvent): @@ -136,4 +148,3 @@ def compose_mapping_node(self, anchor): end_event = self.get_event() node.end_mark = end_event.end_mark return node - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/constructor.py b/addon/globalPlugins/MathCAT/yaml/constructor.py similarity index 73% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/constructor.py rename to addon/globalPlugins/MathCAT/yaml/constructor.py index 619acd30..5bb98137 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/constructor.py +++ b/addon/globalPlugins/MathCAT/yaml/constructor.py @@ -5,7 +5,7 @@ 'FullConstructor', 'UnsafeConstructor', 'Constructor', - 'ConstructorError' + 'ConstructorError', ] from .error import * @@ -36,8 +36,10 @@ def check_state_key(self, key): object, to prevent user-controlled methods from being called during deserialization""" if self.get_state_keys_blacklist_regexp().match(key): - raise ConstructorError(None, None, - "blacklisted key '%s' in instance state found" % (key,), None) + raise ConstructorError( + None, None, + "blacklisted key '%s' in instance state found" % (key,), None, + ) def get_data(self): # Construct and return the next document. @@ -71,8 +73,10 @@ def construct_object(self, node, deep=False): old_deep = self.deep_construct self.deep_construct = True if node in self.recursive_objects: - raise ConstructorError(None, None, - "found unconstructable recursive node", node.start_mark) + raise ConstructorError( + None, None, + "found unconstructable recursive node", node.start_mark, + ) self.recursive_objects[node] = None constructor = None tag_suffix = None @@ -116,39 +120,51 @@ def construct_object(self, node, deep=False): def construct_scalar(self, node): if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) + raise ConstructorError( + None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark, + ) return node.value def construct_sequence(self, node, deep=False): if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] + raise ConstructorError( + None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark, + ) + return [ + self.construct_object(child, deep=deep) + for child in node.value + ] def construct_mapping(self, node, deep=False): if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) + raise ConstructorError( + None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark, + ) mapping = {} for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) if not isinstance(key, collections.abc.Hashable): - raise ConstructorError("while constructing a mapping", node.start_mark, - "found unhashable key", key_node.start_mark) + raise ConstructorError( + "while constructing a mapping", node.start_mark, + "found unhashable key", key_node.start_mark, + ) value = self.construct_object(value_node, deep=deep) mapping[key] = value return mapping def construct_pairs(self, node, deep=False): if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) + raise ConstructorError( + None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark, + ) pairs = [] for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) @@ -191,19 +207,23 @@ def flatten_mapping(self, node): submerge = [] for subnode in value_node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" + % subnode.id, subnode.start_mark, + ) self.flatten_mapping(subnode) submerge.append(subnode.value) submerge.reverse() for value in submerge: merge.extend(value) else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) + raise ConstructorError( + "while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, but found %s" + % value_node.id, value_node.start_mark, + ) elif key_node.tag == 'tag:yaml.org,2002:value': key_node.tag = 'tag:yaml.org,2002:str' index += 1 @@ -295,17 +315,21 @@ def construct_yaml_binary(self, node): try: value = self.construct_scalar(node).encode('ascii') except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) + raise ConstructorError( + None, None, + "failed to convert base64 data into ascii: %s" % exc, + node.start_mark, + ) try: if hasattr(base64, 'decodebytes'): return base64.decodebytes(value) else: return base64.decodestring(value) except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) + raise ConstructorError( + None, None, + "failed to decode base64 data: %s" % exc, node.start_mark, + ) timestamp_regexp = re.compile( r'''^(?P[0-9][0-9][0-9][0-9]) @@ -317,7 +341,8 @@ def construct_yaml_binary(self, node): :(?P[0-9][0-9]) (?:\.(?P[0-9]*))? (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) - (?::(?P[0-9][0-9]))?))?)?$''', re.X) + (?::(?P[0-9][0-9]))?))?)?$''', re.X, + ) def construct_yaml_timestamp(self, node): value = self.construct_scalar(node) @@ -347,8 +372,10 @@ def construct_yaml_timestamp(self, node): tzinfo = datetime.timezone(delta) elif values['tz']: tzinfo = datetime.timezone.utc - return datetime.datetime(year, month, day, hour, minute, second, fraction, - tzinfo=tzinfo) + return datetime.datetime( + year, month, day, hour, minute, second, fraction, + tzinfo=tzinfo, + ) def construct_yaml_omap(self, node): # Note: we do not check for duplicate keys, because it's too @@ -356,17 +383,23 @@ def construct_yaml_omap(self, node): omap = [] yield omap if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) + raise ConstructorError( + "while constructing an ordered map", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark, + ) for subnode in node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) + raise ConstructorError( + "while constructing an ordered map", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark, + ) if len(subnode.value) != 1: - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) + raise ConstructorError( + "while constructing an ordered map", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark, + ) key_node, value_node = subnode.value[0] key = self.construct_object(key_node) value = self.construct_object(value_node) @@ -377,17 +410,23 @@ def construct_yaml_pairs(self, node): pairs = [] yield pairs if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) + raise ConstructorError( + "while constructing pairs", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark, + ) for subnode in node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) + raise ConstructorError( + "while constructing pairs", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark, + ) if len(subnode.value) != 1: - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) + raise ConstructorError( + "while constructing pairs", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark, + ) key_node, value_node = subnode.value[0] key = self.construct_object(key_node) value = self.construct_object(value_node) @@ -424,60 +463,76 @@ def construct_yaml_object(self, node, cls): data.__dict__.update(state) def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag, - node.start_mark) + raise ConstructorError( + None, None, + "could not determine a constructor for the tag %r" % node.tag, + node.start_mark, + ) SafeConstructor.add_constructor( 'tag:yaml.org,2002:null', - SafeConstructor.construct_yaml_null) + SafeConstructor.construct_yaml_null, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:bool', - SafeConstructor.construct_yaml_bool) + SafeConstructor.construct_yaml_bool, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:int', - SafeConstructor.construct_yaml_int) + SafeConstructor.construct_yaml_int, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:float', - SafeConstructor.construct_yaml_float) + SafeConstructor.construct_yaml_float, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:binary', - SafeConstructor.construct_yaml_binary) + SafeConstructor.construct_yaml_binary, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:timestamp', - SafeConstructor.construct_yaml_timestamp) + SafeConstructor.construct_yaml_timestamp, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:omap', - SafeConstructor.construct_yaml_omap) + SafeConstructor.construct_yaml_omap, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:pairs', - SafeConstructor.construct_yaml_pairs) + SafeConstructor.construct_yaml_pairs, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:set', - SafeConstructor.construct_yaml_set) + SafeConstructor.construct_yaml_set, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:str', - SafeConstructor.construct_yaml_str) + SafeConstructor.construct_yaml_str, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:seq', - SafeConstructor.construct_yaml_seq) + SafeConstructor.construct_yaml_seq, +) SafeConstructor.add_constructor( 'tag:yaml.org,2002:map', - SafeConstructor.construct_yaml_map) + SafeConstructor.construct_yaml_map, +) -SafeConstructor.add_constructor(None, - SafeConstructor.construct_undefined) +SafeConstructor.add_constructor( + None, + SafeConstructor.construct_undefined, +) class FullConstructor(SafeConstructor): # 'extend' is blacklisted because it is used by @@ -501,17 +556,21 @@ def construct_python_bytes(self, node): try: value = self.construct_scalar(node).encode('ascii') except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) + raise ConstructorError( + None, None, + "failed to convert base64 data into ascii: %s" % exc, + node.start_mark, + ) try: if hasattr(base64, 'decodebytes'): return base64.decodebytes(value) else: return base64.decodestring(value) except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) + raise ConstructorError( + None, None, + "failed to decode base64 data: %s" % exc, node.start_mark, + ) def construct_python_long(self, node): return self.construct_yaml_int(node) @@ -524,23 +583,31 @@ def construct_python_tuple(self, node): def find_python_module(self, name, mark, unsafe=False): if not name: - raise ConstructorError("while constructing a Python module", mark, - "expected non-empty name appended to the tag", mark) + raise ConstructorError( + "while constructing a Python module", mark, + "expected non-empty name appended to the tag", mark, + ) if unsafe: try: __import__(name) except ImportError as exc: - raise ConstructorError("while constructing a Python module", mark, - "cannot find module %r (%s)" % (name, exc), mark) + raise ConstructorError( + "while constructing a Python module", mark, + "cannot find module %r (%s)" % (name, exc), mark, + ) if name not in sys.modules: - raise ConstructorError("while constructing a Python module", mark, - "module %r is not imported" % name, mark) + raise ConstructorError( + "while constructing a Python module", mark, + "module %r is not imported" % name, mark, + ) return sys.modules[name] def find_python_name(self, name, mark, unsafe=False): if not name: - raise ConstructorError("while constructing a Python object", mark, - "expected non-empty name appended to the tag", mark) + raise ConstructorError( + "while constructing a Python object", mark, + "expected non-empty name appended to the tag", mark, + ) if '.' in name: module_name, object_name = name.rsplit('.', 1) else: @@ -550,43 +617,57 @@ def find_python_name(self, name, mark, unsafe=False): try: __import__(module_name) except ImportError as exc: - raise ConstructorError("while constructing a Python object", mark, - "cannot find module %r (%s)" % (module_name, exc), mark) + raise ConstructorError( + "while constructing a Python object", mark, + "cannot find module %r (%s)" % (module_name, exc), mark, + ) if module_name not in sys.modules: - raise ConstructorError("while constructing a Python object", mark, - "module %r is not imported" % module_name, mark) + raise ConstructorError( + "while constructing a Python object", mark, + "module %r is not imported" % module_name, mark, + ) module = sys.modules[module_name] if not hasattr(module, object_name): - raise ConstructorError("while constructing a Python object", mark, - "cannot find %r in the module %r" - % (object_name, module.__name__), mark) + raise ConstructorError( + "while constructing a Python object", mark, + "cannot find %r in the module %r" + % (object_name, module.__name__), mark, + ) return getattr(module, object_name) def construct_python_name(self, suffix, node): value = self.construct_scalar(node) if value: - raise ConstructorError("while constructing a Python name", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) + raise ConstructorError( + "while constructing a Python name", node.start_mark, + "expected the empty value, but found %r" % value, node.start_mark, + ) return self.find_python_name(suffix, node.start_mark) def construct_python_module(self, suffix, node): value = self.construct_scalar(node) if value: - raise ConstructorError("while constructing a Python module", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) + raise ConstructorError( + "while constructing a Python module", node.start_mark, + "expected the empty value, but found %r" % value, node.start_mark, + ) return self.find_python_module(suffix, node.start_mark) - def make_python_instance(self, suffix, node, - args=None, kwds=None, newobj=False, unsafe=False): + def make_python_instance( + self, suffix, node, + args=None, kwds=None, newobj=False, unsafe=False, + ): if not args: args = [] if not kwds: kwds = {} cls = self.find_python_name(suffix, node.start_mark) if not (unsafe or isinstance(cls, type)): - raise ConstructorError("while constructing a Python instance", node.start_mark, - "expected a class, but found %r" % type(cls), - node.start_mark) + raise ConstructorError( + "while constructing a Python instance", node.start_mark, + "expected a class, but found %r" % type(cls), + node.start_mark, + ) if newobj and isinstance(cls, type): return cls.__new__(cls, *args, **kwds) else: @@ -660,55 +741,68 @@ def construct_python_object_new(self, suffix, node): FullConstructor.add_constructor( 'tag:yaml.org,2002:python/none', - FullConstructor.construct_yaml_null) + FullConstructor.construct_yaml_null, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/bool', - FullConstructor.construct_yaml_bool) + FullConstructor.construct_yaml_bool, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/str', - FullConstructor.construct_python_str) + FullConstructor.construct_python_str, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/unicode', - FullConstructor.construct_python_unicode) + FullConstructor.construct_python_unicode, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/bytes', - FullConstructor.construct_python_bytes) + FullConstructor.construct_python_bytes, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/int', - FullConstructor.construct_yaml_int) + FullConstructor.construct_yaml_int, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/long', - FullConstructor.construct_python_long) + FullConstructor.construct_python_long, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/float', - FullConstructor.construct_yaml_float) + FullConstructor.construct_yaml_float, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/complex', - FullConstructor.construct_python_complex) + FullConstructor.construct_python_complex, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/list', - FullConstructor.construct_yaml_seq) + FullConstructor.construct_yaml_seq, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/tuple', - FullConstructor.construct_python_tuple) + FullConstructor.construct_python_tuple, +) FullConstructor.add_constructor( 'tag:yaml.org,2002:python/dict', - FullConstructor.construct_yaml_map) + FullConstructor.construct_yaml_map, +) FullConstructor.add_multi_constructor( 'tag:yaml.org,2002:python/name:', - FullConstructor.construct_python_name) + FullConstructor.construct_python_name, +) class UnsafeConstructor(FullConstructor): @@ -720,27 +814,33 @@ def find_python_name(self, name, mark): def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): return super(UnsafeConstructor, self).make_python_instance( - suffix, node, args, kwds, newobj, unsafe=True) + suffix, node, args, kwds, newobj, unsafe=True, + ) def set_python_instance_state(self, instance, state): return super(UnsafeConstructor, self).set_python_instance_state( - instance, state, unsafe=True) + instance, state, unsafe=True, + ) UnsafeConstructor.add_multi_constructor( 'tag:yaml.org,2002:python/module:', - UnsafeConstructor.construct_python_module) + UnsafeConstructor.construct_python_module, +) UnsafeConstructor.add_multi_constructor( 'tag:yaml.org,2002:python/object:', - UnsafeConstructor.construct_python_object) + UnsafeConstructor.construct_python_object, +) UnsafeConstructor.add_multi_constructor( 'tag:yaml.org,2002:python/object/new:', - UnsafeConstructor.construct_python_object_new) + UnsafeConstructor.construct_python_object_new, +) UnsafeConstructor.add_multi_constructor( 'tag:yaml.org,2002:python/object/apply:', - UnsafeConstructor.construct_python_object_apply) + UnsafeConstructor.construct_python_object_apply, +) # Constructor is same as UnsafeConstructor. Need to leave this in place in case # people have extended it directly. diff --git a/addon/globalPlugins/MathCAT/yaml/cyaml.py b/addon/globalPlugins/MathCAT/yaml/cyaml.py new file mode 100644 index 00000000..946d60cc --- /dev/null +++ b/addon/globalPlugins/MathCAT/yaml/cyaml.py @@ -0,0 +1,118 @@ + +__all__ = [ + 'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader', + 'CBaseDumper', 'CSafeDumper', 'CDumper', +] + +from yaml._yaml import CParser, CEmitter + +from .constructor import * + +from .serializer import * +from .representer import * + +from .resolver import * + +class CBaseLoader(CParser, BaseConstructor, BaseResolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + BaseConstructor.__init__(self) + BaseResolver.__init__(self) + +class CSafeLoader(CParser, SafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + SafeConstructor.__init__(self) + Resolver.__init__(self) + +class CFullLoader(CParser, FullConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + FullConstructor.__init__(self) + Resolver.__init__(self) + +class CUnsafeLoader(CParser, UnsafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + UnsafeConstructor.__init__(self) + Resolver.__init__(self) + +class CLoader(CParser, Constructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + Constructor.__init__(self) + Resolver.__init__(self) + +class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + CEmitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + Representer.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) + +class CSafeDumper(CEmitter, SafeRepresenter, Resolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + CEmitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + SafeRepresenter.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) + +class CDumper(CEmitter, Serializer, Representer, Resolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + CEmitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + Representer.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) diff --git a/addon/globalPlugins/MathCAT/yaml/dumper.py b/addon/globalPlugins/MathCAT/yaml/dumper.py new file mode 100644 index 00000000..585f8f4f --- /dev/null +++ b/addon/globalPlugins/MathCAT/yaml/dumper.py @@ -0,0 +1,85 @@ + +__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] + +from .emitter import * +from .serializer import * +from .representer import * +from .resolver import * + +class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + Emitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + ) + Serializer.__init__( + self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + Representer.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) + +class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + Emitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + ) + Serializer.__init__( + self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + SafeRepresenter.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) + +class Dumper(Emitter, Serializer, Representer, Resolver): + + def __init__( + self, stream, + default_style=None, default_flow_style=False, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None, sort_keys=True, + ): + Emitter.__init__( + self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + ) + Serializer.__init__( + self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags, + ) + Representer.__init__( + self, default_style=default_style, + default_flow_style=default_flow_style, sort_keys=sort_keys, + ) + Resolver.__init__(self) diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/emitter.py b/addon/globalPlugins/MathCAT/yaml/emitter.py similarity index 89% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/emitter.py rename to addon/globalPlugins/MathCAT/yaml/emitter.py index a664d011..a5b73657 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/emitter.py +++ b/addon/globalPlugins/MathCAT/yaml/emitter.py @@ -15,10 +15,12 @@ class EmitterError(YAMLError): pass class ScalarAnalysis: - def __init__(self, scalar, empty, multiline, - allow_flow_plain, allow_block_plain, - allow_single_quoted, allow_double_quoted, - allow_block): + def __init__( + self, scalar, empty, multiline, + allow_flow_plain, allow_block_plain, + allow_single_quoted, allow_double_quoted, + allow_block, + ): self.scalar = scalar self.empty = empty self.multiline = multiline @@ -35,8 +37,10 @@ class Emitter: 'tag:yaml.org,2002:' : '!!', } - def __init__(self, stream, canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): + def __init__( + self, stream, canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + ): # The stream should have the methods `write` and possibly `flush`. self.stream = stream @@ -164,8 +168,10 @@ def expect_stream_start(self): self.write_stream_start() self.state = self.expect_first_document_start else: - raise EmitterError("expected StreamStartEvent, but got %s" - % self.event) + raise EmitterError( + "expected StreamStartEvent, but got %s" + % self.event, + ) def expect_nothing(self): raise EmitterError("expected nothing, but got %s" % self.event) @@ -192,9 +198,11 @@ def expect_document_start(self, first=False): handle_text = self.prepare_tag_handle(handle) prefix_text = self.prepare_tag_prefix(prefix) self.write_tag_directive(handle_text, prefix_text) - implicit = (first and not self.event.explicit and not self.canonical - and not self.event.version and not self.event.tags - and not self.check_empty_document()) + implicit = ( + first and not self.event.explicit and not self.canonical + and not self.event.version and not self.event.tags + and not self.check_empty_document() + ) if not implicit: self.write_indent() self.write_indicator('---', True) @@ -208,8 +216,10 @@ def expect_document_start(self, first=False): self.write_stream_end() self.state = self.expect_nothing else: - raise EmitterError("expected DocumentStartEvent, but got %s" - % self.event) + raise EmitterError( + "expected DocumentStartEvent, but got %s" + % self.event, + ) def expect_document_end(self): if isinstance(self.event, DocumentEndEvent): @@ -220,8 +230,10 @@ def expect_document_end(self): self.flush_stream() self.state = self.expect_document_start else: - raise EmitterError("expected DocumentEndEvent, but got %s" - % self.event) + raise EmitterError( + "expected DocumentEndEvent, but got %s" + % self.event, + ) def expect_document_root(self): self.states.append(self.expect_document_end) @@ -229,8 +241,10 @@ def expect_document_root(self): # Node handlers. - def expect_node(self, root=False, sequence=False, mapping=False, - simple_key=False): + def expect_node( + self, root=False, sequence=False, mapping=False, + simple_key=False, + ): self.root_context = root self.sequence_context = sequence self.mapping_context = mapping @@ -420,19 +434,25 @@ def expect_block_mapping_value(self): # Checkers. def check_empty_sequence(self): - return (isinstance(self.event, SequenceStartEvent) and self.events - and isinstance(self.events[0], SequenceEndEvent)) + return ( + isinstance(self.event, SequenceStartEvent) and self.events + and isinstance(self.events[0], SequenceEndEvent) + ) def check_empty_mapping(self): - return (isinstance(self.event, MappingStartEvent) and self.events - and isinstance(self.events[0], MappingEndEvent)) + return ( + isinstance(self.event, MappingStartEvent) and self.events + and isinstance(self.events[0], MappingEndEvent) + ) def check_empty_document(self): if not isinstance(self.event, DocumentStartEvent) or not self.events: return False event = self.events[0] - return (isinstance(event, ScalarEvent) and event.anchor is None - and event.tag is None and event.implicit and event.value == '') + return ( + isinstance(event, ScalarEvent) and event.anchor is None + and event.tag is None and event.implicit and event.value == '' + ) def check_simple_key(self): length = 0 @@ -449,10 +469,16 @@ def check_simple_key(self): if self.analysis is None: self.analysis = self.analyze_scalar(self.event.value) length += len(self.analysis.scalar) - return (length < 128 and (isinstance(self.event, AliasEvent) - or (isinstance(self.event, ScalarEvent) - and not self.analysis.empty and not self.analysis.multiline) - or self.check_empty_sequence() or self.check_empty_mapping())) + return ( + length < 128 and ( + isinstance(self.event, AliasEvent) + or ( + isinstance(self.event, ScalarEvent) + and not self.analysis.empty and not self.analysis.multiline + ) + or self.check_empty_sequence() or self.check_empty_mapping() + ) + ) # Anchor, Tag, and Scalar processors. @@ -497,18 +523,28 @@ def choose_scalar_style(self): if self.event.style == '"' or self.canonical: return '"' if not self.event.style and self.event.implicit[0]: - if (not (self.simple_key_context and - (self.analysis.empty or self.analysis.multiline)) - and (self.flow_level and self.analysis.allow_flow_plain - or (not self.flow_level and self.analysis.allow_block_plain))): + if ( + not ( + self.simple_key_context and + (self.analysis.empty or self.analysis.multiline) + ) + and ( + self.flow_level and self.analysis.allow_flow_plain + or (not self.flow_level and self.analysis.allow_block_plain) + ) + ): return '' if self.event.style and self.event.style in '|>': - if (not self.flow_level and not self.simple_key_context - and self.analysis.allow_block): + if ( + not self.flow_level and not self.simple_key_context + and self.analysis.allow_block + ): return self.event.style if not self.event.style or self.event.style == '\'': - if (self.analysis.allow_single_quoted and - not (self.simple_key_context and self.analysis.multiline)): + if ( + self.analysis.allow_single_quoted and + not (self.simple_key_context and self.analysis.multiline) + ): return '\'' return '"' @@ -548,10 +584,14 @@ def prepare_tag_handle(self, handle): if handle[0] != '!' or handle[-1] != '!': raise EmitterError("tag handle must start and end with '!': %r" % handle) for ch in handle[1:-1]: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the tag handle: %r" - % (ch, handle)) + if not ( + '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_' + ): + raise EmitterError( + "invalid character %r in the tag handle: %r" + % (ch, handle), + ) return handle def prepare_tag_prefix(self, prefix): @@ -617,20 +657,26 @@ def prepare_anchor(self, anchor): if not anchor: raise EmitterError("anchor must not be empty") for ch in anchor: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the anchor: %r" - % (ch, anchor)) + if not ( + '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ + or ch in '-_' + ): + raise EmitterError( + "invalid character %r in the anchor: %r" + % (ch, anchor), + ) return anchor def analyze_scalar(self, scalar): # Empty scalar is a special case. if not scalar: - return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, - allow_flow_plain=False, allow_block_plain=True, - allow_single_quoted=True, allow_double_quoted=True, - allow_block=False) + return ScalarAnalysis( + scalar=scalar, empty=True, multiline=False, + allow_flow_plain=False, allow_block_plain=True, + allow_single_quoted=True, allow_double_quoted=True, + allow_block=False, + ) # Indicators and special characters. block_indicators = False @@ -655,8 +701,10 @@ def analyze_scalar(self, scalar): preceded_by_whitespace = True # Last character or followed by a whitespace. - followed_by_whitespace = (len(scalar) == 1 or - scalar[1] in '\0 \t\r\n\x85\u2028\u2029') + followed_by_whitespace = ( + len(scalar) == 1 or + scalar[1] in '\0 \t\r\n\x85\u2028\u2029' + ) # The previous character is a space. previous_space = False @@ -697,9 +745,11 @@ def analyze_scalar(self, scalar): if ch in '\n\x85\u2028\u2029': line_breaks = True if not (ch == '\n' or '\x20' <= ch <= '\x7E'): - if (ch == '\x85' or '\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD' - or '\U00010000' <= ch < '\U0010ffff') and ch != '\uFEFF': + if ( + ch == '\x85' or '\xA0' <= ch <= '\uD7FF' + or '\uE000' <= ch <= '\uFFFD' + or '\U00010000' <= ch < '\U0010ffff' + ) and ch != '\uFEFF': unicode_characters = True if not self.allow_unicode: special_characters = True @@ -732,8 +782,10 @@ def analyze_scalar(self, scalar): # Prepare for the next character. index += 1 preceded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') - followed_by_whitespace = (index+1 >= len(scalar) or - scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029') + followed_by_whitespace = ( + index+1 >= len(scalar) or + scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029' + ) # Let's decide what styles are allowed. allow_flow_plain = True @@ -743,8 +795,10 @@ def analyze_scalar(self, scalar): allow_block = True # Leading and trailing whitespaces are bad for plain scalars. - if (leading_space or leading_break - or trailing_space or trailing_break): + if ( + leading_space or leading_break + or trailing_space or trailing_break + ): allow_flow_plain = allow_block_plain = False # We do not permit trailing spaces for block scalars. @@ -775,13 +829,15 @@ def analyze_scalar(self, scalar): if block_indicators: allow_block_plain = False - return ScalarAnalysis(scalar=scalar, - empty=False, multiline=line_breaks, - allow_flow_plain=allow_flow_plain, - allow_block_plain=allow_block_plain, - allow_single_quoted=allow_single_quoted, - allow_double_quoted=allow_double_quoted, - allow_block=allow_block) + return ScalarAnalysis( + scalar=scalar, + empty=False, multiline=line_breaks, + allow_flow_plain=allow_flow_plain, + allow_block_plain=allow_block_plain, + allow_single_quoted=allow_single_quoted, + allow_double_quoted=allow_double_quoted, + allow_block=allow_block, + ) # Writers. @@ -797,8 +853,10 @@ def write_stream_start(self): def write_stream_end(self): self.flush_stream() - def write_indicator(self, indicator, need_whitespace, - whitespace=False, indention=False): + def write_indicator( + self, indicator, need_whitespace, + whitespace=False, indention=False, + ): if self.whitespace or not need_whitespace: data = indicator else: @@ -931,10 +989,16 @@ def write_double_quoted(self, text, split=True): if end < len(text): ch = text[end] if ch is None or ch in '"\\\x85\u2028\u2029\uFEFF' \ - or not ('\x20' <= ch <= '\x7E' - or (self.allow_unicode - and ('\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD'))): + or not ( + '\x20' <= ch <= '\x7E' + or ( + self.allow_unicode + and ( + '\xA0' <= ch <= '\uD7FF' + or '\uE000' <= ch <= '\uFFFD' + ) + ) + ): if start < end: data = text[start:end] self.column += len(data) diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/error.py b/addon/globalPlugins/MathCAT/yaml/error.py similarity index 83% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/error.py rename to addon/globalPlugins/MathCAT/yaml/error.py index b796b4dc..4e203a6e 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/error.py +++ b/addon/globalPlugins/MathCAT/yaml/error.py @@ -47,8 +47,10 @@ class YAMLError(Exception): class MarkedYAMLError(YAMLError): - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): + def __init__( + self, context=None, context_mark=None, + problem=None, problem_mark=None, note=None, + ): self.context = context self.context_mark = context_mark self.problem = problem @@ -60,10 +62,12 @@ def __str__(self): if self.context is not None: lines.append(self.context) if self.context_mark is not None \ - and (self.problem is None or self.problem_mark is None - or self.context_mark.name != self.problem_mark.name - or self.context_mark.line != self.problem_mark.line - or self.context_mark.column != self.problem_mark.column): + and ( + self.problem is None or self.problem_mark is None + or self.context_mark.name != self.problem_mark.name + or self.context_mark.line != self.problem_mark.line + or self.context_mark.column != self.problem_mark.column + ): lines.append(str(self.context_mark)) if self.problem is not None: lines.append(self.problem) @@ -72,4 +76,3 @@ def __str__(self): if self.note is not None: lines.append(self.note) return '\n'.join(lines) - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/events.py b/addon/globalPlugins/MathCAT/yaml/events.py similarity index 71% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/events.py rename to addon/globalPlugins/MathCAT/yaml/events.py index f79ad389..ce211528 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/events.py +++ b/addon/globalPlugins/MathCAT/yaml/events.py @@ -6,10 +6,14 @@ def __init__(self, start_mark=None, end_mark=None): self.start_mark = start_mark self.end_mark = end_mark def __repr__(self): - attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) + attributes = [ + key for key in ['anchor', 'tag', 'implicit', 'value'] + if hasattr(self, key) + ] + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) return '%s(%s)' % (self.__class__.__name__, arguments) class NodeEvent(Event): @@ -19,8 +23,10 @@ def __init__(self, anchor, start_mark=None, end_mark=None): self.end_mark = end_mark class CollectionStartEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, - flow_style=None): + def __init__( + self, anchor, tag, implicit, start_mark=None, end_mark=None, + flow_style=None, + ): self.anchor = anchor self.tag = tag self.implicit = implicit @@ -43,8 +49,10 @@ class StreamEndEvent(Event): pass class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): + def __init__( + self, start_mark=None, end_mark=None, + explicit=None, version=None, tags=None, + ): self.start_mark = start_mark self.end_mark = end_mark self.explicit = explicit @@ -52,8 +60,10 @@ def __init__(self, start_mark=None, end_mark=None, self.tags = tags class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): + def __init__( + self, start_mark=None, end_mark=None, + explicit=None, + ): self.start_mark = start_mark self.end_mark = end_mark self.explicit = explicit @@ -62,8 +72,10 @@ class AliasEvent(NodeEvent): pass class ScalarEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, value, - start_mark=None, end_mark=None, style=None): + def __init__( + self, anchor, tag, implicit, value, + start_mark=None, end_mark=None, style=None, + ): self.anchor = anchor self.tag = tag self.implicit = implicit @@ -83,4 +95,3 @@ class MappingStartEvent(CollectionStartEvent): class MappingEndEvent(CollectionEndEvent): pass - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/loader.py b/addon/globalPlugins/MathCAT/yaml/loader.py similarity index 100% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/loader.py rename to addon/globalPlugins/MathCAT/yaml/loader.py diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/nodes.py b/addon/globalPlugins/MathCAT/yaml/nodes.py similarity index 85% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/nodes.py rename to addon/globalPlugins/MathCAT/yaml/nodes.py index c4f070c4..3ce5469c 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/nodes.py +++ b/addon/globalPlugins/MathCAT/yaml/nodes.py @@ -24,8 +24,10 @@ def __repr__(self): class ScalarNode(Node): id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): + def __init__( + self, tag, value, + start_mark=None, end_mark=None, style=None, + ): self.tag = tag self.value = value self.start_mark = start_mark @@ -33,8 +35,10 @@ def __init__(self, tag, value, self.style = style class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): + def __init__( + self, tag, value, + start_mark=None, end_mark=None, flow_style=None, + ): self.tag = tag self.value = value self.start_mark = start_mark @@ -46,4 +50,3 @@ class SequenceNode(CollectionNode): class MappingNode(CollectionNode): id = 'mapping' - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/parser.py b/addon/globalPlugins/MathCAT/yaml/parser.py similarity index 82% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/parser.py rename to addon/globalPlugins/MathCAT/yaml/parser.py index 13a5995d..afd8fb3f 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/parser.py +++ b/addon/globalPlugins/MathCAT/yaml/parser.py @@ -128,8 +128,10 @@ def parse_stream_start(self): # Parse the stream start. token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) + event = StreamStartEvent( + token.start_mark, token.end_mark, + encoding=token.encoding, + ) # Prepare the next state. self.state = self.parse_implicit_document_start @@ -139,13 +141,17 @@ def parse_stream_start(self): def parse_implicit_document_start(self): # Parse an implicit document. - if not self.check_token(DirectiveToken, DocumentStartToken, - StreamEndToken): + if not self.check_token( + DirectiveToken, DocumentStartToken, + StreamEndToken, + ): self.tag_handles = self.DEFAULT_TAGS token = self.peek_token() start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=False) + event = DocumentStartEvent( + start_mark, end_mark, + explicit=False, + ) # Prepare the next state. self.states.append(self.parse_document_end) @@ -168,14 +174,18 @@ def parse_document_start(self): start_mark = token.start_mark version, tags = self.process_directives() if not self.check_token(DocumentStartToken): - raise ParserError(None, None, - "expected '', but found %r" - % self.peek_token().id, - self.peek_token().start_mark) + raise ParserError( + None, None, + "expected '', but found %r" + % self.peek_token().id, + self.peek_token().start_mark, + ) token = self.get_token() end_mark = token.end_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=True, version=version, tags=tags) + event = DocumentStartEvent( + start_mark, end_mark, + explicit=True, version=version, tags=tags, + ) self.states.append(self.parse_document_end) self.state = self.parse_document_content else: @@ -197,8 +207,10 @@ def parse_document_end(self): token = self.get_token() end_mark = token.end_mark explicit = True - event = DocumentEndEvent(start_mark, end_mark, - explicit=explicit) + event = DocumentEndEvent( + start_mark, end_mark, + explicit=explicit, + ) # Prepare the next state. self.state = self.parse_document_start @@ -206,8 +218,10 @@ def parse_document_end(self): return event def parse_document_content(self): - if self.check_token(DirectiveToken, - DocumentStartToken, DocumentEndToken, StreamEndToken): + if self.check_token( + DirectiveToken, + DocumentStartToken, DocumentEndToken, StreamEndToken, + ): event = self.process_empty_scalar(self.peek_token().start_mark) self.state = self.states.pop() return event @@ -221,20 +235,26 @@ def process_directives(self): token = self.get_token() if token.name == 'YAML': if self.yaml_version is not None: - raise ParserError(None, None, - "found duplicate YAML directive", token.start_mark) + raise ParserError( + None, None, + "found duplicate YAML directive", token.start_mark, + ) major, minor = token.value if major != 1: - raise ParserError(None, None, - "found incompatible YAML document (version 1.* is required)", - token.start_mark) + raise ParserError( + None, None, + "found incompatible YAML document (version 1.* is required)", + token.start_mark, + ) self.yaml_version = token.value elif token.name == 'TAG': handle, prefix = token.value if handle in self.tag_handles: - raise ParserError(None, None, - "duplicate tag handle %r" % handle, - token.start_mark) + raise ParserError( + None, None, + "duplicate tag handle %r" % handle, + token.start_mark, + ) self.tag_handles[handle] = prefix if self.tag_handles: value = self.yaml_version, self.tag_handles.copy() @@ -302,9 +322,11 @@ def parse_node(self, block=False, indentless_sequence=False): handle, suffix = tag if handle is not None: if handle not in self.tag_handles: - raise ParserError("while parsing a node", start_mark, - "found undefined tag handle %r" % handle, - tag_mark) + raise ParserError( + "while parsing a node", start_mark, + "found undefined tag handle %r" % handle, + tag_mark, + ) tag = self.tag_handles[handle]+suffix else: tag = suffix @@ -318,8 +340,10 @@ def parse_node(self, block=False, indentless_sequence=False): implicit = (tag is None or tag == '!') if indentless_sequence and self.check_token(BlockEntryToken): end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark) + event = SequenceStartEvent( + anchor, tag, implicit, + start_mark, end_mark, + ) self.state = self.parse_indentless_sequence_entry else: if self.check_token(ScalarToken): @@ -331,34 +355,46 @@ def parse_node(self, block=False, indentless_sequence=False): implicit = (False, True) else: implicit = (False, False) - event = ScalarEvent(anchor, tag, implicit, token.value, - start_mark, end_mark, style=token.style) + event = ScalarEvent( + anchor, tag, implicit, token.value, + start_mark, end_mark, style=token.style, + ) self.state = self.states.pop() elif self.check_token(FlowSequenceStartToken): end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) + event = SequenceStartEvent( + anchor, tag, implicit, + start_mark, end_mark, flow_style=True, + ) self.state = self.parse_flow_sequence_first_entry elif self.check_token(FlowMappingStartToken): end_mark = self.peek_token().end_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) + event = MappingStartEvent( + anchor, tag, implicit, + start_mark, end_mark, flow_style=True, + ) self.state = self.parse_flow_mapping_first_key elif block and self.check_token(BlockSequenceStartToken): end_mark = self.peek_token().start_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) + event = SequenceStartEvent( + anchor, tag, implicit, + start_mark, end_mark, flow_style=False, + ) self.state = self.parse_block_sequence_first_entry elif block and self.check_token(BlockMappingStartToken): end_mark = self.peek_token().start_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) + event = MappingStartEvent( + anchor, tag, implicit, + start_mark, end_mark, flow_style=False, + ) self.state = self.parse_block_mapping_first_key elif anchor is not None or tag is not None: # Empty scalars are allowed even if a tag or an anchor is # specified. - event = ScalarEvent(anchor, tag, (implicit, False), '', - start_mark, end_mark) + event = ScalarEvent( + anchor, tag, (implicit, False), '', + start_mark, end_mark, + ) self.state = self.states.pop() else: if block: @@ -366,9 +402,11 @@ def parse_node(self, block=False, indentless_sequence=False): else: node = 'flow' token = self.peek_token() - raise ParserError("while parsing a %s node" % node, start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) + raise ParserError( + "while parsing a %s node" % node, start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark, + ) return event # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END @@ -389,8 +427,10 @@ def parse_block_sequence_entry(self): return self.process_empty_scalar(token.end_mark) if not self.check_token(BlockEndToken): token = self.peek_token() - raise ParserError("while parsing a block collection", self.marks[-1], - "expected , but found %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a block collection", self.marks[-1], + "expected , but found %r" % token.id, token.start_mark, + ) token = self.get_token() event = SequenceEndEvent(token.start_mark, token.end_mark) self.state = self.states.pop() @@ -402,8 +442,10 @@ def parse_block_sequence_entry(self): def parse_indentless_sequence_entry(self): if self.check_token(BlockEntryToken): token = self.get_token() - if not self.check_token(BlockEntryToken, - KeyToken, ValueToken, BlockEndToken): + if not self.check_token( + BlockEntryToken, + KeyToken, ValueToken, BlockEndToken, + ): self.states.append(self.parse_indentless_sequence_entry) return self.parse_block_node() else: @@ -435,8 +477,10 @@ def parse_block_mapping_key(self): return self.process_empty_scalar(token.end_mark) if not self.check_token(BlockEndToken): token = self.peek_token() - raise ParserError("while parsing a block mapping", self.marks[-1], - "expected , but found %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a block mapping", self.marks[-1], + "expected , but found %r" % token.id, token.start_mark, + ) token = self.get_token() event = MappingEndEvent(token.start_mark, token.end_mark) self.state = self.states.pop() @@ -480,14 +524,18 @@ def parse_flow_sequence_entry(self, first=False): self.get_token() else: token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) - + raise ParserError( + "while parsing a flow sequence", self.marks[-1], + "expected ',' or ']', but got %r" % token.id, token.start_mark, + ) + if self.check_token(KeyToken): token = self.peek_token() - event = MappingStartEvent(None, None, True, - token.start_mark, token.end_mark, - flow_style=True) + event = MappingStartEvent( + None, None, True, + token.start_mark, token.end_mark, + flow_style=True, + ) self.state = self.parse_flow_sequence_entry_mapping_key return event elif not self.check_token(FlowSequenceEndToken): @@ -501,8 +549,10 @@ def parse_flow_sequence_entry(self, first=False): def parse_flow_sequence_entry_mapping_key(self): token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowSequenceEndToken): + if not self.check_token( + ValueToken, + FlowEntryToken, FlowSequenceEndToken, + ): self.states.append(self.parse_flow_sequence_entry_mapping_value) return self.parse_flow_node() else: @@ -546,12 +596,16 @@ def parse_flow_mapping_key(self, first=False): self.get_token() else: token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) + raise ParserError( + "while parsing a flow mapping", self.marks[-1], + "expected ',' or '}', but got %r" % token.id, token.start_mark, + ) if self.check_token(KeyToken): token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): + if not self.check_token( + ValueToken, + FlowEntryToken, FlowMappingEndToken, + ): self.states.append(self.parse_flow_mapping_value) return self.parse_flow_node() else: @@ -586,4 +640,3 @@ def parse_flow_mapping_empty_value(self): def process_empty_scalar(self, mark): return ScalarEvent(None, None, (True, False), '', mark, mark) - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/reader.py b/addon/globalPlugins/MathCAT/yaml/reader.py similarity index 84% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/reader.py rename to addon/globalPlugins/MathCAT/yaml/reader.py index 774b0219..5509369c 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/reader.py +++ b/addon/globalPlugins/MathCAT/yaml/reader.py @@ -34,13 +34,17 @@ def __str__(self): if isinstance(self.character, bytes): return "'%s' codec can't decode byte #x%02x: %s\n" \ " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) + % ( + self.encoding, ord(self.character), self.reason, + self.name, self.position, + ) else: return "unacceptable character #x%04x: %s\n" \ " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) + % ( + self.character, self.reason, + self.name, self.position, + ) class Reader(object): # Reader: @@ -113,11 +117,15 @@ def forward(self, length=1): def get_mark(self): if self.stream is None: - return Mark(self.name, self.index, self.line, self.column, - self.buffer, self.pointer) + return Mark( + self.name, self.index, self.line, self.column, + self.buffer, self.pointer, + ) else: - return Mark(self.name, self.index, self.line, self.column, - None, None) + return Mark( + self.name, self.index, self.line, self.column, + None, None, + ) def determine_encoding(self): while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): @@ -140,8 +148,10 @@ def check_printable(self, data): if match: character = match.group() position = self.index+(len(self.buffer)-self.pointer)+match.start() - raise ReaderError(self.name, position, ord(character), - 'unicode', "special characters are not allowed") + raise ReaderError( + self.name, position, ord(character), + 'unicode', "special characters are not allowed", + ) def update(self, length): if self.raw_buffer is None: @@ -153,16 +163,20 @@ def update(self, length): self.update_raw() if self.raw_decode is not None: try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) + data, converted = self.raw_decode( + self.raw_buffer, + 'strict', self.eof, + ) except UnicodeDecodeError as exc: character = self.raw_buffer[exc.start] if self.stream is not None: position = self.stream_pointer-len(self.raw_buffer)+exc.start else: position = exc.start - raise ReaderError(self.name, position, character, - exc.encoding, exc.reason) + raise ReaderError( + self.name, position, character, + exc.encoding, exc.reason, + ) else: data = self.raw_buffer converted = len(data) diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/representer.py b/addon/globalPlugins/MathCAT/yaml/representer.py similarity index 86% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/representer.py rename to addon/globalPlugins/MathCAT/yaml/representer.py index 808ca06d..3f6b7dda 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/representer.py +++ b/addon/globalPlugins/MathCAT/yaml/representer.py @@ -1,6 +1,8 @@ -__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', - 'RepresenterError'] +__all__ = [ + 'BaseRepresenter', 'SafeRepresenter', 'Representer', + 'RepresenterError', +] from .error import * from .nodes import * @@ -230,44 +232,70 @@ def represent_yaml_object(self, tag, data, cls, flow_style=None): def represent_undefined(self, data): raise RepresenterError("cannot represent an object", data) -SafeRepresenter.add_representer(type(None), - SafeRepresenter.represent_none) - -SafeRepresenter.add_representer(str, - SafeRepresenter.represent_str) - -SafeRepresenter.add_representer(bytes, - SafeRepresenter.represent_binary) - -SafeRepresenter.add_representer(bool, - SafeRepresenter.represent_bool) - -SafeRepresenter.add_representer(int, - SafeRepresenter.represent_int) - -SafeRepresenter.add_representer(float, - SafeRepresenter.represent_float) - -SafeRepresenter.add_representer(list, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(tuple, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(dict, - SafeRepresenter.represent_dict) - -SafeRepresenter.add_representer(set, - SafeRepresenter.represent_set) - -SafeRepresenter.add_representer(datetime.date, - SafeRepresenter.represent_date) - -SafeRepresenter.add_representer(datetime.datetime, - SafeRepresenter.represent_datetime) - -SafeRepresenter.add_representer(None, - SafeRepresenter.represent_undefined) +SafeRepresenter.add_representer( + type(None), + SafeRepresenter.represent_none, +) + +SafeRepresenter.add_representer( + str, + SafeRepresenter.represent_str, +) + +SafeRepresenter.add_representer( + bytes, + SafeRepresenter.represent_binary, +) + +SafeRepresenter.add_representer( + bool, + SafeRepresenter.represent_bool, +) + +SafeRepresenter.add_representer( + int, + SafeRepresenter.represent_int, +) + +SafeRepresenter.add_representer( + float, + SafeRepresenter.represent_float, +) + +SafeRepresenter.add_representer( + list, + SafeRepresenter.represent_list, +) + +SafeRepresenter.add_representer( + tuple, + SafeRepresenter.represent_list, +) + +SafeRepresenter.add_representer( + dict, + SafeRepresenter.represent_dict, +) + +SafeRepresenter.add_representer( + set, + SafeRepresenter.represent_set, +) + +SafeRepresenter.add_representer( + datetime.date, + SafeRepresenter.represent_date, +) + +SafeRepresenter.add_representer( + datetime.datetime, + SafeRepresenter.represent_datetime, +) + +SafeRepresenter.add_representer( + None, + SafeRepresenter.represent_undefined, +) class Representer(SafeRepresenter): @@ -291,7 +319,8 @@ def represent_name(self, data): def represent_module(self, data): return self.represent_scalar( - 'tag:yaml.org,2002:python/module:'+data.__name__, '') + 'tag:yaml.org,2002:python/module:'+data.__name__, '', + ) def represent_object(self, data): # We use __reduce__ API to save the data. data.__reduce__ returns @@ -340,7 +369,8 @@ def represent_object(self, data): if not args and not listitems and not dictitems \ and isinstance(state, dict) and newobj: return self.represent_mapping( - 'tag:yaml.org,2002:python/object:'+function_name, state) + 'tag:yaml.org,2002:python/object:'+function_name, state, + ) if not listitems and not dictitems \ and isinstance(state, dict) and not state: return self.represent_sequence(tag+function_name, args) @@ -363,27 +393,42 @@ def represent_ordered_dict(self, data): items = [[key, value] for key, value in data.items()] return self.represent_sequence(tag, [items]) -Representer.add_representer(complex, - Representer.represent_complex) - -Representer.add_representer(tuple, - Representer.represent_tuple) - -Representer.add_multi_representer(type, - Representer.represent_name) - -Representer.add_representer(collections.OrderedDict, - Representer.represent_ordered_dict) - -Representer.add_representer(types.FunctionType, - Representer.represent_name) - -Representer.add_representer(types.BuiltinFunctionType, - Representer.represent_name) - -Representer.add_representer(types.ModuleType, - Representer.represent_module) - -Representer.add_multi_representer(object, - Representer.represent_object) - +Representer.add_representer( + complex, + Representer.represent_complex, +) + +Representer.add_representer( + tuple, + Representer.represent_tuple, +) + +Representer.add_multi_representer( + type, + Representer.represent_name, +) + +Representer.add_representer( + collections.OrderedDict, + Representer.represent_ordered_dict, +) + +Representer.add_representer( + types.FunctionType, + Representer.represent_name, +) + +Representer.add_representer( + types.BuiltinFunctionType, + Representer.represent_name, +) + +Representer.add_representer( + types.ModuleType, + Representer.represent_module, +) + +Representer.add_multi_representer( + object, + Representer.represent_object, +) diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/resolver.py b/addon/globalPlugins/MathCAT/yaml/resolver.py similarity index 87% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/resolver.py rename to addon/globalPlugins/MathCAT/yaml/resolver.py index 3522bdaa..4b255b71 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/resolver.py +++ b/addon/globalPlugins/MathCAT/yaml/resolver.py @@ -96,8 +96,10 @@ def descend_resolver(self, current_node, current_index): if current_node: depth = len(self.resolver_prefix_paths) for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): + if self.check_resolver_prefix( + depth, path, kind, + current_node, current_index, + ): if len(path) > depth: prefix_paths.append((path, kind)) else: @@ -117,8 +119,10 @@ def ascend_resolver(self): self.resolver_exact_paths.pop() self.resolver_prefix_paths.pop() - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): + def check_resolver_prefix( + self, depth, path, kind, + current_node, current_index, + ): node_check, index_check = path[depth-1] if isinstance(node_check, str): if current_node.tag != node_check: @@ -132,8 +136,10 @@ def check_resolver_prefix(self, depth, path, kind, and current_index is None: return if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): + if not ( + isinstance(current_index, ScalarNode) + and index_check == current_index.value + ): return elif isinstance(index_check, int) and not isinstance(index_check, bool): if index_check != current_index: @@ -169,59 +175,76 @@ class Resolver(BaseResolver): Resolver.add_implicit_resolver( 'tag:yaml.org,2002:bool', - re.compile(r'''^(?:yes|Yes|YES|no|No|NO + re.compile( + r'''^(?:yes|Yes|YES|no|No|NO |true|True|TRUE|false|False|FALSE - |on|On|ON|off|Off|OFF)$''', re.X), - list('yYnNtTfFoO')) + |on|On|ON|off|Off|OFF)$''', re.X, + ), + list('yYnNtTfFoO'), +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:float', - re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? + re.compile( + r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? |\.[0-9][0-9_]*(?:[eE][-+][0-9]+)? |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* |[-+]?\.(?:inf|Inf|INF) - |\.(?:nan|NaN|NAN))$''', re.X), - list('-+0123456789.')) + |\.(?:nan|NaN|NAN))$''', re.X, + ), + list('-+0123456789.'), +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:int', - re.compile(r'''^(?:[-+]?0b[0-1_]+ + re.compile( + r'''^(?:[-+]?0b[0-1_]+ |[-+]?0[0-7_]+ |[-+]?(?:0|[1-9][0-9_]*) |[-+]?0x[0-9a-fA-F_]+ - |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), - list('-+0123456789')) + |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X, + ), + list('-+0123456789'), +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:merge', re.compile(r'^(?:<<)$'), - ['<']) + ['<'], +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:null', - re.compile(r'''^(?: ~ + re.compile( + r'''^(?: ~ |null|Null|NULL - | )$''', re.X), - ['~', 'n', 'N', '']) + | )$''', re.X, + ), + ['~', 'n', 'N', ''], +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:timestamp', - re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] + re.compile( + r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? (?:[Tt]|[ \t]+)[0-9][0-9]? :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? - (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), - list('0123456789')) + (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X, + ), + list('0123456789'), +) Resolver.add_implicit_resolver( 'tag:yaml.org,2002:value', re.compile(r'^(?:=)$'), - ['=']) + ['='], +) # The following resolver is only for documentation purposes. It cannot work # because plain scalars cannot start with '!', '&', or '*'. Resolver.add_implicit_resolver( 'tag:yaml.org,2002:yaml', re.compile(r'^(?:!|&|\*)$'), - list('!&*')) - + list('!&*'), +) diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/scanner.py b/addon/globalPlugins/MathCAT/yaml/scanner.py similarity index 86% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/scanner.py rename to addon/globalPlugins/MathCAT/yaml/scanner.py index de925b07..39e2791a 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/scanner.py +++ b/addon/globalPlugins/MathCAT/yaml/scanner.py @@ -255,9 +255,11 @@ def fetch_more_tokens(self): return self.fetch_plain() # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" % ch, - self.get_mark()) + raise ScannerError( + "while scanning for the next token", None, + "found character %r that cannot start any token" % ch, + self.get_mark(), + ) # Simple keys treatment. @@ -288,8 +290,10 @@ def stale_possible_simple_keys(self): if key.line != self.line \ or self.index-key.index > 1024: if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not find expected ':'", self.get_mark()) + raise ScannerError( + "while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark(), + ) del self.possible_simple_keys[level] def save_possible_simple_key(self): @@ -305,18 +309,22 @@ def save_possible_simple_key(self): if self.allow_simple_key: self.remove_possible_simple_key() token_number = self.tokens_taken+len(self.tokens) - key = SimpleKey(token_number, required, - self.index, self.line, self.column, self.get_mark()) + key = SimpleKey( + token_number, required, + self.index, self.line, self.column, self.get_mark(), + ) self.possible_simple_keys[self.flow_level] = key def remove_possible_simple_key(self): # Remove the saved possible key position at the current flow level. if self.flow_level in self.possible_simple_keys: key = self.possible_simple_keys[self.flow_level] - + if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not find expected ':'", self.get_mark()) + raise ScannerError( + "while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark(), + ) del self.possible_simple_keys[self.flow_level] @@ -362,11 +370,15 @@ def fetch_stream_start(self): # Read the token. mark = self.get_mark() - + # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) - + self.tokens.append( + StreamStartToken( + mark, mark, + encoding=self.encoding, + ), + ) + def fetch_stream_end(self): @@ -380,7 +392,7 @@ def fetch_stream_end(self): # Read the token. mark = self.get_mark() - + # Add STREAM-END. self.tokens.append(StreamEndToken(mark, mark)) @@ -388,7 +400,7 @@ def fetch_stream_end(self): self.done = True def fetch_directive(self): - + # Set the current indentation to -1. self.unwind_indent(-1) @@ -488,9 +500,11 @@ def fetch_block_entry(self): # Are we allowed to start a new entry? if not self.allow_simple_key: - raise ScannerError(None, None, - "sequence entries are not allowed here", - self.get_mark()) + raise ScannerError( + None, None, + "sequence entries are not allowed here", + self.get_mark(), + ) # We may need to add BLOCK-SEQUENCE-START. if self.add_indent(self.column): @@ -515,15 +529,17 @@ def fetch_block_entry(self): self.tokens.append(BlockEntryToken(start_mark, end_mark)) def fetch_key(self): - + # Block context needs additional checks. if not self.flow_level: # Are we allowed to start a key (not necessary a simple)? if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping keys are not allowed here", - self.get_mark()) + raise ScannerError( + None, None, + "mapping keys are not allowed here", + self.get_mark(), + ) # We may need to add BLOCK-MAPPING-START. if self.add_indent(self.column): @@ -550,22 +566,26 @@ def fetch_value(self): # Add KEY. key = self.possible_simple_keys[self.flow_level] del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number-self.tokens_taken, - KeyToken(key.mark, key.mark)) + self.tokens.insert( + key.token_number-self.tokens_taken, + KeyToken(key.mark, key.mark), + ) # If this key starts a new block mapping, we need to add # BLOCK-MAPPING-START. if not self.flow_level: if self.add_indent(key.column): - self.tokens.insert(key.token_number-self.tokens_taken, - BlockMappingStartToken(key.mark, key.mark)) + self.tokens.insert( + key.token_number-self.tokens_taken, + BlockMappingStartToken(key.mark, key.mark), + ) # There cannot be two simple keys one after another. self.allow_simple_key = False # It must be a part of a complex key. else: - + # Block context needs additional checks. # (Do we really need them? They will be caught by the parser # anyway.) @@ -574,9 +594,11 @@ def fetch_value(self): # We are allowed to start a complex value if and only if # we can start a simple key. if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping values are not allowed here", - self.get_mark()) + raise ScannerError( + None, None, + "mapping values are not allowed here", + self.get_mark(), + ) # If this value starts a new block mapping, we need to add # BLOCK-MAPPING-START. It will be detected as an error later by @@ -744,8 +766,10 @@ def check_plain(self): # independent. ch = self.peek() return ch not in '\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ - or (self.peek(1) not in '\0 \t\r\n\x85\u2028\u2029' - and (ch == '-' or (not self.flow_level and ch in '?:'))) + or ( + self.peek(1) not in '\0 \t\r\n\x85\u2028\u2029' + and (ch == '-' or (not self.flow_level and ch in '?:')) + ) # Scanners. @@ -812,16 +836,20 @@ def scan_directive_name(self, start_mark): length += 1 ch = self.peek(length) if not length: - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark(), + ) value = self.prefix(length) self.forward(length) ch = self.peek() if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark(), + ) return value def scan_yaml_directive_value(self, start_mark): @@ -830,23 +858,29 @@ def scan_yaml_directive_value(self, start_mark): self.forward() major = self.scan_yaml_directive_number(start_mark) if self.peek() != '.': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or '.', but found %r" % self.peek(), - self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected a digit or '.', but found %r" % self.peek(), + self.get_mark(), + ) self.forward() minor = self.scan_yaml_directive_number(start_mark) if self.peek() not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or ' ', but found %r" % self.peek(), - self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected a digit or ' ', but found %r" % self.peek(), + self.get_mark(), + ) return (major, minor) def scan_yaml_directive_number(self, start_mark): # See the specification for details. ch = self.peek() if not ('0' <= ch <= '9'): - raise ScannerError("while scanning a directive", start_mark, - "expected a digit, but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected a digit, but found %r" % ch, self.get_mark(), + ) length = 0 while '0' <= self.peek(length) <= '9': length += 1 @@ -869,8 +903,10 @@ def scan_tag_directive_handle(self, start_mark): value = self.scan_tag_handle('directive', start_mark) ch = self.peek() if ch != ' ': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected ' ', but found %r" % ch, self.get_mark(), + ) return value def scan_tag_directive_prefix(self, start_mark): @@ -878,8 +914,10 @@ def scan_tag_directive_prefix(self, start_mark): value = self.scan_tag_uri('directive', start_mark) ch = self.peek() if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected ' ', but found %r" % ch, self.get_mark(), + ) return value def scan_directive_ignored_line(self, start_mark): @@ -891,9 +929,11 @@ def scan_directive_ignored_line(self, start_mark): self.forward() ch = self.peek() if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a comment or a line break, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning a directive", start_mark, + "expected a comment or a line break, but found %r" + % ch, self.get_mark(), + ) self.scan_line_break() def scan_anchor(self, TokenClass): @@ -919,16 +959,20 @@ def scan_anchor(self, TokenClass): length += 1 ch = self.peek(length) if not length: - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark(), + ) value = self.prefix(length) self.forward(length) ch = self.peek() if ch not in '\0 \t\r\n\x85\u2028\u2029?:,]}%@`': - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch, self.get_mark(), + ) end_mark = self.get_mark() return TokenClass(value, start_mark, end_mark) @@ -941,9 +985,11 @@ def scan_tag(self): self.forward(2) suffix = self.scan_tag_uri('tag', start_mark) if self.peek() != '>': - raise ScannerError("while parsing a tag", start_mark, - "expected '>', but found %r" % self.peek(), - self.get_mark()) + raise ScannerError( + "while parsing a tag", start_mark, + "expected '>', but found %r" % self.peek(), + self.get_mark(), + ) self.forward() elif ch in '\0 \t\r\n\x85\u2028\u2029': handle = None @@ -967,8 +1013,10 @@ def scan_tag(self): suffix = self.scan_tag_uri('tag', start_mark) ch = self.peek() if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a tag", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a tag", start_mark, + "expected ' ', but found %r" % ch, self.get_mark(), + ) value = (handle, suffix) end_mark = self.get_mark() return TagToken(value, start_mark, end_mark) @@ -1017,14 +1065,14 @@ def scan_block_scalar(self, style): # Unfortunately, folding rules are ambiguous. # # This is the folding according to the specification: - + if folded and line_break == '\n' \ and leading_non_space and self.peek() not in ' \t': if not breaks: chunks.append(' ') else: chunks.append(line_break) - + # This is Clark Evans's interpretation (also in the spec # examples): # @@ -1046,8 +1094,10 @@ def scan_block_scalar(self, style): chunks.extend(breaks) # We are done. - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) + return ScalarToken( + ''.join(chunks), False, start_mark, end_mark, + style, + ) def scan_block_scalar_indicators(self, start_mark): # See the specification for details. @@ -1064,16 +1114,20 @@ def scan_block_scalar_indicators(self, start_mark): if ch in '0123456789': increment = int(ch) if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) + raise ScannerError( + "while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark(), + ) self.forward() elif ch in '0123456789': increment = int(ch) if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) + raise ScannerError( + "while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark(), + ) self.forward() ch = self.peek() if ch in '+-': @@ -1084,9 +1138,11 @@ def scan_block_scalar_indicators(self, start_mark): self.forward() ch = self.peek() if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected chomping or indentation indicators, but found %r" - % ch, self.get_mark()) + raise ScannerError( + "while scanning a block scalar", start_mark, + "expected chomping or indentation indicators, but found %r" + % ch, self.get_mark(), + ) return chomping, increment def scan_block_scalar_ignored_line(self, start_mark): @@ -1098,9 +1154,11 @@ def scan_block_scalar_ignored_line(self, start_mark): self.forward() ch = self.peek() if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected a comment or a line break, but found %r" % ch, - self.get_mark()) + raise ScannerError( + "while scanning a block scalar", start_mark, + "expected a comment or a line break, but found %r" % ch, + self.get_mark(), + ) self.scan_line_break() def scan_block_scalar_indentation(self): @@ -1152,8 +1210,10 @@ def scan_flow_scalar(self, style): chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) self.forward() end_mark = self.get_mark() - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) + return ScalarToken( + ''.join(chunks), False, start_mark, end_mark, + style, + ) ESCAPE_REPLACEMENTS = { '0': '\0', @@ -1210,9 +1270,11 @@ def scan_flow_scalar_non_spaces(self, double, start_mark): self.forward() for k in range(length): if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexadecimal numbers, but found %r" % - (length, self.peek(k)), self.get_mark()) + raise ScannerError( + "while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexadecimal numbers, but found %r" % + (length, self.peek(k)), self.get_mark(), + ) code = int(self.prefix(length), 16) chunks.append(chr(code)) self.forward(length) @@ -1220,8 +1282,10 @@ def scan_flow_scalar_non_spaces(self, double, start_mark): self.scan_line_break() chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch, self.get_mark(), + ) else: return chunks @@ -1235,8 +1299,10 @@ def scan_flow_scalar_spaces(self, double, start_mark): self.forward(length) ch = self.peek() if ch == '\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) + raise ScannerError( + "while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark(), + ) elif ch in '\r\n\x85\u2028\u2029': line_break = self.scan_line_break() breaks = self.scan_flow_scalar_breaks(double, start_mark) @@ -1258,8 +1324,10 @@ def scan_flow_scalar_breaks(self, double, start_mark): prefix = self.prefix(3) if (prefix == '---' or prefix == '...') \ and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected document separator", self.get_mark()) + raise ScannerError( + "while scanning a quoted scalar", start_mark, + "found unexpected document separator", self.get_mark(), + ) while self.peek() in ' \t': self.forward() if self.peek() in '\r\n\x85\u2028\u2029': @@ -1289,9 +1357,11 @@ def scan_plain(self): while True: ch = self.peek(length) if ch in '\0 \t\r\n\x85\u2028\u2029' \ - or (ch == ':' and - self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029' - + (u',[]{}' if self.flow_level else u''))\ + or ( + ch == ':' and + self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029' + + (u',[]{}' if self.flow_level else u'') + )\ or (self.flow_level and ch in ',?[]{}'): break length += 1 @@ -1351,8 +1421,10 @@ def scan_tag_handle(self, name, start_mark): # tag handles. I have allowed it anyway. ch = self.peek() if ch != '!': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch, self.get_mark(), + ) length = 1 ch = self.peek(length) if ch != ' ': @@ -1362,8 +1434,10 @@ def scan_tag_handle(self, name, start_mark): ch = self.peek(length) if ch != '!': self.forward(length) - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) + raise ScannerError( + "while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch, self.get_mark(), + ) length += 1 value = self.prefix(length) self.forward(length) @@ -1390,8 +1464,10 @@ def scan_tag_uri(self, name, start_mark): self.forward(length) length = 0 if not chunks: - raise ScannerError("while parsing a %s" % name, start_mark, - "expected URI, but found %r" % ch, self.get_mark()) + raise ScannerError( + "while parsing a %s" % name, start_mark, + "expected URI, but found %r" % ch, self.get_mark(), + ) return ''.join(chunks) def scan_uri_escapes(self, name, start_mark): @@ -1402,9 +1478,11 @@ def scan_uri_escapes(self, name, start_mark): self.forward() for k in range(2): if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected URI escape sequence of 2 hexadecimal numbers, but found %r" - % self.peek(k), self.get_mark()) + raise ScannerError( + "while scanning a %s" % name, start_mark, + "expected URI escape sequence of 2 hexadecimal numbers, but found %r" + % self.peek(k), self.get_mark(), + ) codes.append(int(self.prefix(2), 16)) self.forward(2) try: diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/serializer.py b/addon/globalPlugins/MathCAT/yaml/serializer.py similarity index 73% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/serializer.py rename to addon/globalPlugins/MathCAT/yaml/serializer.py index fe911e67..1962fb16 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/serializer.py +++ b/addon/globalPlugins/MathCAT/yaml/serializer.py @@ -12,8 +12,10 @@ class Serializer: ANCHOR_TEMPLATE = 'id%03d' - def __init__(self, encoding=None, - explicit_start=None, explicit_end=None, version=None, tags=None): + def __init__( + self, encoding=None, + explicit_start=None, explicit_end=None, version=None, tags=None, + ): self.use_encoding = encoding self.use_explicit_start = explicit_start self.use_explicit_end = explicit_end @@ -48,8 +50,12 @@ def serialize(self, node): raise SerializerError("serializer is not opened") elif self.closed: raise SerializerError("serializer is closed") - self.emit(DocumentStartEvent(explicit=self.use_explicit_start, - version=self.use_version, tags=self.use_tags)) + self.emit( + DocumentStartEvent( + explicit=self.use_explicit_start, + version=self.use_version, tags=self.use_tags, + ), + ) self.anchor_node(node) self.serialize_node(node, None, None) self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) @@ -86,26 +92,41 @@ def serialize_node(self, node, parent, index): detected_tag = self.resolve(ScalarNode, node.value, (True, False)) default_tag = self.resolve(ScalarNode, node.value, (False, True)) implicit = (node.tag == detected_tag), (node.tag == default_tag) - self.emit(ScalarEvent(alias, node.tag, implicit, node.value, - style=node.style)) + self.emit( + ScalarEvent( + alias, node.tag, implicit, node.value, + style=node.style, + ), + ) elif isinstance(node, SequenceNode): - implicit = (node.tag - == self.resolve(SequenceNode, node.value, True)) - self.emit(SequenceStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) + implicit = ( + node.tag + == self.resolve(SequenceNode, node.value, True) + ) + self.emit( + SequenceStartEvent( + alias, node.tag, implicit, + flow_style=node.flow_style, + ), + ) index = 0 for item in node.value: self.serialize_node(item, node, index) index += 1 self.emit(SequenceEndEvent()) elif isinstance(node, MappingNode): - implicit = (node.tag - == self.resolve(MappingNode, node.value, True)) - self.emit(MappingStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) + implicit = ( + node.tag + == self.resolve(MappingNode, node.value, True) + ) + self.emit( + MappingStartEvent( + alias, node.tag, implicit, + flow_style=node.flow_style, + ), + ) for key, value in node.value: self.serialize_node(key, node, None) self.serialize_node(value, node, key) self.emit(MappingEndEvent()) self.ascend_resolver() - diff --git a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/tokens.py b/addon/globalPlugins/MathCAT/yaml/tokens.py similarity index 87% rename from NVDA-addon/addon/globalPlugins/MathCAT/yaml/tokens.py rename to addon/globalPlugins/MathCAT/yaml/tokens.py index 4d0b48a3..f3111adf 100644 --- a/NVDA-addon/addon/globalPlugins/MathCAT/yaml/tokens.py +++ b/addon/globalPlugins/MathCAT/yaml/tokens.py @@ -4,11 +4,15 @@ def __init__(self, start_mark, end_mark): self.start_mark = start_mark self.end_mark = end_mark def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] + attributes = [ + key for key in self.__dict__ + if not key.endswith('_mark') + ] attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) return '%s(%s)' % (self.__class__.__name__, arguments) #class BOMToken(Token): @@ -30,8 +34,10 @@ class DocumentEndToken(Token): class StreamStartToken(Token): id = '' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): + def __init__( + self, start_mark=None, end_mark=None, + encoding=None, + ): self.start_mark = start_mark self.end_mark = end_mark self.encoding = encoding @@ -101,4 +107,3 @@ def __init__(self, value, plain, start_mark, end_mark, style=None): self.start_mark = start_mark self.end_mark = end_mark self.style = style - diff --git a/addon/locale/de/LC_MESSAGES/nvda.po b/addon/locale/de/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..b2e380e0 --- /dev/null +++ b/addon/locale/de/LC_MESSAGES/nvda.po @@ -0,0 +1,372 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2024-07-14 06:30+0200\n" +"Last-Translator: René Linke \n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Fehler beim Starten der mathematischen Navigation: Schauen Sie für Details " +"bitte im Fehlerprotokoll von NVDA nach" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Fehler in der mathematischen Braille-Darstellung: Schauen Sie für Details " +"bitte im Fehlerprotokoll von NVDA nach" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Fehler in der mathematischen Navigation: Schauen Sie für Details bitte im " +"Fehlerprotokoll von NVDA nach" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Navigationsfokus in die Zwischenablage kopieren" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Zwischenablage" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "Kopieren als " + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"Es konnte die mathematische Formel nicht kopiert werden: Schauen Sie für " +"Details bitte im Fehlerprotokoll von NVDA nach" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"Die Initialisierung von MathCAT ist fehlgeschlagen: Schauen Sie für Details " +"bitte im Fehlerprotokoll von NVDA nach" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"Ungültiges MathML gefunden: Schauen Sie für Details bitte im Fehlerprotokoll " +"von NVDA nach" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Fehler beim Vorlesen der mathematischen Formel: Schauen Sie für Details " +"bitte im Fehlerprotokoll von NVDA nach" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Die Sprache der Stimme verwenden (Automatisch)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"Die Quadratwurzel aus x zum Quadrat plus y zum Quadrat" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"Der Bruch mit Zähler x zum n ten " +"Teil Bruchstrich plus 1 " +" und Nenner x zum n ten TeilBruchstrich " +" minus 1 " +"Ende des Bruchs " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "MathCAT-Einstellungen" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Kategorien:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Sprachausgabe" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navigation" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Vorlesen generieren für:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Lernbehinderte" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Blinde" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Sehbehinderte" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Sprache:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "Dezimaltrennzeichen für Zahlen:" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "Automatisch" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Benutzerdefiniert" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Sprachausgabe:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Sprachausführlichkeit:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Kurz und bündig" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Medium" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Ausführlich" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Relative Sprechgeschwindigkeit:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Pausen-Faktor:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "" +"Einen Sound wiedergeben, sobald eine mathematische Formel beginnt oder endet" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Zu verwendender Themenbereich, sofern dieser nicht automatisch bestimmt " +"werden kann:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Allgemein" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Chemische Formeln sprechen:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Lautschrift (H sub 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Aus (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Navigationsmodus für den Beginn der Navigation in einer Gleichung:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Erweitert" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Einfach" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Zeichen" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Navigationsmodus bei Eingabe eines Ausdrucks zurücksetzen" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "" +"Sprache bei der Navigation für den Beginn der Navigation in einer Gleichung:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Vorlesen" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Beschreibung/Übersicht" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Sprache bei der Navigation bei Eingabe eines Ausdrucks zurücksetzen" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Automatisches Herauszoomen von 2D-Notationen" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Sprachausführlichkeit für die Navigation:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Mathematische Formel kopieren als:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "MathML" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "LaTeX" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "ASCIIMath" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Mathematischer Braille-Code für aktualisierbare Braillezeilen:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Mit Punkten 7 &und 8 den aktuellen Navigationsknoten markieren:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Aus" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Erstes Zeichen" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Endpunkte" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Alles" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "OK" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Abbrechen" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Übernehmen" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Auf Standardwerte zurücksetzen" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Hilfe" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "&MathCAT-Einstellungen..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT: Sprachausgabe und Braille aus MathML" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT ist ein Ersatz für den MathPlayer, der nicht mehr weiterentwickelt " +"wird.\n" +" Es bietet Unterstützung via Sprachausgaben und Braille und es werden " +"auch die drei Navigationsmodi von MathPlayer unterstützt.\n" +" Die Sprachqualität ist noch nicht ganz so gut wie die vom " +"MathPlayer,\n" +" aber die Braille-Unterstützung ist viel besser und umfasst " +"Unterstützung für Nemeth, UEB Technical, CMU (Spanisch/Portugiesisch),\n" +" und vietnamesischen Braille-Code-Standards. Es gibt Übersetzungen " +"ins Chinesische (traditionell), Indonesische, Spanische und Vietnamesische.\n" +" und weitere Übersetzungen sind in Arbeit." diff --git a/addon/locale/es/LC_MESSAGES/nvda.po b/addon/locale/es/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..ced7c6e8 --- /dev/null +++ b/addon/locale/es/LC_MESSAGES/nvda.po @@ -0,0 +1,368 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2024-06-21 19:33+0200\n" +"Last-Translator: José Manuel Delicado \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Error al iniciar la navegación matemática: consulta el registro de NVDA para " +"más detalles" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Error al mostrar matemáticas en braille: consulta el registro de NVDA para " +"más detalles" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Error al navegar por el contenido matemático: consulta el registro de NVDA " +"para más detalles" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Copiar el foco de navegación al portapapeles" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Portapapeles" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "copiar como " + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"no se ha podido copiar el contenido matemático: consulta el registro de NVDA " +"para más detalles" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"Ha fallado la inicialización de MathCat: consulta el registro de NVDA para " +"más detalles" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"Se ha encontrado MathML no válido: consulta el registro de NVDA para más " +"detalles" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Error al verbalizar el contenido matemático: consulta el registro de NVDA " +"para más detalles" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Utilizar idioma de la voz (automático)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"la raíz cuadrada de x al cuadrado más y al cuadrado" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"la fracción con numerador x a la n -ésima potencia más 1 y " +"denominador x a la " +"n -" +"ésimapotencia " +" menos 1 " +"fin de fracción " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "Preferencias de MathCat" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Categorías:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Voz" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navegación" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Generar mensajes hablados para:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Discapacidad cognitiva" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Ceguera" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Baja visión" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Idioma:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "Separador decimal de números:" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "Automático" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Personalizado" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Estilo del habla:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Verbosidad del habla:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Breve" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Media" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Detallada" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Velocidad relativa de la voz:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Factor de pausa:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "" +"Reproducir un sonido al empezar y terminar de verbalizar contenido matemático" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Área temática a utilizar cuando no se puede determinar automáticamente:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "General" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Verbalización de fórmulas químicas:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Deletrear (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Desactivada (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Modo de navegación a usar al empezar a navegar por una ecuación:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Mejorado" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Simple" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Carácter" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Restablecer el modo de navegación al entrar en una expresión" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "Modo de voz a usar cuando se empieza a navegar por una ecuación:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Verbalizar" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Describir/resumir" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "" +"Restablecer los ajustes de voz de navegación al entrar en una expresión" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Alejarse automáticamente en notaciones 2D" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Cantidad de habla durante la navegación:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Copiar matemáticas como:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "MathML" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "LaTeX" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "ASCIIMath" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Código matemático para pantallas braille:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Resaltar con los puntos 7 y 8 el nodo actual de navegación:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Desactivado" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Primer carácter" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Extremos" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Todo" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "Aceptar" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Cancelar" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Aplicar" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Restaurar valores por defecto" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Ayuda" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "Opciones de &MathCat..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCat: voz y braille a partir de MathML" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT es un sustituto de MathPlayer, que ha sido descontinuado.\n" +" Proporciona soporte de voz y braille, y también soporta los tres " +"modos de navegación de MathPlayer.\n" +" La calidad de la voz aún no es tan buena como la de MathPlayer,\n" +" pero el soporte braille es mucho mejor e incluye Nemeth, UEB " +"Técnico, CMU (español / portugués)\n" +" y braille vietnamita. Existen traducciones al chino (tradicional), " +"indonesio, español y vietnamita,\n" +" y hay otras traducciones en progreso." diff --git a/addon/locale/fi/LC_MESSAGES/nvda.po b/addon/locale/fi/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..3c1345a4 --- /dev/null +++ b/addon/locale/fi/LC_MESSAGES/nvda.po @@ -0,0 +1,388 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2024-06-11 09:50+0300\n" +"Last-Translator: Sami Määttä \n" +"Language-Team: fi_FI \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.4.4\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Virhe matemaattisen yhtälön navigoinnin aloittamisessa: Katso tarkemmat " +"tiedot NVDA:n virhelokista" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Virhe matemaattisen yhtälön näyttämisessä pistekirjoituksena: Katso " +"tarkemmat tiedot NVDA:n virhelokista" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Virhe matemaattisen yhtälön navigoinnissa: Katso tarkemmat tiedot NVDA:n " +"virhelokista" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Kopioi navigoinnin kohdistus leikepöydälle" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Leikepöytä" + +#. Translators: copy to clipboard +#, fuzzy +msgid "copy as " +msgstr "Kopioi" + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"Matemaattista yhtälöä ei voi kopioida: Katso tarkemmat tiedot NVDA:n " +"virhelokista" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"MathCATin alustus epäonnistui: Katso tarkemmat tiedot NVDA:n virhelokista" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"Löytyi virheellinen MathML-merkintä: Katso tarkemmat tiedot NVDA:n " +"virhelokista" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Virhe puhuttaessa matemaattista yhtälöä: Katso tarkemmat tiedot NVDA:n " +"virhelokista" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Käytä puheäänen kieltä (automaattinen)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "neliöjuuri x neliö plus y neliö" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"murtoluku jonka osoittaja on x n potenssiin plus 1 ja " +"nimittäjä x n potenssiin miinus 1 " +"loppu murtoluku " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "MathCATin asetukset" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Kategoriat:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Puhe" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navigointi" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Pistekirjoitus" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Vamma:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Oppimisvaikeus" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Sokeus" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Heikkonäköisyys" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Kieli:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Puhetyyli:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Puheen määrä:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Suppea" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Keskitaso" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Runsas" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Puheen suhteellinen nopeus:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Taukokerroin:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "" +"Toista äänimerkki, kun matemaattisen sisällön puhuminen aloitetaan/lopetetaan" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "Käytettävä aihealue, kun sitä ei voida määrittää automaattisesti:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Yleiset" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Kemiallisten kaavojen puhuminen:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Tavaaminen (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Pois käytöstä (H ala 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Navigointitila, kun siirryt matemaattisen sisältöön:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Laajennettu" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Yksinkertainen" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Merkki" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Nollaa navigointitila lausekkeeseen siirryttäessä" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "Navigointipuheen tyyli, kun aloitat lausekkeessa navigoimisen:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Puhu" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Kuvaile/anna yhteenveto" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Nollaa navigointipuhe, kun siirryt uuteen lausekkeeseen" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Siirry automaattisesti uloimmalle tasolle 2D-merkintöjen jälkeen" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Puheen määrä navigoitaessa:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Matematiikan pistekirjoitusstandardi:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Korosta nykyinen navigointikohta pisteillä 7 ja 8:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Pois käytöstä" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Ensimmäinen merkki" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Päätepisteet" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Kaikki" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "OK" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Peruuta" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Käytä" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Palauta oletukset" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Ohje" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "&MathCATin asetukset..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT: MathML:ää puheella ja pistekirjoituksella" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT on korvaaja MathPlayerille, jonka kehitys on lopetettu.\n" +" Se tarjoaa puhe- ja pistekirjoitustuen sekä tukee myös MathPlayerin " +"kolmea navigointitilaa.\n" +" Puheen laatu ei vielä ole ihan yhtä hyvä kuin MathPlayerilla,\n" +" mutta pistekirjoituksen tuki on paljon parempi ja sisältää sekä " +"Nemethin että UEB Technicalin. Siinä on myös tuki\n" +" espanjalaiselle/portugalilaiselle CMU- ja vietnamilaisille " +"pistekirjoitussstandardeille. Käännökset kiinaksi (perinteinen),\n" +" indonesiaksi, espanjaksi ja vietnamiksi ovat käytettävissä, ja muita " +"käännöksiä on työn alla." + +#~ msgid "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +#~ msgid "xxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxx" + +#~ msgid "Speech amount:" +#~ msgstr "Puheen määrä:" + +#~ msgid "a page" +#~ msgstr "sivu" + +#~ msgid "Nemeth" +#~ msgstr "Nemeth" + +#~ msgid "UEB" +#~ msgstr "UEB" + +#~ msgid "Vietnam" +#~ msgstr "Vietnam" diff --git a/addon/locale/fr/LC_MESSAGES/nvda.po b/addon/locale/fr/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..15213fe0 --- /dev/null +++ b/addon/locale/fr/LC_MESSAGES/nvda.po @@ -0,0 +1,397 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2023-11-03 15:45+0100\n" +"Last-Translator: Rémy Ruiz \n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.2.2\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Erreur lors du démarrage de la navigation mathématique : voir le journal de " +"NVDA pour plus de détails" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Erreur lors de l'affichage des mathématiques en braille : voir le journal de " +"NVDA pour plus de détails" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Erreur lors de la navigation à travers du contenu mathématique : voir le " +"journal de NVDA pour plus de détails" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Copier le focus de navigation au presse-papiers" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Presse-papiers" + +#. Translators: copy to clipboard +#, fuzzy +msgid "copy as " +msgstr "copier" + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"impossible de copier le contenu mathématique : voir le journal de NVDA pour " +"plus de détails" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"L'initialisation de MathCAT a échoué : voir le journal de NVDA pour plus de " +"détails" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"MathML non valide trouvé : voir le journal de NVDA pour plus de détails" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Erreur lors de la verbalisation du contenu mathématique : voir le journal de " +"NVDA pour plus de détails" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"la racine carrée de x au carré plus y au carré" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, fuzzy, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"la fraction avec numérateur x à la n -ème " +"puissance " +"plus 1 et " +"dénominateur x à la n -ème puissance moins 1 fin de fraction " +"" + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "Préférences MathCAT" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Catégories :" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Parole" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navigation" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Générer la parole pour :" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Difficultés d'apprentissage" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Cécité" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Basse vision" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Langue :" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Style de la parole :" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +#, fuzzy +msgid "Speech verbosity:" +msgstr "Style de la parole :" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Laconique" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Moyen" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Verbeux" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Débit relatif de la parole :" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Facteur de pause :" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "" +"Émettre un son lors du début / fin de la verbalisation du contenu " +"mathématique" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Zone thématique à utiliser lorsque vous ne pouvez pas déterminer " +"automatiquement :" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Général" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Verbalisation des formules chimiques :" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Épeler (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Désactivée (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "" +"Mode de navigation à utiliser lorsque vous commencez à naviguer dans une " +"équation :" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Amélioré" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Simple" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Caractère" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "" +"Réinitialiser le mode de navigation lors de l'entrée dans une expression" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "" +"Mode de parole à utiliser lorsque vous commencez à naviguer dans une " +"équation :" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Verbaliser" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Décrire / résumer" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "" +"Réinitialiser les paramètres de la parole de navigation lors de l'entrée " +"dans une expression" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Zoomer en arrière automatiquement des notations 2D" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Quantité de parole pendant la navigation :" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Code mathématique pour les afficheurs braille :" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Mise en évidence avec les points 7 et 8 Le nœud de navigation actuel :" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Désactivé" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Premier caractère" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Extrêmes" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Tous" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "OK" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Annuler" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Appliquer" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Réinitialiser aux valeurs par défaut" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Aide" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "Paramètres &MathCAT..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT : parole et braille à partir de MathML" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +#, fuzzy +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT remplace MathPlayer qui a été abandonné.\n" +"\t\tIl fournit un support de parole et de braille et prend également en " +"charge les trois modes de navigation de MathPlayer.\n" +"\t\tLa qualité de la parole n'est pas encore aussi bonne que la parole de " +"MathPlayer,\n" +"\t\tmais le support en braille est bien meilleur et comprend à la fois " +"Nemeth et UEB technique. Il inclut également un support des normes braille " +"CMU (espagnol/portugais) et vietnamien.\n" +"\t\tLes traductions de l'Indonésien, de l'espagnol et du vietnamien existent " +"et d'autres traductions sont en cours." + +#~ msgid "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +#~ msgid "xxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxx" + +#~ msgid "Speech amount:" +#~ msgstr "Quantité de parole :" + +#~ msgid "a page" +#~ msgstr "une page" + +#~ msgid "Nemeth" +#~ msgstr "Nemeth" + +#~ msgid "UEB" +#~ msgstr "UEB" + +#~ msgid "Vietnam" +#~ msgstr "Vietnam" diff --git a/addon/locale/pt_BR/LC_MESSAGES/nvda.po b/addon/locale/pt_BR/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..ca89730f --- /dev/null +++ b/addon/locale/pt_BR/LC_MESSAGES/nvda.po @@ -0,0 +1,352 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2024-2025 NVDA Contributors. +# This file is distributed under the same license as the MathCAT package. +# Josevan Barbosa Fernandes , 2024-2025. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2025-05-10 17:54-0300\n" +"Last-Translator: Josevan Barbosa Fernandes \n" +"Language-Team: NVDA Brazilian Portuguese translation team (Equipe de " +"tradução do NVDA para Português Brasileiro) \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.6\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Erro ao iniciar a navegação de matemática: consulte o registro de erros do " +"NVDA para obter detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Erro no brailling math: consulte o registro de erros do NVDA para obter " +"detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Erro na navegação de matemática: consulte o registro de erros do NVDA para " +"obter detalhes" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Copie o foco de navegação para a área de transferência" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Área de transferência" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "compiar como " + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"incapaz de copiar matemática : consulte o registro de erros do NVDA para " +"obter detalhes" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"Falha na inicialização do MathCAT: consulte o registro de erros do NVDA para " +"obter detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"MathML ilegal encontrado: consulte o registro de erros do NVDA para obter " +"detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Erro ao falar matemática: consulte o registro de erros do NVDA para obter " +"detalhes" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Usar o idioma da voz (automático)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "Preferências do MathCAT" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Categorias:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Fala" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navegação" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Gerar fala para:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Dificuldades de aprendizagem" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Cegueira" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Baixa visão" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Idioma:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Personalizado" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Estilo de fala:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Verbosidade de fala:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Fraco" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Médio" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Verboso" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Taxa de fala relativa:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Fator de pausa:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "Fazer um som ao iniciar/terminar a fala em matemática" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Área de assunto a ser usada quando não puder ser determinada automaticamente:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Geral" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Fala para fórmulas químicas:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Soletre (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Modo de navegação a ser usado ao começar a navegar em uma equação:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Aprimorado" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Simples" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Caractere" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Redefinir o modo de navegação ao entrar em uma expressão" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "Fala navegação a ser usado ao começar a navegar em uma equação:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Fala" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Descrição/visão geral" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Redefinir a fala de navegação ao entrar em uma expressão" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Redução automática do zoom de notações 2D" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Quantidade de fala para navegação:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Copiar matemática como:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Código matemático em Braille para telas atualizáveis:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Desligado" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Primeiro caractere" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Pontos finais" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Todo" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "Ok" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Cancelar" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Aplicar" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Redefinir para os padrões" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Ajuda" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "Configurações do &MathCAT..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"O MathCAT é um substituto para o MathPlayer, que foi descontinuado.\n" +" Ele oferece suporte a fala e braile e também suporta os três modos " +"de navegação do MathPlayer.\n" +" A qualidade da fala ainda não é tão boa quanto a do MathPlayer,\n" +" mas o suporte a braile é muito melhor e inclui suporte aos padrões de " +"código braile Nemeth, UEB Technical, CMU (espanhol/português),\n" +" e vietnamita. Existem traduções para chinês (tradicional), indonésio, " +"espanhol e vietnamita\n" +" e outras traduções estão em andamento." diff --git a/addon/locale/pt_PT/LC_MESSAGES/nvda.po b/addon/locale/pt_PT/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..c77457ae --- /dev/null +++ b/addon/locale/pt_PT/LC_MESSAGES/nvda.po @@ -0,0 +1,378 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.3.3\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: 2024-07-04 12:13+0100\n" +"Last-Translator: Ângelo Abrantes \n" +"Language-Team: \n" +"Language: pt_PT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Erro ao iniciar a navegação de matemática: ver registo de erros NVDA para " +"mais detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Erro de braile na matemática: ver registo de erros do NVDA para mais detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Erro na navegação matemática: ver registo de erros do NVDA para mais detalhes" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Copiar o foco de navegação para a área de transferência" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Área de transferência" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "copiar como" + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "unable to copy math: ver registo de erros NVDA para mais detalhes" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"Falha na inicialização do MathCAT: ver registo de erros do NVDA para mais " +"detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "MathML ilegal encontrado: ver registo de erros NVDA para mais detalhes" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Erro ao falar matemática: ver registo de erros do NVDA para mais detalhes" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Utilizar o idioma da voz (automático)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"a raiz quadrada de x ao quadrado mais y ao quadrado" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"a fração com numerador x elevado à enésima potência mais 1 e denominador " +" x " +"elevado à enésima potência menos 1 end fraction " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "Configurações do MathCAT" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Categorias:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Discurso" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Navegação" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Gerar discurso para:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Dificuldades de aprendizagem" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Cegueira" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Baixa visão" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Idioma:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "Separador decimal para números:" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "Auto" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Personalizado" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Estilo de discurso:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Verbosidade do discurso:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Seco" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Média" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Verbosidade" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Velocidade relativa do discurso:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Fator de pausa:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "Fazer um som ao iniciar/terminar o discurso matemático" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Área temática a utilizar quando não puder ser determinada automaticamente:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Geral" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Discurso para fórmulas químicas:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Soletra (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Desligado (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Modo de navegação a utilizar quando se começa a navegar numa equação:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Melhorado" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Simples" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Carater" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Repor o modo de navegação à entrada de uma expressão" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "" +"Discurso de navegação a utilizar quando se começa a navegar numa equação:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Falar" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Descrição/visão geral" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Repor o discurso de navegação à entrada de uma expressão" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Redução automática do zoom das notações 2D" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Quantidade de voz para navegação:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Copiar matemática como:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "MathML" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "LaTeX" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "ASCIIMath" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Código matemático Braille para ecrãs actualizáveis:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Realce com os pontos 7 && 8 o nó de navegação atual:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Desligado" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Primeiro carácter" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Pontos finais" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Todos" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "OK" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Cancelar" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Aplicar" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Restaurar para valores por padrão" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Ajuda" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "Configurações do &MathCAT..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT: fala e braille a partir de MathML" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"O MathCAT é um substituto do MathPlayer que foi descontinuado.\n" +"\t\tFornece suporte de voz e braille, e também suporta os três modos de " +"navegação do MathPlayer.\n" +"\t\tA qualidade da fala ainda não é tão boa como a do MathPlayer,\n" +"\t\tmas o suporte braille é muito melhor e inclui tanto o Nemeth como o UEB " +"Technical.\n" +"\t\tExistem traduções para indonésio, espanhol e vietnamita e outras " +"traduções estão em curso." + +#~ msgid "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +#~ msgid "xxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxxxxxx" + +#~ msgid "Speech amount:" +#~ msgstr "Quantidade de discurso:" + +#~ msgid "a page" +#~ msgstr "uma página" + +#~ msgid "Nemeth" +#~ msgstr "Nemeth" + +#~ msgid "UEB" +#~ msgstr "UEB" + +#~ msgid "Vietnam" +#~ msgstr "Vietname" diff --git a/addon/locale/ru/LC_MESSAGES/nvda.po b/addon/locale/ru/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..dae34db5 --- /dev/null +++ b/addon/locale/ru/LC_MESSAGES/nvda.po @@ -0,0 +1,374 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: MathCAT 0.6.6\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2024-12-19 20:17+0700\n" +"PO-Revision-Date: 2024-12-30 02:34+0500\n" +"Last-Translator: Ruslan Kolodyazhni \n" +"Language-Team: Translators \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.5\n" +"X-Poedit-Basepath: ../../../..\n" +"X-Poedit-SearchPath-0: addon/globalPlugins/MathCAT\n" +"X-Poedit-SearchPath-1: buildVars.py\n" +"X-Poedit-SearchPath-2: Example\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Ошибка при запуске навигации по математическому выражению: подробности " +"смотрите в журнале ошибок NVDA" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "" +"Ошибка отображения математического выражения по брайлю: подробности " +"смотрите в журнале ошибок NVDA" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "" +"Ошибка навигации по математическому выражению: подробности смотрите в " +"журнале ошибок NVDA" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Скопировать элемент в фокусе в буфер обмена" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Буфер обмена" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "копировать как " + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "" +"не удалось скопировать математическое выражение: подробности смотрите в " +"журнале ошибок NVDA" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"Ошибка инициализации MathCat: подробности смотрите в журнале ошибок NVDA" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "" +"Обнаружен недопустимый элемент MathML: подробности смотрите в журнале ошибок " +"NVDA" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "" +"Ошибка произнесения математического выражения: подробности смотрите в " +"журнале ошибок NVDA" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Использовать язык голоса (автоматически)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"квадратный корень из x в квадрате плюс y в квадрате" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"дробь с числителем x to the " +"n -" +"th в степени " +" плюс 1 " +" и знаменателем x to the n -thв степени " +" минус 1 конец дроби " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "Настройки MathCat" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Категории:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Речь" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Навигация" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Брайль" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "" +"Выберете тип речевых сообщений, соответствующий ограничениям здоровья " +"пользователя:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Нарушения обучаемости" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Слепота" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Остаточное зрение" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Язык:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "Десятичный разделитель чисел:" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "Автоматически" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Пользовательский" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Стиль речи:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Объём речи:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Краткий" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Средний" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Подробный" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Относительная скорость речи:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Коэффициент паузы:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "" +"Воспроизводить звук при начале/завершении проговаривания математического " +"выражения" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "" +"Предметная область, используемая в тех случаях, когда она не может быть " +"определена автоматически:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Общее" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Проговаривание химических формул:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Произносить по буквам (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Выключено (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Режим навигации, используемый при перемещении по выражению:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Улучшенный" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Простой" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Символ" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Сбрасывать режим навигации при переходе к выражению" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "Режим речи, используемый при перемещении по выражению:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Проговаривание" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Описание/обзор" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Сбрасывать настройки режима речи при переходе к выражению" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "Автоматическое уменьшение масштаба Двумерных обозначений" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Объём речи при навигации:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Копировать математику как:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "MathML" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "LaTeX" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "ASCIIMath" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Математический код Брайля для обновляемых дисплеев:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Выделять текущий навигационный узел точками 7 && 8:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Выключено" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "Первый символ" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Конечные точки" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Всё" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "ОК" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "Отменить" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Применить" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Сбросить к настройкам по умолчанию" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Справка" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "Настройки &MathCat..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCat: речь и Брайль для MathML" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT - это замена MathPlayer, поддержка которого была прекращена.\n" +"\t\tЭто дополнение обеспечивает поддержку речи и шрифта Брайля, а также " +"поддерживает три режима навигации MathPlayer.\n" +"\t\tКачество речи пока не такое хорошее, как у MathPlayer,\n" +"\t\tно поддержка шрифта Брайля намного лучше и включает в себя Nemeth и UEB " +"Technical. Также поддерживаются стандарты Брайля CMU (испанский/" +"португальский) и вьетнамский.\n" +"\t\tСуществуют переводы на индонезийский, испанский и вьетнамский языки, " +"другие переводы находятся в процессе разработки." diff --git a/addon/locale/tr/LC_MESSAGES/nvda.po b/addon/locale/tr/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..ee00a14f --- /dev/null +++ b/addon/locale/tr/LC_MESSAGES/nvda.po @@ -0,0 +1,374 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the MathCAT package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: MathCAT\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2023-08-14 03:25+0000\n" +"PO-Revision-Date: \n" +"Last-Translator: Umut KORKMAZ \n" +"Language-Team: Umut KORKMAZ \n" +"Language: tr_TR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "" +"Matematik gezintisi başlatılırken hata oluştu: ayrıntılar için NVDA hata " +"günlüğüne bakın" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "Braille matematikte hata: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "Matematikte gezinme hatası: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "Gezinme odağını panoya kopyala" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "Pano" + +#. Translators: copy to clipboard +msgid "copy as " +msgstr "şu şekilde kopyala " + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "matematik kopyalanamıyor: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "" +"MathCAT başlatma başarısız oldu: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "Geçersiz MathML bulundu: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "Matematik konuşma hatası: ayrıntılar için NVDA hata günlüğüne bakın" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "Sesin Dilini Kullan (Otomatik)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "x kare artı y karenin karekökü" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "MathCAT Tercihleri" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "Kategoriler:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "Konuşma" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "Gezinme" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "Braille" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "Aşağıdakiler için konuş:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "Öğrenme güçlüğü çekenler" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "Körler" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "Az görenler" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "Dil:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "Sayılar için ondalık ayırıcı:" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "Oto" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "Özelleştir" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "Konuşma stili:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "Konuşma ayrıntısı:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "Kısa" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "Orta" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "Ayrıntılı" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "Oransal konuşma hızı:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "Duraklatma faktörü:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "Matematik konuşmasını başlatırken/bitirirken ses çal" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "Otomatik olarak belirlenemediği durumlarda kullanılacak konu alanı:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "Genel" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "Kimyasal formüller için konuşma:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "Hecele (H 2 O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "Kapalı (H sub 2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "Bir denklemde dolaşmaya başlarken kullanılacak gezinme modu:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "Gelişmiş" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "Basit" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "Karakter" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "Bir ifadeye girişte gezinme modunu sıfırla" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "Bir denklemde gezinmeye başlarken kullanılacak gezinme konuşması:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "Konuşma" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "Açıklama/genel bakış" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "Bir ifadeye girişte gezinme konuşmasını sıfırla" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "2B gösterimlerde otomatik uzaklaştırma" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "Gezinme için konuşma düzeyi:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "Matematiği şu şekilde kopyala:" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "MathML" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "LaTeX" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "ASCIIMath" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "Yenilenebilir ekranlar için Braille matematik kodu:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "Geçerli gezinme noktasını 7 ve 8 noktalarıyla &vurgulayın:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "Kapalı" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "İlk karakter" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "Uç noktalar" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "Tümü" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "Tamam" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "İptal" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "Uygula" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "Varsayılanlara sıfırla" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "Yardım" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "&MathCAT Ayarları..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT: MathML'den konuşma ve braille" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT, geliştirilmesi durdurulan MathPlayer'ın yerine geçen bir " +"yazılımdır.\n" +" Konuşma ve braille desteği sağlar ve ayrıca MathPlayer'ın üç gezinme " +"modunu da destekler.\n" +" Konuşma kalitesi henüz MathPlayer'ın konuşması kadar iyi değil,\n" +" ancak braille desteği çok daha iyidir ve Nemeth, UEB Teknik, CMU " +"(İspanyolca/Portekizce) desteğini içerir.\n" +" ve Vietnam braille kodu standartları. Çince (Geleneksel), Endonezce, " +"İspanyolca ve Vietnamcaya çeviriler mevcuttur\n" +" diğer çeviriler devam etmektedir." + +#~ msgid "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +#~ msgid "xxxxxxxxxxxxxxxx" +#~ msgstr "xxxxxxxxxxxxxxxx" + +#~ msgid "Speech amount:" +#~ msgstr "Konuşma Düzeği:" + +#~ msgid "a page" +#~ msgstr "bir sayfa" + +#~ msgid "Nemeth" +#~ msgstr "Nemet" + +#~ msgid "UEB" +#~ msgstr "UEB" + +#~ msgid "Vietnam" +#~ msgstr "Vietnam" diff --git a/addon/locale/zh_CN/LC_MESSAGES/nvda.po b/addon/locale/zh_CN/LC_MESSAGES/nvda.po new file mode 100644 index 00000000..02da2c48 --- /dev/null +++ b/addon/locale/zh_CN/LC_MESSAGES/nvda.po @@ -0,0 +1,350 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the 'MathCAT' package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 'MathCAT' '0.4.2'\n" +"Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" +"POT-Creation-Date: 2024-03-23 08:29+0800\n" +"PO-Revision-Date: 2024-03-23 10:34+0800\n" +"Last-Translator: Zhang Yunxi \n" +"Language-Team: NVDA Simplified Chinese team\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.2\n" + +#. Translators: this message directs users to look in the log file +msgid "Error in starting navigation of math: see NVDA error log for details" +msgstr "启动数学导航时出错: 有关详细信息,请查找 NVDA 错误日志" + +#. Translators: this message directs users to look in the log file +msgid "Error in brailling math: see NVDA error log for details" +msgstr "处理数学盲文时出错: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: this message directs users to look in the log file +msgid "Error in navigating math: see NVDA error log for details" +msgstr "在与数学导航时出错: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: Message to be announced during Keyboard Help +msgid "Copy navigation focus to clipboard" +msgstr "复制导航焦点到剪贴板" + +#. Translators: Name of the section in "Input gestures" dialog. +msgid "Clipboard" +msgstr "剪贴板" + +#. Translators: copy to clipboard +#, fuzzy +msgid "copy as " +msgstr "复制" + +#. Translators: this message directs users to look in the log file +msgid "unable to copy math: see NVDA error log for details" +msgstr "无法复制 MathML: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: this message directs users to look in the log file +msgid "MathCAT initialization failed: see NVDA error log for details" +msgstr "MathCat 初始化失败: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: this message directs users to look in the log file +msgid "Illegal MathML found: see NVDA error log for details" +msgstr "发现非法 MathML: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: this message directs users to look in the log file +msgid "Error in speaking math: see NVDA error log for details" +msgstr "朗读数学时出错: 有关详细信息,请查看 NVDA 错误日志" + +#. Translators: menu item -- use the language of the voice chosen in the NVDA speech settings dialog +#. "Auto" == "Automatic" -- other items in menu are "English (en)", etc., so this matches that style +msgid "Use Voice's Language (Auto)" +msgstr "使用语音的语言 (自动)" + +#. Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" +msgid "" +"the square root of x squared plus y squared" +msgstr "" +"the square root of x squared plus y squared" + +#. Translators: this is a test string that is spoken. Only translate "the fraction with numerator" +#. and other parts NOT inside '<.../>', +#, fuzzy, python-brace-format +msgid "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " +msgstr "" +"the fraction with numerator x to the n -th power plus 1 and " +"denominator x to the " +"n -" +"thpower minus 1 " +"end fraction " + +#. Translators: title for MathCAT preferences dialog +msgid "MathCAT Preferences" +msgstr "MathCat 首选项" + +#. Translators: A heading that labels three navigation pane tab names in the MathCAT dialog +msgid "Categories:" +msgstr "分类:" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Speech" +msgstr "语音" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Navigation" +msgstr "导航" + +#. Translators: these are navigation pane headings for the MathCAT preferences dialog under the title "Categories" +msgid "Braille" +msgstr "盲文" + +#. Translators: this is the text label for whom to target the speech for (options are below) +msgid "Generate speech for:" +msgstr "为谁朗读:" + +#. Translators: these are the categories of impairments that MathCAT supports +#. Translators: Learning disabilities includes dyslexia and ADHD +msgid "Learning disabilities" +msgstr "学习障碍" + +#. Translators: target people who are blind +msgid "Blindness" +msgstr "盲人" + +#. Translators: target people who have low vision +msgid "Low vision" +msgstr "低视力" + +#. Translators: label for pull down allowing users to choose the speech language for math +msgid "Language:" +msgstr "语言:" + +#. Translators: label for pull down to specify what character to use in numbers as the decimal separator +msgid "Decimal separator for numbers:" +msgstr "" + +#. Translators: options for decimal separator -- "Auto" = automatically pick the choice based on the language +msgid "Auto" +msgstr "" + +#. Translators: options for decimal separator -- "Custom" = user sets it +#. Currently there is no UI for how it is done yet, but eventually there will be a dialog that pops up to set it +msgid "Custom" +msgstr "" + +#. Translators: label for pull down allowing users to choose the "style" (version, rules) of speech for math +msgid "Speech style:" +msgstr "语音风格:" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech verbosity:" +msgstr "语音详细度:" + +#. Translators: options for speech verbosity -- "terse" = use less words +#. Translators: options for navigation verbosity -- "terse" = use less words +msgid "Terse" +msgstr "简要" + +#. Translators: options for speech verbosity -- "medium" = try to be nether too terse nor too verbose words +#. Translators: options for navigation verbosity -- "medium" = try to be nether too terse nor too verbose words +msgid "Medium" +msgstr "适中" + +#. Translators: options for speech verbosity -- "verbose" = use more words +#. Translators: options for navigation verbosity -- "verbose" = use more words +msgid "Verbose" +msgstr "详细" + +#. Translators: label for slider that specifies a percentage of the normal speech rate that should be used for math +msgid "Relative speech rate:" +msgstr "相对语速:" + +#. Translators: label for slider that specifies relative factor to increase or decrease pauses in the math speech +msgid "Pause factor:" +msgstr "停顿时常:" + +#. Translators: label for check box controling a beep sound +msgid "Make a sound when starting/ending math speech" +msgstr "语音首尾音效提示" + +#. Translators: label for pull down to specify a subject area (Geometry, Calculus, ...) +msgid "Subject area to be used when it cannot be determined automatically:" +msgstr "无法自动确定时要使用的主题区域:" + +#. Translators: a generic (non-specific) math subject area +msgid "General" +msgstr "常规" + +#. Translators: label for pull down to specify how verbose/terse the speech should be +msgid "Speech for chemical formulas:" +msgstr "朗读化学式:" + +#. Translators: values for chemistry options with example speech in parenthesis +msgid "Spell it out (H 2 O)" +msgstr "拼写 (H2O)" + +#. Translators: values for chemistry options with example speech in parenthesis (never interpret as chemistry) +msgid "Off (H sub 2 O)" +msgstr "关闭 (H 下标2 O)" + +#. Translators: label for pull down to specify one of three modes use to navigate math expressions +msgid "Navigation mode to use when beginning to navigate an equation:" +msgstr "公式导航模式:" + +#. Translators: names of different modes of navigation. "Enhanced" mode understands math structure +msgid "Enhanced" +msgstr "增强" + +#. Translators: "Simple" walks by character expect for things like fractions, roots, and scripts +msgid "Simple" +msgstr "简单" + +#. Translators: "Character" moves around by character, automatically moving into fractions, etc +msgid "Character" +msgstr "字符" + +#. Translators: label for checkbox that controls whether any changes to the navigation mode should be preserved +msgid "Reset navigation mode on entry to an expression" +msgstr "进入表达式时重置导航模式" + +#. Translators: label for pull down to specify whether the expression is spoken or described (an overview) +msgid "Navigation speech to use when beginning to navigate an equation:" +msgstr "在导航公式时使用的表达方式:" + +#. Translators: "Speak" the expression after moving to it +msgid "Speak" +msgstr "说出" + +#. Translators: "Describe" the expression after moving to it ("overview is a synonym") +msgid "Describe/overview" +msgstr "描述/概述" + +#. Translators: label for checkbox that controls whether any changes to the speak vs overview reading should be ignored +msgid "Reset navigation speech on entry to an expression" +msgstr "在进入表达式时重置导航表达方式" + +#. Translators: label for checkbox that controls whether arrow keys move out of fractions, etc., +#. or whether you have to manually back out of the fraction, etc. +msgid "Automatic zoom out of 2D notations" +msgstr "自动跳出 2 维结构" + +#. Translators: label for pull down to specify whether you want a terse or verbose reading of navigation commands +msgid "Speech amount for navigation:" +msgstr "导航时语音详细度:" + +#. Translators: label for pull down to specify how math will be copied to the clipboard +msgid "Copy math as:" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "MathML" +msgid "MathML" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "LaTeX" +msgid "LaTeX" +msgstr "" + +#. Translators: options for Copy expression to clipboard as -- "ASCIIMath" +msgid "ASCIIMath" +msgstr "" + +#. Translators: label for pull down to specify which braille code to use +msgid "Braille math code for refreshable displays:" +msgstr "为可刷新盲文显示器提供的数学码:" + +#. Translators: label for pull down to specify how braille dots should be modified when navigating/selecting subexprs +msgid "Highlight with dots 7 && 8 the current nav node:" +msgstr "用 7 && 8 点高亮当前导航点:" + +#. Translators: options for using dots 7 and 8: +#. Translators: "off" -- don't highlight +msgid "Off" +msgstr "关闭" + +#. Translators: "First character" -- only the first character of the current navigation node uses dots 7 & 8 +msgid "First character" +msgstr "首字符" + +#. Translators: "Endpoints" -- only the first and last character of the current navigation node uses dots 7 & 8 +msgid "Endpoints" +msgstr "仅首尾" + +#. Translators: "All" -- all the characters for the current navigation node use dots 7 & 8 +msgid "All" +msgstr "全部" + +#. Translators: dialog "ok" button +msgid "OK" +msgstr "确认" + +#. Translators: dialog "cancel" button +msgid "Cancel" +msgstr "取消" + +#. Translators: dialog "apply" button +msgid "Apply" +msgstr "应用" + +#. Translators: button to reset all the preferences to their default values +msgid "Reset to defaults" +msgstr "重置为默认" + +#. Translators: button to bring up a help page +msgid "Help" +msgstr "帮助" + +#. Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog +msgid "&MathCAT Settings..." +msgstr "MathCAT 设置 (&M)..." + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "MathCAT: speech and braille from MathML" +msgstr "MathCAT: 从 MathML 产生语音和盲文" + +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "" +"MathCAT is a replacement for MathPlayer which has been discontinued.\n" +" It provides speech and braille support, and also supports " +"MathPlayer's three modes of navigation.\n" +" The speech quality is not quite as good as MathPlayer's speech yet,\n" +" but the braille support is much better and includes support for " +"Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n" +" and Vietnamese braille code standards. Translations to Chinese " +"(Traditional), Indonesian, Spanish, and Vietnamese exist\n" +" and other translations are in progress." +msgstr "" +"MathCAT 是已停产的 MathPlayer 的替代品。\n" +" 它提供语音和盲文支持,还支持 MathPlayer 的三种导航模式。\n" +" 语音质量相比 MathPlayer 还没有那么好,,\n" +" 但盲文支持要好得多,包括对 Nemeth、UEB Technical、CMU (西班牙语/葡萄" +"牙语)和越南盲文代码标准的支持。\n" +" 已经翻译为中文(繁体)、印尼语、西班牙语和越南语,\n" +" 其他翻译正在进行中。" diff --git a/build-nvda-addon.sh b/build-nvda-addon.sh index 70d754e9..c5c7c4fc 100644 --- a/build-nvda-addon.sh +++ b/build-nvda-addon.sh @@ -1,14 +1,19 @@ -#!/bin/csh -# RUN THIS FILE to build the NVDA addon - -rm -rf NVDA-addon/addon/globalPlugins/MathCAT/Rules -# NVDA is currently uses 32 bit python 3.7 -# We need to tell PYO3 that -set PYO3_PYTHON_64=c:/Users/neils/AppData/Local/Programs/Python/Python39/python.exe -set PYO3_PYTHON_32=c:/Users/neils/AppData/Local/Programs/Python/Python37-32/python.exe -env PYO3_PYTHON=$PYO3_PYTHON_32 cargo build --target i686-pc-windows-msvc --release -cp target/i686-pc-windows-msvc/release/libmathcat_py.dll NVDA-addon/addon/globalPlugins/MathCAT/libmathcat.pyd -cd NVDA-addon +#!/bin/bash +(set -o igncr) 2>/dev/null && set -o igncr; # comment is needed +PYO3_PYTHON_64=C:/Software/Python/Python313 +PYO3_PYTHON_32=C:/Software/Python/Python311-32 +STFLAGS='-C linker=lld' +PYO3_PYTHON=$YO3_PYTHON_32/python.exe STFLAGS="$STFLAGS" cargo build --target i686-pc-windows-msvc --release + + +cp target/i686-pc-windows-msvc/release/libmathcat_py.dll addon/globalPlugins/MathCAT/libmathcat_py.pyd + +# for testing +cp target/i686-pc-windows-msvc/release/libmathcat_py.dll Example/libmathcat_py.pyd +cp -r addon/globalPlugins/MathCAT/Rules Example sed 's/^import wx\.xrc/# import wx.xrc/' --in-place "addon/globalPlugins/MathCAT/MathCATgui.py" -rm MathCAT-*.nvda-addon -scons +rm -f MathCAT-*.nvda-addon + +# use "pip install SCons" to add scons to the python env +echo ${PYO3_PYTHON_32} +${PYO3_PYTHON_32}/Scripts/scons diff --git a/build.rs b/build.rs index 9287be6d..ac4f5757 100644 --- a/build.rs +++ b/build.rs @@ -7,16 +7,10 @@ use std::path::PathBuf; use zip::ZipArchive; fn main() { - let archive = libmathcat::ZIPPED_RULE_FILES; + let archive = libmathcat::shim_filesystem::ZIPPED_RULE_FILES; let archive = std::io::Cursor::new(archive); - let location = PathBuf::from("NVDA-addon/addon/globalPlugins/MathCAT"); + let location = PathBuf::from("addon/globalPlugins/MathCAT"); let mut zip_archive = ZipArchive::new(archive).unwrap(); zip_archive.extract(&location).expect("Zip extraction failed"); - - // the test dir 'zz' doesn't need to be part of the addon - let mut zz_dir = location.clone(); - zz_dir.push("Rules/Languages/zz"); - std::fs::remove_dir_all(&zz_dir) - .expect(&format!("Failed to remove directory {}", zz_dir.to_str().unwrap())); -} \ No newline at end of file +} diff --git a/build64-nvda-addon.sh b/build64-nvda-addon.sh new file mode 100644 index 00000000..56bc30e7 --- /dev/null +++ b/build64-nvda-addon.sh @@ -0,0 +1,22 @@ +#!/bin/bash +(set -o igncr) 2>/dev/null && set -o igncr; # comment is needed +PYO3_PYTHON_64=C:/Users/neils/AppData/Local/Python/pythoncore-3.13-64 +PYO3_PYTHON_32=C:/Software/Python/Python311-32 +export PYO3_PYTHON_64 PYO3_PYTHON_32 +STFLAGS='-C linker=lld' +echo "before cargo build" +PYO3_PYTHON=${PYO3_PYTHON_64}/python.exe STFLAGS="$STFLAGS" cargo build --target x86_64-pc-windows-msvc --release +echo "after cargo build" + +echo "before cp target/x86_64-pc-windows-msvc/release/libmathcat_py.dll addon/globalPlugins/MathCAT/libmathcat_py.pyd" + +cp target/x86_64-pc-windows-msvc/release/libmathcat_py.dll addon/globalPlugins/MathCAT/libmathcat_py.pyd + +# for testing +cp target/x86_64-pc-windows-msvc/release/libmathcat_py.dll Example/libmathcat_py.pyd +cp -r addon/globalPlugins/MathCAT/Rules Example +sed 's/^import wx\.xrc/# import wx.xrc/' --in-place "addon/globalPlugins/MathCAT/MathCATgui.py" +rm -f MathCAT-*.nvda-addon + +# use "pip install SCons" to add scons to the python env +${PYO3_PYTHON_64}/Scripts/scons diff --git a/buildVars.py b/buildVars.py new file mode 100644 index 00000000..36fd01d8 --- /dev/null +++ b/buildVars.py @@ -0,0 +1,87 @@ +# -*- coding: UTF-8 -*- + +# Build customizations +# Change this file instead of sconstruct or manifest files, whenever possible. + + +# Since some strings in `addon_info` are translatable, +# we need to include them in the .po files. +# Gettext recognizes only strings given as parameters to the `_` function. +# To avoid initializing translations in this module we simply roll our own "fake" `_` function +# which returns whatever is given to it as an argument. +def _(arg): + return arg + + +# Add-on information variables +addon_info = { + # add-on Name/identifier, internal for NVDA + "addon_name": "MathCAT", + # Add-on summary, usually the user visible name of the addon. + # Translators: Summary for this add-on + # to be shown on installation and add-on information found in Add-ons Manager. + "addon_summary": _("MathCAT: speech and braille from MathML"), + # Add-on description + "addon_description": _( + # Translators: Long description to be shown for this add-on on add-on information from add-ons manager + """MathCAT is a replacement for MathPlayer which has been discontinued. + It provides speech and braille support, and also supports MathPlayer's three modes of navigation. + The supported languages are: Chinese (Traditional), English, Finnish, French, + German, Greek, Hungarian, Indonesian, Norwegian, Polish, Russian, Spanish, Swedish, and Vietnamese. + The supported braille codes are: Nemeth, UEB Technical, CMU (Spanish/Portuguese), + Russian, Swedish, and Vietnamese braille code standards. + Also supported are the German/Austrian braille code standards for ASCIIMath and LaTeX math markup. + Other language and braille code translations are in progress.""", + ), + # version + "addon_version": "0.7.6-beta.9", + # Author(s) + "addon_author": "Neil Soiffer ", + # URL for the add-on documentation support + "addon_url": "https://nsoiffer.github.io/MathCAT/", + # URL for the add-on repository where the source code can be found + "addon_sourceURL": "https://github.com/NSoiffer/MathCATForPython", + # Documentation file name + "addon_docFileName": "readme.html", + # Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional) + "addon_minimumNVDAVersion": "2026.1", + # Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version) + "addon_lastTestedNVDAVersion": "2026.1", + # Add-on update channel (default is None, denoting stable releases, + # and for development releases, use "dev".) + # Do not change unless you know what you are doing! + "addon_updateChannel": "dev", + # Add-on license such as GPL 2 + "addon_license": "MIT and GPL 2", + # URL for the license document the ad-on is licensed under + "addon_licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE", +} + +# Define the python files that are the sources of your add-on. +# You can either list every file (using ""/") as a path separator, +# or use glob expressions. +# For example to include all files with a ".py" extension from the "globalPlugins" dir of your add-on +# the list can be written as follows: +# pythonSources = ["addon/globalPlugins/*.py"] +# For more information on SCons Glob expressions please take a look at: +# https://scons.org/doc/production/HTML/scons-user/apd.html +pythonSources = ["addon/globalPlugins/**/*.py"] + +# Files that contain strings for translation. Usually your python sources +i18nSources = pythonSources + ["buildVars.py"] + +# Files that will be ignored when building the nvda-addon file +# Paths are relative to the addon directory, not to the root directory of your addon sources. +excludedFiles = [] + +# Base language for the NVDA add-on +# If your add-on is written in a language other than english, modify this variable. +# For example, set baseLanguage to "es" if your add-on is primarily written in spanish. +baseLanguage = "en" + +# Markdown extensions for add-on documentation +# Most add-ons do not require additional Markdown extensions. +# If you need to add support for markup such as tables, fill out the below list. +# Extensions string must be of the form "markdown.extensions.extensionName" +# e.g. "markdown.extensions.tables" to add tables. +markdownExtensions = [] diff --git a/flake8.ini b/flake8.ini new file mode 100644 index 00000000..a9dc16f7 --- /dev/null +++ b/flake8.ini @@ -0,0 +1,44 @@ +# Custom Flake8 configuration for community add-on template +# Based on NVDA's Flake8 configuration with modifications for the basic add-on template (edited by Joseph Lee) + +[flake8] + +# Plugins +use-flake8-tabs = True +# Not all checks are replaced by flake8-tabs, however, pycodestyle is still not compatible with tabs. +use-pycodestyle-indent = False +continuation-style = hanging +## The following are replaced by flake8-tabs plugin, reported as ET codes rather than E codes. +# E121, E122, E123, E126, E127, E128, +## The following (all disabled) are not replaced by flake8-tabs, +# E124 - Requires mixing spaces and tabs: Closing bracket does not match visual indentation. +# E125 - Does not take tabs into consideration: Continuation line with same indent as next logical line. +# E129 - Requires mixing spaces and tabs: Visually indented line with same indent as next logical line +# E131 - Requires mixing spaces and tabs: Continuation line unaligned for hanging indent +# E133 - Our preference handled by ET126: Closing bracket is missing indentation + + +# Reporting +statistics = True +doctests = True +show-source = True + +# Options +max-complexity = 15 +max-line-length = 130 +# Final bracket should match indentation of the start of the line of the opening bracket +hang-closing = False + +ignore = + ET113, # use of alignment as indentation, but option continuation-style=hanging does not permit this + W191, # indentation contains tabs + W503, # line break before binary operator. We want W504(line break after binary operator) + +builtins = # inform flake8 about functions we consider built-in. + _, # translation lookup + pgettext, # translation lookup + +exclude = # don't bother looking in the following subdirectories / files. + .git, + __pycache__, + addon\globalPlugins\MathCAT\yaml\* diff --git a/NVDA-addon/manifest-translated.ini.tpl b/manifest-translated.ini.tpl similarity index 100% rename from NVDA-addon/manifest-translated.ini.tpl rename to manifest-translated.ini.tpl diff --git a/NVDA-addon/manifest.ini.tpl b/manifest.ini.tpl similarity index 100% rename from NVDA-addon/manifest.ini.tpl rename to manifest.ini.tpl diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f83a0734 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,211 @@ +[build-system] +requires = ["setuptools~=72.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "MathCATForPython" +dynamic = ["version"] +description = "Math Capable Assistive Technology (MathCAT) Python bindings and addon for NVDA" +maintainers = [ + {name = "Neil Soiffer", email = "soiffer@alum.nit.edu"}, +] +requires-python = ">=3.11,<3.12" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU General Public License v2", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3", + "Topic :: Accessibility", +] +readme = "readme.md" +license = {file = "LICENSE"} +dependencies = [ + "wxPython==4.2.2", +] + +[project.urls] +Repository = "http://github.com/NSoiffer/MathCATForPython" + +[tool.ruff] +line-length = 110 + +builtins = [ + # translation lookup + "_", + # translation lookup + "ngettext", + # translation lookup + "pgettext", + # translation lookup + "npgettext", +] + +include = [ + "*.py", + "sconstruct", +] + +exclude = [ + ".git", + "__pycache__", + ".venv", + "./addon/globalPlugins/MathCAT/yaml/*", +] + +[tool.ruff.format] +indent-style = "tab" +line-ending = "lf" + +[tool.ruff.lint.mccabe] +max-complexity = 15 + +[tool.ruff.lint] +ignore = [ + # indentation contains tabs + "W191", +] +logger-objects = ["logHandler.log"] + +[tool.ruff.lint.per-file-ignores] +# sconscripts contains many inbuilt functions not recognised by the lint, +# so ignore F821. +"sconstruct" = ["F821"] + +[tool.pyright] +venvPath = ".venv" +venv = "." +pythonPlatform = "Windows" +typeCheckingMode = "strict" + +include = [ + "**/*.py", +] + +exclude = [ + "sconstruct", + ".git", + "__pycache__", + ".venv", + # When excluding concrete paths relative to a directory, + # not matching multiple folders by name e.g. `__pycache__`, + # paths are relative to the configuration file. +] + +# Tell pyright where to load python code from +extraPaths = [ + "./addon", + "../nvda/source", +] + +# General config +analyzeUnannotatedFunctions = true +deprecateTypingAliases = true + +# Stricter typing +strictParameterNoneValue = true +strictListInference = true +strictDictionaryInference = true +strictSetInference = true + +# Compliant rules +reportAssertAlwaysTrue = true +reportAssertTypeFailure = true +reportDuplicateImport = true +reportIncompleteStub = true +reportInconsistentOverload = true +reportInconsistentConstructor = true +reportInvalidStringEscapeSequence = true +reportInvalidStubStatement = true +reportInvalidTypeVarUse = true +reportMatchNotExhaustive = true +reportMissingModuleSource = true +reportMissingImports = false # temporarily setting to false for MathCAT integration +reportNoOverloadImplementation = true +reportOptionalContextManager = true +reportOverlappingOverload = true +reportPrivateImportUsage = true +reportPropertyTypeMismatch = true +reportSelfClsParameterName = true +reportShadowedImports = true +reportTypeCommentUsage = true +reportTypedDictNotRequiredAccess = true +reportUndefinedVariable = true +reportUnusedExpression = true +reportUnboundVariable = true +reportUnhashable = true +reportUnnecessaryCast = true +reportUnnecessaryContains = true +reportUnnecessaryTypeIgnoreComment = true +reportUntypedClassDecorator = true +reportUntypedFunctionDecorator = true +reportUnusedClass = true +reportUnusedCoroutine = true +reportUnusedExcept = true + +# Should switch to true when possible +reportDeprecated = false + +# Can be enabled by generating type stubs for modules via pyright CLI +reportMissingTypeStubs = false + +reportUnsupportedDunderAll = false +reportAbstractUsage = false +reportUntypedBaseClass = false +reportOptionalIterable = false +reportCallInDefaultInitializer = false +reportInvalidTypeArguments = false +reportUntypedNamedTuple = false +reportRedeclaration = false +reportOptionalCall = false +reportConstantRedefinition = false +reportWildcardImportFromLibrary = false +reportIncompatibleVariableOverride = false +reportInvalidTypeForm = false +reportGeneralTypeIssues = false +reportOptionalOperand = false +reportUnnecessaryComparison = false +reportFunctionMemberAccess = false +reportUnnecessaryIsInstance = false +reportUnusedFunction = false +reportImportCycles = false +reportUnusedImport = false +reportUnusedVariable = false +reportOperatorIssue = false +reportAssignmentType = false +reportReturnType = false +reportPossiblyUnboundVariable = false +reportMissingSuperCall = false +reportUninitializedInstanceVariable = false +reportUnknownLambdaType = false +reportMissingTypeArgument = false +reportImplicitStringConcatenation = false +reportIncompatibleMethodOverride = false +reportPrivateUsage = false +reportUnusedCallResult = false +reportOptionalSubscript = false +reportCallIssue = false +reportOptionalMemberAccess = false +reportImplicitOverride = false +reportIndexIssue = false +reportAttributeAccessIssue = false +reportArgumentType = false +reportUnknownParameterType = false +reportMissingParameterType = false +reportUnknownVariableType = false +reportUnknownArgumentType = false +reportUnknownMemberType = false + +[dependency-groups] +dev = [ + "SCons==4.8.1", + "setuptools~=72.0", +] +lint = [ + "ruff==0.8.1", + "pre-commit==4.0.1", + "pyright==1.1.396", +] + +[tool.setuptools.dynamic] +version = {attr = "buildVersion.version_detailed"} diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..5192e37d --- /dev/null +++ b/readme.md @@ -0,0 +1,351 @@ +# MathCAT + +* Author: Neil Soiffer +* NVDA compatibility: 2025.1 or later (switching to a more modern Python makes it incompatible with earlier versions of NVDA) for 32 bit NVDA versions; 2026.1 for 64 bit NVDA versions. +* Download [stable version][1] + +MathCAT replaces MathPlayer because MathPlayer is no longer supported. MathCAT generates speech and braille from MathML. The speech for math produced by MathCAT is enhanced with prosody so that it sounds more natural. The speech can be navigated in three modes using the same commands as MathPlayer. In addition, the navigation node is indicated on a braille display. + +The supported languages are: Chinese (Traditional), English, Finnish, French, +German, Greek, Hungarian, Indonesian, Norwegian, Polish, Russian, Spanish, Swedish, and Vietnamese. + +The supported braille codes are: Nemeth, UEB Technical, CMU (Spanish/Portuguese), +Russian, Swedish, and Vietnamese braille code standards. +Also supported are the German/Austrian braille code standards for ASCIIMath and LaTeX math markup. + +Other language and braille code translations are in progress. + +MathCAT has a number of configuration options that control speech, navigation, and braille. +Many of these can be set in the MathCAT settings dialog (found NVDA Preferences menu). +For more information on these settings, see the [MathCAT documentation](https://nsoiffer.github.io/MathCAT/users.html). +The documentation includes a link to [a table listing all of the navigation commands in MathCAT](https://nsoiffer.github.io/MathCAT/nav-commands.html). + +Note: MathCAT is a general library for generating speech and braille from MathML. It is used by other AT projects besides NVDA. For information on the MathCAT project in general, see the main [MathCAT Documentation page](https://nsoiffer.github.io/MathCAT). + +Who should use MathCAT: + +* Those who use one of the supported languages or braille codes. +* Those who need high quality Nemeth braille (MathPlayer's Nemeth is based on liblouis' Nemeth generation which has a number of significant bugs that are technically difficult to fix). +* Those who use Eloquence as a voice +* Those who are reading PDF or HTML documents whose source is LaTeX that has turned on tagging. Those documents make use of a new MathML feature for expressing author intents, which can improve speech. + +Who should NOT use MathCAT: + +* Anyone who prefers Access8Math (for speech or other features) + +## MathCAT Update Log + +### Version v0.7.6-beta.8 + +## User-facing highlights + +| Area | Since 0.7.5 | +| ---- | ----------- | +| Languages | New: Greek, French, Hungarian, Polish, Russian; Improvements German, Norwegian, Traditional Chinease | +| Braille | New: Russian; Nemeth 2022-ish fixes; unicode ranges; BrailleCode refactor | +| Content | Augmented matrices; chemistry; currency/mtext cleanup; SSML fix | +| Quality | Fuzzing; better errors; dual `no-unsafe` CI track | + +## Details + +### Languages & speech + +* **Polish (`pl`)** — new +* **French (`fr`)** — new +* **Greek (`el`)** — new +* **Hungarian (`hu`)** — new +* **Russian (`ru`)** — new +* **Norwegian (`nb`)** — speech + navigation enhancements +* **German (`de`)** — digit rules, units, other speech fixes +* **Traditional Chinese (`zn-tw`)** — navigation / related rule updates +* **English** — core concept names (#381); principal Log vs log; identity / **augmented matrices**; black circled Latin letters; circled-number support; unit-definition cleanups +* **Intent** — inference tweaks + +### Braille + +* **Russian** — new +* **Nemeth** — 2022-oriented updates (ellipses, multipurpose between scripts, currency, typeform prefs cleanup, bugfixes) +* Unicode tables — **collapse to ranges** + range fixups +* Refactor — **`BrailleCode` trait + registry** +* Table-related braille/rules work +* Remove incomplete **ASCIIMath-fi** packaging that broke builds + +### Canonicalization, chemistry, robustness + +* Chemistry — mmultiscripts/scripts merge, atomic-number scoring, chem-element intent/heuristics, chem test fixes +* Bad mmultiscripts / empty bases / `data-split` cleanup +* Currency symbols split out of `mtext` / `mi` / `mn`; less aggressive `?` fill-in +* Empty `` / degree edge cases; mtext-as-number → `mn` where appropriate +* Stronger panic/error reporting (tests + containers) +* **SSML** — stopped incorrectly escaping SSML (#585) + +### Internals + +* Rust **edition 2024**; `lazy_static` → **`LazyLock`**; API cleanups (`AsRef`, etc.) +* **anyhow** instead of error-chain +* Optional **`no-unsafe`** via `sxd-document-no-unsafe` / `sxd-xpath-no-unsafe` (default = classic mode) +* CI — build/test/clippy for default **and** `no-unsafe`; fuzz both configs; CI on all branches; Python tooling CI; coverage; pre-release fixes +* **cargo-fuzz** harness, dictionary, corpus cache, regression helpers +* **audit-translations** and related Python/uv tooling +* AGENTS.md, CODEOWNERS, CLI / `mathml2text` path +* Dependency bumps; BrailleDocs publish exclude + +### Docs + +* README / product-page copy; AT notes (JAWS, Orca, Kurzweil, etc.) +* User guides / translators’ guide work; example HTML for translator testing + +### Version 0.7.5 + +#### Bug Fixes and Enhancements + +* Fixed bug in MathCAT dialog that prevented proper selection of Norwegian +* Fixed bug in German translation for division involving units (=> "pro") +* If MathML is directly embedded inside of a MathML leaf element, it will be spoken well. +* Improved Chemistry so that if the intent property `:chemical-formula` is used, it is inherited and overrides heuristics to determine if something is a chemical formula. For example, "A=B" will now speak "=" as "double bond" If some parent is marked with `intent=':chemical-formula'`. +* For ASCIIMath, added translations for chars with umlauts and also for ß (goes to "ss" because there is no defined ASCIIMath encoding for it) +* Added literal speech for"×", "‼", and "/" to English. + +#### API Additions + +* Added calls for `GetSupportedLanguages`, `GetSupportedSpeechStyles`, and `GetSupportedBrailleCodes`. + +### Version 0.7.2 + +* Added German translation. There is still more work to do on this, but I'm told it is usable. +* Added Norwegian translation +* Improved reading of "neuter" units +* Changed some character wording ("if and only if", "implies", "triangle") +* Fix problems with the zip files and regional variants. This should allow en-gb and zh-tw to be available. +* Fixed bugs in navigating in character mode and simple mode. +* Changed the names of some characters to be more semantic (e.g., "long double left right arrow" is not "if and only if"). +* Add some "literal" (not semantic) names for characters for LiteralSpeak and navigation. +* Fixed some bugs dealing with "intent" +* Fix a bug with generating id's that could cause a crash once every 36^4 times +* Add another heuristic to prevent something from being a potential function (when the potential function name appears within the argument) +* Fixed reading of a degree symbol followed by "F" or "C". +* Corrected the rule for what is allowed for "intent" +* Improved the inference rules for units (supports "mi" if it is marked as "normal") +* Fixed a navigation bug with log, ln, and lg +* Improved error messages -* these should aid in reporting problems in speech and navigation +* Improved speech for fractions that involve units ("meters *per* second") +* Many improvements to the recognition of Chemistry +* Fixed a Nemeth bug where a script end and baseline indicator were emitted when neither should have been present. +* Added varepsilon character to UEB +* Fixed off-by-one error when computing what to highlight in braille. +* Add definitions for "ⅆ", "ⅇ", "ⅈ" to braille codes + +### Version 0.6.10 + +* Update manifest to indicate compatibility with 2025.1, no external changes +* Because of internal NVDA changes to some speech engines, you may need to tweak MathCAT's "PauseFactor" setting +* Internal improvements to type hints and doc strings thanks to @seanbudd and @RyanMcCleary + +### Version 0.6.9 + +* Update manifest to indicate compatibility with 2025.1 +* Fix unhandled exception when opening up a new user preference file +* Strip '_' and '-' from unmatched intent names (and literals). For example "my-function" should be "my function". +* Add rule for "x check" (inverted hat) +* Add rule for repeating decimals for 'en'. This only handles the line over the repeating part, not other notations. + +### Version 0.6.8 + +Lots of changes because it has been a while since the last official release. + +#### Speech + +* Added "LiteralSpeak" style that does not infer what the meaning of the math and therefore, how that meaning spoken. +* Added Swedish to supported languages. +* Added Finnish to supported languages. +* For Vietnamese, added optional pitch change and beep for capital letters +* MathCAT will switch the voice when reading math if a different language from the current voice was set in the preference dialog. +* Added a en-UK variant with some British ways to speak bracketing chars. +* Added English rules for div, grad, and curl (calculus) +* Added English rule for $P(A|B)$ so that | is spoken as "given" +* Added more cases where invisible times is spoken (before roots) +* In terse mode, integer subscripts are spoken as "x 1" instead of "x sub 1". +* Added ability for authors to insert pauses (English only at the moment) +* Added a pause before row/equation/line labels +* Changed the speech for ≈ from "congruent to" to "approximately equal to" +* Added inference for cross-product and dot-product +* Added inference for div, grad, and curl +* Added special speech for zero, identity, and diagonal matrices in English +* Be more restrictive when inferring a table +* Changed speech for the general cases of `mover` and `munder` from "modified x with y above it" to "quantity x with y above it" +* Improved rule for {} so that it isn't always spoken as "set of ...". It could just be bracketing chars. +* Tweaked the speech for ∈ inside of a set so that the word "is" is dropped when part of a set -* "the set of all x is an element of ..." sounds poor. +* Improved rule for chemistry recognition for atomic numbers. +* Update to speech hint property names in the proposed MathML Core property list +* Add speech for coordinates ("the point at 1 comma 2") +* Added pauses for a "," +* Added an experimental `:pause-long`, `:pause-medium`, `:pause-short` for intent +* Added an 'xlong' pause +* Increased the meaning of short/medium/long pauses from 150ms/300ms/600ms to 200ms/400ms/800ms. As always, these are scaled to the speech rate. +* In MathML 4, `mlabedtr` is deprecated. A workaround is to use the intent property `:equation-label` on an `mtd` and this is now supported +* Added speech for units (e.g., "km", "in") -* won't work for single letter units such as "m" and "s" unless marked as a unit +* Terse mode now says "of" for functions except for trig/log functions. It was a little too terse before. + +#### Navigation + +* Added "Speech" to copy menu when navigating so that you can copy out the text used to speak the focus point in the expression being explored. +* Substantial rewrite of the navigation rules so that follow the inferred meaning. For example, if MathCAT says "absolute value of x" and you "zoom in", then you move to the "x", not to a vertical bar. As another example, if MathCAT determines that a table consists of rows of equations, navigation won't concatenate the columns so that the table acts like there is only one column. +* "Speak Overview" didn't do anything (fixed). Overviews remain under-developed. + +#### Braille + +* Added support for Finnish version of AsciiMath braille. +* Added support for Swedish braille. +* Added support for Vietnamese accents position for Vietnamese braille vowel "rhymes". +* Added preferences so that Nemeth users can remap typeforms (e.g, map BlackBoard Bold to a different character). +* Changed Blackboard typeform indicator to reuse italic indicator instead of reusing the script indicator. By changing the Nemeth typeform values in NVDA's addon subdirectory `addons\MathCAT\globalPlugins\MathCAT\Rulesprefs.yaml or adding it to `%AppData%\MathCAT\prefs.yaml`, you can restore the old mapping. + +#### Other + +* All the language and braille Rule files are zipped up per directory and unzipped on demand. + * This currently saves ~5mb when Rules.zip is unzipped, and will save even more as more languages and braille codes are added. + * If you know certain languages or braille code will definitely be used (e.g., it is the default), then the files in those directories can be manually unzipped to save a few tens of milliseconds the first time the language/braille code by that user. +* Added new preference DecimalSeparator. + * The default value is Auto, with other values being ".", ",", and "Custom". The first three values set DecimalSeparators and BlockSeparators. + * Auto sets those preferences based on the value of the Language pref. For some language such as Spanish, , is used in some countries and . is used in others. In this case, it is best to set the language to also include the country code (e.g, es-es or es-mx) to ensure the right value is used. +* Added more Unicode chars to include both all Unicode chars marked as "Sm" and those with a mathclass (except Alphabetic and Glyph classes) in the Unicode standard. +* Add support for some (upcoming) new Unicode characters (equilibrium arrows and others) used in Chemistry into UEB and Nemeth +* Fixed a bug with double-struck numbers for Nemeth. +* Several fixes for recognizing chemistry. + +#### Fixes + +* Fixed bug with espeak where it would slow down +* Forgot to implement relative slowdown when navigating -* fixed +* Fixed sans-serif indicator for Nemeth braille. +* Fixed a bug where empty cells in a table that is piecewise, m:system-of-equations or lines were spoken. +* Fixed bug where open/closed intervals were inferred when brackets/parens were nested (can't be an interval). +* Fixed a bug in UEB where passage mode should have been used for capitals. +* Fixed a crash with UEB in certain conditions with runs of capital letters. +* Fixed bug in Navigation of tables (previously reported "Error in Navigation"). +* Fixed bug moving to previous/next column in tables when at a table row level. +* Fixed bug when trying to correct bad MathML representation of chemistry inside of the base of a script. +* Fixed Vietnamese braille for "/". +* In the dialog code, the file location and %APPDATA% are now used to find the Rules and prefs files. +* After changing how prefs work in a previous version, I forgot to change MathRate and PauseFactor to be numbers, not strings. +* Fixed bug in the braille Rules (missed change from earlier) where a third argument should have been given to say to look in the Braille definitions.yaml files and not the speech ones when looking up the value of a definition. +* Cleaned up use of definitions.yaml. +* Fixed some bugs in the MathML cleanup for "," decimal separators. +* Found a bug in braille highlighting when nothing is highlighted (maybe never happens which is why I didn't see it in practice?) +* Fixed "Describe" mode so that it works -* it is still very minimal and probably not useful yet +* Add space after math speech to work around a MS Word bug that concatinates the next character in the text onto the math. + +### Version 0.5.6 + +* Added Copy As... to the MathCAT dialog (in the "Navagation" pane). +* Fixed a bug where the language reverted to English when changing speech styles. +* Fixed a bug with navigation and braille +* Fixed some Asciimath spacing problems. +* Improved chemistry recognition +* Updated MathCAT to new BANA Nemeth chemistry spec (still only single line and special case style/font changes not handled) +* Fix a crash when non-ASCII digits (e.g., bold digits) are used in numbers +* Don't use italic indicators in braille codes when the math alphanumeric italic chars are used +* Some other smaller bug fixes that weren't reported by users + +### Version 0.5.0 + +* Added German LaTeX braille code. Unlike other braille codes, this generates ASCII chars and uses the current braille output table to translate the characters to braille. +* Added (expermental) ASCIIMath braille code. Like the LaTeX braille code, this generates ASCII chars and uses the current braille output table to translate the characters to braille. +* Added "CopyAs" preference that supports copying as MathML, LaTeX, or ASCIIMath using cntl+C when focused on MathML (as before). The currently focused node is copied. Note: this is only listed in the prefs.yaml file and is not exposed (yet) in the MathCAT Preferences dialog. + +### Version 0.4.2 + +* Fixed language switching when voice changes and MathCAT language is "Auto" +* Added more checks for $Impairments to improve reading when it is not set for those who are blind +* Nemeth: fix for "~" when it isn't part of an mrow +* UEB: character additions, "~" spacing fix if prefix, xor fix, +* MathML cleanup for accented vowels (mainly for Vietnamese) +* Major rewrite of preference reading/updating code with big speedup -* added `CheckRuleFiles` pref to control which files are checked for updates +* Added two new interface calls -* enables setting the navigaton location from the braille cursor (not part of MathCAT addon yet) + +### Version 0.3.11 + +* Upgraded to python 3.11 and verified working with NVDA 2024.1 +* Fix bugs in Vietnamese braille and also in Speech, mostly for chemistry. +* Fix broken braille when braille code and dependent language don't match (specifically Vietnam braille and Vietnamese speech) +* Fix whitespace bug in HTML inside of tokens +* Improve roman numeral detection + +### Version 0.3.9 + +* Added Traditional Chinese translation (thanks to Hon-Jang Yang) +* Fixed bug with navigating into the base of a scripted expression that has parenthesis +* Significantly changed the way whitespace is handled. This mainly affects braille output (spaces and "omission" detection). +* Improved recognition of chemistry +* UEB braille fixes that came up from adding chemistry examples +* UEB fixes for adding auxillary parenthesis in some cases + +### Version 0.3.8 + +Braille: + +* Dialog has been internationalized for several languages (many thanks to the translators!) +* Initial implementation of CMU -* the braille code used in Spanish and Portuguese speaking countries +* Fix some UEB bugs and added some characters for UEB +* Significant improvements to Vietnamese braille + +Other fixes: + +* Change relative rate dialog slider to have a maximum value of 100% (now only allows setting slower rates). Also, added step sizes so it is easier to raise/lower the rate significantly. +* Fix eSpeak bug that sometimes cut off speech when the relative rate was changed +* Improvements to Vietnamese speech +* Fixed bug with OneCore voices saying "a" +* Fixed some navigation bugs when `AutoZoomOut` is False (not the default) +* Fix updating around language changes and some other dialog changes so they take effect immediately upon clicking "Apply" or "OK". +* Added an "Use Voice's Language" option so that out of the box, MathCAT will speak in the right language (if there is a translation) +* Several improvements for cleaning up poor MathML code + +### Version 0.3.3 + +This release has a number of bug fixes in it. The major new features and bug fixes are: + +* Added Spanish Translation (thanks to Noelia Ruiz and María Allo Roldán) +* Modified navigation so that it starts zoomed in one level +* Added cntrl+alt+arrow as a way to navigate tabular structures. These keys should be more memorable because they are used for table navigation in NVDA. +* Worked around NVDA bug for eSpeak voices that caused them to slow down when the relative MathRate was set to be slower than the text speech rate. +* Worked around a OneCore voice problem so that they will speak the long 'a' sound. + +There are lots of small tweaks to the speech and some bug fixes for both Nemeth and UEB. + +Note: there is now an option to get Vietnam's braille standard as braille output. This is still a work in progress and is too buggy to be used other than for testing. I expect the next MathCAT release will contain a reliable implementation. + +### Version 0.2.5 + +* More improvements chemistry +* Fixes for Nemeth: +* * Added "omission" rules +* * Added some rules for English Language Indicators +* * Added more cases where the Mulitpurpose indicator is needed +* * Fixes related to Nemeth and punctuation + +### Version 0.2 + +* Lots of bug fixes +* Improvements to speech +* A preference setting to control the duration of pausing (works with changes to relative speech rate for math) +* Support to recognize chemistry notation and speak it appropriately +* Translations to Indonesian and Vietnamese + +## Development Environment + +How to set up your developer environment: + +1. Install Python 3.11 (32-bit). +1. Set up your virtual environment. + * `python -m venv .venv` +1. Install python dependencies to `.venv`. + * Activate the virtual environment + `.venv\Scripts\activate` + * Install lint dependencies + `pip install ruff==0.8.1 pre-commit==4.0.1 pyright==1.1.396` +1. Import NVDA code. + * NVDA source code needs to be discoverable to get type hints, namespace resolution, code completion, and other IDE hints. + * The relative path `../nvda/source` is included in the pyright configuration in `pyproject.toml`. + * The [NVDA repository](https://github.com/nvaccess/nvda) should be cloned into the same parent directory. + i.e. as a sibling to this repository. + Alternatively, update `../nvda/source` in `pyproject.toml` to another location where the NVDA repository has been cloned. diff --git a/sconstruct b/sconstruct new file mode 100644 index 00000000..979b6ffd --- /dev/null +++ b/sconstruct @@ -0,0 +1,357 @@ +# NVDA add-on template SCONSTRUCT file +# Copyright (C) 2012-2023 Rui Batista, Noelia Martinez, Joseph Lee +# This file is covered by the GNU General Public License. +# See the file COPYING.txt for more details. + +import codecs +import gettext +import os +import os.path +import zipfile +import sys +import markdown + +# While names imported below are available by default in every SConscript +# Linters aren't aware about them. +# To avoid Flake8 F821 warnings about them they are imported explicitly. +# When using other Scons functions please add them to the line below. +from SCons.Script import BoolVariable, Builder, Copy, Environment, Variables + +# Bytecode should not be written for build vars module to keep the repository root folder clean. +import buildVars # NOQA: E402 + +sys.dont_write_bytecode = True + + +def md2html(source, dest): + + # Use extensions if defined. + mdExtensions = buildVars.markdownExtensions + lang = os.path.basename(os.path.dirname(source)).replace("_", "-") + localeLang = os.path.basename(os.path.dirname(source)) + try: + _ = gettext.translation( + "nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang] + ).gettext + summary = _(buildVars.addon_info["addon_summary"]) + except Exception: + summary = buildVars.addon_info["addon_summary"] + title = "{addonSummary} {addonVersion}".format( + addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] + ) + headerDic = { + '[[!meta title="': "# ", + '"]]': " #", + } + with codecs.open(source, "r", "utf-8") as f: + mdText = f.read() + for k, v in headerDic.items(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText, extensions=mdExtensions) + # Optimization: build resulting HTML text in one go instead of writing parts separately. + docText = "\n".join( + [ + "", + '' % lang, + "", + '' + '', + '', + "%s" % title, + "\n", + htmlText, + "\n", + ] + ) + with codecs.open(dest, "w", "utf-8") as f: + f.write(docText) + + +def mdTool(env): + mdAction = env.Action( + lambda target, source, env: md2html(source[0].path, target[0].path), + lambda target, source, env: "Generating % s" % target[0], + ) + mdBuilder = env.Builder( + action=mdAction, + suffix=".html", + src_suffix=".md", + ) + env["BUILDERS"]["markdown"] = mdBuilder + + +def validateVersionNumber(key, val, env): + # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. + # Ignore all this if version number is not specified, in which case json generator will validate this info. + if val == "0.0.0": + return + versionNumber = val.split(".") + if len(versionNumber) < 3: + raise ValueError("versionNumber must have three parts (major.minor.patch)") + if not all([part.isnumeric() for part in versionNumber]): + raise ValueError("versionNumber (major.minor.patch) must be integers") + + +vars = Variables() +vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) +vars.Add( + "versionNumber", + "Version number of the form major.minor.patch", + "0.0.0", + validateVersionNumber, +) +vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) +vars.Add( + "channel", + "Update channel for this build", + buildVars.addon_info["addon_updateChannel"], +) + +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", mdTool]) +env.Append(**buildVars.addon_info) + +if env["dev"]: + import datetime + + buildDate = datetime.datetime.now() + year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) + versionTimestamp = "".join([year, month.zfill(2), day.zfill(2)]) + env["addon_version"] = f"{versionTimestamp}-dev" + env["versionNumber"] = f"{versionTimestamp}.0.0" + env["channel"] = "dev" +elif env["version"] is not None: + env["addon_version"] = env["version"] +if "channel" in env and env["channel"] is not None: + env["addon_updateChannel"] = env["channel"] + +buildVars.addon_info["addon_version"] = env["addon_version"] +buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] + +addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") + + +def addonGenerator(target, source, env, for_signature): + action = env.Action( + lambda target, source, env: createAddonBundleFromPath( + source[0].abspath, target[0].abspath + ) + and None, + lambda target, source, env: "Generating Addon %s" % target[0], + ) + return action + + +def manifestGenerator(target, source, env, for_signature): + action = env.Action( + lambda target, source, env: generateManifest( + source[0].abspath, target[0].abspath + ) + and None, + lambda target, source, env: "Generating manifest %s" % target[0], + ) + return action + + +def translatedManifestGenerator(target, source, env, for_signature): + dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) + lang = os.path.basename(dir) + action = env.Action( + lambda target, source, env: generateTranslatedManifest( + source[1].abspath, lang, target[0].abspath + ) + and None, + lambda target, source, env: "Generating translated manifest %s" % target[0], + ) + return action + + +env["BUILDERS"]["NVDAAddon"] = Builder(generator=addonGenerator) +env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) +env["BUILDERS"]["NVDATranslatedManifest"] = Builder( + generator=translatedManifestGenerator +) + + +def createAddonHelp(dir): + docsDir = os.path.join(dir, "doc") + if os.path.isfile("style.css"): + cssPath = os.path.join(docsDir, "style.css") + cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) + env.Depends(addon, cssTarget) + if os.path.isfile("readme.md"): + readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") + readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) + env.Depends(addon, readmeTarget) + + +def createAddonBundleFromPath(path, dest): + """Creates a bundle from a directory that contains an addon manifest file.""" + basedir = os.path.abspath(path) + with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: + # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. + for dir, dirnames, filenames in os.walk(basedir): + relativePath = os.path.relpath(dir, basedir) + for filename in filenames: + pathInBundle = os.path.join(relativePath, filename) + absPath = os.path.join(dir, filename) + if pathInBundle not in buildVars.excludedFiles: + z.write(absPath, pathInBundle) + createAddonStoreJson(dest) + return dest + + +def createAddonStoreJson(bundle): + """Creates add-on store JSON file from an add-on package and manifest data.""" + import json + import hashlib + + # Set different json file names and version number properties based on version number parsing results. + if env["versionNumber"] == "0.0.0": + env["versionNumber"] = buildVars.addon_info["addon_version"] + versionNumberParsed = env["versionNumber"].split(".") + if all([part.isnumeric() for part in versionNumberParsed]): + if len(versionNumberParsed) == 1: + versionNumberParsed += ["0", "0"] + elif len(versionNumberParsed) == 2: + versionNumberParsed.append("0") + else: + versionNumberParsed = [] + if len(versionNumberParsed): + major, minor, patch = [int(part) for part in versionNumberParsed] + jsonFilename = f"{major}.{minor}.{patch}.json" + else: + jsonFilename = f'{buildVars.addon_info["addon_version"]}.json' + major, minor, patch = 0, 0, 0 + print("Generating % s" % jsonFilename) + sha256 = hashlib.sha256() + with open(bundle, "rb") as f: + for byte_block in iter(lambda: f.read(65536), b""): + sha256.update(byte_block) + hashValue = sha256.hexdigest() + try: + minimumNVDAVersion = buildVars.addon_info["addon_minimumNVDAVersion"].split(".") + except AttributeError: + minimumNVDAVersion = [0, 0, 0] + minMajor, minMinor = minimumNVDAVersion[:2] + minPatch = minimumNVDAVersion[-1] if len(minimumNVDAVersion) == 3 else "0" + try: + lastTestedNVDAVersion = buildVars.addon_info[ + "addon_lastTestedNVDAVersion" + ].split(".") + except AttributeError: + lastTestedNVDAVersion = [0, 0, 0] + lastTestedMajor, lastTestedMinor = lastTestedNVDAVersion[:2] + lastTestedPatch = ( + lastTestedNVDAVersion[-1] if len(lastTestedNVDAVersion) == 3 else "0" + ) + channel = buildVars.addon_info["addon_updateChannel"] + if channel is None: + channel = "stable" + addonStoreEntry = { + "addonId": buildVars.addon_info["addon_name"], + "displayName": buildVars.addon_info["addon_summary"], + "URL": "", + "description": buildVars.addon_info["addon_description"], + "sha256": hashValue, + "homepage": buildVars.addon_info["addon_url"], + "addonVersionName": buildVars.addon_info["addon_version"], + "addonVersionNumber": {"major": major, "minor": minor, "patch": patch}, + "minNVDAVersion": { + "major": int(minMajor), + "minor": int(minMinor), + "patch": int(minPatch), + }, + "lastTestedVersion": { + "major": int(lastTestedMajor), + "minor": int(lastTestedMinor), + "patch": int(lastTestedPatch), + }, + "channel": channel, + "publisher": "", + "sourceURL": buildVars.addon_info["addon_sourceURL"], + "license": buildVars.addon_info["addon_license"], + "licenseURL": buildVars.addon_info["addon_licenseURL"], + } + with open(jsonFilename, "w") as addonStoreJson: + json.dump(addonStoreEntry, addonStoreJson, indent="\t") + + +def generateManifest(source, dest): + addon_info = buildVars.addon_info + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**addon_info) + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) + + +def generateTranslatedManifest(source, language, out): + _ = gettext.translation( + "nvda", localedir=os.path.join("addon", "locale"), languages=[language] + ).gettext + vars = {} + for var in ("addon_summary", "addon_description"): + vars[var] = _(buildVars.addon_info[var]) + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + result = manifest_template.format(**vars) + with codecs.open(out, "w", "utf-8") as f: + f.write(result) + + +def expandGlobs(files): + return [f for pattern in files for f in env.Glob(pattern)] + + +addon = env.NVDAAddon(addonFile, env.Dir("addon")) + +langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] + +# Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated +for dir in langDirs: + poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) + moFile = env.gettextMoFile(poFile) + env.Depends(moFile, poFile) + translatedManifest = env.NVDATranslatedManifest( + dir.File("manifest.ini"), [moFile, os.path.join("manifest-translated.ini.tpl")] + ) + env.Depends(translatedManifest, ["buildVars.py"]) + env.Depends(addon, [translatedManifest, moFile]) + +pythonFiles = expandGlobs(buildVars.pythonSources) +for file in pythonFiles: + env.Depends(addon, file) + +# Convert markdown files to html +# We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager +createAddonHelp("addon") +for mdFile in env.Glob(os.path.join("addon", "doc", "*", "*.md")): + htmlFile = env.markdown(mdFile) + env.Depends(htmlFile, mdFile) + env.Depends(addon, htmlFile) + +# Pot target +i18nFiles = expandGlobs(buildVars.i18nSources) +gettextvars = { + "gettext_package_bugs_address": "nvda-translations@groups.io", + "gettext_package_name": buildVars.addon_info["addon_name"], + "gettext_package_version": buildVars.addon_info["addon_version"], +} + +pot = env.gettextPotFile("${addon_name}.pot", i18nFiles, **gettextvars) +env.Alias("pot", pot) +env.Depends(pot, i18nFiles) +mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars) +env.Alias("mergePot", mergePot) +env.Depends(mergePot, i18nFiles) + +# Generate Manifest path +manifest = env.NVDAManifest( + os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl") +) +# Ensure manifest is rebuilt if buildVars is updated. +env.Depends(manifest, "buildVars.py") + +env.Depends(addon, manifest) +env.Default(addon) +env.Clean(addon, [".sconsign.dblite", "addon/doc/" + buildVars.baseLanguage + "/"]) diff --git a/NVDA-addon/site_scons/site_tools/gettexttool/__init__.py b/site_scons/site_tools/gettexttool/__init__.py similarity index 72% rename from NVDA-addon/site_scons/site_tools/gettexttool/__init__.py rename to site_scons/site_tools/gettexttool/__init__.py index db9a0096..3e8af277 100644 --- a/NVDA-addon/site_scons/site_tools/gettexttool/__init__.py +++ b/site_scons/site_tools/gettexttool/__init__.py @@ -1,4 +1,4 @@ -""" This tool allows generation of gettext .mo compiled files, pot files from source code files +"""This tool allows generation of gettext .mo compiled files, pot files from source code files and pot files for merging. Three new builders are added into the constructed environment: @@ -15,6 +15,7 @@ """ + from SCons.Action import Action @@ -36,20 +37,24 @@ def generate(env): env.SetDefault(gettext_package_name="") env.SetDefault(gettext_package_version="") - env['BUILDERS']['gettextMoFile'] = env.Builder( + env["BUILDERS"]["gettextMoFile"] = env.Builder( action=Action("msgfmt -o $TARGET $SOURCE", "Compiling translation $SOURCE"), suffix=".mo", - src_suffix=".po" + src_suffix=".po", ) - env['BUILDERS']['gettextPotFile'] = env.Builder( - action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), - suffix=".pot") + env["BUILDERS"]["gettextPotFile"] = env.Builder( + action=Action( + "xgettext " + XGETTEXT_COMMON_ARGS, + "Generating pot file $TARGET", + ), + suffix=".pot", + ) - env['BUILDERS']['gettextMergePotFile'] = env.Builder( + env["BUILDERS"]["gettextMergePotFile"] = env.Builder( action=Action( "xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, - "Generating pot file $TARGET" + "Generating pot file $TARGET", ), - suffix=".pot" + suffix=".pot", ) diff --git a/src/lib.rs b/src/lib.rs index 8854114b..c96412f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! 1. whatever preferences the AT needs to set, it is done with calls to [`SetPreference`]. //! 2. the MathML is sent over via [`SetMathML`]. //! 3. AT calls to get the speech [`GetSpokenText`] and calls [`GetBraille`] to get the (Unicode) braille. -//! +//! //! Navigation can be done via calls to either: //! * [`DoNavigateKeyPress`] (takes key events as input) //! * [`DoNavigateCommand`] (takes the commands the key events internally map to) @@ -14,7 +14,7 @@ //! * [`GetNavigationMathML`] -- returns a string representing the MathML for the selected node //! Note: a second integer is returned. This is the offset in characters for a leaf node. //! This is needed when navigating by character for multi-symbol leaf nodes such as "sin" and "1234" -//! +//! //! It is also possible to find out what preferences are currently set by calling [`GetPreference`] //! //! AT can pass key strokes to allow a user to navigate the MathML by calling [`DoNavigateKeyPress`]; the speech is returned. @@ -64,6 +64,24 @@ pub fn GetVersion(_py: Python) -> PyResult { return Ok( get_version() ); } +#[pyfunction] +/// Returns a list of all supported languages (["en", "es", ...]) +pub fn GetSupportedLanguages(_py: Python) -> PyResult> { // type in Python is list[str] + return convert_error(get_supported_languages()); +} + +#[pyfunction] +/// Returns a list of all supported speech styles given a language (["ClearSpeak", "SimpleSpeak", ...]) +pub fn GetSupportedSpeechStyles(_py: Python, lang: String) -> PyResult> { // type in Python is list[str] + return convert_error(get_supported_speech_styles(lang)); +} + +#[pyfunction] +/// Returns a list of all supported braille codes (["UEB", "Nemeth", ...]) +pub fn GetSupportedBrailleCodes(_py: Python) -> PyResult> { // type in Python is list[str] + return convert_error(get_supported_braille_codes()); +} + #[pyfunction] /// Get the spoken text of the MathML that was set. /// The speech takes into account any AT or user preferences. @@ -95,15 +113,23 @@ pub fn GetPreference(_py: Python, name: String) -> PyResult { } #[pyfunction] -#[allow(unused_variables)] /// Get the braille associated with the MathML node with a given id (MathML set by `SetMathML`]). /// An empty string can be used to return the braille associated with the entire expression. -/// +/// /// The braille returned depends upon the preference for braille output. pub fn GetBraille(_py: Python, nav_node_id: String) -> PyResult { return convert_error( get_braille(nav_node_id) ); } +#[pyfunction] +/// Get the braille associated with the MathML node with a given id (MathML set by `SetMathML`]). +/// An empty string can be used to return the braille associated with the entire expression. +/// +/// The braille returned depends upon the preference for braille output. +pub fn GetNavigationBraille(_py: Python) -> PyResult { + return convert_error( get_navigation_braille() ); +} + #[pyfunction] /// Given a key code along with the modifier keys, the current node is moved accordingly (or value reported in some cases). /// @@ -116,17 +142,17 @@ pub fn DoNavigateKeyPress(_py: Python, key: usize, shift_key: bool, control_key: /// Given a command, the current node is moved accordingly (or value reported in some cases). /// /// The spoken text for the new current node is returned. -/// +/// /// The list of legal commands are: -/// "MovePrevious", "MoveNext", "MoveStart", "MoveEnd", "MoveLineStart", "MoveLineEnd", -/// "MoveCellPrevious", "MoveCellNext", "MoveCellUp", "MoveCellDown", "MoveColumnStart", "MoveColumnEnd", -/// "ZoomIn", "ZoomOut", "ZoomOutAll", "ZoomInAll", -/// "MoveLastLocation", -/// "ReadPrevious", "ReadNext", "ReadCurrent", "ReadCellCurrent", "ReadStart", "ReadEnd", "ReadLineStart", "ReadLineEnd", -/// "DescribePrevious", "DescribeNext", "DescribeCurrent", -/// "WhereAmI", "WhereAmIAll", -/// "ToggleZoomLockUp", "ToggleZoomLockDown", "ToggleSpeakMode", -/// "Exit", +/// "MovePrevious", "MoveNext", "MoveStart", "MoveEnd", "MoveLineStart", "MoveLineEnd", +/// "MoveCellPrevious", "MoveCellNext", "MoveCellUp", "MoveCellDown", "MoveColumnStart", "MoveColumnEnd", +/// "ZoomIn", "ZoomOut", "ZoomOutAll", "ZoomInAll", +/// "MoveLastLocation", +/// "ReadPrevious", "ReadNext", "ReadCurrent", "ReadCellCurrent", "ReadStart", "ReadEnd", "ReadLineStart", "ReadLineEnd", +/// "DescribePrevious", "DescribeNext", "DescribeCurrent", +/// "WhereAmI", "WhereAmIAll", +/// "ToggleZoomLockUp", "ToggleZoomLockDown", "ToggleSpeakMode", +/// "Exit", /// "MoveTo0","MoveTo1","MoveTo2","MoveTo3","MoveTo4","MoveTo5","MoveTo6","MoveTo7","MoveTo8","MoveTo9", /// "Read0","Read1","Read2","Read3","Read4","Read5","Read6","Read7","Read8","Read9", /// "Describe0","Describe1","Describe2","Describe3","Describe4","Describe5","Describe6","Describe7","Describe8","Describe9", @@ -148,14 +174,18 @@ pub fn GetNavigationMathML(_py: Python) -> PyResult<(String, usize)> { } #[pymodule] -fn libmathcat(_py: Python, m: &PyModule) -> PyResult<()> { +fn libmathcat_py(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(SetRulesDir, m)?)?; m.add_function(wrap_pyfunction!(SetMathML, m)?)?; m.add_function(wrap_pyfunction!(GetVersion, m)?)?; + m.add_function(wrap_pyfunction!(GetSupportedLanguages, m)?)?; + m.add_function(wrap_pyfunction!(GetSupportedSpeechStyles, m)?)?; + m.add_function(wrap_pyfunction!(GetSupportedBrailleCodes, m)?)?; m.add_function(wrap_pyfunction!(GetSpokenText, m)?)?; m.add_function(wrap_pyfunction!(SetPreference, m)?)?; m.add_function(wrap_pyfunction!(GetPreference, m)?)?; m.add_function(wrap_pyfunction!(GetBraille, m)?)?; + m.add_function(wrap_pyfunction!(GetNavigationBraille, m)?)?; m.add_function(wrap_pyfunction!(DoNavigateKeyPress, m)?)?; m.add_function(wrap_pyfunction!(DoNavigateCommand, m)?)?; m.add_function(wrap_pyfunction!(GetNavigationMathMLId, m)?)?; @@ -171,7 +201,7 @@ mod py_tests { #[test] fn test_setting() { // this isn't a real test - pyo3::prepare_freethreaded_python(); + Python::initialize(); let mathml_str = "(451,231)"; match convert_error( libmathcat::interface::set_mathml(mathml_str.to_string()) ) { Ok(_mathml_with_ids) => println!("MathML is set w/o error"), @@ -183,4 +213,4 @@ mod py_tests { Err(e) => panic!("Error remains {}", e.to_string()), } } -} \ No newline at end of file +} diff --git a/style.css b/style.css new file mode 100644 index 00000000..90f99d14 --- /dev/null +++ b/style.css @@ -0,0 +1,26 @@ +@charset "utf-8"; +body { +font-family : Verdana, Arial, Helvetica, Sans-serif; +line-height: 1.2em; +} +h1, h2 {text-align: center} +dt { +font-weight : bold; +float : left; +width: 10%; +clear: left +} +dd { +margin : 0 0 0.4em 0; +float : left; +width: 90%; +display: block; +} +p { clear : both; +} +a { text-decoration : underline; +} +:active { +text-decoration : none; +} +a:focus, a:hover {outline: solid} diff --git a/tasks.json b/tasks.json new file mode 100644 index 00000000..93112892 --- /dev/null +++ b/tasks.json @@ -0,0 +1,37 @@ +// tasks.json +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "flake8-whole-project", + "type": "shell", + "command": "flake8 .", + "presentation": { + "echo": true, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true, + "revealProblems": "never" + }, + "problemMatcher": { + "owner": "flake8", + "source": "flake8-whole-project", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.+):(\\d+):(\\d+): ((\\w+)\\d+) (.+)$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "severity": 5, + "message": 6 + } + } + } + ] + } + \ No newline at end of file diff --git a/v0.7.2-rc.3.json b/v0.7.2-rc.3.json new file mode 100644 index 00000000..6bc9502e --- /dev/null +++ b/v0.7.2-rc.3.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n It provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n The speech quality is not quite as good as MathPlayer's speech yet,\n but the braille support is much better and includes support for Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n and Vietnamese braille code standards. Translations to Chinese (Traditional), Indonesian, Spanish, and Vietnamese exist\n and other translations are in progress.", + "sha256": "4639ac555798a60ce021ab7b31657caaa9e16ad4c0b234f71c10dc20589f83a1", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "v0.7.2-rc.3", + "addonVersionNumber": { + "major": 0, + "minor": 0, + "patch": 0 + }, + "minNVDAVersion": { + "major": 2025, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2025, + "minor": 3, + "patch": 0 + }, + "channel": "dev", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/v0.7.2-rc.4.json b/v0.7.2-rc.4.json new file mode 100644 index 00000000..a1ffe6f6 --- /dev/null +++ b/v0.7.2-rc.4.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n It provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n The speech quality is not quite as good as MathPlayer's speech yet,\n but the braille support is much better and includes support for Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n and Vietnamese braille code standards. Translations to Chinese (Traditional), Indonesian, Spanish, and Vietnamese exist\n and other translations are in progress.", + "sha256": "9f26aacf95923e9710a4e09dbc509328edb312a3940326f23079e39f4bc0a22b", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "v0.7.2-rc.4", + "addonVersionNumber": { + "major": 0, + "minor": 0, + "patch": 0 + }, + "minNVDAVersion": { + "major": 2025, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2025, + "minor": 3, + "patch": 0 + }, + "channel": "dev", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file diff --git a/v0.7.4-beta1.json b/v0.7.4-beta1.json new file mode 100644 index 00000000..6c050ab5 --- /dev/null +++ b/v0.7.4-beta1.json @@ -0,0 +1,29 @@ +{ + "addonId": "MathCAT", + "displayName": "MathCAT: speech and braille from MathML", + "URL": "", + "description": "MathCAT is a replacement for MathPlayer which has been discontinued.\n It provides speech and braille support, and also supports MathPlayer's three modes of navigation.\n The speech quality is not quite as good as MathPlayer's speech yet,\n but the braille support is much better and includes support for Nemeth, UEB Technical, CMU (Spanish/Portuguese),\n and Vietnamese braille code standards. Translations to Chinese (Traditional), Indonesian, Spanish, and Vietnamese exist\n and other translations are in progress.", + "sha256": "8b16a3e386630f57ff5a8b50c95ca6fa5858f78e5212b1d1acd341e89c5e7625", + "homepage": "https://nsoiffer.github.io/MathCAT/", + "addonVersionName": "v0.7.4-beta1", + "addonVersionNumber": { + "major": 0, + "minor": 0, + "patch": 0 + }, + "minNVDAVersion": { + "major": 2025, + "minor": 1, + "patch": 0 + }, + "lastTestedVersion": { + "major": 2025, + "minor": 3, + "patch": 0 + }, + "channel": "dev", + "publisher": "", + "sourceURL": "https://github.com/NSoiffer/MathCATForPython", + "license": "MIT and GPL 2", + "licenseURL": "https://raw.githubusercontent.com/NSoiffer/MathCAT/main/LICENSE" +} \ No newline at end of file