diff --git a/.appveyor.yml b/.appveyor.yml index 87f6cbde6384..13705adc99f9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,6 @@ # With infos from # http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ # https://packaging.python.org/en/latest/appveyor/ -# https://github.com/rmcgibbo/python-appveyor-conda-example --- # Backslashes in quotes need to be escaped: \ -> "\\" @@ -18,7 +17,7 @@ skip_commits: clone_depth: 50 -image: Visual Studio 2017 +image: Visual Studio 2022 environment: @@ -30,7 +29,6 @@ environment: matrix: - PYTHON_VERSION: "3.11" - CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" TEST_ALL: "yes" # We always use a 64-bit machine, but can build x86 distributions @@ -46,35 +44,32 @@ cache: - '%USERPROFILE%\.cache\matplotlib' init: - - echo %PYTHON_VERSION% %CONDA_INSTALL_LOCN% + - ps: + Invoke-Webrequest + -URI https://micro.mamba.pm/api/micromamba/win-64/latest + -OutFile C:\projects\micromamba.tar.bz2 + - ps: C:\PROGRA~1\7-Zip\7z.exe x C:\projects\micromamba.tar.bz2 -aoa -oC:\projects\ + - ps: C:\PROGRA~1\7-Zip\7z.exe x C:\projects\micromamba.tar -ttar -aoa -oC:\projects\ + - 'set PATH=C:\projects\Library\bin;%PATH%' + - micromamba shell init --shell cmd.exe + - micromamba config set always_yes true + - micromamba config prepend channels conda-forge + - micromamba info install: - - set PATH=%CONDA_INSTALL_LOCN%;%CONDA_INSTALL_LOCN%\scripts;%PATH%; - - conda config --set always_yes true - - conda config --set show_channel_urls yes - - conda config --prepend channels conda-forge - - # For building, use a new environment - # Add python version to environment - # `^ ` escapes spaces for indentation - - echo ^ ^ - python=%PYTHON_VERSION% >> environment.yml - - conda env create -f environment.yml - - activate mpl-dev - - conda install -c conda-forge pywin32 - - echo %PYTHON_VERSION% %TARGET_ARCH% - # Show the installed packages + versions - - conda list + - micromamba env create -f environment.yml python=%PYTHON_VERSION% pywin32 + - micromamba activate mpl-dev test_script: # Now build the thing.. - set LINK=/LIBPATH:%cd%\lib - - pip install -v --no-build-isolation --config-settings=setup-args="--vsenv" --editable .[dev] + - pip install -v --no-build-isolation --editable .[dev] # this should show no freetype dll... - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" - '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' # this are optional dependencies so that we don't skip so many tests... - - if x%TEST_ALL% == xyes conda install -q ffmpeg inkscape + - if x%TEST_ALL% == xyes micromamba install -q ffmpeg inkscape # miktex is available on conda, but seems to fail with permission errors. # missing packages on conda-forge for imagemagick # This install sometimes failed randomly :-( @@ -95,11 +90,11 @@ artifacts: type: Zip on_finish: - - conda install codecov - - codecov -e PYTHON_VERSION PLATFORM -n "$PYTHON_VERSION Windows" + - micromamba install codecov + - codecov -e PYTHON_VERSION PLATFORM -n "%PYTHON_VERSION% Windows" on_failure: - # Generate a html for visual tests + # Generate an html for visual tests - python tools/visualize_tests.py --no-browser - echo zipping images after a failure... - 7z a result_images.zip result_images\ | grep -v "Compressing" diff --git a/.circleci/config.yml b/.circleci/config.yml index 5b4cbf5570b8..85622ffa7013 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,7 +67,7 @@ commands: fonts-install: steps: - restore_cache: - key: fonts-4 + key: fonts-5 - run: name: Install custom fonts command: | @@ -80,7 +80,7 @@ commands: -O ~/.local/share/fonts/xkcd-Script.ttf || true fc-cache -f -v - save_cache: - key: fonts-4 + key: fonts-5 paths: - ~/.local/share/fonts/ @@ -103,10 +103,10 @@ commands: - run: name: Install Python dependencies command: | - python -m pip install --user -r requirements/dev/build-requirements.txt + python -m pip install --user --group build python -m pip install --user \ numpy<< parameters.numpy_version >> \ - -r requirements/doc/doc-requirements.txt + --group doc python -m pip install --no-deps --user \ git+https://github.com/matplotlib/mpl-sphinx-theme.git @@ -125,7 +125,7 @@ commands: --no-build-isolation --editable .[dev] fi - save_cache: - key: build-deps-2 + key: build-deps-3 paths: - subprojects/packagecache @@ -147,7 +147,7 @@ commands: export RELEASE_TAG='-t release' fi mkdir -p logs - make html O="-T $RELEASE_TAG -j1 -w /tmp/sphinxerrorswarnings.log" + make html O="-T $RELEASE_TAG -j4 -w /tmp/sphinxerrorswarnings.log" rm -r build/html/_sources working_directory: doc - save_cache: @@ -216,9 +216,9 @@ commands: # jobs: - docs-python39: + docs-python3: docker: - - image: cimg/python:3.9 + - image: cimg/python:3.12 resource_class: large steps: - checkout @@ -259,4 +259,4 @@ workflows: jobs: # NOTE: If you rename this job, then you must update the `if` condition # and `circleci-jobs` option in `.github/workflows/circleci.yml`. - - docs-python39 + - docs-python3 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 36e8bcf5476f..000000000000 --- a/.flake8 +++ /dev/null @@ -1,90 +0,0 @@ -[flake8] -max-line-length = 88 -select = - # flake8 default - D, E, F, W, -ignore = - # flake8 default - E121,E123,E126,E226,E24,E704,W503,W504, - # Additional ignores: - E127, E131, - E266, - E305, E306, - E741, - F841, - # pydocstyle - D100, D101, D102, D103, D104, D105, D106, - D200, D202, D204, D205, - D301, - D400, D401, D403, D404 - # ignored by pydocstyle numpy docstring convention - D107, D203, D212, D402, D413, D415, D416, D417, - # while D213 is ignored by the numpy docstring convention, it's left out above, - # because we want to enforce it. - -exclude = - .git - build - doc/gallery - doc/tutorials - # External files. - tools/gh_api.py - .tox - .eggs - -per-file-ignores = - lib/matplotlib/_cm.py: E202, E203, E302 - lib/matplotlib/_mathtext.py: E221, E251 - lib/matplotlib/_mathtext_data.py: E203, E261 - lib/matplotlib/backends/backend_template.py: F401 - lib/matplotlib/mathtext.py: E221 - lib/matplotlib/pylab.py: F401, F403 - lib/matplotlib/pyplot.py: F811 - lib/matplotlib/tests/test_mathtext.py: E501 - lib/matplotlib/transforms.py: E201, E202, E203 - lib/matplotlib/tri/_triinterpolate.py: E201, E221 - lib/mpl_toolkits/axes_grid1/axes_size.py: E272 - lib/mpl_toolkits/axisartist/angle_helper.py: E221 - - doc/conf.py: E402 - galleries/users_explain/quick_start.py: E402 - galleries/users_explain/artists/paths.py: E402 - galleries/users_explain/artists/patheffects_guide.py: E402 - galleries/users_explain/artists/transforms_tutorial.py: E402, E501 - galleries/users_explain/colors/colormaps.py: E501 - galleries/users_explain/colors/colors.py: E402 - galleries/tutorials/artists.py: E402 - galleries/users_explain/axes/constrainedlayout_guide.py: E402 - galleries/users_explain/axes/legend_guide.py: E402 - galleries/users_explain/axes/tight_layout_guide.py: E402 - galleries/users_explain/animations/animations.py: E501 - galleries/tutorials/images.py: E501 - galleries/tutorials/pyplot.py: E402, E501 - galleries/users_explain/text/annotations.py: E402, E501 - galleries/users_explain/text/mathtext.py: E501 - galleries/users_explain/text/text_intro.py: E402 - galleries/users_explain/text/text_props.py: E501 - - galleries/examples/animation/frame_grabbing_sgskip.py: E402 - galleries/examples/images_contours_and_fields/tricontour_demo.py: E201 - galleries/examples/images_contours_and_fields/tripcolor_demo.py: E201 - galleries/examples/images_contours_and_fields/triplot_demo.py: E201 - galleries/examples/lines_bars_and_markers/marker_reference.py: E402 - galleries/examples/misc/print_stdout_sgskip.py: E402 - galleries/examples/misc/table_demo.py: E201 - galleries/examples/style_sheets/bmh.py: E501 - galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py: E402 - galleries/examples/text_labels_and_annotations/custom_legends.py: E402 - galleries/examples/ticks/date_concise_formatter.py: E402 - galleries/examples/ticks/date_formatters_locators.py: F401 - galleries/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py: E402 - galleries/examples/user_interfaces/embedding_in_gtk3_sgskip.py: E402 - galleries/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py: E402 - galleries/examples/user_interfaces/embedding_in_gtk4_sgskip.py: E402 - galleries/examples/user_interfaces/gtk3_spreadsheet_sgskip.py: E402 - galleries/examples/user_interfaces/gtk4_spreadsheet_sgskip.py: E402 - galleries/examples/user_interfaces/mpl_with_glade3_sgskip.py: E402 - galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py: E402 - galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py: E402 - galleries/examples/userdemo/pgf_preamble_sgskip.py: E402 -force-check = True diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 045386dc7402..f3595d2b7865 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -15,7 +15,9 @@ body: attributes: label: Code for reproduction description: >- - If possible, please provide a minimum self-contained example. + If possible, please provide a minimum self-contained example. If you + have used generative AI as an aid see + https://matplotlib.org/devdocs/devel/contribute.html#restrictions-on-generative-ai-usage placeholder: Paste your code here. This field is automatically formatted as Python code. render: Python validations: @@ -80,6 +82,8 @@ body: options: - pip - conda + - pixi + - uv - Linux package manager - from source (.tar.gz) - git checkout diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index dc80f6d7c91d..635f11028226 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -7,5 +7,5 @@ contact_links: url: https://discourse.matplotlib.org about: If you have a usage question - name: Chat with devs - url: https://gitter.im/matplotlib/matplotlib + url: https://discourse.matplotlib.org/chat/c/matplotlib/2 about: Ask short questions about contributing to Matplotlib diff --git a/.github/ISSUE_TEMPLATE/tag_proposal.yml b/.github/ISSUE_TEMPLATE/tag_proposal.yml index aa3345336089..2bb856d26be6 100644 --- a/.github/ISSUE_TEMPLATE/tag_proposal.yml +++ b/.github/ISSUE_TEMPLATE/tag_proposal.yml @@ -2,7 +2,7 @@ name: Tag Proposal description: Suggest a new tag or subcategory for the gallery of examples title: "[Tag]: " -labels: [Tag proposal] +labels: ["Documentation: tags"] body: - type: markdown attributes: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7f17a80dc4b6..06cd0d2fd3bf 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,15 +4,24 @@ out the development guide https://matplotlib.org/devdocs/devel/index.html --> ## PR summary - +## AI Disclosure + ## PR checklist diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 34902e5236df..ba29818c46de 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,9 @@ updates: actions: patterns: - "*" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + exclude-paths: + - "ci/minver-requirements.txt" diff --git a/.github/labeler.yml b/.github/labeler.yml index 43a1246ba68a..77b79146b47f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,7 +1,9 @@ --- "CI: Run cibuildwheel": - changed-files: - - any-glob-to-any-file: ['.github/workflows/cibuildwheel.yml'] + - any-glob-to-any-file: + - '.github/workflows/cibuildwheel.yml' + - '.github/workflows/wasm.yml' "CI: Run cygwin": - changed-files: - any-glob-to-any-file: ['.github/workflows/cygwin.yml'] @@ -89,6 +91,7 @@ - 'doc/conf.py' - 'doc/Makefile' - 'doc/make.bat' + - 'doc/sphinxext/**' "Documentation: devdocs": - changed-files: - any-glob-to-any-file: diff --git a/.github/workflows/autoclose_comment.yml b/.github/workflows/autoclose_comment.yml new file mode 100644 index 000000000000..4d18d82a801f --- /dev/null +++ b/.github/workflows/autoclose_comment.yml @@ -0,0 +1,80 @@ +--- +name: autoclose comment +# Post comment on PRs when labeled with "status: autoclose candidate". +# Based on scikit-learn's autoclose bot at +# https://github.com/scikit-learn/scikit-learn/blob/main/.github/workflows/autoclose-comment.yml + +permissions: + contents: read + pull-requests: write + +on: + pull_request_target: + types: + - labeled + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + +jobs: + post_comment: + name: post_comment + if: "${{ contains(github.event.label.name, 'status: autoclose candidate') }}" + runs-on: ubuntu-latest + + steps: + + - name: comment on potential autoclose + run: | + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/$GH_REPO/issues/$PULL_REQUEST_NUMBER/comments \ + -f "body=$BODY" + env: + BODY: > + ⏰ This pull request might be automatically closed in two weeks from now. + + + Thank you for your contribution to Matplotlib and for the effort you + have put into this PR. This pull request does not yet meet the + quality and clarity standards needed for an effective review. + Project maintainers have limited time for code reviews, and our goal + is to prioritize well-prepared contributions to keep Matplotlib + maintainable. + + + Matplotlib maintainers cannot provide one-to-one guidance on this PR. + However, if you ask focused, well-researched questions, a community + member may be willing to help. 💬 + + + To increase the chance of a productive review: + + - Use [the template provided in the PR + description](https://github.com/matplotlib/matplotlib/blob/main/.github/PULL_REQUEST_TEMPLATE.md) + and fill it out as completely as possible, especially the summary and AI Disclosure sections. + + - Make sure your PR conforms to our [PR + checklist](https://matplotlib.org/devdocs/devel/pr_guide.html#summary-for-pull-request-authors). + + + As the author, you are responsible for driving this PR, which entails doing + necessary background research as well as presenting its context and your + thought process. If you are a new contributor, or do not know how to + fulfill these requirements, we recommend that you familiarize + yourself with Matplotlib's + [development conventions](https://matplotlib.org/devdocs/devel/index.html) + or engage with the community via our [Discourse](https://discourse.matplotlib.org/) + or one of our [meetings](https://scientific-python.org/calendars/) + before submitting code. + + + If you substantially improve this PR within two weeks, leave a comment + and a team member may remove the `status: autoclose candidate` label and the + PR stays open. Cosmetic changes or incomplete fixes will not be + sufficient. Maintainers will assess improvements on their own + schedule. Please do not ping (`@`) maintainers. diff --git a/.github/workflows/autoclose_schedule.yml b/.github/workflows/autoclose_schedule.yml new file mode 100644 index 000000000000..7e0fbd2055e9 --- /dev/null +++ b/.github/workflows/autoclose_schedule.yml @@ -0,0 +1,37 @@ +--- +name: autoclose schedule +# Autoclose PRs labeled with "status: autoclose candidate" after 2 weeks. +# Based on scikit-learn's implementation at +# https://github.com/scikit-learn/scikit-learn/blob/main/.github/workflows/autoclose-schedule.yml + +permissions: + contents: read + pull-requests: write + +on: + schedule: + - cron: '0 2 * * *' # runs daily at 02:00 UTC + workflow_dispatch: + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + + autoclose: + name: autoclose labeled PRs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.13' + - name: Install PyGithub + run: pip install -Uq PyGithub + + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Close PRs labeled more than 14 days ago + run: | + python tools/autoclose_prs.py diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 41f5bca65f18..2bb7f9544902 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -18,35 +18,39 @@ on: - reopened - labeled -permissions: - contents: read +permissions: {} jobs: build_sdist: if: >- - github.event_name == 'push' || - github.event_name == 'pull_request' && ( - ( - github.event.action == 'labeled' && - github.event.label.name == 'CI: Run cibuildwheel' - ) || - contains(github.event.pull_request.labels.*.name, - 'CI: Run cibuildwheel') + github.repository == 'matplotlib/matplotlib' && ( + github.event_name == 'push' || + github.event_name == 'pull_request' && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cibuildwheel' + ) || + contains(github.event.pull_request.labels.*.name, + 'CI: Run cibuildwheel') + ) ) name: Build sdist - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest + permissions: + contents: read outputs: SDIST_NAME: ${{ steps.sdist.outputs.SDIST_NAME }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 name: Install Python with: - python-version: 3.9 + python-version: '3.11' # Something changed somewhere that prevents the downloaded-at-build-time # licenses from being included in built wheels, so pre-download them so @@ -69,7 +73,7 @@ jobs: run: twine check dist/* - name: Upload sdist result - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: cibw-sdist path: dist/*.tar.gz @@ -77,17 +81,21 @@ jobs: build_wheels: if: >- - github.event_name == 'push' || - github.event_name == 'pull_request' && ( - ( - github.event.action == 'labeled' && - github.event.label.name == 'CI: Run cibuildwheel' - ) || - contains(github.event.pull_request.labels.*.name, - 'CI: Run cibuildwheel') + github.repository == 'matplotlib/matplotlib' && ( + github.event_name == 'push' || + github.event_name == 'pull_request' && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cibuildwheel' + ) || + contains(github.event.pull_request.labels.*.name, + 'CI: Run cibuildwheel') + ) ) needs: build_sdist name: Build wheels on ${{ matrix.os }} for ${{ matrix.cibw_archs }} + permissions: + contents: read runs-on: ${{ matrix.os }} env: CIBW_BEFORE_BUILD: >- @@ -100,112 +108,94 @@ jobs: CIBW_AFTER_BUILD: >- twine check {wheel} && python {package}/ci/check_wheel_licenses.py {wheel} - CIBW_CONFIG_SETTINGS: setup-args="--vsenv" + # On Windows, we explicitly request MSVC compilers (as GitHub Action runners have + # MinGW on PATH that would be picked otherwise), switch to a static build for + # runtimes, but use dynamic linking for `VCRUNTIME140.dll`, `VCRUNTIME140_1.dll`, + # and the UCRT. This avoids requiring specific versions of `MSVCP140.dll`, while + # keeping shared state with the rest of the Python process/extensions. + CIBW_CONFIG_SETTINGS_WINDOWS: >- + setup-args="--vsenv" + setup-args="-Db_vscrt=mt" + setup-args="-Dcpp_link_args=['ucrt.lib','vcruntime.lib','/nodefaultlib:libucrt.lib','/nodefaultlib:libvcruntime.lib']" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_SKIP: "*-musllinux_aarch64" CIBW_TEST_COMMAND: >- python {package}/ci/check_version_number.py MACOSX_DEPLOYMENT_TARGET: "10.12" - MPL_DISABLE_FH4: "yes" strategy: matrix: include: - - os: ubuntu-20.04 + - os: ubuntu-latest cibw_archs: "x86_64" - - os: ubuntu-20.04 + - os: ubuntu-24.04-arm cibw_archs: "aarch64" - os: windows-latest - cibw_archs: "auto64" - - os: macos-12 + cibw_archs: "AMD64" + - os: windows-11-arm + cibw_archs: "ARM64" + - os: macos-15-intel cibw_archs: "x86_64" - os: macos-14 cibw_archs: "arm64" steps: - - name: Set up QEMU - if: matrix.cibw_archs == 'aarch64' - uses: docker/setup-qemu-action@v3 - with: - platforms: arm64 - - name: Download sdist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: cibw-sdist path: dist/ - - name: Build wheels for CPython 3.12 - uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # v2.18.1 + - name: Purge Strawberry Perl + if: startsWith(matrix.os, 'windows-') + run: Remove-Item -Recurse C:\Strawberry + + - name: Build wheels for CPython 3.14 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: - CIBW_BUILD: "cp312-*" + CIBW_BUILD: "cp314-* cp314t-*" + CIBW_ENABLE: "cpython-freethreading cpython-prerelease" CIBW_ARCHS: ${{ matrix.cibw_archs }} + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 - - name: Build wheels for CPython 3.11 - uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # v2.18.1 + - name: Build wheels for CPython 3.13 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: - CIBW_BUILD: "cp311-*" + CIBW_BUILD: "cp313-* cp313t-*" + CIBW_ENABLE: cpython-freethreading CIBW_ARCHS: ${{ matrix.cibw_archs }} - - name: Build wheels for CPython 3.10 - uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # v2.18.1 + - name: Build wheels for CPython 3.12 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: - CIBW_BUILD: "cp310-*" + CIBW_BUILD: "cp312-*" CIBW_ARCHS: ${{ matrix.cibw_archs }} - - name: Build wheels for CPython 3.9 - uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # v2.18.1 + - name: Build wheels for CPython 3.11 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: - CIBW_BUILD: "cp39-*" + CIBW_BUILD: "cp311-*" CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for PyPy - uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # v2.18.1 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: - CIBW_BUILD: "pp39-*" + CIBW_BUILD: "pp311-*" CIBW_ARCHS: ${{ matrix.cibw_archs }} - if: matrix.cibw_archs != 'aarch64' + CIBW_ENABLE: pypy + if: matrix.cibw_archs != 'aarch64' && matrix.os != 'windows-latest' && matrix.os != 'windows-11-arm' - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: cibw-wheels-${{ runner.os }}-${{ matrix.cibw_archs }} path: ./wheelhouse/*.whl if-no-files-found: error - - publish: - if: github.event_name == 'push' && github.ref_type == 'tag' - name: Upload release to PyPI - needs: [build_sdist, build_wheels] - runs-on: ubuntu-latest - environment: release - permissions: - id-token: write - attestations: write - contents: read - steps: - - name: Download packages - uses: actions/download-artifact@v4 - with: - pattern: cibw-* - path: dist - merge-multiple: true - - - name: Print out packages - run: ls dist - - - name: Generate artifact attestation for sdist and wheel - uses: actions/attest-build-provenance@49df96e17e918a15956db358890b08e61c704919 # v1.2.0 - with: - subject-path: dist/matplotlib-* - - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450 # v1.8.14 diff --git a/.github/workflows/circleci.yml b/.github/workflows/circleci.yml index 8f9e3190c5e2..49dc4ea3b3ec 100644 --- a/.github/workflows/circleci.yml +++ b/.github/workflows/circleci.yml @@ -1,25 +1,29 @@ --- name: "CircleCI artifact handling" on: [status] + +permissions: {} + jobs: circleci_artifacts_redirector_job: - if: "${{ github.event.context == 'ci/circleci: docs-python39' }}" + if: "${{ github.event.context == 'ci/circleci: docs-python3' }}" permissions: statuses: write runs-on: ubuntu-latest name: Run CircleCI artifacts redirector steps: - name: GitHub Action step - uses: larsoner/circleci-artifacts-redirector-action@master + uses: + scientific-python/circleci-artifacts-redirector-action@5d358ff96e96429a5c64a969bb4a574555439f4f # v1.3.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} api-token: ${{ secrets.CIRCLECI_TOKEN }} artifact-path: 0/doc/build/html/index.html - circleci-jobs: docs-python39 + circleci-jobs: docs-python3 job-title: View the built docs post_warnings_as_review: - if: "${{ github.event.context == 'ci/circleci: docs-python39' }}" + if: "${{ github.event.context == 'ci/circleci: docs-python3' }}" permissions: contents: read checks: write @@ -27,16 +31,20 @@ jobs: runs-on: ubuntu-latest name: Post warnings/errors as review steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Fetch result artifacts id: fetch-artifacts + env: + target_url: "${{ github.event.target_url }}" run: | - python .circleci/fetch_doc_logs.py "${{ github.event.target_url }}" + python .circleci/fetch_doc_logs.py "${target_url}" - name: Set up reviewdog if: "${{ steps.fetch-artifacts.outputs.count != 0 }}" - uses: reviewdog/action-setup@v1 + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 with: reviewdog_version: latest diff --git a/.github/workflows/clean_pr.yml b/.github/workflows/clean_pr.yml index 77e49f7c1d9e..75f6a451c7ee 100644 --- a/.github/workflows/clean_pr.yml +++ b/.github/workflows/clean_pr.yml @@ -2,17 +2,19 @@ name: PR cleanliness on: [pull_request] -permissions: - contents: read +permissions: {} jobs: pr_clean: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: '0' + persist-credentials: false - name: Check for added-and-deleted files run: | git fetch --quiet origin "$GITHUB_BASE_REF" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 203b0eee9ca4..83f47a0e27bf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -10,8 +10,11 @@ on: schedule: - cron: '45 19 * * 1' +permissions: {} + jobs: analyze: + if: github.repository == 'matplotlib/matplotlib' name: Analyze runs-on: ubuntu-latest permissions: @@ -26,10 +29,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} @@ -40,4 +45,4 @@ jobs: pip install --user -v . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 diff --git a/.github/workflows/conflictcheck.yml b/.github/workflows/conflictcheck.yml index 3110839e5150..2058da8ca9fb 100644 --- a/.github/workflows/conflictcheck.yml +++ b/.github/workflows/conflictcheck.yml @@ -9,15 +9,17 @@ on: pull_request_target: types: [synchronize] -permissions: - pull-requests: write +permissions: {} jobs: main: + if: github.repository == 'matplotlib/matplotlib' runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - name: Check if PRs have merge conflicts - uses: eps1lon/actions-label-merge-conflict@1b1b1fcde06a9b3d089f3464c96417961dde1168 # v3.0.2 + uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 with: dirtyLabel: "status: needs rebase" repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 58c132315b6f..bd9ec07de39f 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -29,10 +29,8 @@ on: # 5:47 UTC on Saturdays - cron: "47 5 * * 6" workflow_dispatch: - workflow: "*" -permissions: - contents: read +permissions: {} env: NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. @@ -48,11 +46,15 @@ jobs: test-cygwin: runs-on: windows-latest + permissions: + contents: read name: Python 3.${{ matrix.python-minor-version }} on Cygwin + # Enable these when Cygwin has Python 3.12. if: >- github.event_name == 'workflow_dispatch' || - github.event_name == 'schedule' || + (false && github.event_name == 'schedule') || ( + false && github.repository == 'matplotlib/matplotlib' && !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && @@ -72,17 +74,18 @@ jobs: ) strategy: matrix: - python-minor-version: [9] + python-minor-version: [12] steps: - name: Fix line endings run: git config --global core.autocrlf input - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - - uses: cygwin/cygwin-install-action@v4 + - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6 with: packages: >- ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel @@ -138,21 +141,21 @@ jobs: # FreeType build fails with bash, succeeds with dash - name: Cache pip - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: C:\cygwin\home\runneradmin\.cache\pip - key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- - name: Cache ccache - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: C:\cygwin\home\runneradmin\.ccache key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- - name: Cache Matplotlib - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | C:\cygwin\home\runneradmin\.cache\matplotlib @@ -172,15 +175,9 @@ jobs: - name: Install Python dependencies shell: bash.exe -eo pipefail -o igncr "{0}" run: | - python -m pip install --upgrade pip setuptools wheel - python -m pip install kiwisolver 'numpy>=1.22,<1.26' pillow importlib_resources - grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt - python -m pip install meson-python pybind11 + python -m pip install --group build 'numpy>=1.25,<1.26' export PATH="/usr/local/bin:$PATH" - python -m pip install --no-build-isolation 'contourpy>=1.0.1' - python -m pip install --upgrade cycler fonttools \ - packaging pyparsing python-dateutil setuptools-scm \ - -r requirements_test.txt sphinx ipython + python -m pip install --upgrade --group test sphinx ipython python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && echo 'PyGObject is available' || diff --git a/.github/workflows/do_not_merge.yml b/.github/workflows/do_not_merge.yml index dde5bfb5ec81..0c263623942b 100644 --- a/.github/workflows/do_not_merge.yml +++ b/.github/workflows/do_not_merge.yml @@ -15,7 +15,8 @@ jobs: env: has_tag: >- ${{contains(github.event.pull_request.labels.*.name, 'status: needs comment/discussion') || - contains(github.event.pull_request.labels.*.name, 'status: waiting for other PR')}} + contains(github.event.pull_request.labels.*.name, 'status: waiting for other PR') || + contains(github.event.pull_request.labels.*.name, 'DO NOT MERGE') }} steps: - name: Check for label if: ${{'true' == env.has_tag}} @@ -23,7 +24,7 @@ jobs: echo "This PR cannot be merged because it has one of the following labels: " echo "* status: needs comment/discussion" echo "* status: waiting for other PR" - echo "${{env.has_tag}}" + echo "* DO NOT MERGE" exit 1 - name: Allow merging if: ${{'false' == env.has_tag}} diff --git a/.github/workflows/good-first-issue.yml b/.github/workflows/good-first-issue.yml index 8905511fc01d..6543f05a0837 100644 --- a/.github/workflows/good-first-issue.yml +++ b/.github/workflows/good-first-issue.yml @@ -4,6 +4,9 @@ on: issues: types: - labeled + +permissions: {} + jobs: add-comment: if: github.event.label.name == 'Good first issue' @@ -12,19 +15,22 @@ jobs: issues: write steps: - name: Add comment - uses: peter-evans/create-or-update-comment@v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 with: issue-number: ${{ github.event.issue.number }} body: | ### Good first issue - notes for new contributors - This issue is suited to new contributors because it does not require understanding of the - Matplotlib internals. To get started, please see our [contributing - guide](https://matplotlib.org/stable/devel/index). + This issue is suited to new contributors because it does not require + understanding of the Matplotlib internals. This is a non-urgent task + intended for human contributors to learn how to contribute; therefore please + do not try to automate a solution using AI. To get started, please see our + [contributing guide](https://matplotlib.org/stable/devel/index). - **We do not assign issues**. Check the *Development* section in the sidebar for linked pull - requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please - collaborate on the work by reviewing it rather than duplicating it in a competing PR. + **We do not assign issues**. Check the *Development* section in the sidebar + for linked pull requests (PRs). If there are none, feel free to start + working on it. If there is an open PR, please collaborate on the work by + reviewing it rather than duplicating it in a competing PR. If something is unclear, please reach out on any of our [communication channels](https://matplotlib.org/stable/devel/contributing.html#get-connected). diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index dc7a0716bfe8..2914c64a8461 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -3,6 +3,8 @@ name: "Pull Request Labeler" on: - pull_request_target +permissions: {} + jobs: labeler: permissions: @@ -10,6 +12,6 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: sync-labels: true diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 000000000000..e73165c504d8 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,104 @@ +--- +name: Linting +on: [pull_request] + +permissions: {} + +jobs: + pre-commit: + name: precommit + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.x" + - uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1 + with: + extra_args: --hook-stage manual --all-files + + ruff: + name: ruff + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Set up Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + + - name: Install ruff + run: pip3 install ruff + + - name: Set up reviewdog + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + + - name: Run ruff + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + ruff check --output-format rdjson | \ + reviewdog -f=rdjson \ + -tee -reporter=github-check -filter-mode nofilter + mypy: + name: mypy + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Set up Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + + - name: Install mypy + run: pip3 install --group build --group typing + + - name: Set up reviewdog + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + + - name: Run mypy + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + mypy --config pyproject.toml | \ + reviewdog -f=mypy -name=mypy \ + -tee -reporter=github-check -filter-mode nofilter + + + eslint: + name: eslint + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: eslint + uses: reviewdog/action-eslint@556a3fdaf8b4201d4d74d406013386aa4f7dab96 # v1.34.0 + with: + filter_mode: nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-check + workdir: 'lib/matplotlib/backends/web_backend/' diff --git a/.github/workflows/mypy-stubtest.yml b/.github/workflows/mypy-stubtest.yml index 969aacccad74..81fcd48462e8 100644 --- a/.github/workflows/mypy-stubtest.yml +++ b/.github/workflows/mypy-stubtest.yml @@ -2,24 +2,27 @@ name: Mypy Stubtest on: [pull_request] -permissions: - contents: read - checks: write +permissions: {} jobs: mypy-stubtest: name: mypy-stubtest runs-on: ubuntu-latest + permissions: + contents: read + checks: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python 3 - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: 3.9 + python-version: '3.11' - name: Set up reviewdog - uses: reviewdog/action-setup@v1 + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 - name: Install tox run: python -m pip install tox @@ -30,7 +33,7 @@ jobs: run: | set -o pipefail tox -e stubtest | \ - sed -e "s!.tox/stubtest/lib/python3.9/site-packages!lib!g" | \ + sed -e "s!.tox/stubtest/lib/python3.11/site-packages!lib!g" | \ reviewdog \ -efm '%Eerror: %m' \ -efm '%CStub: in file %f:%l' \ diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml index 54e81f06b166..661234c3e16f 100644 --- a/.github/workflows/nightlies.yml +++ b/.github/workflows/nightlies.yml @@ -8,8 +8,7 @@ on: # Run on demand with workflow dispatch workflow_dispatch: -permissions: - actions: read +permissions: {} jobs: upload_nightly_wheels: @@ -21,6 +20,8 @@ jobs: # to work in subsequent jobs. # https://github.com/mamba-org/setup-micromamba#about-login-shells shell: bash -e -l {0} + permissions: + actions: read if: github.repository_owner == 'matplotlib' steps: @@ -31,35 +32,35 @@ jobs: run: | PROJECT_REPO="matplotlib/matplotlib" BRANCH="main" - WORKFLOW_NAME="cibuildwheel.yml" ARTIFACT_PATTERN="cibw-wheels-*" - gh run --repo "${PROJECT_REPO}" \ - list --branch "${BRANCH}" \ - --workflow "${WORKFLOW_NAME}" \ - --json event,status,conclusion,databaseId > runs.json - RUN_ID=$( - jq --compact-output \ - '[ - .[] | - # Filter on "push" events to main (merged PRs) ... - select(.event == "push") | - # that have completed successfully ... - select(.status == "completed" and .conclusion == "success") - ] | - # and get ID of latest build of wheels. - sort_by(.databaseId) | reverse | .[0].databaseId' runs.json - ) - gh run --repo "${PROJECT_REPO}" view "${RUN_ID}" - gh run --repo "${PROJECT_REPO}" \ - download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}" + for WORKFLOW_NAME in cibuildwheel.yml wasm.yml; do + gh run --repo "${PROJECT_REPO}" \ + list --branch "${BRANCH}" \ + --workflow "${WORKFLOW_NAME}" \ + --json event,status,conclusion,databaseId > runs.json + RUN_ID=$( + jq --compact-output \ + '[ + .[] | + # Filter on "push" events to main (merged PRs) ... + select(.event == "push") | + # that have completed successfully ... + select(.status == "completed" and .conclusion == "success") + ] | + # and get ID of latest build of wheels. + sort_by(.databaseId) | reverse | .[0].databaseId' runs.json + ) + gh run --repo "${PROJECT_REPO}" view "${RUN_ID}" + gh run --repo "${PROJECT_REPO}" download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}" + done mkdir dist mv ${ARTIFACT_PATTERN}/*.whl dist/ ls -l dist/ - name: Upload wheels to Anaconda Cloud as nightlies - uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # 0.5.0 + uses: scientific-python/upload-nightly-action@5748273c71e2d8d3a61f3a11a16421c8954f9ecf # 0.6.3 with: artifacts_path: dist anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} diff --git a/.github/workflows/pr_welcome.yml b/.github/workflows/pr_welcome.yml index 533f676a0fab..48691e61d87b 100644 --- a/.github/workflows/pr_welcome.yml +++ b/.github/workflows/pr_welcome.yml @@ -1,39 +1,51 @@ --- name: PR Greetings -on: [pull_request_target] +on: + pull_request_target: + types: opened + issues: + types: opened -permissions: - pull-requests: write +permissions: {} jobs: greeting: runs-on: ubuntu-latest - + permissions: + issues: write + pull-requests: write steps: - - uses: actions/first-interaction@v1 + - uses: plbstl/first-contribution@7c31f41b0e7a70adfcae06cf964679f61af6780b # v4.3.0 with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - pr-message: >+ + labels: first-contribution + pr-opened-msg: >+ Thank you for opening your first PR into Matplotlib! If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall - through the cracks. + through the cracks. We also ask that you please finish addressing + any review comments on this PR and wait for it to be merged (or + closed) before opening a new one, as it can be a valuable learning + experience to go through the review process. - You can also join us [on - gitter](https://gitter.im/matplotlib/matplotlib) for real-time - discussion. + You can also join us [on discourse + chat](https://discourse.matplotlib.org/chat/c/matplotlib/2) for + real-time discussion. For details on testing, writing docs, and our review process, please see [the developer - guide](https://matplotlib.org/devdocs/devel/index.html) + guide](https://matplotlib.org/devdocs/devel/index.html). + + **Please let us know if (and how) you use AI, it will help us give you + better feedback on your PR.** We strive to be a welcoming and open project. Please follow our [Code of Conduct](https://github.com/matplotlib/matplotlib/blob/main/CODE_OF_CONDUCT.md). + issue-opened-msg: "" diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml deleted file mode 100644 index fbd724571d80..000000000000 --- a/.github/workflows/reviewdog.yml +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: Linting -on: [pull_request] - -permissions: - contents: read - checks: write - pull-requests: write - -jobs: - flake8: - name: flake8 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3 - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: Install flake8 - run: pip3 install -r requirements/testing/flake8.txt - - - name: Set up reviewdog - uses: reviewdog/action-setup@v1 - - - name: Run flake8 - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -o pipefail - flake8 --docstring-convention=all | \ - reviewdog -f=pep8 -name=flake8 \ - -tee -reporter=github-check -filter-mode nofilter - mypy: - name: mypy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3 - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: Install mypy - run: pip3 install -r requirements/testing/mypy.txt -r requirements/testing/all.txt - - - name: Set up reviewdog - uses: reviewdog/action-setup@v1 - - - name: Run mypy - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -o pipefail - mypy --config pyproject.toml | \ - reviewdog -f=mypy -name=mypy \ - -tee -reporter=github-check -filter-mode nofilter - - - eslint: - name: eslint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: eslint - uses: reviewdog/action-eslint@v1 - with: - filter_mode: nofilter - github_token: ${{ secrets.GITHUB_TOKEN }} - reporter: github-check - workdir: 'lib/matplotlib/backends/web_backend/' diff --git a/.github/workflows/stale-tidy.yml b/.github/workflows/stale-tidy.yml index 92a81ee856e4..feb1fe701d70 100644 --- a/.github/workflows/stale-tidy.yml +++ b/.github/workflows/stale-tidy.yml @@ -4,12 +4,16 @@ on: schedule: - cron: '30 1 * * 2,4,6' +permissions: {} + jobs: stale: if: github.repository == 'matplotlib/matplotlib' runs-on: ubuntu-latest + permissions: + issues: write steps: - - uses: actions/stale@v9 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} operations-per-run: 300 @@ -20,5 +24,5 @@ jobs: close-issue-label: "status: closed as inactive" days-before-issue-close: 30 ascending: true - exempt-issue-labels: "keep" + exempt-issue-labels: "keep,status: confirmed bug" exempt-pr-labels: "keep,status: orphaned PR" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c606d4288bd2..63f1a1ce3b05 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -2,14 +2,19 @@ name: 'Label inactive PRs' on: schedule: - - cron: '30 1 * * 1,3,5' + - cron: '30 1 * * 1' + +permissions: {} jobs: stale: if: github.repository == 'matplotlib/matplotlib' runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - - uses: actions/stale@v9 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} operations-per-run: 20 @@ -36,3 +41,4 @@ jobs: ascending: true exempt-issue-labels: "keep" exempt-pr-labels: "keep,status: orphaned PR" + sort-by: updated diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8875a38cc1bb..80fb7ed124fd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,8 @@ on: # 5:47 UTC on Saturdays - cron: "47 5 * * 6" workflow_dispatch: - workflow: "*" + +permissions: {} env: NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. @@ -49,63 +50,63 @@ jobs: matrix: include: - name-suffix: "(Minimum Versions)" - os: ubuntu-20.04 - python-version: 3.9 - extra-requirements: '-c requirements/testing/minver.txt' - pyqt5-ver: '==5.12.2 sip==5.0.0' # oldest versions with a Py3.9 wheel. - pyqt6-ver: '==6.1.0 PyQt6-Qt6==6.1.0' - pyside2-ver: '==5.15.1' # oldest version with working Py3.9 wheel. - pyside6-ver: '==6.0.0' + os: ubuntu-22.04 + python-version: '3.11' + extra-requirements: '-c ci/minver-requirements.txt' delete-font-cache: true - - os: ubuntu-20.04 - python-version: 3.9 - # One CI run tests ipython/matplotlib-inline before backend mapping moved to mpl - extra-requirements: '-r requirements/testing/extra.txt "ipython==7.19" "matplotlib-inline<0.1.7"' - CFLAGS: "-fno-lto" # Ensure that disabling LTO works. - # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 - # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.5.1,!=6.6.0' - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' - - os: ubuntu-20.04 - python-version: '3.10' - extra-requirements: '-r requirements/testing/extra.txt' - # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 - # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.5.1,!=6.6.0' - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' - os: ubuntu-22.04 python-version: '3.11' - # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.6.0' - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - - os: ubuntu-22.04 + CFLAGS: "-fno-lto" # Ensure that disabling LTO works. + extra-requirements: '--group test-extra' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - name-suffix: "(Extra TeX packages)" + os: ubuntu-22.04 + python-version: '3.13' + extra-packages: 'texlive-fonts-extra texlive-lang-cyrillic' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - name-suffix: "Free-threaded" + os: ubuntu-22.04 + python-version: '3.13t' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - os: ubuntu-24.04 python-version: '3.12' - # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.6.0' - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' - - os: macos-12 # This runnre is on Intel chips. - python-version: 3.9 - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' + - os: ubuntu-24.04 + python-version: '3.14' + - os: ubuntu-24.04-arm + python-version: '3.12' + - os: macos-14 # This runner is on M1 (arm64) chips. + python-version: '3.11' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.12' - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 - pyside6-ver: '!=6.5.1' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + - os: macos-15 # This runner is on M1 (arm64) chips. + python-version: '3.13' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + - os: macos-15 # This runner is on M1 (arm64) chips. + python-version: '3.14' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true - name: Install OS dependencies run: | @@ -117,12 +118,12 @@ jobs: ccache \ cm-super \ dvipng \ - ffmpeg \ fonts-freefont-otf \ fonts-noto-cjk \ fonts-wqy-zenhei \ gdb \ gir1.2-gtk-3.0 \ + gir1.2-gtk-4.0 \ graphviz \ inkscape \ language-pack-de \ @@ -131,7 +132,7 @@ jobs: libcairo2-dev \ libffi-dev \ libgeos-dev \ - libgirepository1.0-dev \ + libnotify4 \ libsdl2-2.0-0 \ libxkbcommon-x11-0 \ libxcb-cursor0 \ @@ -151,39 +152,59 @@ jobs: texlive-latex-recommended \ texlive-luatex \ texlive-pictures \ - texlive-xetex - if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then - sudo apt-get install -yy --no-install-recommends libopengl0 - else # ubuntu-22.04 + texlive-xetex \ + ${{ matrix.extra-packages }} + if [[ "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then + sudo apt-get install -yy --no-install-recommends ffmpeg poppler-utils + fi + if [[ "${{ matrix.os }}" = ubuntu-22.04 ]]; then sudo apt-get install -yy --no-install-recommends \ - gir1.2-gtk-4.0 libnotify4 + libgirepository1.0-dev + else # ubuntu-24.04 + sudo apt-get install -yy --no-install-recommends \ + libgirepository-2.0-dev fi ;; macOS) brew update - brew install ccache ghostscript gobject-introspection gtk4 ninja - brew install --cask font-noto-sans-cjk inkscape + # Periodically, Homebrew updates Python and fails to overwrite the + # existing not-managed-by-Homebrew copy without explicitly being told + # to do so. GitHub/Azure continues to avoid fixing their runner images: + # https://github.com/actions/runner-images/issues/9966 + # so force an overwrite even if there are no Python updates. + # We don't even care about Homebrew's Python because we use the one + # from actions/setup-python. + for python_package in $(brew list | grep python@); do + brew unlink ${python_package} + brew link --overwrite ${python_package} + done + # Workaround for https://github.com/actions/runner-images/issues/10984 + brew uninstall --ignore-dependencies --force pkg-config@0.29.2 + brew install ccache ffmpeg ghostscript gobject-introspection gtk4 imagemagick ninja + brew install --cask font-noto-sans-cjk font-noto-sans-cjk-sc inkscape ;; esac - name: Cache pip - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip - key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + key: | + ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ + hashFiles('pyproject.toml', 'ci/minver-requirements.txt') }} restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache pip - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 if: startsWith(runner.os, 'macOS') with: path: ~/Library/Caches/pip - key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache ccache - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | ~/.ccache @@ -191,16 +212,16 @@ jobs: restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-ccache- - name: Cache Matplotlib - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | ~/.cache/matplotlib !~/.cache/matplotlib/tex.cache !~/.cache/matplotlib/test_cache - key: 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} + key: 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} restore-keys: | - 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- - 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl- + 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- + 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl- - name: Install Python dependencies run: | @@ -216,24 +237,21 @@ jobs: # Install dependencies from PyPI. # Preinstall build requirements to enable no-build-isolation builds. - python -m pip install --upgrade $PRE \ - 'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \ - numpy packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ - 'meson-python>=0.13.1' 'pybind11>=2.6' \ - -r requirements/testing/all.txt \ - ${{ matrix.extra-requirements }} + python -m pip install --upgrade $PRE --prefer-binary \ + --group build --group test ${{ matrix.extra-requirements }} # Install optional dependencies from PyPI. # Sphinx is needed to run sphinxext tests python -m pip install --upgrade sphinx!=6.1.2 + if [[ "${{ matrix.python-version }}" != '3.13t' ]]; then # GUI toolkits are pip-installable only for some versions of Python # so don't fail if we can't install them. Make it easier to check # whether the install was successful by trying to import the toolkit # (sometimes, the install appears to be successful but shared # libraries cannot be loaded at runtime, so an actual import is a # better check). - python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && + python -m pip install --upgrade pycairo 'cairocffi>=0.8' 'PyGObject${{ matrix.pygobject-ver }}' && ( python -c 'import gi; gi.require_version("Gtk", "4.0"); from gi.repository import Gtk' && echo 'PyGObject 4 is available' || echo 'PyGObject 4 is not available' @@ -242,36 +260,41 @@ jobs: echo 'PyGObject 3 is available' || echo 'PyGObject 3 is not available' ) - python -mpip install --upgrade pyqt5${{ matrix.pyqt5-ver }} && - python -c 'import PyQt5.QtCore' && - echo 'PyQt5 is available' || - echo 'PyQt5 is not available' - # Even though PySide2 wheels can be installed on Python 3.12, they are broken and since PySide2 is + # PyQt5 does not have any wheels for ARM on Linux. + if [[ "${{ matrix.os }}" != 'ubuntu-24.04-arm' ]]; then + python -mpip install --upgrade --only-binary :all: pyqt5 && + python -c 'import PyQt5.QtCore' && + echo 'PyQt5 is available' || + echo 'PyQt5 is not available' + fi + # Even though PySide2 wheels can be installed on Python 3.12+, they are broken and since PySide2 is # deprecated, they are unlikely to be fixed. For the same deprecation reason, there are no wheels # on M1 macOS, so don't bother there either. - if [[ "${{ matrix.os }}" != 'macos-14' - && "${{ matrix.python-version }}" != '3.12' ]]; then - python -mpip install --upgrade pyside2${{ matrix.pyside2-ver }} && + if [[ "${{ matrix.os }}" != 'macos-14' && "${{ matrix.python-version }}" == '3.11' + ]]; then + python -mpip install --upgrade pyside2 && python -c 'import PySide2.QtCore' && echo 'PySide2 is available' || echo 'PySide2 is not available' fi - python -mpip install --upgrade pyqt6${{ matrix.pyqt6-ver }} && + python -mpip install --upgrade --only-binary :all: pyqt6 && python -c 'import PyQt6.QtCore' && echo 'PyQt6 is available' || echo 'PyQt6 is not available' - python -mpip install --upgrade pyside6${{ matrix.pyside6-ver }} && + python -mpip install --upgrade --only-binary :all: pyside6 && python -c 'import PySide6.QtCore' && echo 'PySide6 is available' || echo 'PySide6 is not available' - python -mpip install --upgrade \ + python -mpip install --upgrade --only-binary :all: \ -f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \ wxPython && python -c 'import wx' && echo 'wxPython is available' || echo 'wxPython is not available' + fi # Skip backends on Python 3.13t. + - name: Install the nightly dependencies # Only install the nightly dependencies during the scheduled event if: github.event_name == 'schedule' && matrix.name-suffix != '(Minimum Versions)' @@ -310,6 +333,9 @@ jobs: - name: Run pytest run: | + if [[ "${{ matrix.python-version }}" == '3.13t' ]]; then + export PYTHON_GIL=0 + fi pytest -rfEsXR -n auto \ --maxfail=50 --timeout=300 --durations=25 \ --cov-report=xml --cov=lib --log-level=DEBUG --color=yes @@ -317,32 +343,29 @@ jobs: - name: Cleanup non-failed image files if: failure() run: | - function remove_files() { - local extension=$1 - find ./result_images -type f -name "*-expected*.$extension" | while read file; do - if [[ $file == *"-expected_pdf"* ]]; then - base=${file%-expected_pdf.$extension}_pdf - elif [[ $file == *"-expected_eps"* ]]; then - base=${file%-expected_eps.$extension}_eps - elif [[ $file == *"-expected_svg"* ]]; then - base=${file%-expected_svg.$extension}_svg - else - base=${file%-expected.$extension} - fi - if [[ ! -e "${base}-failed-diff.$extension" ]]; then - if [[ -e "$file" ]]; then - rm "$file" - echo "Removed $file" - fi - if [[ -e "${base}.$extension" ]]; then - rm "${base}.$extension" - echo " Removed ${base}.$extension" - fi - fi + find ./result_images -name "*-expected*.png" | while read file; do + if [[ $file == *-expected_???.png ]]; then + extension=${file: -7:3} + base=${file%*-expected_$extension.png}_$extension + else + extension="png" + base=${file%-expected.png} + fi + if [[ ! -e ${base}-failed-diff.png ]]; then + indent="" + list=($file $base.png) + if [[ $extension != "png" ]]; then + list+=(${base%_$extension}-expected.$extension ${base%_$extension}.$extension) + fi + for to_remove in "${list[@]}"; do + if [[ -e $to_remove ]]; then + rm $to_remove + echo "${indent}Removed $to_remove" + fi + indent+=" " done - } - - remove_files "png"; remove_files "svg"; remove_files "pdf"; remove_files "eps"; + fi + done if [ "$(find ./result_images -mindepth 1 -type d)" ]; then find ./result_images/* -type d -empty -delete @@ -352,11 +375,18 @@ jobs: if: ${{ !cancelled() && github.event_name != 'schedule' }} run: | if [[ "${{ runner.os }}" != 'macOS' ]]; then - lcov --rc lcov_branch_coverage=1 --capture --directory . \ + LCOV_IGNORE_ERRORS=',' # do not ignore any lcov errors by default + if [[ "${{ matrix.os }}" = ubuntu-24.04 || "${{ matrix.os }}" = ubuntu-24.04-arm ]]; then + # filter mismatch errors detected by lcov 2.x + LCOV_IGNORE_ERRORS='mismatch' + fi + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --capture --directory . --exclude $PWD/subprojects --exclude $PWD/build \ --output-file coverage.info - lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ - --extract coverage.info $PWD/src/'*' $PWD/lib/'*' - lcov --rc lcov_branch_coverage=1 --list coverage.info + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --output-file coverage.info --extract coverage.info $PWD/src/'*' + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --list coverage.info find . -name '*.gc*' -delete else xcrun llvm-profdata merge -sparse default.*.profraw \ @@ -366,12 +396,12 @@ jobs: fi - name: Upload code coverage if: ${{ !cancelled() && github.event_name != 'schedule' }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}" token: ${{ secrets.CODECOV_TOKEN }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: failure() with: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" @@ -388,7 +418,7 @@ jobs: steps: - name: Create issue on failure - uses: imjohnbo/issue-bot@v3 + uses: imjohnbo/issue-bot@3188c6ce06249206709d3b1f274d0d4c5a521601 # v3.4.5 with: title: "[TST] Upcoming dependency test failures" body: | diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 000000000000..e1668d99af60 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,63 @@ +--- +name: Build wasm wheels + +on: + # Save CI by only running this on release branches or tags. + push: + branches: + - main + - v[0-9]+.[0-9]+.x + tags: + - v* + # Also allow running this action on PRs if requested by applying the + # "Run cibuildwheel" label. + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + +permissions: + contents: read + +jobs: + build_wasm: + if: >- + ( + github.event_name == 'push' || + github.event_name == 'pull_request' && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cibuildwheel' + ) || + contains(github.event.pull_request.labels.*.name, + 'CI: Run cibuildwheel') + ) + ) + name: Build wasm + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + name: Install Python + with: + python-version: '3.13' + + - name: Build wheels for wasm + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 + env: + CIBW_BUILD: "cp312-*" + CIBW_PLATFORM: "pyodide" + CIBW_TEST_COMMAND: "true" + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cibw-wheels-wasm + path: ./wheelhouse/*.whl + if-no-files-found: error diff --git a/.gitignore b/.gitignore index b6f9e1ee74f4..0460152a792f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ pip-wheel-metadata/* .tox # build subproject files subprojects/*/ +subprojects/.* !subprojects/packagefiles/ # OS generated files # @@ -103,6 +104,20 @@ __conda_version__.txt lib/png.lib lib/z.lib +# uv files # +############ +uv.lock + +# Environments # +################ +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + # Jupyter files # ################# diff --git a/.mailmap b/.mailmap index 44005da6e2d8..bbadcb8ff879 100644 --- a/.mailmap +++ b/.mailmap @@ -11,6 +11,8 @@ Alon Hershenhorn Alvaro Sanchez +Andreas C Mueller Andreas Mueller + Andrew Dawson anykraus @@ -32,6 +34,8 @@ Carsten Schelp Casper van der Wel +CharlesHe16 + Chris Holdgraf Cho Yin Yong @@ -57,6 +61,8 @@ David Kua Devashish Deshpande +Diego Leal Petrola + Dietmar Schwertberger Dora Fraeman Caswell @@ -84,16 +90,26 @@ Hajoon Choi hannah +Hannes Breytenbach + Hans Moritz Günther Harshal Prakash Patankar Harshit Patni +Harutaka Kawamura + +Hood Chatham Hood + +Ian Hunt-Isaak + ImportanceOfBeingErnest J. Goutin JGoutin +Jakub Klus <48711526+deep-jkl@users.noreply.github.com> + Jack Kelly Jack Kelly @@ -105,6 +121,8 @@ Jake Vanderplas James R. Evans +Jan-Hendrik Müller <44469195+kolibril13@users.noreply.github.com> + Jeff Lutgen Jeffrey Bingham @@ -112,12 +130,18 @@ Jeffrey Bingham Jens Hedegaard Nielsen Jens Hedegaard Nielsen +Jerome F. Villegas + Joel Frederico <458871+joelfrederico@users.noreply.github.com> +Johan von Forstner + John Hunter Jorrit Wronski +Jose Manuel Martí + Joseph Fox-Rabinovitz Mad Physicist Joseph Fox-Rabinovitz Joseph Fox-Rabinovitz @@ -149,11 +173,15 @@ Leon Yin Lion Krischer +luz paz + Manan Kevadiya Manan Kevadiya <43081866+manan2501@users.noreply.github.com> Manuel Nuno Melo +Marat Kopytjuk + Marco Gorelli Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> @@ -215,6 +243,8 @@ Paul Ivanov Per Parker +Péter Leéh + Peter Würtz Peter Würtz @@ -222,12 +252,18 @@ Phil Elson Phil Elson Phil Elson +Philipp Nagel + productivememberofsociety666 none Rishikesh +Ruth Nainggolan Ruth G. N <32371319+ruthgn@users.noreply.github.com> + RyanPan +Richard Sheridan + Samesh Lakhotia Samesh Lakhotia <43701530+sameshl@users.noreply.github.com> ' @@ -236,6 +272,8 @@ Scott Lasley Sebastian Raschka Sebastian Raschka +ShawnChen1996 + Sidharth Bansal Sidharth Bansal <20972099+SidharthBansal@users.noreply.github.com> @@ -257,6 +295,7 @@ Taras Kuzyo Terence Honles +Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell <“tcaswell@gmail.com”> diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14817e95929f..9ddfcf6de835 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,30 +5,31 @@ ci: exclude: | (?x)^( extern| + subprojects/packagefiles| LICENSE| lib/matplotlib/mpl-data| doc/devel/gitwash| - doc/users/prev| + doc/release/prev| doc/api/prev| - lib/matplotlib/tests/tinypages + lib/matplotlib/tests/data/tinypages ) repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-added-large-files - id: check-docstring-first exclude: lib/matplotlib/typing.py # docstring used for attribute flagged by check - id: end-of-file-fixer - exclude_types: [svg] + exclude_types: [diff, svg] - id: mixed-line-ending - id: name-tests-test args: ["--pytest-test-first"] - id: no-commit-to-branch # Default is master and main. - id: trailing-whitespace - exclude_types: [svg] + exclude_types: [diff, svg] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 + rev: v1.15.0 hooks: - id: mypy additional_dependencies: @@ -41,17 +42,16 @@ repos: args: ["--config-file=pyproject.toml", "lib/matplotlib"] files: lib/matplotlib # Only run when files in lib/matplotlib are changed. pass_filenames: false - - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 + + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.11.5 hooks: - - id: flake8 - additional_dependencies: - - pydocstyle>5.1.0 - - flake8-docstrings>1.4.0 - - flake8-force - args: ["--docstring-convention=all"] + # Run the linter. + - id: ruff + args: [--fix, --show-fixes] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.4.1 hooks: - id: codespell files: ^.*\.(py|c|cpp|h|m|md|rst|yml)$ @@ -61,25 +61,25 @@ repos: - "--skip" - "doc/project/credits.rst" - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 6.0.1 hooks: - id: isort name: isort (python) files: ^galleries/tutorials/|^galleries/examples/|^galleries/plot_types/ - repo: https://github.com/rstcheck/rstcheck - rev: v6.2.0 + rev: v6.2.4 hooks: - id: rstcheck additional_dependencies: - sphinx>=1.8.1 - tomli - repo: https://github.com/adrienverge/yamllint - rev: v1.35.1 + rev: v1.37.0 hooks: - id: yamllint args: ["--strict", "--config-file=.yamllint.yml"] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.4 + rev: 0.33.0 hooks: # TODO: Re-enable this when https://github.com/microsoft/azure-pipelines-vscode/issues/567 is fixed. # - id: check-azure-pipelines diff --git a/.yamllint.yml b/.yamllint.yml index 3b30533ececa..2be81b28c7fb 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -3,7 +3,7 @@ extends: default rules: line-length: - max: 111 + max: 120 allow-non-breakable-words: true truthy: check-keys: false diff --git a/LICENSE/LICENSE_FREETYPE b/LICENSE/LICENSE_FREETYPE new file mode 100644 index 000000000000..8b9ce9e2e6e3 --- /dev/null +++ b/LICENSE/LICENSE_FREETYPE @@ -0,0 +1,46 @@ +FREETYPE LICENSES +----------------- + +The FreeType 2 font engine is copyrighted work and cannot be used +legally without a software license. In order to make this project +usable to a vast majority of developers, we distribute it under two +mutually exclusive open-source licenses. + +This means that *you* must choose *one* of the two licenses described +below, then obey all its terms and conditions when using FreeType 2 in +any of your projects or products. + + - The FreeType License, found in the file `docs/FTL.TXT`, which is + similar to the original BSD license *with* an advertising clause + that forces you to explicitly cite the FreeType project in your + product's documentation. All details are in the license file. + This license is suited to products which don't use the GNU General + Public License. + + Note that this license is compatible to the GNU General Public + License version 3, but not version 2. + + - The GNU General Public License version 2, found in + `docs/GPLv2.TXT` (any later version can be used also), for + programs which already use the GPL. Note that the FTL is + incompatible with GPLv2 due to its advertisement clause. + +The contributed BDF and PCF drivers come with a license similar to +that of the X Window System. It is compatible to the above two +licenses (see files `src/bdf/README` and `src/pcf/README`). The same +holds for the source code files `src/base/fthash.c` and +`include/freetype/internal/fthash.h`; they were part of the BDF driver +in earlier FreeType versions. + +The gzip module uses the zlib license (see `src/gzip/zlib.h`) which +too is compatible to the above two licenses. + +The files `src/autofit/ft-hb.c` and `src/autofit/ft-hb.h` contain code +taken almost verbatim from the HarfBuzz file `hb-ft.cc`, which uses +the 'Old MIT' license, compatible to the above two licenses. + +The MD5 checksum support (only used for debugging in development +builds) is in the public domain. + + +--- end of LICENSE.TXT --- diff --git a/LICENSE/LICENSE_HARFBUZZ b/LICENSE/LICENSE_HARFBUZZ new file mode 100644 index 000000000000..1dd917e9f2e7 --- /dev/null +++ b/LICENSE/LICENSE_HARFBUZZ @@ -0,0 +1,42 @@ +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010-2022 Google, Inc. +Copyright © 2015-2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012,2015 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2011 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod +Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. +Copyright © 1998-2005 David Turner and Werner Lemberg +Copyright © 2016 Igalia S.L. +Copyright © 2022 Matthias Clasen +Copyright © 2018,2021 Khaled Hosny +Copyright © 2018,2019,2020 Adobe, Inc +Copyright © 2013-2015 Alexei Podtelezhnikov + +For full copyright notices consult the individual files in the package. + + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/LICENSE/LICENSE_LAST_RESORT_FONT b/LICENSE/LICENSE_LAST_RESORT_FONT new file mode 100644 index 000000000000..5fe3297bc1e1 --- /dev/null +++ b/LICENSE/LICENSE_LAST_RESORT_FONT @@ -0,0 +1,97 @@ +Last Resort High-Efficiency Font License +======================================== + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +SPDX-License-Identifier: OFL-1.1 diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build b/LICENSE/LICENSE_LIBRAQM similarity index 86% rename from subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build rename to LICENSE/LICENSE_LIBRAQM index ec288041f388..97e2489b7798 100644 --- a/subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build +++ b/LICENSE/LICENSE_LIBRAQM @@ -1,4 +1,7 @@ -Copyright (c) 2018 The Meson development team +The MIT License (MIT) + +Copyright © 2015 Information Technology Authority (ITA) +Copyright © 2016-2023 Khaled Hosny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSE/LICENSE_SHEENBIDI b/LICENSE/LICENSE_SHEENBIDI new file mode 100755 index 000000000000..d64569567334 --- /dev/null +++ b/LICENSE/LICENSE_SHEENBIDI @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 7b9c99597c0d..bbbbec35331e 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ [![Conda](https://img.shields.io/conda/vn/conda-forge/matplotlib)](https://anaconda.org/conda-forge/matplotlib) [![Downloads](https://img.shields.io/pypi/dm/matplotlib)](https://pypi.org/project/matplotlib) [![NUMFocus](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org) +[![LFX Health Score](https://insights.linuxfoundation.org/api/badge/health-score?project=matplotlib)](https://insights.linuxfoundation.org/project/matplotlib) [![Discourse help forum](https://img.shields.io/badge/help_forum-discourse-blue.svg)](https://discourse.matplotlib.org) -[![Gitter](https://badges.gitter.im/matplotlib/matplotlib.svg)](https://gitter.im/matplotlib/matplotlib) +[![Discourse chat](https://img.shields.io/badge/chat-discourse-mediumaquamarine)](https://discourse.matplotlib.org/chat/c/matplotlib/2) [![GitHub issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/matplotlib/matplotlib/issues) [![Contributing](https://img.shields.io/badge/PR-Welcome-%23FF8300.svg?)](https://matplotlib.org/stable/devel/index.html) @@ -14,14 +15,14 @@ [![Codecov status](https://codecov.io/github/matplotlib/matplotlib/badge.svg?branch=main&service=github)](https://app.codecov.io/gh/matplotlib/matplotlib) [![EffVer Versioning](https://img.shields.io/badge/version_scheme-EffVer-0097a7)](https://jacobtomlinson.dev/effver) -![Matplotlib logotype](https://matplotlib.org/_static/logo2.svg) +![Matplotlib logotype](https://matplotlib.org/stable/_static/logo2.svg) Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python. Check out our [home page](https://matplotlib.org/) for more information. -![image](https://matplotlib.org/_static/readme_preview.png) +![image](https://matplotlib.org/stable/_static/readme_preview.png) Matplotlib produces publication-quality figures in a variety of hardcopy formats and interactive environments across platforms. Matplotlib can be @@ -60,9 +61,9 @@ Our active mailing lists (which are mirrored on Discourse) are: - [Development](https://mail.python.org/mailman/listinfo/matplotlib-devel) mailing list: -[Gitter](https://gitter.im/matplotlib/matplotlib) is for coordinating -development and asking questions directly related to contributing to -matplotlib. +[Discourse Chat](https://discourse.matplotlib.org/chat/c/matplotlib/2) is for +coordinating development and asking questions directly related to contributing +to matplotlib. ## Citing Matplotlib diff --git a/SECURITY.md b/SECURITY.md index ce022ca60a0f..4400a4501b51 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,14 +8,13 @@ versions. | Version | Supported | | ------- | ------------------ | +| 3.10.x | :white_check_mark: | | 3.9.x | :white_check_mark: | -| 3.8.x | :white_check_mark: | +| 3.8.x | :x: | | 3.7.x | :x: | | 3.6.x | :x: | | 3.5.x | :x: | -| 3.4.x | :x: | -| 3.3.x | :x: | -| < 3.3 | :x: | +| < 3.5 | :x: | ## Reporting a Vulnerability diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 91e653b033f2..829a1c7b9005 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -49,33 +49,15 @@ stages: - job: Pytest strategy: matrix: - Linux_py39: - vmImage: 'ubuntu-20.04' # keep one job pinned to the oldest image - python.version: '3.9' - Linux_py310: - vmImage: 'ubuntu-latest' - python.version: '3.10' - Linux_py311: - vmImage: 'ubuntu-latest' - python.version: '3.11' - macOS_py39: - vmImage: 'macOS-latest' - python.version: '3.9' - macOS_py310: - vmImage: 'macOS-latest' - python.version: '3.10' - macOS_py311: - vmImage: 'macOS-latest' + Windows_py311: + vmImage: 'windows-2022' # Keep one job pinned to the oldest image python.version: '3.11' - Windows_py39: - vmImage: 'windows-2019' # keep one job pinned to the oldest image - python.version: '3.9' - Windows_py310: + Windows_py312: vmImage: 'windows-latest' - python.version: '3.10' - Windows_py311: + python.version: '3.12' + Windows_py313: vmImage: 'windows-latest' - python.version: '3.11' + python.version: '3.13' maxParallel: 4 pool: vmImage: '$(vmImage)' @@ -87,76 +69,19 @@ stages: displayName: 'Use Python $(python.version)' - bash: | - set -e - case "$AGENT_OS" in - Linux) - echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries - sudo apt update - sudo apt install --no-install-recommends \ - cm-super \ - dvipng \ - ffmpeg \ - fonts-freefont-otf \ - fonts-noto-cjk \ - fonts-wqy-zenhei \ - gdb \ - gir1.2-gtk-3.0 \ - graphviz \ - inkscape \ - language-pack-de \ - lcov \ - libcairo2 \ - libgirepository-1.0-1 \ - lmodern \ - ninja-build \ - poppler-utils \ - texlive-fonts-recommended \ - texlive-latex-base \ - texlive-latex-extra \ - texlive-latex-recommended \ - texlive-luatex \ - texlive-pictures \ - texlive-xetex - ;; - Darwin) - brew update - brew install --cask xquartz - brew install ccache ffmpeg imagemagick mplayer ninja pkg-config - brew install --cask font-noto-sans-cjk-sc - ;; - Windows_NT) - choco install ninja - ;; - *) - exit 1 - ;; - esac + choco install ninja displayName: 'Install dependencies' - bash: | python -m pip install --upgrade pip - python -m pip install --upgrade -r requirements/dev/build-requirements.txt - python -m pip install -r requirements/testing/all.txt -r requirements/testing/extra.txt + python -m pip install --upgrade --group build + python -m pip install --prefer-binary --group test --group test-extra displayName: 'Install dependencies with pip' - bash: | - case "$AGENT_OS" in - Linux) - export CPPFLAGS='--coverage -fprofile-abs-path' - ;; - Darwin) - export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw' - export CPPFLAGS="$CPPFLAGS -fcoverage-mapping" - ;; - Windows_NT) - CONFIG='--config-settings=setup-args=--vsenv' - CONFIG="$CONFIG --config-settings=setup-args=-Dcpp_link_args=-PROFILE" - CONFIG="$CONFIG --config-settings=setup-args=-Dbuildtype=debug" - ;; - *) - exit 1 - ;; - esac + CONFIG='--config-settings=setup-args=--vsenv' + CONFIG="$CONFIG --config-settings=setup-args=-Dcpp_link_args=-PROFILE" + CONFIG="$CONFIG --config-settings=setup-args=-Dbuildtype=debug" python -m pip install \ --no-build-isolation $CONFIG \ @@ -171,102 +96,52 @@ stages: - bash: | set -e - if [[ "$AGENT_OS" == 'Windows_NT' ]]; then - SESSION_ID=$(python -c "import uuid; print(uuid.uuid4(), end='')") - echo "Coverage session ID: ${SESSION_ID}" - VS=$(ls -d /c/Program\ Files*/Microsoft\ Visual\ Studio/*/Enterprise) - echo "Visual Studio: ${VS}" - DIR="$VS/Common7/IDE/Extensions/Microsoft/CodeCoverage.Console" - if [[ -d $DIR ]]; then - # This is for MSVC 2022 (on windows-latest). - TOOL="$DIR/Microsoft.CodeCoverage.Console.exe" - for f in build/cp*/src/*.pyd; do - echo $f - echo "==============================" - "$TOOL" instrument $f --session-id $SESSION_ID \ - --log-level Verbose --log-file instrument.log - cat instrument.log - rm instrument.log - done - echo "Starting $TOOL in server mode" - "$TOOL" collect \ - --session-id $SESSION_ID --server-mode \ - --output-format cobertura --output extensions.xml \ - --log-level Verbose --log-file extensions.log & - VS_VER=2022 - else - DIR="$VS"/Team\ Tools/Dynamic\ Code\ Coverage\ Tools/amd64 - if [[ -d $DIR ]]; then - # This is for MSVC 2019 (on windows-2019). - VSINSTR="$VS"/Team\ Tools/Performance\ Tools/vsinstr.exe - for f in build/cp*/src/*.pyd; do - "$VSINSTR" $f -Verbose -Coverage - done - TOOL="$DIR/CodeCoverage.exe" - cat > extensions.config << EOF - - true - - - .*\\.*\.pyd - - - - EOF - echo "Starting $TOOL in server mode" - "$TOOL" collect \ - -config:extensions.config -session:$SESSION_ID \ - -output:extensions.coverage -verbose & - echo "Started $TOOL" - VS_VER=2019 - fi - fi - echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" - fi + SESSION_ID=$(python -c "import uuid; print(uuid.uuid4(), end='')") + echo "Coverage session ID: ${SESSION_ID}" + VS=$(ls -d /c/Program\ Files*/Microsoft\ Visual\ Studio/*/Enterprise) + echo "Visual Studio: ${VS}" + DIR="$VS/Common7/IDE/Extensions/Microsoft/CodeCoverage.Console" + # This is for MSVC 2022 (on windows-latest). + TOOL="$DIR/Microsoft.CodeCoverage.Console.exe" + for f in build/cp*/src/*.pyd; do + echo $f + echo "==============================" + "$TOOL" instrument $f --session-id $SESSION_ID \ + --log-level Verbose --log-file instrument.log + cat instrument.log + rm instrument.log + done + echo "Starting $TOOL in server mode" + "$TOOL" collect \ + --session-id $SESSION_ID --server-mode \ + --output-format cobertura --output extensions.xml \ + --log-level Verbose --log-file extensions.log & + VS_VER=2022 + + echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" + PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ --maxfail=50 --timeout=300 --durations=25 \ --junitxml=junit/test-results.xml --cov-report=xml --cov=lib - if [[ -n $SESSION_ID ]]; then - if [[ $VS_VER == 2022 ]]; then - "$TOOL" shutdown $SESSION_ID - echo "Coverage collection log" - echo "=======================" - cat extensions.log - else - "$TOOL" shutdown -session:$SESSION_ID - fi + + if [[ $VS_VER == 2022 ]]; then + "$TOOL" shutdown $SESSION_ID + echo "Coverage collection log" + echo "=======================" + cat extensions.log + else + "$TOOL" shutdown -session:$SESSION_ID fi displayName: 'pytest' - bash: | - case "$AGENT_OS" in - Linux) - lcov --rc lcov_branch_coverage=1 --capture --directory . \ - --output-file coverage.info - lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ - --extract coverage.info $PWD/src/'*' $PWD/lib/'*' - lcov --rc lcov_branch_coverage=1 --list coverage.info - find . -name '*.gc*' -delete - ;; - Darwin) - xcrun llvm-profdata merge -sparse default.*.profraw \ - -o default.profdata - xcrun llvm-cov export -format="lcov" build/*/src/*.so \ - -instr-profile default.profdata > info.lcov - ;; - Windows_NT) - if [[ -f extensions.coverage ]]; then - # For MSVC 2019. - "$VS_COVERAGE_TOOL" analyze -output:extensions.xml \ - -include_skipped_functions -include_skipped_modules \ - extensions.coverage - rm extensions.coverage - fi - ;; - *) - exit 1 - ;; - esac + if [[ -f extensions.coverage ]]; then + # For MSVC 2019. + "$VS_COVERAGE_TOOL" analyze -output:extensions.xml \ + -include_skipped_functions -include_skipped_modules \ + extensions.coverage + rm extensions.coverage + fi displayName: 'Filter C coverage' condition: succeededOrFailed() - bash: | diff --git a/ci/codespell-ignore-words.txt b/ci/codespell-ignore-words.txt index acbb2e8f39b5..e138f26e216a 100644 --- a/ci/codespell-ignore-words.txt +++ b/ci/codespell-ignore-words.txt @@ -1,4 +1,5 @@ aas +ABD axises coo curvelinear @@ -14,5 +15,9 @@ oint oly te thisy +ure whis wit +Copin +socio-economic +Ines diff --git a/ci/minver-requirements.txt b/ci/minver-requirements.txt new file mode 100644 index 000000000000..3b6aea9e7ca3 --- /dev/null +++ b/ci/minver-requirements.txt @@ -0,0 +1,23 @@ +# Extra pip requirements for the minimum-version CI run + +contourpy==1.0.1 +cycler==0.10 +fonttools==4.22.0 +importlib-resources==3.2.0 +kiwisolver==1.3.2 +meson-python==0.13.2 +meson==1.1.0 +numpy==1.25.0 +packaging==20.0 +pillow==9.0.1 +pyparsing==3.0.0 +pytest==7.0.0 +python-dateutil==2.7 + +# Test ipython/matplotlib-inline before backend mapping moved to mpl. +# This should be tested for a reasonably long transition period, +# but we will eventually remove the test when we no longer support +# ipython/matplotlib-inline versions from before the transition. +ipython==7.29.0 +ipykernel==5.5.6 +matplotlib-inline<0.1.7 diff --git a/ci/mypy-stubtest-allowlist.txt b/ci/mypy-stubtest-allowlist.txt index 73dfb1d8ceb0..0e199889cb07 100644 --- a/ci/mypy-stubtest-allowlist.txt +++ b/ci/mypy-stubtest-allowlist.txt @@ -1,48 +1,58 @@ # Non-typed (and private) modules/functions -matplotlib.backends.* -matplotlib.tests.* -matplotlib.pylab.* -matplotlib._.* -matplotlib.rcsetup._listify_validator -matplotlib.rcsetup._validate_linestyle -matplotlib.ft2font.Glyph -matplotlib.testing.jpl_units.* -matplotlib.sphinxext.* +matplotlib\.backends\..* +matplotlib\.tests(\..*)? +matplotlib\.pylab(\..*)? +matplotlib\._.* +matplotlib\.rcsetup\._listify_validator +matplotlib\.rcsetup\._validate_linestyle +matplotlib\.ft2font\.Glyph +matplotlib\.ft2font\.LayoutItem +matplotlib\.testing\.jpl_units\..* +matplotlib\.sphinxext(\..*)? # set methods have heavy dynamic usage of **kwargs, with differences for subclasses # which results in technically inconsistent signatures, but not actually a problem -matplotlib.*\.set$ +matplotlib\..*\.set$ # Typed inline, inconsistencies largely due to imports -matplotlib.pyplot.* -matplotlib.typing.* +matplotlib\.pyplot\..* +matplotlib\.typing\..* # Other decorator modifying signature # Backcompat decorator which does not modify runtime reported signature -matplotlib.offsetbox.*Offset[Bb]ox.get_offset +matplotlib\.offsetbox\..*Offset[Bb]ox\.get_offset # Inconsistent super/sub class parameter name (maybe rename for consistency) -matplotlib.projections.polar.RadialLocator.nonsingular -matplotlib.ticker.LogLocator.nonsingular -matplotlib.ticker.LogitLocator.nonsingular +matplotlib\.projections\.polar\.RadialLocator\.nonsingular +matplotlib\.ticker\.LogLocator\.nonsingular +matplotlib\.ticker\.LogitLocator\.nonsingular # Stdlib/Enum considered inconsistent (no fault of ours, I don't think) -matplotlib.backend_bases._Mode.__new__ -matplotlib.units.Number.__hash__ +matplotlib\.backend_bases\._Mode\.__new__ # 3.6 Pending deprecations -matplotlib.figure.Figure.set_constrained_layout -matplotlib.figure.Figure.set_constrained_layout_pads -matplotlib.figure.Figure.set_tight_layout - -# positional-only argument name lacking leading underscores -matplotlib.axes._base._AxesBase.axis +matplotlib\.figure\.Figure\.set_constrained_layout +matplotlib\.figure\.Figure\.set_constrained_layout_pads +matplotlib\.figure\.Figure\.set_tight_layout # Maybe should be abstractmethods, required for subclasses, stubs define once -matplotlib.tri.*TriInterpolator.__call__ -matplotlib.tri.*TriInterpolator.gradient +matplotlib\.tri\..*TriInterpolator\.__call__ +matplotlib\.tri\..*TriInterpolator\.gradient # TypeVar used only in type hints -matplotlib.backend_bases.FigureCanvasBase._T -matplotlib.backend_managers.ToolManager._T -matplotlib.spines.Spine._T +matplotlib\.backend_bases\.FigureCanvasBase\._T +matplotlib\.backend_managers\.ToolManager\._T +matplotlib\.spines\.Spine\._T + +# Parameter inconsistency due to 3.10 deprecation +matplotlib\.figure\.FigureBase\.get_figure + +# getitem method only exists for 3.10 deprecation backcompatability +matplotlib\.inset\.InsetIndicator\.__getitem__ + +# only defined in stubs; not present at runtime +matplotlib\.animation\.EventSourceProtocol + +# Avoid a regression in NewType handling for stubtest +# https://github.com/python/mypy/issues/19877 +matplotlib\.ft2font\.GlyphIndexType\.__init__ diff --git a/ci/schemas/conda-environment.json b/ci/schemas/conda-environment.json index 458676942a44..fb1e821778c3 100644 --- a/ci/schemas/conda-environment.json +++ b/ci/schemas/conda-environment.json @@ -1,6 +1,6 @@ { "title": "conda environment file", - "description": "Support for conda's enviroment.yml files (e.g. `conda env export > environment.yml`)", + "description": "Support for conda's environment.yml files (e.g. `conda env export > environment.yml`)", "id": "https://raw.githubusercontent.com/Microsoft/vscode-python/main/schemas/conda-environment.json", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { diff --git a/doc/Makefile b/doc/Makefile index 7eda39d87624..baed196a3ee2 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -18,6 +18,7 @@ help: clean: @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) rm -rf "$(SOURCEDIR)/build" + rm -rf "$(SOURCEDIR)/_tags" rm -rf "$(SOURCEDIR)/api/_as_gen" rm -rf "$(SOURCEDIR)/gallery" rm -rf "$(SOURCEDIR)/plot_types" diff --git a/doc/_embedded_plots/axes_margins.py b/doc/_embedded_plots/axes_margins.py new file mode 100644 index 000000000000..d026840c3c15 --- /dev/null +++ b/doc/_embedded_plots/axes_margins.py @@ -0,0 +1,42 @@ +import numpy as np +import matplotlib.pyplot as plt + +fig, ax = plt.subplots(figsize=(6.5, 4)) +x = np.linspace(0, 1, 33) +y = -np.sin(x * 2*np.pi) +ax.plot(x, y, 'o') +ax.margins(0.5, 0.2) +ax.set_title("margins(x=0.5, y=0.2)") + +# fix the Axes limits so that the following helper drawings +# cannot change them further. +ax.set(xlim=ax.get_xlim(), ylim=ax.get_ylim()) + + +def arrow(p1, p2, **props): + ax.annotate("", p1, p2, + arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0, **props)) + + +axmin, axmax = ax.get_xlim() +aymin, aymax = ax.get_ylim() +xmin, xmax = x.min(), x.max() +ymin, ymax = y.min(), y.max() + +y0 = -0.8 +ax.axvspan(axmin, xmin, color=("orange", 0.1)) +ax.axvspan(xmax, axmax, color=("orange", 0.1)) +arrow((xmin, y0), (xmax, y0), color="sienna") +arrow((xmax, y0), (axmax, y0), color="orange") +ax.text((xmax + axmax)/2, y0+0.05, "x margin\n* x data range", + ha="center", va="bottom", color="orange") +ax.text(0.55, y0+0.1, "x data range", va="bottom", color="sienna") + +x0 = 0.1 +ax.axhspan(aymin, ymin, color=("tab:green", 0.1)) +ax.axhspan(ymax, aymax, color=("tab:green", 0.1)) +arrow((x0, ymin), (x0, ymax), color="darkgreen") +arrow((x0, ymax), (x0, aymax), color="tab:green") +ax.text(x0, (ymax + aymax) / 2, " y margin * y data range", + va="center", color="tab:green") +ax.text(x0, 0.5, " y data range", color="darkgreen") diff --git a/doc/_embedded_plots/figure_subplots_adjust.py b/doc/_embedded_plots/figure_subplots_adjust.py new file mode 100644 index 000000000000..d32a029fe05d --- /dev/null +++ b/doc/_embedded_plots/figure_subplots_adjust.py @@ -0,0 +1,34 @@ +import matplotlib.pyplot as plt + + +fig, axs = plt.subplots(2, 2, figsize=(6.5, 4)) +fig.set_facecolor('lightblue') +fig.subplots_adjust(0.1, 0.1, 0.9, 0.9, 0.4, 0.4) + +overlay = fig.add_axes([0, 0, 1, 1], zorder=100) +overlay.axis("off") +xycoords = 'figure fraction' +arrowprops = dict(arrowstyle="<->", shrinkA=0, shrinkB=0) + +for ax in axs.flat: + ax.set(xticks=[], yticks=[]) + +overlay.annotate("", (0, 0.75), (0.1, 0.75), + xycoords=xycoords, arrowprops=arrowprops) # left +overlay.annotate("", (0.435, 0.25), (0.565, 0.25), + xycoords=xycoords, arrowprops=arrowprops) # wspace +overlay.annotate("", (0, 0.8), (0.9, 0.8), + xycoords=xycoords, arrowprops=arrowprops) # right +fig.text(0.05, 0.7, "left", ha="center") +fig.text(0.5, 0.3, "wspace", ha="center") +fig.text(0.05, 0.83, "right", ha="center") + +overlay.annotate("", (0.75, 0), (0.75, 0.1), + xycoords=xycoords, arrowprops=arrowprops) # bottom +overlay.annotate("", (0.25, 0.435), (0.25, 0.565), + xycoords=xycoords, arrowprops=arrowprops) # hspace +overlay.annotate("", (0.8, 0), (0.8, 0.9), + xycoords=xycoords, arrowprops=arrowprops) # top +fig.text(0.65, 0.05, "bottom", va="center") +fig.text(0.28, 0.5, "hspace", va="center") +fig.text(0.82, 0.05, "top", va="center") diff --git a/doc/_embedded_plots/grouped_bar.py b/doc/_embedded_plots/grouped_bar.py new file mode 100644 index 000000000000..f02e269328d2 --- /dev/null +++ b/doc/_embedded_plots/grouped_bar.py @@ -0,0 +1,15 @@ +import matplotlib.pyplot as plt + +categories = ['A', 'B'] +data0 = [1.0, 3.0] +data1 = [1.4, 3.4] +data2 = [1.8, 3.8] + +fig, ax = plt.subplots(figsize=(4, 2.2)) +ax.grouped_bar( + [data0, data1, data2], + tick_labels=categories, + labels=['dataset 0', 'dataset 1', 'dataset 2'], + colors=['#1f77b4', '#58a1cf', '#abd0e6'], +) +ax.legend() diff --git a/doc/_embedded_plots/hatch_classes.py b/doc/_embedded_plots/hatch_classes.py new file mode 100644 index 000000000000..cb9cd7d4b356 --- /dev/null +++ b/doc/_embedded_plots/hatch_classes.py @@ -0,0 +1,28 @@ +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle + +fig, ax = plt.subplots() + +pattern_to_class = { + '/': 'NorthEastHatch', + '\\': 'SouthEastHatch', + '|': 'VerticalHatch', + '-': 'HorizontalHatch', + '+': 'VerticalHatch + HorizontalHatch', + 'x': 'NorthEastHatch + SouthEastHatch', + 'o': 'SmallCircles', + 'O': 'LargeCircles', + '.': 'SmallFilledCircles', + '*': 'Stars', +} + +for i, (hatch, classes) in enumerate(pattern_to_class.items()): + r = Rectangle((0.1, i+0.5), 0.8, 0.8, fill=False, hatch=hatch*2) + ax.add_patch(r) + h = ax.annotate(f"'{hatch}'", xy=(1.2, .5), xycoords=r, + family='monospace', va='center', ha='left') + ax.annotate(pattern_to_class[hatch], xy=(1.5, .5), xycoords=h, + family='monospace', va='center', ha='left', color='tab:blue') + +ax.set(xlim=(0, 5), ylim=(0, i+1.5), yinverted=True) +ax.set_axis_off() diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index aae167d1f6f8..25bad17c3938 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -167,6 +167,14 @@ div.wide-table table th.stub { display: inline; } +/* sdd is a custom class that strips out styling from dropdowns + * Example usage: + * + * .. dropdown:: + * :class-container: sdd + * + */ + .sdd.sd-dropdown { box-shadow: none!important; } @@ -185,3 +193,30 @@ div.wide-table table th.stub { .sdd.sd-dropdown .sd-card-header +.sd-card-body{ --pst-sd-dropdown-color: none; } + +/* section-toc is a custom class that removes the page title from a toctree listing + * and shifts the resulting list left + * Example usage: + * + * .. rst-class:: section-toc + * .. toctree:: + * + */ + .section-toc.toctree-wrapper .toctree-l1>a{ + display: none; +} +.section-toc.toctree-wrapper .toctree-l1>ul{ + padding-left: 0; +} + +.sidebar-cheatsheets { + margin-bottom: 3em; +} + +.sidebar-cheatsheets > h3 { + margin-top: 0; +} + +.sidebar-cheatsheets > img { + width: 100%; +} diff --git a/doc/_static/multipage_pdf_thumbnail.svg b/doc/_static/multipage_pdf_thumbnail.svg new file mode 100644 index 000000000000..864c4c647492 --- /dev/null +++ b/doc/_static/multipage_pdf_thumbnail.svg @@ -0,0 +1,12 @@ + + + + + + + + + + Multipage + PDF + diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json index 3d712e4ff8e9..36e743db21b8 100644 --- a/doc/_static/switcher.json +++ b/doc/_static/switcher.json @@ -1,15 +1,20 @@ [ { - "name": "3.9 (stable)", - "version": "stable", + "name": "3.10 (stable)", + "version": "3.10.8", "url": "https://matplotlib.org/stable/", "preferred": true }, { - "name": "3.10 (dev)", + "name": "3.11 (dev)", "version": "dev", "url": "https://matplotlib.org/devdocs/" }, + { + "name": "3.9", + "version": "3.9.3", + "url": "https://matplotlib.org/3.9.3/" + }, { "name": "3.8", "version": "3.8.4", diff --git a/doc/_static/transforms.png b/doc/_static/transforms.png deleted file mode 100644 index ab07fb575961..000000000000 Binary files a/doc/_static/transforms.png and /dev/null differ diff --git a/doc/_static/zenodo_cache/12652732.svg b/doc/_static/zenodo_cache/12652732.svg new file mode 100644 index 000000000000..cde5c5f37839 --- /dev/null +++ b/doc/_static/zenodo_cache/12652732.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.12652732 + + + 10.5281/zenodo.12652732 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/13308876.svg b/doc/_static/zenodo_cache/13308876.svg new file mode 100644 index 000000000000..749bc3c19026 --- /dev/null +++ b/doc/_static/zenodo_cache/13308876.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.13308876 + + + 10.5281/zenodo.13308876 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14249941.svg b/doc/_static/zenodo_cache/14249941.svg new file mode 100644 index 000000000000..f9165f17fdf0 --- /dev/null +++ b/doc/_static/zenodo_cache/14249941.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14249941 + + + 10.5281/zenodo.14249941 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14436121.svg b/doc/_static/zenodo_cache/14436121.svg new file mode 100644 index 000000000000..1e4a7cd5b7a4 --- /dev/null +++ b/doc/_static/zenodo_cache/14436121.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14436121 + + + 10.5281/zenodo.14436121 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14464227.svg b/doc/_static/zenodo_cache/14464227.svg new file mode 100644 index 000000000000..7126d239d6a5 --- /dev/null +++ b/doc/_static/zenodo_cache/14464227.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14464227 + + + 10.5281/zenodo.14464227 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14940554.svg b/doc/_static/zenodo_cache/14940554.svg new file mode 100644 index 000000000000..6e7d5c37bf7b --- /dev/null +++ b/doc/_static/zenodo_cache/14940554.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14940554 + + + 10.5281/zenodo.14940554 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/15375714.svg b/doc/_static/zenodo_cache/15375714.svg new file mode 100644 index 000000000000..d5e403138561 --- /dev/null +++ b/doc/_static/zenodo_cache/15375714.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.15375714 + + + 10.5281/zenodo.15375714 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/16644850.svg b/doc/_static/zenodo_cache/16644850.svg new file mode 100644 index 000000000000..89910032da4e --- /dev/null +++ b/doc/_static/zenodo_cache/16644850.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.16644850 + + + 10.5281/zenodo.16644850 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/16999430.svg b/doc/_static/zenodo_cache/16999430.svg new file mode 100644 index 000000000000..44c448643e91 --- /dev/null +++ b/doc/_static/zenodo_cache/16999430.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.16999430 + + + 10.5281/zenodo.16999430 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/17298696.svg b/doc/_static/zenodo_cache/17298696.svg new file mode 100644 index 000000000000..9aa8d7c94349 --- /dev/null +++ b/doc/_static/zenodo_cache/17298696.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.17298696 + + + 10.5281/zenodo.17298696 + + + \ No newline at end of file diff --git a/doc/_templates/autofunctions.rst b/doc/_templates/autofunctions.rst deleted file mode 100644 index 291b8eee2ede..000000000000 --- a/doc/_templates/autofunctions.rst +++ /dev/null @@ -1,22 +0,0 @@ - -{{ fullname | escape | underline }} - - -.. automodule:: {{ fullname }} - :no-members: - -{% block functions %} -{% if functions %} - -Functions ---------- - -.. autosummary:: - :template: autosummary.rst - :toctree: - -{% for item in functions %}{% if item not in ['plotting', 'colormaps'] %} - {{ item }}{% endif %}{% endfor %} - -{% endif %} -{% endblock %} diff --git a/doc/_templates/cheatsheet_sidebar.html b/doc/_templates/cheatsheet_sidebar.html index 3f2b7c4f4db1..2ca6548ddd4d 100644 --- a/doc/_templates/cheatsheet_sidebar.html +++ b/doc/_templates/cheatsheet_sidebar.html @@ -1,6 +1,6 @@ ' '
' f'over {color_block(self.get_over())}' + '
' '') def copy(self): @@ -976,43 +1087,69 @@ class LinearSegmentedColormap(Colormap): segments. """ - def __init__(self, name, segmentdata, N=256, gamma=1.0): + def __init__(self, name, segmentdata, N=256, gamma=1.0, *, + bad=None, under=None, over=None): """ - Create colormap from linear mapping segments + Create colormap from linear mapping segments. - segmentdata argument is a dictionary with a red, green and blue - entries. Each entry should be a list of *x*, *y0*, *y1* tuples, - forming rows in a table. Entries for alpha are optional. + Parameters + ---------- + name : str + The name of the colormap. + segmentdata : dict + A dictionary with keys "red", "green", "blue" for the color channels. + Each entry should be a list of *x*, *y0*, *y1* tuples, forming rows + in a table. Entries for alpha are optional. + + Example: suppose you want red to increase from 0 to 1 over + the bottom half, green to do the same over the middle half, + and blue over the top half. Then you would use:: + + { + 'red': [(0.0, 0.0, 0.0), + (0.5, 1.0, 1.0), + (1.0, 1.0, 1.0)], + 'green': [(0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0)], + 'blue': [(0.0, 0.0, 0.0), + (0.5, 0.0, 0.0), + (1.0, 1.0, 1.0)] + } + + Each row in the table for a given color is a sequence of + *x*, *y0*, *y1* tuples. In each sequence, *x* must increase + monotonically from 0 to 1. For any input value *z* falling + between *x[i]* and *x[i+1]*, the output value of a given color + will be linearly interpolated between *y1[i]* and *y0[i+1]*:: - Example: suppose you want red to increase from 0 to 1 over - the bottom half, green to do the same over the middle half, - and blue over the top half. Then you would use:: + row i: x y0 y1 + / + / + row i+1: x y0 y1 - cdict = {'red': [(0.0, 0.0, 0.0), - (0.5, 1.0, 1.0), - (1.0, 1.0, 1.0)], + Hence, y0 in the first row and y1 in the last row are never used. + + N : int + The number of RGB quantization levels. + gamma : float + Gamma correction factor for input distribution x of the mapping. + See also https://en.wikipedia.org/wiki/Gamma_correction. + bad : :mpltype:`color`, default: transparent + The color for invalid values (NaN or masked). - 'green': [(0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.75, 1.0, 1.0), - (1.0, 1.0, 1.0)], + .. versionadded:: 3.11 - 'blue': [(0.0, 0.0, 0.0), - (0.5, 0.0, 0.0), - (1.0, 1.0, 1.0)]} + under : :mpltype:`color`, default: color of the lowest value + The color for low out-of-range values. - Each row in the table for a given color is a sequence of - *x*, *y0*, *y1* tuples. In each sequence, *x* must increase - monotonically from 0 to 1. For any input value *z* falling - between *x[i]* and *x[i+1]*, the output value of a given color - will be linearly interpolated between *y1[i]* and *y0[i+1]*:: + .. versionadded:: 3.11 - row i: x y0 y1 - / - / - row i+1: x y0 y1 + over : :mpltype:`color`, default: color of the highest value + The color for high out-of-range values. - Hence y0 in the first row and y1 in the last row are never used. + .. versionadded:: 3.11 See Also -------- @@ -1022,7 +1159,7 @@ def __init__(self, name, segmentdata, N=256, gamma=1.0): """ # True only if all colors in map are identical; needed for contouring. self.monochrome = False - super().__init__(name, N) + super().__init__(name, N, bad=bad, under=under, over=over) self._segmentdata = segmentdata self._gamma = gamma @@ -1038,7 +1175,7 @@ def _init(self): self._lut[:-3, 3] = _create_lookup_table( self.N, self._segmentdata['alpha'], 1) self._isinit = True - self._set_extremes() + self._update_lut_extremes() def set_gamma(self, gamma): """Set a new gamma value and regenerate colormap.""" @@ -1046,7 +1183,7 @@ def set_gamma(self, gamma): self._init() @staticmethod - def from_list(name, colors, N=256, gamma=1.0): + def from_list(name, colors, N=256, gamma=1.0, *, bad=None, under=None, over=None): """ Create a `LinearSegmentedColormap` from a list of colors. @@ -1059,22 +1196,42 @@ def from_list(name, colors, N=256, gamma=1.0): range :math:`[0, 1]`; i.e. 0 maps to ``colors[0]`` and 1 maps to ``colors[-1]``. If (value, color) pairs are given, the mapping is from *value* - to *color*. This can be used to divide the range unevenly. + to *color*. This can be used to divide the range unevenly. The + values must increase monotonically from 0 to 1. N : int The number of RGB quantization levels. gamma : float + + bad : :mpltype:`color`, default: transparent + The color for invalid values (NaN or masked). + under : :mpltype:`color`, default: color of the lowest value + The color for low out-of-range values. + over : :mpltype:`color`, default: color of the highest value + The color for high out-of-range values. """ if not np.iterable(colors): raise ValueError('colors must be iterable') - if (isinstance(colors[0], Sized) and len(colors[0]) == 2 - and not isinstance(colors[0], str)): - # List of value, color pairs - vals, colors = zip(*colors) - else: + try: + # Assume the passed colors are a list of colors + # and not a (value, color) tuple. + r, g, b, a = to_rgba_array(colors).T vals = np.linspace(0, 1, len(colors)) + except Exception as e: + # Assume the passed values are a list of + # (value, color) tuples. + try: + _vals, _colors = itertools.zip_longest(*colors) + except Exception as e2: + raise e2 from e + vals = np.asarray(_vals) + if np.min(vals) < 0 or np.max(vals) > 1 or np.any(np.diff(vals) <= 0): + raise ValueError( + "the values passed in the (value, color) pairs " + "must increase monotonically from 0 to 1." + ) + r, g, b, a = to_rgba_array(_colors).T - r, g, b, a = to_rgba_array(colors).T cdict = { "red": np.column_stack([vals, r, r]), "green": np.column_stack([vals, g, g]), @@ -1082,7 +1239,8 @@ def from_list(name, colors, N=256, gamma=1.0): "alpha": np.column_stack([vals, a, a]), } - return LinearSegmentedColormap(name, cdict, N, gamma) + return LinearSegmentedColormap(name, cdict, N, gamma, + bad=bad, under=under, over=over) def resampled(self, lutsize): """Return a new colormap with *lutsize* entries.""" @@ -1140,7 +1298,7 @@ class ListedColormap(Colormap): Parameters ---------- - colors : list, array + colors : list of :mpltype:`color` or array Sequence of Matplotlib color specifications (color names or RGB(A) values). name : str, optional @@ -1157,19 +1315,43 @@ class ListedColormap(Colormap): N > len(colors) the list will be extended by repetition. + + .. deprecated:: 3.11 + + This parameter will be removed. Please instead ensure that + the list of passed colors is the required length. + + bad : :mpltype:`color`, default: transparent + The color for invalid values (NaN or masked). + + .. versionadded:: 3.11 + + under : :mpltype:`color`, default: color of the lowest value + The color for low out-of-range values. + + .. versionadded:: 3.11 + + over : :mpltype:`color`, default: color of the highest value + The color for high out-of-range values. + + .. versionadded:: 3.11 """ - def __init__(self, colors, name='from_list', N=None): - self.monochrome = False # Are all colors identical? (for contour.py) + + @_api.delete_parameter( + "3.11", "N", + message="Passing 'N' to ListedColormap is deprecated since %(since)s " + "and will be removed in %(removal)s. Please ensure the list " + "of passed colors is the required length instead." + ) + def __init__(self, colors, name='unnamed', N=None, *, + bad=None, under=None, over=None): if N is None: self.colors = colors N = len(colors) else: if isinstance(colors, str): self.colors = [colors] * N - self.monochrome = True elif np.iterable(colors): - if len(colors) == 1: - self.monochrome = True self.colors = list( itertools.islice(itertools.cycle(colors), N)) else: @@ -1179,53 +1361,1038 @@ def __init__(self, colors, name='from_list', N=None): pass else: self.colors = [gray] * N - self.monochrome = True - super().__init__(name, N) + super().__init__(name, N, bad=bad, under=under, over=over) + + def _init(self): + self._lut = np.zeros((self.N + 3, 4), float) + self._lut[:-3] = to_rgba_array(self.colors) + self._isinit = True + self._update_lut_extremes() + + @property + def monochrome(self): + """Return whether all colors in the colormap are identical.""" + # Replacement for the attribute *monochrome*. This ensures a consistent + # response independent of the way the ListedColormap was created, which + # was not the case for the manually set attribute. + # + # TODO: It's a separate discussion whether we need this property on + # colormaps at all (at least as public API). It's a very special edge + # case and we only use it for contours internally. + self._ensure_inited() + return self.N <= 1 or np.all(self._lut[0] == self._lut[1:self.N]) + + def resampled(self, lutsize): + """Return a new colormap with *lutsize* entries.""" + colors = self(np.linspace(0, 1, lutsize)) + new_cmap = ListedColormap(colors, name=self.name) + # Keep the over/under values too + new_cmap._rgba_over = self._rgba_over + new_cmap._rgba_under = self._rgba_under + new_cmap._rgba_bad = self._rgba_bad + return new_cmap + + def reversed(self, name=None): + """ + Return a reversed instance of the Colormap. + + Parameters + ---------- + name : str, optional + The name for the reversed colormap. If None, the + name is set to ``self.name + "_r"``. + + Returns + ------- + ListedColormap + A reversed instance of the colormap. + """ + if name is None: + name = self.name + "_r" + + colors_r = list(reversed(self.colors)) + new_cmap = ListedColormap(colors_r, name=name) + # Reverse the over/under values too + new_cmap._rgba_over = self._rgba_under + new_cmap._rgba_under = self._rgba_over + new_cmap._rgba_bad = self._rgba_bad + return new_cmap + + +class MultivarColormap: + """ + Class for holding multiple `~matplotlib.colors.Colormap` for use in a + `~matplotlib.cm.ScalarMappable` object + """ + def __init__(self, colormaps, combination_mode, name='multivariate colormap'): + """ + Parameters + ---------- + colormaps: list or tuple of `~matplotlib.colors.Colormap` objects + The individual colormaps that are combined + combination_mode: str, 'sRGB_add' or 'sRGB_sub' + Describe how colormaps are combined in sRGB space + + - If 'sRGB_add' -> Mixing produces brighter colors + `sRGB = sum(colors)` + - If 'sRGB_sub' -> Mixing produces darker colors + `sRGB = 1 - sum(1 - colors)` + name : str, optional + The name of the colormap family. + """ + self.name = name + + if not np.iterable(colormaps) \ + or len(colormaps) == 1 \ + or isinstance(colormaps, str): + raise ValueError("A MultivarColormap must have more than one colormap.") + colormaps = list(colormaps) # ensure cmaps is a list, i.e. not a tuple + for i, cmap in enumerate(colormaps): + if isinstance(cmap, str): + colormaps[i] = mpl.colormaps[cmap] + elif not isinstance(cmap, Colormap): + raise ValueError("colormaps must be a list of objects that subclass" + " Colormap or a name found in the colormap registry.") + + self._colormaps = colormaps + _api.check_in_list(['sRGB_add', 'sRGB_sub'], combination_mode=combination_mode) + self._combination_mode = combination_mode + self.n_variates = len(colormaps) + self._rgba_bad = (0.0, 0.0, 0.0, 0.0) # If bad, don't paint anything. + + def __call__(self, X, alpha=None, bytes=False, clip=True): + r""" + Parameters + ---------- + X : tuple (X0, X1, ...) of length equal to the number of colormaps + X0, X1 ...: + float or int, `~numpy.ndarray` or scalar + The data value(s) to convert to RGBA. + For floats, *Xi...* should be in the interval ``[0.0, 1.0]`` to + return the RGBA values ``X*100`` percent along the Colormap line. + For integers, *Xi...* should be in the interval ``[0, self[i].N)`` to + return RGBA values *indexed* from colormap [i] with index ``Xi``, where + self[i] is colormap i. + alpha : float or array-like or None + Alpha must be a scalar between 0 and 1, a sequence of such + floats with shape matching *Xi*, or None. + bytes : bool, default: False + If False (default), the returned RGBA values will be floats in the + interval ``[0, 1]`` otherwise they will be `numpy.uint8`\s in the + interval ``[0, 255]``. + clip : bool, default: True + If True, clip output to 0 to 1 + + Returns + ------- + Tuple of RGBA values if X[0] is scalar, otherwise an array of + RGBA values with a shape of ``X.shape + (4, )``. + """ + + if len(X) != len(self): + raise ValueError( + f'For the selected colormap the data must have a first dimension ' + f'{len(self)}, not {len(X)}') + rgba, mask_bad = self[0]._get_rgba_and_mask(X[0], bytes=False) + for c, xx in zip(self[1:], X[1:]): + sub_rgba, sub_mask_bad = c._get_rgba_and_mask(xx, bytes=False) + rgba[..., :3] += sub_rgba[..., :3] # add colors + rgba[..., 3] *= sub_rgba[..., 3] # multiply alpha + mask_bad |= sub_mask_bad + + if self.combination_mode == 'sRGB_sub': + rgba[..., :3] -= len(self) - 1 + + rgba[mask_bad] = self.get_bad() + + if clip: + rgba = np.clip(rgba, 0, 1) + + if alpha is not None: + if clip: + alpha = np.clip(alpha, 0, 1) + if np.shape(alpha) not in [(), np.shape(X[0])]: + raise ValueError( + f"alpha is array-like but its shape {np.shape(alpha)} does " + f"not match that of X[0] {np.shape(X[0])}") + rgba[..., -1] *= alpha + + if bytes: + if not clip: + raise ValueError( + "clip cannot be false while bytes is true" + " as uint8 does not support values below 0" + " or above 255.") + rgba = (rgba * 255).astype('uint8') + + if not np.iterable(X[0]): + rgba = tuple(rgba) + + return rgba + + def copy(self): + """Return a copy of the multivarcolormap.""" + return self.__copy__() + + def __copy__(self): + cls = self.__class__ + cmapobject = cls.__new__(cls) + cmapobject.__dict__.update(self.__dict__) + cmapobject._colormaps = [cm.copy() for cm in self._colormaps] + cmapobject._rgba_bad = np.copy(self._rgba_bad) + return cmapobject + + def __eq__(self, other): + if not isinstance(other, MultivarColormap): + return False + if len(self) != len(other): + return False + for c0, c1 in zip(self, other): + if c0 != c1: + return False + if not all(self._rgba_bad == other._rgba_bad): + return False + if self.combination_mode != other.combination_mode: + return False + return True + + def __getitem__(self, item): + return self._colormaps[item] + + def __iter__(self): + for c in self._colormaps: + yield c + + def __len__(self): + return len(self._colormaps) + + def __str__(self): + return self.name + + def get_bad(self): + """Get the color for masked values.""" + return np.array(self._rgba_bad) + + def resampled(self, lutshape): + """ + Return a new colormap with *lutshape* entries. + + Parameters + ---------- + lutshape : tuple of (`int`, `None`) + The tuple must have a length matching the number of variates. + For each element in the tuple, if `int`, the corresponding colorbar + is resampled, if `None`, the corresponding colorbar is not resampled. + + Returns + ------- + MultivarColormap + """ + + if not np.iterable(lutshape) or len(lutshape) != len(self): + raise ValueError(f"lutshape must be of length {len(self)}") + new_cmap = self.copy() + for i, s in enumerate(lutshape): + if s is not None: + new_cmap._colormaps[i] = self[i].resampled(s) + return new_cmap + + def with_extremes(self, *, bad=None, under=None, over=None): + """ + Return a copy of the `MultivarColormap` with modified out-of-range attributes. + + The *bad* keyword modifies the copied `MultivarColormap` while *under* and + *over* modifies the attributes of the copied component colormaps. + Note that *under* and *over* colors are subject to the mixing rules determined + by the *combination_mode*. + + Parameters + ---------- + bad: :mpltype:`color`, default: None + If Matplotlib color, the bad value is set accordingly in the copy + + under tuple of :mpltype:`color`, default: None + If tuple, the `under` value of each component is set with the values + from the tuple. + + over tuple of :mpltype:`color`, default: None + If tuple, the `over` value of each component is set with the values + from the tuple. + + Returns + ------- + MultivarColormap + copy of self with attributes set + + """ + new_cm = self.copy() + if bad is not None: + new_cm._rgba_bad = to_rgba(bad) + if under is not None: + if not np.iterable(under) or len(under) != len(new_cm): + raise ValueError("*under* must contain a color for each scalar colormap" + f" i.e. be of length {len(new_cm)}.") + else: + for c, b in zip(new_cm, under): + # in-place change is ok, since we've just created c as a copy + c._set_extremes(under=b) + if over is not None: + if not np.iterable(over) or len(over) != len(new_cm): + raise ValueError("*over* must contain a color for each scalar colormap" + f" i.e. be of length {len(new_cm)}.") + else: + for c, b in zip(new_cm, over): + # in-place change is ok, since we've just created c as a copy + c._set_extremes(over=b) + return new_cm + + @property + def combination_mode(self): + return self._combination_mode + + def _repr_png_(self): + """Generate a PNG representation of the Colormap.""" + X = np.tile(np.linspace(0, 1, _REPR_PNG_SIZE[0]), + (_REPR_PNG_SIZE[1], 1)) + pixels = np.zeros((_REPR_PNG_SIZE[1]*len(self), _REPR_PNG_SIZE[0], 4), + dtype=np.uint8) + for i, c in enumerate(self): + pixels[i*_REPR_PNG_SIZE[1]:(i+1)*_REPR_PNG_SIZE[1], :] = c(X, bytes=True) + png_bytes = io.BytesIO() + title = self.name + ' multivariate colormap' + author = f'Matplotlib v{mpl.__version__}, https://matplotlib.org' + pnginfo = PngInfo() + pnginfo.add_text('Title', title) + pnginfo.add_text('Description', title) + pnginfo.add_text('Author', author) + pnginfo.add_text('Software', author) + Image.fromarray(pixels).save(png_bytes, format='png', pnginfo=pnginfo) + return png_bytes.getvalue() + + def _repr_html_(self): + """Generate an HTML representation of the MultivarColormap.""" + return ''.join([c._repr_html_() for c in self._colormaps]) + + +class BivarColormap: + """ + Base class for all bivariate to RGBA mappings. + + Designed as a drop-in replacement for Colormap when using a 2D + lookup table. To be used with `~matplotlib.cm.ScalarMappable`. + """ + + def __init__(self, N=256, M=256, shape='square', origin=(0, 0), + name='bivariate colormap'): + """ + Parameters + ---------- + N : int, default: 256 + The number of RGB quantization levels along the first axis. + M : int, default: 256 + The number of RGB quantization levels along the second axis. + shape : {'square', 'circle', 'ignore', 'circleignore'} + + - 'square' each variate is clipped to [0,1] independently + - 'circle' the variates are clipped radially to the center + of the colormap, and a circular mask is applied when the colormap + is displayed + - 'ignore' the variates are not clipped, but instead assigned the + 'outside' color + - 'circleignore' a circular mask is applied, but the data is not + clipped and instead assigned the 'outside' color + + origin : (float, float), default: (0,0) + The relative origin of the colormap. Typically (0, 0), for colormaps + that are linear on both axis, and (.5, .5) for circular colormaps. + Used when getting 1D colormaps from 2D colormaps. + name : str, optional + The name of the colormap. + """ + + self.name = name + self.N = int(N) # ensure that N is always int + self.M = int(M) + _api.check_in_list(['square', 'circle', 'ignore', 'circleignore'], shape=shape) + self._shape = shape + self._rgba_bad = (0.0, 0.0, 0.0, 0.0) # If bad, don't paint anything. + self._rgba_outside = (1.0, 0.0, 1.0, 1.0) + self._isinit = False + self.n_variates = 2 + self._origin = (float(origin[0]), float(origin[1])) + '''#: When this colormap exists on a scalar mappable and colorbar_extend + #: is not False, colorbar creation will pick up ``colorbar_extend`` as + #: the default value for the ``extend`` keyword in the + #: `matplotlib.colorbar.Colorbar` constructor. + self.colorbar_extend = False''' + + def __call__(self, X, alpha=None, bytes=False): + r""" + Parameters + ---------- + X : tuple (X0, X1), X0 and X1: float or int or array-like + The data value(s) to convert to RGBA. + + - For floats, *X* should be in the interval ``[0.0, 1.0]`` to + return the RGBA values ``X*100`` percent along the Colormap. + - For integers, *X* should be in the interval ``[0, Colormap.N)`` to + return RGBA values *indexed* from the Colormap with index ``X``. + + alpha : float or array-like or None, default: None + Alpha must be a scalar between 0 and 1, a sequence of such + floats with shape matching X0, or None. + bytes : bool, default: False + If False (default), the returned RGBA values will be floats in the + interval ``[0, 1]`` otherwise they will be `numpy.uint8`\s in the + interval ``[0, 255]``. + + Returns + ------- + Tuple of RGBA values if X is scalar, otherwise an array of + RGBA values with a shape of ``X.shape + (4, )``. + """ + + if len(X) != 2: + raise ValueError( + f'For a `BivarColormap` the data must have a first dimension ' + f'2, not {len(X)}') + + if not self._isinit: + self._init() + + X0 = np.ma.array(X[0], copy=True) + X1 = np.ma.array(X[1], copy=True) + # clip to shape of colormap, circle square, etc. + self._clip((X0, X1)) + + # Native byteorder is faster. + if not X0.dtype.isnative: + X0 = X0.byteswap().view(X0.dtype.newbyteorder()) + if not X1.dtype.isnative: + X1 = X1.byteswap().view(X1.dtype.newbyteorder()) + + if X0.dtype.kind == "f": + X0 *= self.N + # xa == 1 (== N after multiplication) is not out of range. + X0[X0 == self.N] = self.N - 1 + + if X1.dtype.kind == "f": + X1 *= self.M + # xa == 1 (== N after multiplication) is not out of range. + X1[X1 == self.M] = self.M - 1 + + # Pre-compute the masks before casting to int (which can truncate) + mask_outside = (X0 < 0) | (X1 < 0) | (X0 >= self.N) | (X1 >= self.M) + # If input was masked, get the bad mask from it; else mask out nans. + mask_bad_0 = X0.mask if np.ma.is_masked(X0) else np.isnan(X0) + mask_bad_1 = X1.mask if np.ma.is_masked(X1) else np.isnan(X1) + mask_bad = mask_bad_0 | mask_bad_1 + + with np.errstate(invalid="ignore"): + # We need this cast for unsigned ints as well as floats + X0 = X0.astype(int) + X1 = X1.astype(int) + + # Set masked values to zero + # The corresponding rgb values will be replaced later + for X_part in [X0, X1]: + X_part[mask_outside] = 0 + X_part[mask_bad] = 0 + + rgba = self._lut[X0, X1] + if np.isscalar(X[0]): + rgba = np.copy(rgba) + rgba[mask_outside] = self._rgba_outside + rgba[mask_bad] = self._rgba_bad + if bytes: + rgba = (rgba * 255).astype(np.uint8) + if alpha is not None: + alpha = np.clip(alpha, 0, 1) + if bytes: + alpha *= 255 # Will be cast to uint8 upon assignment. + if np.shape(alpha) not in [(), np.shape(X0)]: + raise ValueError( + f"alpha is array-like but its shape {np.shape(alpha)} does " + f"not match that of X[0] {np.shape(X0)}") + rgba[..., -1] = alpha + # If the "bad" color is all zeros, then ignore alpha input. + if (np.array(self._rgba_bad) == 0).all(): + rgba[mask_bad] = (0, 0, 0, 0) + + if not np.iterable(X[0]): + rgba = tuple(rgba) + return rgba + + @property + def lut(self): + """ + For external access to the lut, i.e. for displaying the cmap. + For circular colormaps this returns a lut with a circular mask. + + Internal functions (such as to_rgb()) should use _lut + which stores the lut without a circular mask + A lut without the circular mask is needed in to_rgb() because the + conversion from floats to ints results in some some pixel-requests + just outside of the circular mask + + """ + if not self._isinit: + self._init() + lut = np.copy(self._lut) + if self.shape == 'circle' or self.shape == 'circleignore': + n = np.linspace(-1, 1, self.N) + m = np.linspace(-1, 1, self.M) + radii_sqr = (n**2)[:, np.newaxis] + (m**2)[np.newaxis, :] + mask_outside = radii_sqr > 1 + lut[mask_outside, 3] = 0 + return lut + + def __copy__(self): + cls = self.__class__ + cmapobject = cls.__new__(cls) + cmapobject.__dict__.update(self.__dict__) + + cmapobject._rgba_outside = np.copy(self._rgba_outside) + cmapobject._rgba_bad = np.copy(self._rgba_bad) + cmapobject._shape = self.shape + if self._isinit: + cmapobject._lut = np.copy(self._lut) + return cmapobject + + def __eq__(self, other): + if not isinstance(other, BivarColormap): + return False + # To compare lookup tables the Colormaps have to be initialized + if not self._isinit: + self._init() + if not other._isinit: + other._init() + if not np.array_equal(self._lut, other._lut): + return False + if not np.array_equal(self._rgba_bad, other._rgba_bad): + return False + if not np.array_equal(self._rgba_outside, other._rgba_outside): + return False + if self.shape != other.shape: + return False + return True + + def get_bad(self): + """Get the color for masked values.""" + return self._rgba_bad + + def get_outside(self): + """Get the color for out-of-range values.""" + return self._rgba_outside + + def resampled(self, lutshape, transposed=False): + """ + Return a new colormap with *lutshape* entries. + + Note that this function does not move the origin. + + Parameters + ---------- + lutshape : tuple of ints or None + The tuple must be of length 2, and each entry is either an int or None. + + - If an int, the corresponding axis is resampled. + - If negative the corresponding axis is resampled in reverse + - If -1, the axis is inverted + - If 1 or None, the corresponding axis is not resampled. + + transposed : bool, default: False + if True, the axes are swapped after resampling + + Returns + ------- + BivarColormap + """ + + if not np.iterable(lutshape) or len(lutshape) != 2: + raise ValueError("lutshape must be of length 2") + lutshape = [lutshape[0], lutshape[1]] + if lutshape[0] is None or lutshape[0] == 1: + lutshape[0] = self.N + if lutshape[1] is None or lutshape[1] == 1: + lutshape[1] = self.M + + inverted = [False, False] + if lutshape[0] < 0: + inverted[0] = True + lutshape[0] = -lutshape[0] + if lutshape[0] == 1: + lutshape[0] = self.N + if lutshape[1] < 0: + inverted[1] = True + lutshape[1] = -lutshape[1] + if lutshape[1] == 1: + lutshape[1] = self.M + x_0, x_1 = np.mgrid[0:1:(lutshape[0] * 1j), 0:1:(lutshape[1] * 1j)] + if inverted[0]: + x_0 = x_0[::-1, :] + if inverted[1]: + x_1 = x_1[:, ::-1] + + # we need to use shape = 'square' while resampling the colormap. + # if the colormap has shape = 'circle' we would otherwise get *outside* in the + # resampled colormap + shape_memory = self._shape + self._shape = 'square' + if transposed: + new_lut = self((x_1, x_0)) + new_cmap = BivarColormapFromImage(new_lut, name=self.name, + shape=shape_memory, + origin=self.origin[::-1]) + else: + new_lut = self((x_0, x_1)) + new_cmap = BivarColormapFromImage(new_lut, name=self.name, + shape=shape_memory, + origin=self.origin) + self._shape = shape_memory + + new_cmap._rgba_bad = self._rgba_bad + new_cmap._rgba_outside = self._rgba_outside + return new_cmap + + def reversed(self, axis_0=True, axis_1=True): + """ + Reverses both or one of the axis. + """ + r_0 = -1 if axis_0 else 1 + r_1 = -1 if axis_1 else 1 + return self.resampled((r_0, r_1)) + + def transposed(self): + """ + Transposes the colormap by swapping the order of the axis + """ + return self.resampled((None, None), transposed=True) + + def with_extremes(self, *, bad=None, outside=None, shape=None, origin=None): + """ + Return a copy of the `BivarColormap` with modified attributes. + + Note that the *outside* color is only relevant if `shape` = 'ignore' + or 'circleignore'. + + Parameters + ---------- + bad : :mpltype:`color`, optional + If given, the *bad* value is set accordingly in the copy. + + outside : :mpltype:`color`, optional + If given and shape is 'ignore' or 'circleignore', values + *outside* the colormap are colored accordingly in the copy. + + shape : {'square', 'circle', 'ignore', 'circleignore'} + + - If 'square' each variate is clipped to [0,1] independently + - If 'circle' the variates are clipped radially to the center + of the colormap, and a circular mask is applied when the colormap + is displayed + - If 'ignore' the variates are not clipped, but instead assigned the + *outside* color + - If 'circleignore' a circular mask is applied, but the data is not + clipped and instead assigned the *outside* color + + origin : (float, float) + The relative origin of the colormap. Typically (0, 0), for colormaps + that are linear on both axis, and (.5, .5) for circular colormaps. + Used when getting 1D colormaps from 2D colormaps. + + Returns + ------- + BivarColormap + copy of self with attributes set + """ + new_cm = self.copy() + if bad is not None: + new_cm._rgba_bad = to_rgba(bad) + if outside is not None: + new_cm._rgba_outside = to_rgba(outside) + if shape is not None: + _api.check_in_list(['square', 'circle', 'ignore', 'circleignore'], + shape=shape) + new_cm._shape = shape + if origin is not None: + new_cm._origin = (float(origin[0]), float(origin[1])) + + return new_cm + + def _init(self): + """Generate the lookup table, ``self._lut``.""" + raise NotImplementedError("Abstract class only") + + @property + def shape(self): + return self._shape + + @property + def origin(self): + return self._origin + + def _clip(self, X): + """ + For internal use when applying a BivarColormap to data. + i.e. cm.ScalarMappable().to_rgba() + Clips X[0] and X[1] according to 'self.shape'. + X is modified in-place. + + Parameters + ---------- + X: np.array + array of floats or ints to be clipped + shape : {'square', 'circle', 'ignore', 'circleignore'} + + - If 'square' each variate is clipped to [0,1] independently + - If 'circle' the variates are clipped radially to the center + of the colormap. + It is assumed that a circular mask is applied when the colormap + is displayed + - If 'ignore' the variates are not clipped, but instead assigned the + 'outside' color + - If 'circleignore' a circular mask is applied, but the data is not clipped + and instead assigned the 'outside' color + + """ + if self.shape == 'square': + for X_part, mx in zip(X, (self.N, self.M)): + X_part[X_part < 0] = 0 + if X_part.dtype.kind == "f": + X_part[X_part > 1] = 1 + else: + X_part[X_part >= mx] = mx - 1 + + elif self.shape == 'ignore': + for X_part, mx in zip(X, (self.N, self.M)): + X_part[X_part < 0] = -1 + if X_part.dtype.kind == "f": + X_part[X_part > 1] = -1 + else: + X_part[X_part >= mx] = -1 + + elif self.shape == 'circle' or self.shape == 'circleignore': + for X_part in X: + if X_part.dtype.kind != "f": + raise NotImplementedError( + "Circular bivariate colormaps are only" + " implemented for use with with floats") + radii_sqr = (X[0] - 0.5)**2 + (X[1] - 0.5)**2 + mask_outside = radii_sqr > 0.25 + if self.shape == 'circle': + overextend = 2 * np.sqrt(radii_sqr[mask_outside]) + X[0][mask_outside] = (X[0][mask_outside] - 0.5) / overextend + 0.5 + X[1][mask_outside] = (X[1][mask_outside] - 0.5) / overextend + 0.5 + else: + X[0][mask_outside] = -1 + X[1][mask_outside] = -1 + + def __getitem__(self, item): + """Creates and returns a colorbar along the selected axis""" + if not self._isinit: + self._init() + extremes = ( + dict(bad=self._rgba_bad, over=self._rgba_outside, under=self._rgba_outside) + if self.shape in ['ignore', 'circleignore'] + else dict(bad=self._rgba_bad) + ) + if item == 0: + origin_1_as_int = int(self._origin[1]*self.M) + if origin_1_as_int > self.M-1: + origin_1_as_int = self.M-1 + one_d_lut = self._lut[:, origin_1_as_int] + new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_0', **extremes) + + elif item == 1: + origin_0_as_int = int(self._origin[0]*self.N) + if origin_0_as_int > self.N-1: + origin_0_as_int = self.N-1 + one_d_lut = self._lut[origin_0_as_int, :] + new_cmap = ListedColormap(one_d_lut, name=f'{self.name}_1', **extremes) + else: + raise KeyError(f"only 0 or 1 are" + f" valid keys for BivarColormap, not {item!r}") + return new_cmap + + def _repr_png_(self): + """Generate a PNG representation of the BivarColormap.""" + if not self._isinit: + self._init() + pixels = self.lut + if pixels.shape[0] < _BIVAR_REPR_PNG_SIZE: + pixels = np.repeat(pixels, + repeats=_BIVAR_REPR_PNG_SIZE//pixels.shape[0], + axis=0)[:256, :] + if pixels.shape[1] < _BIVAR_REPR_PNG_SIZE: + pixels = np.repeat(pixels, + repeats=_BIVAR_REPR_PNG_SIZE//pixels.shape[1], + axis=1)[:, :256] + pixels = (pixels[::-1, :, :] * 255).astype(np.uint8) + png_bytes = io.BytesIO() + title = self.name + ' BivarColormap' + author = f'Matplotlib v{mpl.__version__}, https://matplotlib.org' + pnginfo = PngInfo() + pnginfo.add_text('Title', title) + pnginfo.add_text('Description', title) + pnginfo.add_text('Author', author) + pnginfo.add_text('Software', author) + Image.fromarray(pixels).save(png_bytes, format='png', pnginfo=pnginfo) + return png_bytes.getvalue() + + def _repr_html_(self): + """Generate an HTML representation of the Colormap.""" + png_bytes = self._repr_png_() + png_base64 = base64.b64encode(png_bytes).decode('ascii') + def color_block(color): + hex_color = to_hex(color, keep_alpha=True) + return (f'
') + + return ('
' + f'{self.name} ' + '
' + '
' + '
' + '
' + f'{color_block(self.get_outside())} outside' + '
' + '
' + f'bad {color_block(self.get_bad())}' + '
') + + def copy(self): + """Return a copy of the colormap.""" + return self.__copy__() + + +class SegmentedBivarColormap(BivarColormap): + """ + BivarColormap object generated by supersampling a regular grid. + + Parameters + ---------- + patch : np.array + Patch is required to have a shape (k, l, 3), and will get supersampled + to a lut of shape (N, N, 4). + N : int + The number of RGB quantization levels along each axis. + shape : {'square', 'circle', 'ignore', 'circleignore'} + + - If 'square' each variate is clipped to [0,1] independently + - If 'circle' the variates are clipped radially to the center + of the colormap, and a circular mask is applied when the colormap + is displayed + - If 'ignore' the variates are not clipped, but instead assigned the + 'outside' color + - If 'circleignore' a circular mask is applied, but the data is not clipped + + origin : (float, float) + The relative origin of the colormap. Typically (0, 0), for colormaps + that are linear on both axis, and (.5, .5) for circular colormaps. + Used when getting 1D colormaps from 2D colormaps. + + name : str, optional + The name of the colormap. + """ + + def __init__(self, patch, N=256, shape='square', origin=(0, 0), + name='segmented bivariate colormap'): + _api.check_shape((None, None, 3), patch=patch) + self.patch = patch + super().__init__(N, N, shape, origin, name=name) + + def _init(self): + # Perform bilinear interpolation + + s = self.patch.shape + + # Indices (whole and fraction) of the new grid points + row = np.linspace(0, s[0] - 1, self.N)[:, np.newaxis] + col = np.linspace(0, s[1] - 1, self.N)[np.newaxis, :] + left = row.astype(int) # floor not needed because all values are nonnegative + top = col.astype(int) # floor not needed because all values are nonnegative + row_frac = (row - left)[:, :, np.newaxis] + col_frac = (col - top)[:, :, np.newaxis] + + # Indices of the next edges, clipping where needed + right = np.clip(left + 1, 0, s[0] - 1) + bottom = np.clip(top + 1, 0, s[1] - 1) + + # Values at the corners + tl = self.patch[left, top, :] + tr = self.patch[right, top, :] + bl = self.patch[left, bottom, :] + br = self.patch[right, bottom, :] + + # Interpolate between the corners + lut = (tl * (1 - row_frac) * (1 - col_frac) + + tr * row_frac * (1 - col_frac) + + bl * (1 - row_frac) * col_frac + + br * row_frac * col_frac) + + # Add the alpha channel + self._lut = np.concatenate([lut, np.ones((self.N, self.N, 1))], axis=2) + + self._isinit = True + + +class BivarColormapFromImage(BivarColormap): + """ + BivarColormap object generated by supersampling a regular grid. + + Parameters + ---------- + lut : nparray of shape (N, M, 3) or (N, M, 4) + The look-up-table + shape: {'square', 'circle', 'ignore', 'circleignore'} + + - If 'square' each variate is clipped to [0,1] independently + - If 'circle' the variates are clipped radially to the center + of the colormap, and a circular mask is applied when the colormap + is displayed + - If 'ignore' the variates are not clipped, but instead assigned the + 'outside' color + - If 'circleignore' a circular mask is applied, but the data is not clipped + + origin: (float, float) + The relative origin of the colormap. Typically (0, 0), for colormaps + that are linear on both axis, and (.5, .5) for circular colormaps. + Used when getting 1D colormaps from 2D colormaps. + name : str, optional + The name of the colormap. + + """ + + def __init__(self, lut, shape='square', origin=(0, 0), name='from image'): + # We can allow for a PIL.Image as input in the following way, but importing + # matplotlib.image.pil_to_array() results in a circular import + # For now, this function only accepts numpy arrays. + # i.e.: + # if isinstance(Image, lut): + # lut = image.pil_to_array(lut) + lut = np.array(lut, copy=True) + if lut.ndim != 3 or lut.shape[2] not in (3, 4): + raise ValueError("The lut must be an array of shape (n, m, 3) or (n, m, 4)", + " or a PIL.image encoded as RGB or RGBA") + + if lut.dtype == np.uint8: + lut = lut.astype(np.float32)/255 + if lut.shape[2] == 3: + new_lut = np.empty((lut.shape[0], lut.shape[1], 4), dtype=lut.dtype) + new_lut[:, :, :3] = lut + new_lut[:, :, 3] = 1. + lut = new_lut + self._lut = lut + super().__init__(lut.shape[0], lut.shape[1], shape, origin, name=name) + + def _init(self): + self._isinit = True + + +class Norm(ABC): + """ + Abstract base class for normalizations. + + Subclasses include `Normalize` which maps from a scalar to + a scalar. However, this class makes no such requirement, and subclasses may + support the normalization of multiple variates simultaneously, with + separate normalization for each variate. + """ + + def __init__(self): + self.callbacks = cbook.CallbackRegistry(signals=["changed"]) - def _init(self): - self._lut = np.zeros((self.N + 3, 4), float) - self._lut[:-3] = to_rgba_array(self.colors) - self._isinit = True - self._set_extremes() + @property + @abstractmethod + def vmin(self): + """Lower limit of the input data interval; maps to 0.""" + pass - def resampled(self, lutsize): - """Return a new colormap with *lutsize* entries.""" - colors = self(np.linspace(0, 1, lutsize)) - new_cmap = ListedColormap(colors, name=self.name) - # Keep the over/under values too - new_cmap._rgba_over = self._rgba_over - new_cmap._rgba_under = self._rgba_under - new_cmap._rgba_bad = self._rgba_bad - return new_cmap + @property + @abstractmethod + def vmax(self): + """Upper limit of the input data interval; maps to 1.""" + pass - def reversed(self, name=None): + @property + @abstractmethod + def clip(self): """ - Return a reversed instance of the Colormap. + Determines the behavior for mapping values outside the range ``[vmin, vmax]``. + + See the *clip* parameter in `.Normalize`. + """ + pass + + @abstractmethod + def __call__(self, value, clip=None): + """ + Normalize the data and return the normalized data. Parameters ---------- - name : str, optional - The name for the reversed colormap. If None, the - name is set to ``self.name + "_r"``. + value + Data to normalize. + clip : bool, optional + See the description of the parameter *clip* in `.Normalize`. - Returns - ------- - ListedColormap - A reversed instance of the colormap. + If ``None``, defaults to ``self.clip`` (which defaults to + ``False``). + + Notes + ----- + If not already initialized, ``self.vmin`` and ``self.vmax`` are + initialized using ``self.autoscale_None(value)``. """ - if name is None: - name = self.name + "_r" + pass - colors_r = list(reversed(self.colors)) - new_cmap = ListedColormap(colors_r, name=name, N=self.N) - # Reverse the over/under values too - new_cmap._rgba_over = self._rgba_under - new_cmap._rgba_under = self._rgba_over - new_cmap._rgba_bad = self._rgba_bad - return new_cmap + @abstractmethod + def autoscale(self, A): + """Set *vmin*, *vmax* to min, max of *A*.""" + pass + + @abstractmethod + def autoscale_None(self, A): + """If *vmin* or *vmax* are not set, use the min/max of *A* to set them.""" + pass + + @abstractmethod + def scaled(self): + """Return whether *vmin* and *vmax* are both set.""" + pass + + def _changed(self): + """ + Call this whenever the norm is changed to notify all the + callback listeners to the 'changed' signal. + """ + self.callbacks.process('changed') + + @property + @abstractmethod + def n_components(self): + """ + The number of normalized components. + This is the number of elements of the parameter to ``__call__`` and of + *vmin*, *vmax*. + """ + pass -class Normalize: + +class Normalize(Norm): """ A class which, when called, maps values within the interval ``[vmin, vmax]`` linearly to the interval ``[0.0, 1.0]``. The mapping of @@ -1275,14 +2442,15 @@ def __init__(self, vmin=None, vmax=None, clip=False): ----- If ``vmin == vmax``, input data will be mapped to 0. """ + super().__init__() self._vmin = _sanitize_extrema(vmin) self._vmax = _sanitize_extrema(vmax) self._clip = clip self._scale = None - self.callbacks = cbook.CallbackRegistry(signals=["changed"]) @property def vmin(self): + # docstring inherited return self._vmin @vmin.setter @@ -1294,6 +2462,7 @@ def vmin(self, value): @property def vmax(self): + # docstring inherited return self._vmax @vmax.setter @@ -1305,6 +2474,7 @@ def vmax(self, value): @property def clip(self): + # docstring inherited return self._clip @clip.setter @@ -1313,13 +2483,6 @@ def clip(self, value): self._clip = value self._changed() - def _changed(self): - """ - Call this whenever the norm is changed to notify all the - callback listeners to the 'changed' signal. - """ - self.callbacks.process('changed') - @staticmethod def process_value(value): """ @@ -1361,24 +2524,7 @@ def process_value(value): return result, is_scalar def __call__(self, value, clip=None): - """ - Normalize the data and return the normalized data. - - Parameters - ---------- - value - Data to normalize. - clip : bool, optional - See the description of the parameter *clip* in `.Normalize`. - - If ``None``, defaults to ``self.clip`` (which defaults to - ``False``). - - Notes - ----- - If not already initialized, ``self.vmin`` and ``self.vmax`` are - initialized using ``self.autoscale_None(value)``. - """ + # docstring inherited if clip is None: clip = self.clip @@ -1429,7 +2575,7 @@ def inverse(self, value): return vmin + value * (vmax - vmin) def autoscale(self, A): - """Set *vmin*, *vmax* to min, max of *A*.""" + # docstring inherited with self.callbacks.blocked(): # Pause callbacks while we are updating so we only get # a single update signal at the end @@ -1438,7 +2584,7 @@ def autoscale(self, A): self._changed() def autoscale_None(self, A): - """If *vmin* or *vmax* are not set, use the min/max of *A* to set them.""" + # docstring inherited A = np.asanyarray(A) if isinstance(A, np.ma.MaskedArray): @@ -1452,9 +2598,22 @@ def autoscale_None(self, A): self.vmax = A.max() def scaled(self): - """Return whether *vmin* and *vmax* are both set.""" + # docstring inherited return self.vmin is not None and self.vmax is not None + @property + def n_components(self): + """ + The number of distinct components supported (1). + + This is the number of elements of the parameter to ``__call__`` and of + *vmin*, *vmax*. + + This class support only a single component, as opposed to `MultiNorm` + which supports multiple components. + """ + return 1 + class TwoSlopeNorm(Normalize): def __init__(self, vcenter, vmin=None, vmax=None): @@ -1736,7 +2895,7 @@ def _make_norm_from_scale( unlike to arbitrary lambdas. """ - class Norm(base_norm_cls): + class ScaleNorm(base_norm_cls): def __reduce__(self): cls = type(self) # If the class is toplevel-accessible, it is possible to directly @@ -1816,15 +2975,15 @@ def autoscale_None(self, A): return super().autoscale_None(in_trf_domain) if base_norm_cls is Normalize: - Norm.__name__ = f"{scale_cls.__name__}Norm" - Norm.__qualname__ = f"{scale_cls.__qualname__}Norm" + ScaleNorm.__name__ = f"{scale_cls.__name__}Norm" + ScaleNorm.__qualname__ = f"{scale_cls.__qualname__}Norm" else: - Norm.__name__ = base_norm_cls.__name__ - Norm.__qualname__ = base_norm_cls.__qualname__ - Norm.__module__ = base_norm_cls.__module__ - Norm.__doc__ = base_norm_cls.__doc__ + ScaleNorm.__name__ = base_norm_cls.__name__ + ScaleNorm.__qualname__ = base_norm_cls.__qualname__ + ScaleNorm.__module__ = base_norm_cls.__module__ + ScaleNorm.__doc__ = base_norm_cls.__doc__ - return Norm + return ScaleNorm def _create_empty_object_of_class(cls): @@ -2180,6 +3339,300 @@ def inverse(self, value): return value +class MultiNorm(Norm): + """ + A class which contains multiple scalar norms. + """ + + def __init__(self, norms, vmin=None, vmax=None, clip=None): + """ + Parameters + ---------- + norms : list of (str or `Normalize`) + The constituent norms. The list must have a minimum length of 1. + vmin, vmax : None or list of (float or None) + Limits of the constituent norms. + If a list, one value is assigned to each of the constituent + norms. + If None, the limits of the constituent norms + are not changed. + clip : None or list of bools, default: None + Determines the behavior for mapping values outside the range + ``[vmin, vmax]`` for the constituent norms. + If a list, each value is assigned to each of the constituent + norms. + If None, the behaviour of the constituent norms is not changed. + """ + if cbook.is_scalar_or_string(norms): + raise ValueError( + "MultiNorm must be assigned an iterable of norms, where each " + f"norm is of type `str`, or `Normalize`, not {type(norms)}") + + if len(norms) < 1: + raise ValueError("MultiNorm must be assigned at least one norm") + + def resolve(norm): + if isinstance(norm, str): + scale_cls = _api.getitem_checked(scale._scale_mapping, norm=norm) + return mpl.colorizer._auto_norm_from_scale(scale_cls)() + elif isinstance(norm, Normalize): + return norm + else: + raise ValueError( + "Each norm assigned to MultiNorm must be " + f"of type `str`, or `Normalize`, not {type(norm)}") + + self._norms = tuple(resolve(norm) for norm in norms) + + self.callbacks = cbook.CallbackRegistry(signals=["changed"]) + + self.vmin = vmin + self.vmax = vmax + self.clip = clip + + for n in self._norms: + n.callbacks.connect('changed', self._changed) + + @property + def n_components(self): + """Number of norms held by this `MultiNorm`.""" + return len(self._norms) + + @property + def norms(self): + """The individual norms held by this `MultiNorm`.""" + return self._norms + + @property + def vmin(self): + """The lower limit of each constituent norm.""" + return tuple(n.vmin for n in self._norms) + + @vmin.setter + def vmin(self, values): + if values is None: + return + if not np.iterable(values) or len(values) != self.n_components: + raise ValueError("*vmin* must have one component for each norm. " + f"Expected an iterable of length {self.n_components}, " + f"but got {values!r}") + with self.callbacks.blocked(): + for norm, v in zip(self.norms, values): + norm.vmin = v + self._changed() + + @property + def vmax(self): + """The upper limit of each constituent norm.""" + return tuple(n.vmax for n in self._norms) + + @vmax.setter + def vmax(self, values): + if values is None: + return + if not np.iterable(values) or len(values) != self.n_components: + raise ValueError("*vmax* must have one component for each norm. " + f"Expected an iterable of length {self.n_components}, " + f"but got {values!r}") + with self.callbacks.blocked(): + for norm, v in zip(self.norms, values): + norm.vmax = v + self._changed() + + @property + def clip(self): + """The clip behaviour of each constituent norm.""" + return tuple(n.clip for n in self._norms) + + @clip.setter + def clip(self, values): + if values is None: + return + if not np.iterable(values) or len(values) != self.n_components: + raise ValueError("*clip* must have one component for each norm. " + f"Expected an iterable of length {self.n_components}, " + f"but got {values!r}") + with self.callbacks.blocked(): + for norm, v in zip(self.norms, values): + norm.clip = v + self._changed() + + def _changed(self): + """ + Call this whenever the norm is changed to notify all the + callback listeners to the 'changed' signal. + """ + self.callbacks.process('changed') + + def __call__(self, values, clip=None): + """ + Normalize the data and return the normalized data. + + Each component of the input is normalized via the constituent norm. + + Parameters + ---------- + values : array-like + The input data, as an iterable or a structured numpy array. + + - If iterable, must be of length `n_components`. Each element can be a + scalar or array-like and is normalized through the corresponding norm. + - If structured array, must have `n_components` fields. Each field + is normalized through the corresponding norm. + + clip : list of bools or None, optional + Determines the behavior for mapping values outside the range + ``[vmin, vmax]``. See the description of the parameter *clip* in + `.Normalize`. + If ``None``, defaults to ``self.clip`` (which defaults to + ``False``). + + Returns + ------- + tuple + Normalized input values + + Notes + ----- + If not already initialized, ``self.vmin`` and ``self.vmax`` are + initialized using ``self.autoscale_None(values)``. + """ + if clip is None: + clip = self.clip + if not np.iterable(clip) or len(clip) != self.n_components: + raise ValueError("*clip* must have one component for each norm. " + f"Expected an iterable of length {self.n_components}, " + f"but got {clip!r}") + + values = self._iterable_components_in_data(values, self.n_components) + result = tuple(n(v, clip=c) for n, v, c in zip(self.norms, values, clip)) + return result + + def inverse(self, values): + """ + Map the normalized values (i.e., index in the colormap) back to data values. + + Parameters + ---------- + values : array-like + The input data, as an iterable or a structured numpy array. + + - If iterable, must be of length `n_components`. Each element can be a + scalar or array-like and is mapped through the corresponding norm. + - If structured array, must have `n_components` fields. Each field + is mapped through the the corresponding norm. + + """ + values = self._iterable_components_in_data(values, self.n_components) + result = tuple(n.inverse(v) for n, v in zip(self.norms, values)) + return result + + def autoscale(self, A): + """ + For each constituent norm, set *vmin*, *vmax* to min, max of the corresponding + component in *A*. + + Parameters + ---------- + A : array-like + The input data, as an iterable or a structured numpy array. + + - If iterable, must be of length `n_components`. Each element + is used for the limits of one constituent norm. + - If structured array, must have `n_components` fields. Each field + is used for the limits of one constituent norm. + """ + with self.callbacks.blocked(): + A = self._iterable_components_in_data(A, self.n_components) + for n, a in zip(self.norms, A): + n.autoscale(a) + self._changed() + + def autoscale_None(self, A): + """ + If *vmin* or *vmax* are not set on any constituent norm, + use the min/max of the corresponding component in *A* to set them. + + Parameters + ---------- + A : array-like + The input data, as an iterable or a structured numpy array. + + - If iterable, must be of length `n_components`. Each element + is used for the limits of one constituent norm. + - If structured array, must have `n_components` fields. Each field + is used for the limits of one constituent norm. + """ + with self.callbacks.blocked(): + A = self._iterable_components_in_data(A, self.n_components) + for n, a in zip(self.norms, A): + n.autoscale_None(a) + self._changed() + + def scaled(self): + """Return whether both *vmin* and *vmax* are set on all constituent norms.""" + return all(n.scaled() for n in self.norms) + + @staticmethod + def _iterable_components_in_data(data, n_components): + """ + Provides an iterable over the components contained in the data. + + An input array with `n_components` fields is returned as a tuple of length n + referencing slices of the original array. + + Parameters + ---------- + data : array-like + The input data, as an iterable or a structured numpy array. + + - If iterable, must be of length `n_components` + - If structured array, must have `n_components` fields. + + Returns + ------- + tuple of np.ndarray + + """ + if isinstance(data, np.ndarray) and data.dtype.fields is not None: + # structured array + if len(data.dtype.fields) != n_components: + raise ValueError( + "Structured array inputs to MultiNorm must have the same " + "number of fields as components in the MultiNorm. Expected " + f"{n_components}, but got {len(data.dtype.fields)} fields" + ) + else: + return tuple(data[field] for field in data.dtype.names) + try: + n_elements = len(data) + except TypeError: + raise ValueError("MultiNorm expects a sequence with one element per " + f"component as input, but got {data!r} instead") + if n_elements != n_components: + if isinstance(data, np.ndarray) and data.shape[-1] == n_components: + if len(data.shape) == 2: + raise ValueError( + f"MultiNorm expects a sequence with one element per component. " + "You can use `data_transposed = data.T` " + "to convert the input data of shape " + f"{data.shape} to a compatible shape {data.shape[::-1]}") + else: + raise ValueError( + f"MultiNorm expects a sequence with one element per component. " + "You can use `data_as_list = [data[..., i] for i in " + "range(data.shape[-1])]` to convert the input data of shape " + f" {data.shape} to a compatible list") + + raise ValueError( + "MultiNorm expects a sequence with one element per component. " + f"This MultiNorm has {n_components} components, but got a sequence " + f"with {n_elements} elements" + ) + + return tuple(data[i] for i in range(n_elements)) + + def rgb_to_hsv(arr): """ Convert an array of float RGB values (in the range [0, 1]) to HSV values. @@ -2202,13 +3655,26 @@ def rgb_to_hsv(arr): f"shape {arr.shape} was found.") in_shape = arr.shape - arr = np.array( - arr, copy=False, - dtype=np.promote_types(arr.dtype, np.float32), # Don't work on ints. - ndmin=2, # In case input was 1D. - ) + # ensure numerics are done at least on float32; ints are cast as well + arr = np.asarray(arr, dtype=np.promote_types(arr.dtype, np.float32)) + if arr.ndim == 1: + arr = np.expand_dims(arr, axis=0) # ensure arr is 2D + out = np.zeros_like(arr) arr_max = arr.max(-1) + # Check if input is in the expected range + if np.any(arr_max > 1): + raise ValueError( + "Input array must be in the range [0, 1]. " + f"Found a maximum value of {arr_max.max()}" + ) + + if arr.min() < 0: + raise ValueError( + "Input array must be in the range [0, 1]. " + f"Found a minimum value of {arr.min()}" + ) + ipos = arr_max > 0 delta = np.ptp(arr, -1) s = np.zeros_like(delta) @@ -2786,23 +4252,18 @@ def from_levels_and_colors(levels, colors, extend='neither'): color_slice = slice_map[extend] n_data_colors = len(levels) - 1 - n_expected = n_data_colors + color_slice.start - (color_slice.stop or 0) + n_extend_colors = color_slice.start - (color_slice.stop or 0) # 0, 1 or 2 + n_expected = n_data_colors + n_extend_colors if len(colors) != n_expected: raise ValueError( - f'With extend == {extend!r} and {len(levels)} levels, ' - f'expected {n_expected} colors, but got {len(colors)}') - - cmap = ListedColormap(colors[color_slice], N=n_data_colors) - - if extend in ['min', 'both']: - cmap.set_under(colors[0]) - else: - cmap.set_under('none') - - if extend in ['max', 'both']: - cmap.set_over(colors[-1]) - else: - cmap.set_over('none') + f'Expected {n_expected} colors ({n_data_colors} colors for {len(levels)} ' + f'levels, and {n_extend_colors} colors for extend == {extend!r}), ' + f'but got {len(colors)}') + + data_colors = colors[color_slice] + under_color = colors[0] if extend in ['min', 'both'] else 'none' + over_color = colors[-1] if extend in ['max', 'both'] else 'none' + cmap = ListedColormap(data_colors, under=under_color, over=over_color) cmap.colorbar_extend = extend diff --git a/lib/matplotlib/colors.pyi b/lib/matplotlib/colors.pyi index 514801b714b8..07bf01b8f995 100644 --- a/lib/matplotlib/colors.pyi +++ b/lib/matplotlib/colors.pyi @@ -1,4 +1,5 @@ from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence +from abc import ABC, abstractmethod from matplotlib import cbook, scale import re @@ -68,7 +69,15 @@ class Colormap: name: str N: int colorbar_extend: bool - def __init__(self, name: str, N: int = ...) -> None: ... + def __init__( + self, + name: str, + N: int = ..., + *, + bad: ColorType | None = ..., + under: ColorType | None = ..., + over: ColorType | None = ... + ) -> None: ... @overload def __call__( self, X: Sequence[float] | np.ndarray, alpha: ArrayLike | None = ..., bytes: bool = ... @@ -103,6 +112,7 @@ class Colormap: under: ColorType | None = ..., over: ColorType | None = ... ) -> Colormap: ... + def with_alpha(self, alpha: float) -> Colormap: ... def is_gray(self) -> bool: ... def resampled(self, lutsize: int) -> Colormap: ... def reversed(self, name: str | None = ...) -> Colormap: ... @@ -120,26 +130,152 @@ class LinearSegmentedColormap(Colormap): ], N: int = ..., gamma: float = ..., + *, + bad: ColorType | None = ..., + under: ColorType | None = ..., + over: ColorType | None = ..., ) -> None: ... def set_gamma(self, gamma: float) -> None: ... @staticmethod def from_list( - name: str, colors: ArrayLike | Sequence[tuple[float, ColorType]], N: int = ..., gamma: float = ... + name: str, colors: ArrayLike | Sequence[tuple[float, ColorType]], N: int = ..., gamma: float = ..., + *, bad: ColorType | None = ..., under: ColorType | None = ..., over: ColorType | None = ..., ) -> LinearSegmentedColormap: ... def resampled(self, lutsize: int) -> LinearSegmentedColormap: ... def reversed(self, name: str | None = ...) -> LinearSegmentedColormap: ... class ListedColormap(Colormap): - monochrome: bool colors: ArrayLike | ColorType def __init__( - self, colors: ArrayLike | ColorType, name: str = ..., N: int | None = ... + self, colors: ArrayLike | ColorType, name: str = ..., N: int | None = ..., + *, bad: ColorType | None = ..., under: ColorType | None = ..., over: ColorType | None = ... ) -> None: ... + @property + def monochrome(self) -> bool: ... def resampled(self, lutsize: int) -> ListedColormap: ... def reversed(self, name: str | None = ...) -> ListedColormap: ... -class Normalize: +class MultivarColormap: + name: str + n_variates: int + def __init__(self, colormaps: list[Colormap], combination_mode: Literal['sRGB_add', 'sRGB_sub'], name: str = ...) -> None: ... + @overload + def __call__( + self, X: Sequence[Sequence[float]] | np.ndarray, alpha: ArrayLike | None = ..., bytes: bool = ..., clip: bool = ... + ) -> np.ndarray: ... + @overload + def __call__( + self, X: Sequence[float], alpha: float | None = ..., bytes: bool = ..., clip: bool = ... + ) -> tuple[float, float, float, float]: ... + @overload + def __call__( + self, X: ArrayLike, alpha: ArrayLike | None = ..., bytes: bool = ..., clip: bool = ... + ) -> tuple[float, float, float, float] | np.ndarray: ... + def copy(self) -> MultivarColormap: ... + def __copy__(self) -> MultivarColormap: ... + def __eq__(self, other: Any) -> bool: ... + def __getitem__(self, item: int) -> Colormap: ... + def __iter__(self) -> Iterator[Colormap]: ... + def __len__(self) -> int: ... + def get_bad(self) -> np.ndarray: ... + def resampled(self, lutshape: Sequence[int | None]) -> MultivarColormap: ... + def with_extremes( + self, + *, + bad: ColorType | None = ..., + under: Sequence[ColorType] | None = ..., + over: Sequence[ColorType] | None = ... + ) -> MultivarColormap: ... + @property + def combination_mode(self) -> str: ... + def _repr_html_(self) -> str: ... + def _repr_png_(self) -> bytes: ... + +class BivarColormap: + name: str + N: int + M: int + n_variates: int + def __init__( + self, N: int = ..., M: int | None = ..., shape: Literal['square', 'circle', 'ignore', 'circleignore'] = ..., + origin: Sequence[float] = ..., name: str = ... + ) -> None: ... + @overload + def __call__( + self, X: Sequence[Sequence[float]] | np.ndarray, alpha: ArrayLike | None = ..., bytes: bool = ... + ) -> np.ndarray: ... + @overload + def __call__( + self, X: Sequence[float], alpha: float | None = ..., bytes: bool = ... + ) -> tuple[float, float, float, float]: ... + @overload + def __call__( + self, X: ArrayLike, alpha: ArrayLike | None = ..., bytes: bool = ... + ) -> tuple[float, float, float, float] | np.ndarray: ... + @property + def lut(self) -> np.ndarray: ... + @property + def shape(self) -> str: ... + @property + def origin(self) -> tuple[float, float]: ... + def copy(self) -> BivarColormap: ... + def __copy__(self) -> BivarColormap: ... + def __getitem__(self, item: int) -> Colormap: ... + def __eq__(self, other: Any) -> bool: ... + def get_bad(self) -> np.ndarray: ... + def get_outside(self) -> np.ndarray: ... + def resampled(self, lutshape: Sequence[int | None], transposed: bool = ...) -> BivarColormap: ... + def transposed(self) -> BivarColormap: ... + def reversed(self, axis_0: bool = ..., axis_1: bool = ...) -> BivarColormap: ... + def with_extremes( + self, + *, + bad: ColorType | None = ..., + outside: ColorType | None = ..., + shape: str | None = ..., + origin: None | Sequence[float] = ..., + ) -> MultivarColormap: ... + def _repr_html_(self) -> str: ... + def _repr_png_(self) -> bytes: ... + +class SegmentedBivarColormap(BivarColormap): + def __init__( + self, patch: np.ndarray, N: int = ..., shape: Literal['square', 'circle', 'ignore', 'circleignore'] = ..., + origin: Sequence[float] = ..., name: str = ... + ) -> None: ... + +class BivarColormapFromImage(BivarColormap): + def __init__( + self, lut: np.ndarray, shape: Literal['square', 'circle', 'ignore', 'circleignore'] = ..., + origin: Sequence[float] = ..., name: str = ... + ) -> None: ... + +class Norm(ABC): callbacks: cbook.CallbackRegistry + def __init__(self) -> None: ... + @property + @abstractmethod + def vmin(self) -> float | tuple[float] | None: ... + @property + @abstractmethod + def vmax(self) -> float | tuple[float] | None: ... + @property + @abstractmethod + def clip(self) -> bool | tuple[bool]: ... + @abstractmethod + def __call__(self, value: np.ndarray, clip: bool | None = ...) -> ArrayLike: ... + @abstractmethod + def autoscale(self, A: ArrayLike) -> None: ... + @abstractmethod + def autoscale_None(self, A: ArrayLike) -> None: ... + @abstractmethod + def scaled(self) -> bool: ... + @abstractmethod + @property + def n_components(self) -> int: ... + + +class Normalize(Norm): def __init__( self, vmin: float | None = ..., vmax: float | None = ..., clip: bool = ... ) -> None: ... @@ -172,6 +308,8 @@ class Normalize: def autoscale(self, A: ArrayLike) -> None: ... def autoscale_None(self, A: ArrayLike) -> None: ... def scaled(self) -> bool: ... + @property + def n_components(self) -> Literal[1]: ... class TwoSlopeNorm(Normalize): def __init__( @@ -276,6 +414,44 @@ class BoundaryNorm(Normalize): class NoNorm(Normalize): ... +class MultiNorm(Norm): + # Here "type: ignore[override]" is used for functions with a return type + # that differs from the function in the base class. + # i.e. where `MultiNorm` returns a tuple and Normalize returns a `float` etc. + def __init__( + self, + norms: ArrayLike, + vmin: ArrayLike | None = ..., + vmax: ArrayLike | None = ..., + clip: ArrayLike | None = ... + ) -> None: ... + @property + def norms(self) -> tuple[Normalize, ...]: ... + @property # type: ignore[override] + def vmin(self) -> tuple[float | None, ...]: ... + @vmin.setter + def vmin(self, values: ArrayLike | None) -> None: ... + @property # type: ignore[override] + def vmax(self) -> tuple[float | None, ...]: ... + @vmax.setter + def vmax(self, valued: ArrayLike | None) -> None: ... + @property # type: ignore[override] + def clip(self) -> tuple[bool, ...]: ... + @clip.setter + def clip(self, values: ArrayLike | None) -> None: ... + @overload + def __call__(self, values: tuple[np.ndarray, ...], clip: ArrayLike | bool | None = ...) -> tuple[np.ndarray, ...]: ... + @overload + def __call__(self, values: tuple[float, ...], clip: ArrayLike | bool | None = ...) -> tuple[float, ...]: ... + @overload + def __call__(self, values: ArrayLike, clip: ArrayLike | bool | None = ...) -> tuple: ... + def inverse(self, values: ArrayLike) -> tuple: ... # type: ignore[override] + def autoscale(self, A: ArrayLike) -> None: ... + def autoscale_None(self, A: ArrayLike) -> None: ... + def scaled(self) -> bool: ... + @property + def n_components(self) -> int: ... + def rgb_to_hsv(arr: ArrayLike) -> np.ndarray: ... def hsv_to_rgb(hsv: ArrayLike) -> np.ndarray: ... diff --git a/lib/matplotlib/container.py b/lib/matplotlib/container.py index 0f082e298afc..96b14cfd26f7 100644 --- a/lib/matplotlib/container.py +++ b/lib/matplotlib/container.py @@ -73,6 +73,48 @@ def __init__(self, patches, errorbar=None, *, datavalues=None, self.orientation = orientation super().__init__(patches, **kwargs) + @property + def bottoms(self): + """ + Return the values at the lower end of the bars. + + .. versionadded:: 3.11 + """ + if self.orientation == 'vertical': + return [p.get_y() for p in self.patches] + elif self.orientation == 'horizontal': + return [p.get_x() for p in self.patches] + else: + raise ValueError("orientation must be 'vertical' or 'horizontal'.") + + @property + def tops(self): + """ + Return the values at the upper end of the bars. + + .. versionadded:: 3.11 + """ + if self.orientation == 'vertical': + return [p.get_y() + p.get_height() for p in self.patches] + elif self.orientation == 'horizontal': + return [p.get_x() + p.get_width() for p in self.patches] + else: + raise ValueError("orientation must be 'vertical' or 'horizontal'.") + + @property + def position_centers(self): + """ + Return the centers of bar positions. + + .. versionadded:: 3.11 + """ + if self.orientation == 'vertical': + return [p.get_x() + p.get_width() / 2 for p in self.patches] + elif self.orientation == 'horizontal': + return [p.get_y() + p.get_height() / 2 for p in self.patches] + else: + raise ValueError("orientation must be 'vertical' or 'horizontal'.") + class ErrorbarContainer(Container): """ @@ -87,12 +129,12 @@ class ErrorbarContainer(Container): lines : tuple Tuple of ``(data_line, caplines, barlinecols)``. - - data_line : :class:`~matplotlib.lines.Line2D` instance of - x, y plot markers and/or line. - - caplines : tuple of :class:`~matplotlib.lines.Line2D` instances of - the error bar caps. - - barlinecols : list of :class:`~matplotlib.collections.LineCollection` - with the horizontal and vertical error ranges. + - data_line : A `~matplotlib.lines.Line2D` instance of x, y plot markers + and/or line. + - caplines : A tuple of `~matplotlib.lines.Line2D` instances of the error + bar caps. + - barlinecols : A tuple of `~matplotlib.collections.LineCollection` with the + horizontal and vertical error ranges. has_xerr, has_yerr : bool ``True`` if the errorbar has x/y errors. @@ -106,6 +148,78 @@ def __init__(self, lines, has_xerr=False, has_yerr=False, **kwargs): super().__init__(lines, **kwargs) +class PieContainer: + """ + Container for the artists of pie charts (e.g. created by `.Axes.pie`). + + .. versionadded:: 3.11 + + .. warning:: + The class name ``PieContainer`` name is provisional and may change in future + to reflect development of its functionality. + + You can access the wedge patches and further parameters by the attributes. + + Attributes + ---------- + wedges : list of `~matplotlib.patches.Wedge` + The artists of the pie wedges. + + values : `numpy.ndarray` + The data that the pie is based on. + + fracs : `numpy.ndarray` + The fraction of the pie that each wedge represents. + + texts : list of list of `~matplotlib.text.Text` + The artists of any labels on the pie wedges. Each inner list has one + text label per wedge. + + """ + def __init__(self, wedges, values, normalize): + self.wedges = wedges + self._texts = [] + self._values = values + self._normalize = normalize + + @property + def texts(self): + # Only return non-empty sublists. An empty sublist may have been added + # for backwards compatibility of the Axes.pie return value (see __getitem__). + return [t_list for t_list in self._texts if t_list] + + @property + def values(self): + result = self._values.copy() + result.flags.writeable = False + return result + + @property + def fracs(self): + if self._normalize: + result = self._values / self._values.sum() + else: + result = self._values + + result.flags.writeable = False + return result + + def add_texts(self, texts): + """Add a list of `~matplotlib.text.Text` objects to the container.""" + self._texts.append(texts) + + def remove(self): + """Remove all wedges and texts from the axes""" + for artist_list in self.wedges, self._texts: + for artist in cbook.flatten(artist_list): + artist.remove() + + def __getitem__(self, key): + # needed to support unpacking into a tuple for backward compatibility of the + # Axes.pie return value + return (self.wedges, *self._texts)[key] + + class StemContainer(Container): """ Container for the artists created in a :meth:`.Axes.stem` plot. @@ -115,13 +229,13 @@ class StemContainer(Container): Attributes ---------- - markerline : :class:`~matplotlib.lines.Line2D` + markerline : `~matplotlib.lines.Line2D` The artist of the markers at the stem heads. - stemlines : list of :class:`~matplotlib.lines.Line2D` + stemlines : `~matplotlib.collections.LineCollection` The artists of the vertical lines for all stems. - baseline : :class:`~matplotlib.lines.Line2D` + baseline : `~matplotlib.lines.Line2D` The artist of the horizontal baseline. """ def __init__(self, markerline_stemlines_baseline, **kwargs): @@ -130,7 +244,7 @@ def __init__(self, markerline_stemlines_baseline, **kwargs): ---------- markerline_stemlines_baseline : tuple Tuple of ``(markerline, stemlines, baseline)``. - ``markerline`` contains the `.LineCollection` of the markers, + ``markerline`` contains the `.Line2D` of the markers, ``stemlines`` is a `.LineCollection` of the main lines, ``baseline`` is the `.Line2D` of the baseline. """ diff --git a/lib/matplotlib/container.pyi b/lib/matplotlib/container.pyi index 9cc2e1ac2acc..772801b16d6d 100644 --- a/lib/matplotlib/container.pyi +++ b/lib/matplotlib/container.pyi @@ -1,11 +1,13 @@ from matplotlib.artist import Artist from matplotlib.lines import Line2D from matplotlib.collections import LineCollection -from matplotlib.patches import Rectangle +from matplotlib.patches import Rectangle, Wedge +from matplotlib.text import Text from collections.abc import Callable from typing import Any, Literal from numpy.typing import ArrayLike +from numpy import ndarray class Container(tuple): def __new__(cls, *args, **kwargs): ... @@ -32,19 +34,43 @@ class BarContainer(Container): orientation: Literal["vertical", "horizontal"] | None = ..., **kwargs ) -> None: ... + @property + def bottoms(self) -> list[float]: ... + @property + def tops(self) -> list[float]: ... + @property + def position_centers(self) -> list[float]: ... class ErrorbarContainer(Container): - lines: tuple[Line2D, Line2D, LineCollection] + lines: tuple[Line2D, tuple[Line2D, ...], tuple[LineCollection, ...]] has_xerr: bool has_yerr: bool def __init__( self, - lines: tuple[Line2D, Line2D, LineCollection], + lines: tuple[Line2D, tuple[Line2D, ...], tuple[LineCollection, ...]], has_xerr: bool = ..., has_yerr: bool = ..., **kwargs ) -> None: ... +class PieContainer(Container): + wedges: list[Wedge] + def __init__( + self, + wedges: list[Wedge], + values: ndarray, + normalize: bool, + ) -> None: ... + @property + def texts(self) -> list[list[Text]]: ... + @property + def values(self) -> ndarray: ... + @property + def fracs(self) -> ndarray: ... + def add_texts(self, + texts: list[Text], + ) -> None: ... + class StemContainer(Container): markerline: Line2D stemlines: LineCollection diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 0e6068c64b62..76481280729a 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -24,10 +24,11 @@ import matplotlib.cbook as cbook import matplotlib.patches as mpatches import matplotlib.transforms as mtransforms +from . import artist def _contour_labeler_event_handler(cs, inline, inline_spacing, event): - canvas = cs.axes.figure.canvas + canvas = cs.axes.get_figure(root=True).canvas is_button = event.name == "button_press_event" is_key = event.name == "key_press_event" # Quit (even if not in infinite mode; this is consistent with @@ -75,7 +76,7 @@ def clabel(self, levels=None, *, a subset of ``cs.levels``. If not given, all levels are labeled. fontsize : str or float, default: :rc:`font.size` - Size in points or relative size e.g., 'smaller', 'x-large'. + Size in points or relative size e.g., 'small', 'x-large'. See `.Text.set_size` for accepted string values. colors : :mpltype:`color` or colors or None, default: None @@ -142,8 +143,24 @@ def clabel(self, levels=None, *, ------- labels A list of `.Text` instances for the labels. + + Note: The returned Text instances should not be individually + removed or have their geometry modified, e.g. by changing text or font size. + If you need such a modification, remove the entire + `.ContourSet` and recreate it. """ + if self.filled: + _api.warn_deprecated( + "3.11", + message="clabel() is not designed to be used with filled contours and " + "may result in inconsistent plots. Applying clabel() to filled " + "contours is thus deprecated since %(since)s. If you need " + "labels with filled contours, instead draw contour lines " + "using contour() in addition to contourf() and add the labels " + "to the contour lines." + ) + # Based on the input arguments, clabel() adds a list of "label # specific" attributes to the ContourSet object. These attributes are # all of the form label* and names should be fairly self explanatory. @@ -183,10 +200,14 @@ def clabel(self, levels=None, *, self.labelMappable = self self.labelCValueList = np.take(self.cvalues, self.labelIndiceList) else: - cmap = mcolors.ListedColormap(colors, N=len(self.labelLevelList)) - self.labelCValueList = list(range(len(self.labelLevelList))) - self.labelMappable = cm.ScalarMappable(cmap=cmap, - norm=mcolors.NoNorm()) + # handling of explicit colors for labels: + # make labelCValueList contain integers [0, 1, 2, ...] and a cmap + # so that cmap(i) == colors[i] + num_levels = len(self.labelLevelList) + colors = cbook._resize_sequence(mcolors.to_rgba_array(colors), num_levels) + self.labelMappable = cm.ScalarMappable( + cmap=mcolors.ListedColormap(colors), norm=mcolors.NoNorm()) + self.labelCValueList = list(range(num_levels)) self.labelXYs = [] @@ -199,7 +220,8 @@ def clabel(self, levels=None, *, if not inline: print('Remove last label by clicking third mouse button.') mpl._blocking_input.blocking_input_loop( - self.axes.figure, ["button_press_event", "key_press_event"], + self.axes.get_figure(root=True), + ["button_press_event", "key_press_event"], timeout=-1, handler=functools.partial( _contour_labeler_event_handler, self, inline, inline_spacing)) @@ -222,8 +244,8 @@ def too_close(self, x, y, lw): def _get_nth_label_width(self, nth): """Return the width of the *nth* label, in pixels.""" - fig = self.axes.figure - renderer = fig._get_renderer() + fig = self.axes.get_figure(root=False) + renderer = fig.get_figure(root=True)._get_renderer() return (Text(0, 0, self.get_text(self.labelLevelList[nth], self.labelFmt), figure=fig, fontproperties=self._label_font_props) @@ -310,14 +332,6 @@ def _split_path_and_get_label_rotation(self, path, idx, screen_pos, lw, spacing= determine rotation and then to break contour if desired. The extra spacing is taken into account when breaking the path, but not when computing the angle. """ - if hasattr(self, "_old_style_split_collections"): - vis = False - for coll in self._old_style_split_collections: - vis |= coll.get_visible() - coll.remove() - self.set_visible(vis) - del self._old_style_split_collections # Invalidate them. - xys = path.vertices codes = path.codes @@ -406,97 +420,6 @@ def interp_vec(x, xp, fp): return [np.interp(x, xp, col) for col in fp.T] return angle, Path(xys, codes) - @_api.deprecated("3.8") - def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5): - """ - Calculate the appropriate label rotation given the linecontour - coordinates in screen units, the index of the label location and the - label width. - - If *lc* is not None or empty, also break contours and compute - inlining. - - *spacing* is the empty space to leave around the label, in pixels. - - Both tasks are done together to avoid calculating path lengths - multiple times, which is relatively costly. - - The method used here involves computing the path length along the - contour in pixel coordinates and then looking approximately (label - width / 2) away from central point to determine rotation and then to - break contour if desired. - """ - - if lc is None: - lc = [] - # Half the label width - hlw = lw / 2.0 - - # Check if closed and, if so, rotate contour so label is at edge - closed = _is_closed_polygon(slc) - if closed: - slc = np.concatenate([slc[ind:-1], slc[:ind + 1]]) - if len(lc): # Rotate lc also if not empty - lc = np.concatenate([lc[ind:-1], lc[:ind + 1]]) - ind = 0 - - # Calculate path lengths - pl = np.zeros(slc.shape[0], dtype=float) - dx = np.diff(slc, axis=0) - pl[1:] = np.cumsum(np.hypot(dx[:, 0], dx[:, 1])) - pl = pl - pl[ind] - - # Use linear interpolation to get points around label - xi = np.array([-hlw, hlw]) - if closed: # Look at end also for closed contours - dp = np.array([pl[-1], 0]) - else: - dp = np.zeros_like(xi) - - # Get angle of vector between the two ends of the label - must be - # calculated in pixel space for text rotation to work correctly. - (dx,), (dy,) = (np.diff(np.interp(dp + xi, pl, slc_col)) - for slc_col in slc.T) - rotation = np.rad2deg(np.arctan2(dy, dx)) - - if self.rightside_up: - # Fix angle so text is never upside-down - rotation = (rotation + 90) % 180 - 90 - - # Break contour if desired - nlc = [] - if len(lc): - # Expand range by spacing - xi = dp + xi + np.array([-spacing, spacing]) - - # Get (integer) indices near points of interest; use -1 as marker - # for out of bounds. - I = np.interp(xi, pl, np.arange(len(pl)), left=-1, right=-1) - I = [np.floor(I[0]).astype(int), np.ceil(I[1]).astype(int)] - if I[0] != -1: - xy1 = [np.interp(xi[0], pl, lc_col) for lc_col in lc.T] - if I[1] != -1: - xy2 = [np.interp(xi[1], pl, lc_col) for lc_col in lc.T] - - # Actually break contours - if closed: - # This will remove contour if shorter than label - if all(i != -1 for i in I): - nlc.append(np.vstack([xy2, lc[I[1]:I[0]+1], xy1])) - else: - # These will remove pieces of contour if they have length zero - if I[0] != -1: - nlc.append(np.vstack([lc[:I[0]+1], xy1])) - if I[1] != -1: - nlc.append(np.vstack([xy2, lc[I[1]:]])) - - # The current implementation removes contours completely - # covered by labels. Uncomment line below to keep - # original contour if this is the preferred behavior. - # if not len(nlc): nlc = [lc] - - return rotation, nlc - def add_label(self, x, y, rotation, lev, cvalue): """Add a contour label, respecting whether *use_clabeltext* was set.""" data_x, data_y = self.axes.transData.inverted().transform((x, y)) @@ -519,12 +442,6 @@ def add_label(self, x, y, rotation, lev, cvalue): # Add label to plot here - useful for manual mode label selection self.axes.add_artist(t) - @_api.deprecated("3.8", alternative="add_label") - def add_label_clabeltext(self, x, y, rotation, lev, cvalue): - """Add contour label with `.Text.set_transform_rotates_text`.""" - with cbook._setattr_cm(self, _use_clabeltext=True): - self.add_label(x, y, rotation, lev, cvalue) - def add_label_near(self, x, y, inline=True, inline_spacing=5, transform=None): """ @@ -550,6 +467,8 @@ def add_label_near(self, x, y, inline=True, inline_spacing=5, if transform is None: transform = self.axes.transData if transform: + x = self.axes.convert_xunits(x) + y = self.axes.convert_yunits(y) x, y = transform.transform((x, y)) idx_level_min, idx_vtx_min, proj = self._find_nearest_contour( @@ -601,16 +520,14 @@ def labels(self, inline, inline_spacing): def remove(self): super().remove() for text in self.labelTexts: - text.remove() - - -def _is_closed_polygon(X): - """ - Return whether first and last object in a sequence are the same. These are - presumably coordinates on a polygonal curve, in which case this function - tests if that curve is closed. - """ - return np.allclose(X[0], X[-1], rtol=1e-10, atol=1e-13) + try: + text.remove() + except ValueError: + _api.warn_external( + "Some labels were manually removed from the ContourSet. " + "To remove labels cleanly, remove the entire ContourSet " + "and recreate it.") + self.labelTexts.clear() def _find_closest_point_on_path(xys, p): @@ -649,16 +566,9 @@ def _find_closest_point_on_path(xys, p): return (d2s[imin], projs[imin], (imin, imin+1)) -_docstring.interpd.update(contour_set_attributes=r""" +_docstring.interpd.register(contour_set_attributes=r""" Attributes ---------- -ax : `~matplotlib.axes.Axes` - The Axes object in which the contours are drawn. - -collections : `.silent_list` of `.PathCollection`\s - The `.Artist`\s representing the contour. This is a list of - `.PathCollection`\s for both line and filled contours. - levels : array The values of the contour levels. @@ -668,7 +578,7 @@ def _find_closest_point_on_path(xys, p): """) -@_docstring.dedent_interpd +@_docstring.interpd class ContourSet(ContourLabeler, mcoll.Collection): """ Store a set of contour lines or filled regions. @@ -716,8 +626,8 @@ def __init__(self, ax, *args, levels=None, filled=False, linewidths=None, linestyles=None, hatches=(None,), alpha=None, origin=None, extent=None, cmap=None, colors=None, norm=None, vmin=None, vmax=None, - extend='neither', antialiased=None, nchunk=0, locator=None, - transform=None, negative_linestyles=None, clip_path=None, + colorizer=None, extend='neither', antialiased=None, nchunk=0, + locator=None, transform=None, negative_linestyles=None, **kwargs): """ Draw contour lines or filled regions, depending on @@ -771,8 +681,8 @@ def __init__(self, ax, *args, super().__init__( antialiaseds=antialiased, alpha=alpha, - clip_path=clip_path, transform=transform, + colorizer=colorizer, ) self.axes = ax self.levels = levels @@ -785,6 +695,16 @@ def __init__(self, ax, *args, self.nchunk = nchunk self.locator = locator + + if "color" in kwargs: + raise _api.kwarg_error("ContourSet.__init__", "color") + + if colorizer: + self._set_colorizer_check_keywords(colorizer, cmap=cmap, + norm=norm, vmin=vmin, + vmax=vmax, colors=colors) + norm = colorizer.norm + cmap = colorizer.cmap if (isinstance(norm, mcolors.LogNorm) or isinstance(self.locator, ticker.LogLocator)): self.logscale = True @@ -803,12 +723,8 @@ def __init__(self, ax, *args, self.origin = mpl.rcParams['image.origin'] self._orig_linestyles = linestyles # Only kept for user access. - self.negative_linestyles = negative_linestyles - # If negative_linestyles was not defined as a keyword argument, define - # negative_linestyles with rcParams - if self.negative_linestyles is None: - self.negative_linestyles = \ - mpl.rcParams['contour.negative_linestyle'] + self.negative_linestyles = mpl._val_or_rc(negative_linestyles, + 'contour.negative_linestyle') kwargs = self._process_args(*args, **kwargs) self._process_levels() @@ -816,6 +732,11 @@ def __init__(self, ax, *args, self._extend_min = self.extend in ['min', 'both'] self._extend_max = self.extend in ['max', 'both'] if self.colors is not None: + if mcolors.is_color_like(self.colors): + color_sequence = [self.colors] + else: + color_sequence = self.colors + ncolors = len(self.levels) if self.filled: ncolors -= 1 @@ -832,19 +753,19 @@ def __init__(self, ax, *args, total_levels = (ncolors + int(self._extend_min) + int(self._extend_max)) - if (len(self.colors) == total_levels and + if (len(color_sequence) == total_levels and (self._extend_min or self._extend_max)): use_set_under_over = True if self._extend_min: i0 = 1 - cmap = mcolors.ListedColormap(self.colors[i0:None], N=ncolors) - - if use_set_under_over: - if self._extend_min: - cmap.set_under(self.colors[0]) - if self._extend_max: - cmap.set_over(self.colors[-1]) + cmap = mcolors.ListedColormap( + cbook._resize_sequence(color_sequence[i0:], ncolors), + under=(color_sequence[0] + if use_set_under_over and self._extend_min else None), + over=(color_sequence[-1] + if use_set_under_over and self._extend_max else None), + ) # label lists must be initialized here self.labelTexts = [] @@ -869,22 +790,18 @@ def __init__(self, ax, *args, _api.warn_external('linewidths is ignored by contourf') # Lower and upper contour levels. lowers, uppers = self._get_lowers_and_uppers() - self.set( - edgecolor="none", - # Default zorder taken from Collection - zorder=kwargs.pop("zorder", 1), - ) - + self.set(edgecolor="none") else: self.set( facecolor="none", linewidths=self._process_linewidths(linewidths), linestyle=self._process_linestyles(linestyles), + label="_nolegend_", # Default zorder taken from LineCollection, which is higher # than for filled contours so that lines are displayed on top. - zorder=kwargs.pop("zorder", 2), - label="_nolegend_", + zorder=2, ) + self.set(**kwargs) # Let user-set values override defaults. self.axes.add_collection(self, autolim=False) self.sticky_edges.x[:] = [self._mins[0], self._maxs[0]] @@ -894,69 +811,15 @@ def __init__(self, ax, *args, self.changed() # set the colors - if kwargs: - _api.warn_external( - 'The following kwargs were not used by contour: ' + - ", ".join(map(repr, kwargs)) - ) - allsegs = property(lambda self: [ [subp.vertices for subp in p._iter_connected_components()] for p in self.get_paths()]) allkinds = property(lambda self: [ [subp.codes for subp in p._iter_connected_components()] for p in self.get_paths()]) - tcolors = _api.deprecated("3.8")(property(lambda self: [ - (tuple(rgba),) for rgba in self.to_rgba(self.cvalues, self.alpha)])) - tlinewidths = _api.deprecated("3.8")(property(lambda self: [ - (w,) for w in self.get_linewidths()])) alpha = property(lambda self: self.get_alpha()) linestyles = property(lambda self: self._orig_linestyles) - @_api.deprecated("3.8", alternative="set_antialiased or get_antialiased", - addendum="Note that get_antialiased returns an array.") - @property - def antialiased(self): - return all(self.get_antialiased()) - - @antialiased.setter - def antialiased(self, aa): - self.set_antialiased(aa) - - @_api.deprecated("3.8") - @property - def collections(self): - # On access, make oneself invisible and instead add the old-style collections - # (one PathCollection per level). We do not try to further split contours into - # connected components as we already lost track of what pairs of contours need - # to be considered as single units to draw filled regions with holes. - if not hasattr(self, "_old_style_split_collections"): - self.set_visible(False) - fcs = self.get_facecolor() - ecs = self.get_edgecolor() - lws = self.get_linewidth() - lss = self.get_linestyle() - self._old_style_split_collections = [] - for idx, path in enumerate(self._paths): - pc = mcoll.PathCollection( - [path] if len(path.vertices) else [], - alpha=self.get_alpha(), - antialiaseds=self._antialiaseds[idx % len(self._antialiaseds)], - transform=self.get_transform(), - zorder=self.get_zorder(), - label="_nolegend_", - facecolor=fcs[idx] if len(fcs) else "none", - edgecolor=ecs[idx] if len(ecs) else "none", - linewidths=[lws[idx % len(lws)]], - linestyles=[lss[idx % len(lss)]], - ) - if self.filled: - pc.set(hatch=self.hatches[idx % len(self.hatches)]) - self._old_style_split_collections.append(pc) - for col in self._old_style_split_collections: - self.axes.add_collection(col) - return self._old_style_split_collections - def get_transform(self): """Return the `.Transform` instance used by this ContourSet.""" if self._transform is None: @@ -1116,12 +979,29 @@ def changed(self): label.set_color(self.labelMappable.to_rgba(cv)) super().changed() - def _autolev(self, N): + def _ensure_locator_exists(self, N): """ - Select contour levels to span the data. + Set a locator on this ContourSet if it's not already set. - The target number of levels, *N*, is used only when the - scale is not log and default locator is used. + Parameters + ---------- + N : int or None + If *N* is an int, it is used as the target number of levels. + Otherwise when *N* is None, a reasonable default is chosen; + for logscales the LogLocator chooses, N=7 is the default + otherwise. + """ + if self.locator is None: + if self.logscale: + self.locator = ticker.LogLocator(numticks=N) + else: + if N is None: + N = 7 # Hard coded default + self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1) + + def _autolev(self): + """ + Select contour levels to span the data. We need two more levels for filled contours than for line contours, because for the latter we need to specify @@ -1130,12 +1010,6 @@ def _autolev(self, N): one contour line, but two filled regions, and therefore three levels to provide boundaries for both regions. """ - if self.locator is None: - if self.logscale: - self.locator = ticker.LogLocator() - else: - self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1) - lev = self.locator.tick_values(self.zmin, self.zmax) try: @@ -1163,22 +1037,21 @@ def _process_contour_level_args(self, args, z_dtype): """ Determine the contour levels and store in self.levels. """ - if self.levels is None: + levels_arg = self.levels + if levels_arg is None: if args: + # Set if levels manually provided levels_arg = args[0] elif np.issubdtype(z_dtype, bool): - if self.filled: - levels_arg = [0, .5, 1] - else: - levels_arg = [.5] - else: - levels_arg = 7 # Default, hard-wired. - else: - levels_arg = self.levels - if isinstance(levels_arg, Integral): - self.levels = self._autolev(levels_arg) + # Set default values for bool data types + levels_arg = [0, .5, 1] if self.filled else [.5] + + if isinstance(levels_arg, Integral) or levels_arg is None: + self._ensure_locator_exists(levels_arg) + self.levels = self._autolev() else: self.levels = np.asarray(levels_arg, np.float64) + if self.filled and len(self.levels) < 2: raise ValueError("Filled contours require at least 2 levels.") if len(self.levels) > 1 and np.min(np.diff(self.levels)) <= 0.0: @@ -1408,6 +1281,7 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True): return (i_level, segment, index, xmin, ymin, d2) + @artist.allow_rasterization def draw(self, renderer): paths = self._paths n_paths = len(paths) @@ -1415,17 +1289,22 @@ def draw(self, renderer): super().draw(renderer) return # In presence of hatching, draw contours one at a time. + edgecolors = self.get_edgecolors() + if edgecolors.size == 0: + edgecolors = ("none",) for idx in range(n_paths): - with cbook._setattr_cm(self, _paths=[paths[idx]]), self._cm_set( + with self._cm_set( + paths=[paths[idx]], hatch=self.hatches[idx % len(self.hatches)], array=[self.get_array()[idx]], linewidths=[self.get_linewidths()[idx % len(self.get_linewidths())]], linestyles=[self.get_linestyles()[idx % len(self.get_linestyles())]], + edgecolors=edgecolors[idx % len(edgecolors)], ): super().draw(renderer) -@_docstring.dedent_interpd +@_docstring.interpd class QuadContourSet(ContourSet): """ Create and store a set of contour lines or filled regions. @@ -1453,8 +1332,7 @@ def _process_args(self, *args, corner_mask=None, algorithm=None, **kwargs): else: import contourpy - if algorithm is None: - algorithm = mpl.rcParams['contour.algorithm'] + algorithm = mpl._val_or_rc(algorithm, 'contour.algorithm') mpl.rcParams.validate["contour.algorithm"](algorithm) self._algorithm = algorithm @@ -1480,7 +1358,7 @@ def _process_args(self, *args, corner_mask=None, algorithm=None, **kwargs): # if the transform is not trans data, and some part of it # contains transData, transform the xs and ys to data coordinates if (t != self.axes.transData and - any(t.contains_branch_seperately(self.axes.transData))): + any(t.contains_branch_separately(self.axes.transData))): trans_to_data = t - self.axes.transData pts = np.vstack([x.flat, y.flat]).T transformed_pts = trans_to_data.transform(pts) @@ -1606,7 +1484,7 @@ def _initialize_x_y(self, z): return np.meshgrid(x, y) -_docstring.interpd.update(contour_doc=""" +_docstring.interpd.register(contour_doc=""" `.contour` and `.contourf` draw contour lines and filled contours, respectively. Except as noted, function signatures and return values are the same for both versions. @@ -1633,13 +1511,22 @@ def _initialize_x_y(self, z): levels : int or array-like, optional Determines the number and positions of the contour lines / regions. - If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries - to automatically choose no more than *n+1* "nice" contour levels - between minimum and maximum numeric values of *Z*. + If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries to + automatically choose no more than *n+2* "nice" contour level boundaries + between the minimum and maximum numeric values of *Z*. These boundaries + define where lines are drawn (for `contour`) or where filled regions + are separated (for `contourf`). + + If not given, a reasonable default is chosen; for linear scales, + *n*=7 is the default. If array-like, draw contour lines at the specified levels. The values must be in increasing order. + If not specified, a reasonable default is automatically chosen. For + linear scales, this corresponds to *levels=7*. For logarithmic + scales, `~matplotlib.ticker.LogLocator` is used instead. + Returns ------- `~.contour.QuadContourSet` @@ -1660,10 +1547,12 @@ def _initialize_x_y(self, z): The sequence is cycled for the levels in ascending order. If the sequence is shorter than the number of levels, it's repeated. - As a shortcut, single color strings may be used in place of - one-element lists, i.e. ``'red'`` instead of ``['red']`` to color - all levels with the same color. This shortcut does only work for - color strings, not for other ways of specifying colors. + As a shortcut, a single color may be used in place of one-element lists, i.e. + ``'red'`` instead of ``['red']`` to color all levels with the same color. + + .. versionchanged:: 3.10 + Previously a single color had to be expressed as a string, but now any + valid color format may be passed. By default (value *None*), the colormap specified by *cmap* will be used. @@ -1686,6 +1575,10 @@ def _initialize_x_y(self, z): This parameter is ignored if *colors* is set. +%(colorizer_doc)s + + This parameter is ignored if *colors* is set. + origin : {*None*, 'upper', 'lower', 'image'}, default: None Determines the orientation and exact position of *Z* by specifying the position of ``Z[0, 0]``. This is only relevant, if *X*, *Y* @@ -1731,10 +1624,10 @@ def _initialize_x_y(self, z): An existing `.QuadContourSet` does not get notified if properties of its colormap are changed. Therefore, an explicit - call `.QuadContourSet.changed()` is needed after modifying the + call `~.ContourSet.changed()` is needed after modifying the colormap. The explicit call can be left out, if a colorbar is assigned to the `.QuadContourSet` because it internally calls - `.QuadContourSet.changed()`. + `~.ContourSet.changed()`. Example:: @@ -1797,7 +1690,7 @@ def _initialize_x_y(self, z): specifies the line style for negative contours. If *negative_linestyles* is *None*, the default is taken from - :rc:`contour.negative_linestyles`. + :rc:`contour.negative_linestyle`. *negative_linestyles* can also be an iterable of the above strings specifying a set of linestyles to be used. If this iterable is shorter than @@ -1826,6 +1719,13 @@ def _initialize_x_y(self, z): data : indexable object, optional DATA_PARAMETER_PLACEHOLDER +rasterized : bool, optional + *Only applies to* `.contourf`. + Rasterize the contour plot when drawing vector graphics. This can + speed up rendering and produce smaller files for large data sets. + See also :doc:`/gallery/misc/rasterization_demo`. + + Notes ----- 1. `.contourf` differs from the MATLAB version in that it does not draw diff --git a/lib/matplotlib/contour.pyi b/lib/matplotlib/contour.pyi index c386bea47ab7..2a89d6016170 100644 --- a/lib/matplotlib/contour.pyi +++ b/lib/matplotlib/contour.pyi @@ -1,9 +1,9 @@ import matplotlib.cm as cm from matplotlib.artist import Artist from matplotlib.axes import Axes -from matplotlib.collections import Collection, PathCollection +from matplotlib.collections import Collection +from matplotlib.colorizer import Colorizer, ColorizingArtist from matplotlib.colors import Colormap, Normalize -from matplotlib.font_manager import FontProperties from matplotlib.path import Path from matplotlib.patches import Patch from matplotlib.text import Text @@ -24,7 +24,7 @@ class ContourLabeler: rightside_up: bool labelLevelList: list[float] labelIndiceList: list[int] - labelMappable: cm.ScalarMappable + labelMappable: cm.ScalarMappable | ColorizingArtist labelCValueList: list[ColorType] labelXYs: list[tuple[float, float]] def clabel( @@ -51,20 +51,9 @@ class ContourLabeler: def locate_label( self, linecontour: ArrayLike, labelwidth: float ) -> tuple[float, float, float]: ... - def calc_label_rot_and_inline( - self, - slc: ArrayLike, - ind: int, - lw: float, - lc: ArrayLike | None = ..., - spacing: int = ..., - ) -> tuple[float, list[ArrayLike]]: ... def add_label( self, x: float, y: float, rotation: float, lev: float, cvalue: ColorType ) -> None: ... - def add_label_clabeltext( - self, x: float, y: float, rotation: float, lev: float, cvalue: ColorType - ) -> None: ... def add_label_near( self, x: float, @@ -96,12 +85,6 @@ class ContourSet(ContourLabeler, Collection): clip_path: Patch | Path | TransformedPath | TransformedPatchPath | None labelTexts: list[Text] labelCValues: list[ColorType] - @property - def tcolors(self) -> list[tuple[tuple[float, float, float, float]]]: ... - - # only for not filled - @property - def tlinewidths(self) -> list[tuple[float]]: ... @property def allkinds(self) -> list[list[np.ndarray | None]]: ... @@ -110,12 +93,6 @@ class ContourSet(ContourLabeler, Collection): @property def alpha(self) -> float | None: ... @property - def antialiased(self) -> bool: ... - @antialiased.setter - def antialiased(self, aa: bool | Sequence[bool]) -> None: ... - @property - def collections(self) -> list[PathCollection]: ... - @property def linestyles(self) -> ( None | Literal["solid", "dashed", "dashdot", "dotted"] | @@ -141,6 +118,7 @@ class ContourSet(ContourLabeler, Collection): norm: str | Normalize | None = ..., vmin: float | None = ..., vmax: float | None = ..., + colorizer: Colorizer | None = ..., extend: Literal["neither", "both", "min", "max"] = ..., antialiased: bool | None = ..., nchunk: int = ..., diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index c12d9f31ba4b..f5f0918dcc13 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -37,7 +37,7 @@ is achievable for (approximately) 70 years on either side of the epoch, and 20 microseconds for the rest of the allowable range of dates (year 0001 to 9999). The epoch can be changed at import time via `.dates.set_epoch` or -:rc:`dates.epoch` to other dates if necessary; see +:rc:`date.epoch` to other dates if necessary; see :doc:`/gallery/ticks/date_precision_and_epochs` for a discussion. .. note:: @@ -267,7 +267,7 @@ def set_epoch(epoch): """ Set the epoch (origin for dates) for datetime calculations. - The default epoch is :rc:`dates.epoch` (by default 1970-01-01T00:00). + The default epoch is :rc:`date.epoch`. If microsecond accuracy is desired, the date being plotted needs to be within approximately 70 years of the epoch. Matplotlib internally @@ -309,7 +309,7 @@ def get_epoch(): def _dt64_to_ordinalf(d): """ - Convert `numpy.datetime64` or an `numpy.ndarray` of those types to + Convert a `numpy.ndarray` of np.datetime64 to Gregorian date as UTC float relative to the epoch (see `.get_epoch`). Roundoff is float64 precision. Practically: microseconds for dates between 290301 BC, 294241 AD, milliseconds for larger dates @@ -537,7 +537,7 @@ def drange(dstart, dend, delta): # calculate end of the interval which will be generated dinterval_end = dstart + num * delta - # ensure, that an half open interval will be generated [dstart, dend) + # ensure, that a half open interval will be generated [dstart, dend) if dinterval_end >= dend: # if the endpoint is greater than or equal to dend, # just subtract one delta @@ -796,7 +796,12 @@ def format_ticks(self, values): if show_offset: # set the offset string: - self.offset_string = tickdatetime[-1].strftime(offsetfmts[level]) + if (self._locator.axis and + self._locator.axis.__name__ in ('xaxis', 'yaxis') + and self._locator.axis.get_inverted()): + self.offset_string = tickdatetime[0].strftime(offsetfmts[level]) + else: + self.offset_string = tickdatetime[-1].strftime(offsetfmts[level]) if self._usetex: self.offset_string = _wrap_in_tex(self.offset_string) else: @@ -1525,13 +1530,15 @@ def __init__(self, byweekday=1, interval=1, tz=None): """ Parameters ---------- - byweekday : int or list of int, default: all days + byweekday : int, list of int, constant from :mod:`dateutil.rrule`, or \ + list of constants, default: 1 (Tuesday) Ticks will be placed on every weekday in *byweekday*. Default is - every day. + every Tuesday. - Elements of *byweekday* must be one of MO, TU, WE, TH, FR, SA, - SU, the constants from :mod:`dateutil.rrule`, which have been - imported into the :mod:`matplotlib.dates` namespace. + Elements of *byweekday* (if a sequence) must be either integers or + MO, TU, WE, TH, FR, SA, SU, the constants from + :mod:`dateutil.rrule`, which have been imported into the + :mod:`matplotlib.dates` namespace. interval : int, default: 1 The interval between each iteration. For example, if ``interval=2``, mark every second occurrence. diff --git a/lib/matplotlib/dates.pyi b/lib/matplotlib/dates.pyi new file mode 100644 index 000000000000..426082679393 --- /dev/null +++ b/lib/matplotlib/dates.pyi @@ -0,0 +1,37 @@ +import datetime +from collections.abc import Sequence +from typing import overload + +import numpy as np +import numpy.typing as npt + +TZ = str | datetime.tzinfo + +def _get_tzinfo(tz: TZ | None=None) -> datetime.tzinfo: ... +def _reset_epoch_test_example() -> None: ... +def set_epoch(epoch: str) -> None: ... +def get_epoch() -> str: ... +def _dt64_to_ordinalf(d: npt.NDArray[np.datetime64]) -> npt.NDArray[np.floating]: ... +def _from_ordinalf(x: float, tz: TZ | None=None) -> datetime.datetime: ... +# Ideally str | Sequence[str] would get an override, but because a str is a valid Sequence[str], +# it's not possible to distinguish between them in the type system +# See https://github.com/python/typing/issues/256 +def datestr2num(d: str | Sequence[str], default: datetime.datetime | None=None) -> float | npt.NDArray[np.floating]: ... + +@overload +def date2num(d: datetime.datetime | np.datetime64) -> float: ... +@overload +def date2num(d: Sequence[datetime.datetime] | Sequence[np.datetime64]) -> npt.NDArray[np.floating]: ... + +@overload +def num2date(x: float, tz: TZ | None=None) -> datetime.datetime: ... +@overload +def num2date(x: Sequence[float], tz: TZ | None=None) -> list[datetime.datetime]: ... + +@overload +def num2timedelta(x: float) -> datetime.timedelta: ... +@overload +def num2timedelta(x: Sequence[float]) -> list[datetime.timedelta]: ... + +def drange(dstart: datetime.datetime, dend: datetime.datetime, delta: datetime.timedelta) -> npt.NDArray[np.floating]: ... +def _wrap_in_tex(text: str) -> str: ... diff --git a/lib/matplotlib/dviread.py b/lib/matplotlib/dviread.py index 82f43b56292d..dd5efdc85b34 100644 --- a/lib/matplotlib/dviread.py +++ b/lib/matplotlib/dviread.py @@ -17,20 +17,24 @@ ... """ -from collections import namedtuple +import dataclasses import enum -from functools import lru_cache, partial, wraps import logging import os -from pathlib import Path import re import struct import subprocess import sys +from collections import namedtuple +from functools import cache, cached_property, lru_cache, partial, wraps +from pathlib import Path +import fontTools.agl import numpy as np -from matplotlib import _api, cbook +import matplotlib as mpl +from matplotlib import _api, cbook, font_manager +from matplotlib.ft2font import LoadFlags _log = logging.getLogger(__name__) @@ -66,62 +70,75 @@ class Text(namedtuple('Text', 'x y font glyph width')): """ A glyph in the dvi file. - The *x* and *y* attributes directly position the glyph. The *font*, - *glyph*, and *width* attributes are kept public for back-compatibility, - but users wanting to draw the glyph themselves are encouraged to instead - load the font specified by `font_path` at `font_size`, warp it with the - effects specified by `font_effects`, and load the glyph specified by - `glyph_name_or_index`. - """ + In order to render the glyph, load the glyph at index ``text.index`` + from the font at ``text.font.resolve_path()`` with size ``text.font.size``, + warped with ``text.font.effects``, then draw it at position + ``(text.x, text.y)``. - def _get_pdftexmap_entry(self): - return PsfontsMap(find_tex_file("pdftex.map"))[self.font.texname] - - @property - def font_path(self): - """The `~pathlib.Path` to the font for this glyph.""" - psfont = self._get_pdftexmap_entry() - if psfont.filename is None: - raise ValueError("No usable font file found for {} ({}); " - "the font may lack a Type-1 version" - .format(psfont.psname.decode("ascii"), - psfont.texname.decode("ascii"))) - return Path(psfont.filename) - - @property - def font_size(self): - """The font size.""" - return self.font.size + ``text.glyph`` is the glyph number actually stored in the dvi file (whose + interpretation depends on the font). ``text.width`` is the glyph width in + dvi units. + """ @property - def font_effects(self): + def index(self): """ - The "font effects" dict for this glyph. - - This dict contains the values for this glyph of SlantFont and - ExtendFont (if any), read off :file:`pdftex.map`. + The FreeType index of this glyph (that can be passed to FT_Load_Glyph). """ - return self._get_pdftexmap_entry().effects + # See DviFont._index_dvi_to_freetype for details on the index mapping. + return self.font._index_dvi_to_freetype(self.glyph) - @property + font_path = property(lambda self: self.font.resolve_path()) + font_size = property(lambda self: self.font.size) + font_effects = property(lambda self: self.font.effects) + + @property # To be deprecated together with font_path, font_size, font_effects. def glyph_name_or_index(self): """ - Either the glyph name or the native charmap glyph index. - - If :file:`pdftex.map` specifies an encoding for this glyph's font, that - is a mapping of glyph indices to Adobe glyph names; use it to convert - dvi indices to glyph names. Callers can then convert glyph names to - glyph indices (with FT_Get_Name_Index/get_name_index), and load the - glyph using FT_Load_Glyph/load_glyph. - - If :file:`pdftex.map` specifies no encoding, the indices directly map - to the font's "native" charmap; glyphs should directly load using - FT_Load_Char/load_char after selecting the native charmap. + The glyph name, the native charmap glyph index, or the raw glyph index. + + If the font is a TrueType file (which can currently only happen for + DVI files generated by xetex or luatex), then this number is the raw + index of the glyph, which can be passed to FT_Load_Glyph/load_glyph. + + Otherwise, the font is a PostScript font. For such fonts, if + :file:`pdftex.map` specifies an encoding for this glyph's font, + that is a mapping of glyph indices to Adobe glyph names; which + is used by this property to convert dvi numbers to glyph names. + Callers can then convert glyph names to glyph indices (with + FT_Get_Name_Index/get_name_index), and load the glyph using + FT_Load_Glyph/load_glyph. + + If :file:`pdftex.map` specifies no encoding for a PostScript font, + this number is an index to the font's "native" charmap; glyphs should + directly load using FT_Load_Char/load_char after selecting the native + charmap. """ + # The last section is only true on luatex since luaotfload 3.23; this + # must be checked by the code generated by texmanager. (luaotfload's + # docs states "No one should rely on the mapping between DVI character + # codes and font glyphs [prior to v3.15] unless they tightly + # control all involved versions and are deeply familiar with the + # implementation", but a further mapping bug was fixed in luaotfload + # commit 8f2dca4, first included in v3.23). entry = self._get_pdftexmap_entry() return (_parse_enc(entry.encoding)[self.glyph] if entry.encoding is not None else self.glyph) + def _as_unicode_or_name(self): + if self.font.subfont: + raise NotImplementedError("Indexing TTC fonts is not supported yet") + path = self.font.resolve_path() + if path.name.lower().endswith("pk"): + # PK fonts have no encoding information; report glyphs as ASCII but + # with a "?" to indicate that this is just a guess. + return (f"{chr(self.glyph)}?" if chr(self.glyph).isprintable() else + f"pk{self.glyph:#02x}") + face = font_manager.get_font(path) + glyph_name = face.get_glyph_name(self.index) + glyph_str = fontTools.agl.toUnicode(glyph_name) + return glyph_str or glyph_name + # Opcode argument parsing # @@ -132,20 +149,20 @@ def glyph_name_or_index(self): # raw: Return delta as is. raw=lambda dvi, delta: delta, # u1: Read 1 byte as an unsigned number. - u1=lambda dvi, delta: dvi._arg(1, signed=False), + u1=lambda dvi, delta: dvi._read_arg(1, signed=False), # u4: Read 4 bytes as an unsigned number. - u4=lambda dvi, delta: dvi._arg(4, signed=False), + u4=lambda dvi, delta: dvi._read_arg(4, signed=False), # s4: Read 4 bytes as a signed number. - s4=lambda dvi, delta: dvi._arg(4, signed=True), + s4=lambda dvi, delta: dvi._read_arg(4, signed=True), # slen: Read delta bytes as a signed number, or None if delta is None. - slen=lambda dvi, delta: dvi._arg(delta, signed=True) if delta else None, + slen=lambda dvi, delta: dvi._read_arg(delta, signed=True) if delta else None, # slen1: Read (delta + 1) bytes as a signed number. - slen1=lambda dvi, delta: dvi._arg(delta + 1, signed=True), + slen1=lambda dvi, delta: dvi._read_arg(delta + 1, signed=True), # ulen1: Read (delta + 1) bytes as an unsigned number. - ulen1=lambda dvi, delta: dvi._arg(delta + 1, signed=False), + ulen1=lambda dvi, delta: dvi._read_arg(delta + 1, signed=False), # olen1: Read (delta + 1) bytes as an unsigned number if less than 4 bytes, # as a signed number if 4 bytes. - olen1=lambda dvi, delta: dvi._arg(delta + 1, signed=(delta == 3)), + olen1=lambda dvi, delta: dvi._read_arg(delta + 1, signed=(delta == 3)), ) @@ -230,6 +247,7 @@ def __init__(self, filename, dpi): self.dpi = dpi self.fonts = {} self.state = _dvistate.pre + self._missing_font = None def __enter__(self): """Context manager enter method, does nothing.""" @@ -270,7 +288,8 @@ def _output(self): Output the text and boxes belonging to the most recent page. page = dvi._output() """ - minx, miny, maxx, maxy = np.inf, np.inf, -np.inf, -np.inf + minx = miny = np.inf + maxx = maxy = -np.inf maxy_pure = -np.inf for elt in self.text + self.boxes: if isinstance(elt, Box): @@ -337,6 +356,8 @@ def _read(self): while True: byte = self.file.read(1)[0] self._dtable[byte](self, byte) + if self._missing_font: + raise self._missing_font.to_exception() name = self._dtable[byte].__name__ if name == "_push": down_stack.append(down_stack[-1]) @@ -354,7 +375,7 @@ def _read(self): self.close() return False - def _arg(self, nbytes, signed=False): + def _read_arg(self, nbytes, signed=False): """ Read and return a big-endian integer *nbytes* long. Signedness is determined by the *signed* keyword. @@ -364,11 +385,15 @@ def _arg(self, nbytes, signed=False): @_dispatch(min=0, max=127, state=_dvistate.inpage) def _set_char_immediate(self, char): self._put_char_real(char) + if isinstance(self.fonts[self.f], cbook._ExceptionInfo): + return self.h += self.fonts[self.f]._width_of(char) @_dispatch(min=128, max=131, state=_dvistate.inpage, args=('olen1',)) def _set_char(self, char): self._put_char_real(char) + if isinstance(self.fonts[self.f], cbook._ExceptionInfo): + return self.h += self.fonts[self.f]._width_of(char) @_dispatch(132, state=_dvistate.inpage, args=('s4', 's4')) @@ -382,20 +407,22 @@ def _put_char(self, char): def _put_char_real(self, char): font = self.fonts[self.f] - if font._vf is None: + if isinstance(font, cbook._ExceptionInfo): + self._missing_font = font + elif font._vf is None: self.text.append(Text(self.h, self.v, font, char, font._width_of(char))) else: scale = font._scale for x, y, f, g, w in font._vf[char].text: - newf = DviFont(scale=_mul2012(scale, f._scale), - tfm=f._tfm, texname=f.texname, vf=f._vf) - self.text.append(Text(self.h + _mul2012(x, scale), - self.v + _mul2012(y, scale), + newf = DviFont(scale=_mul1220(scale, f._scale), + metrics=f._metrics, texname=f.texname, vf=f._vf) + self.text.append(Text(self.h + _mul1220(x, scale), + self.v + _mul1220(y, scale), newf, g, newf._width_of(g))) - self.boxes.extend([Box(self.h + _mul2012(x, scale), - self.v + _mul2012(y, scale), - _mul2012(a, scale), _mul2012(b, scale)) + self.boxes.extend([Box(self.h + _mul1220(x, scale), + self.v + _mul1220(y, scale), + _mul1220(a, scale), _mul1220(b, scale)) for x, y, a, b in font._vf[char].boxes]) @_dispatch(137, state=_dvistate.inpage, args=('s4', 's4')) @@ -413,7 +440,7 @@ def _nop(self, _): @_dispatch(139, state=_dvistate.outer, args=('s4',)*11) def _bop(self, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, p): self.state = _dvistate.inpage - self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 + self.h = self.v = self.w = self.x = self.y = self.z = 0 self.stack = [] self.text = [] # list of Text objects self.boxes = [] # list of Box objects @@ -485,21 +512,41 @@ def _fnt_def(self, k, c, s, d, a, l): def _fnt_def_real(self, k, c, s, d, a, l): n = self.file.read(a + l) - fontname = n[-l:].decode('ascii') - tfm = _tfmfile(fontname) + fontname = n[-l:] + if fontname.startswith(b"[") and c == 0x4c756146: # c == "LuaF" + # See https://chat.stackexchange.com/rooms/106428 (and also + # https://tug.org/pipermail/dvipdfmx/2021-January/000168.html). + # AFAICT luatex's dvi drops info re: OpenType variation-axis values. + self.fonts[k] = DviFont.from_luatex(s, n) + return + fontname = fontname.decode("ascii") + try: + tfm = _tfmfile(fontname) + except FileNotFoundError as exc: + if fontname.startswith("[") and fontname.endswith(";") and c == 0: + exc.add_note( + "This dvi file was likely generated with a too-old " + "version of luaotfload; luaotfload 3.23 is required.") + # Explicitly allow defining missing fonts for Vf support; we only + # register an error when trying to load a glyph from a missing font + # and throw that error in Dvi._read. For Vf, _finalize_packet + # checks whether a missing glyph has been used, and in that case + # skips the glyph definition. + self.fonts[k] = cbook._ExceptionInfo.from_exception(exc) + return if c != 0 and tfm.checksum != 0 and c != tfm.checksum: - raise ValueError('tfm checksum mismatch: %s' % n) + raise ValueError(f'tfm checksum mismatch: {n}') try: vf = _vffile(fontname) except FileNotFoundError: vf = None - self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf) + self.fonts[k] = DviFont(scale=s, metrics=tfm, texname=n, vf=vf) @_dispatch(247, state=_dvistate.pre, args=('u1', 'u4', 'u4', 'u4', 'u1')) def _pre(self, i, num, den, mag, k): self.file.read(k) # comment in the dvi file - if i != 2: - raise ValueError("Unknown dvi format %d" % i) + if i not in [2, 7]: # 2: pdftex, luatex; 7: xetex + raise ValueError(f"Unknown dvi format {i}") if num != 25400000 or den != 7227 * 2**16: raise ValueError("Nonstandard units in dvi file") # meaning: TeX always uses those exact values, so it @@ -519,13 +566,66 @@ def _post(self, _): # TODO: actually read the postamble and finale? # currently post_post just triggers closing the file - @_dispatch(249) - def _post_post(self, _): + @_dispatch(249, args=()) + def _post_post(self): + raise NotImplementedError + + @_dispatch(250, args=()) + def _begin_reflect(self): + raise NotImplementedError + + @_dispatch(251, args=()) + def _end_reflect(self): raise NotImplementedError - @_dispatch(min=250, max=255) - def _malformed(self, offset): - raise ValueError(f"unknown command: byte {250 + offset}") + @_dispatch(252, args=()) + def _define_native_font(self): + k = self._read_arg(4, signed=False) + s = self._read_arg(4, signed=False) + flags = self._read_arg(2, signed=False) + l = self._read_arg(1, signed=False) + n = self.file.read(l) + i = self._read_arg(4, signed=False) + effects = {} + if flags & 0x0200: + effects["rgba"] = [self._read_arg(1, signed=False) for _ in range(4)] + if flags & 0x1000: + effects["extend"] = self._read_arg(4, signed=True) / 65536 + if flags & 0x2000: + effects["slant"] = self._read_arg(4, signed=True) / 65536 + if flags & 0x4000: + effects["embolden"] = self._read_arg(4, signed=True) / 65536 + self.fonts[k] = DviFont.from_xetex(s, n, i, effects) + + @_dispatch(253, args=()) + def _set_glyphs(self): + w = self._read_arg(4, signed=False) + k = self._read_arg(2, signed=False) + xy = [self._read_arg(4, signed=True) for _ in range(2 * k)] + g = [self._read_arg(2, signed=False) for _ in range(k)] + font = self.fonts[self.f] + for i in range(k): + self.text.append(Text(self.h + xy[2 * i], self.v + xy[2 * i + 1], + font, g[i], font._width_of(g[i]))) + self.h += w + + @_dispatch(254, args=()) + def _set_text_and_glyphs(self): + l = self._read_arg(2, signed=False) + t = self.file.read(2 * l) # utf16 + w = self._read_arg(4, signed=False) + k = self._read_arg(2, signed=False) + xy = [self._read_arg(4, signed=True) for _ in range(2 * k)] + g = [self._read_arg(2, signed=False) for _ in range(k)] + font = self.fonts[self.f] + for i in range(k): + self.text.append(Text(self.h + xy[2 * i], self.v + xy[2 * i + 1], + font, g[i], font._width_of(g[i]))) + self.h += w + + @_dispatch(255) + def _malformed(self, raw): + raise ValueError("unknown command: byte 255") class DviFont: @@ -543,10 +643,10 @@ class DviFont: ---------- scale : float Factor by which the font is scaled from its natural size. - tfm : Tfm + metrics : Tfm | TtfMetrics TeX font metrics for this font texname : bytes - Name of the font as used internally by TeX and friends, as an ASCII + Name of the font as used internally in the DVI file, as an ASCII bytestring. This is usually very different from any external font names; `PsfontsMap` can be used to find the external name of the font. vf : Vf @@ -555,29 +655,80 @@ class DviFont: Attributes ---------- texname : bytes + fname : str + Compatibility shim so that DviFont can be used with + ``_backend_pdf_ps.CharacterTracker``; not a real filename. size : float Size of the font in Adobe points, converted from the slightly smaller TeX points. - widths : list - Widths of glyphs in glyph-space units, typically 1/1000ths of - the point size. - """ - __slots__ = ('texname', 'size', 'widths', '_scale', '_vf', '_tfm') - def __init__(self, scale, tfm, texname, vf): + def __init__(self, scale, metrics, texname, vf): _api.check_isinstance(bytes, texname=texname) self._scale = scale - self._tfm = tfm + self._metrics = metrics self.texname = texname self._vf = vf - self.size = scale * (72.0 / (72.27 * 2**16)) - try: - nchars = max(tfm.width) + 1 - except ValueError: - nchars = 0 - self.widths = [(1000*tfm.width.get(char, 0)) >> 20 - for char in range(nchars)] + self._path = None + self._encoding = None + + @classmethod + def from_luatex(cls, scale, texname): + path_b, sep, rest = texname[1:].rpartition(b"]") + if not (texname.startswith(b"[") and sep and rest[:1] in [b"", b":"]): + raise ValueError(f"Invalid modern font name: {texname}") + # utf8 on Windows, not utf16! + path = path_b.decode("utf8") if os.name == "nt" else os.fsdecode(path_b) + subfont = 0 + effects = {} + if rest[1:]: + for kv in rest[1:].decode("ascii").split(";"): + key, val = kv.split("=", 1) + if key == "index": + subfont = val + elif key in ["embolden", "slant", "extend"]: + effects[key] = int(val) / 65536 + else: + _log.warning("Ignoring invalid key-value pair: %r", kv) + metrics = TtfMetrics(path) + font = cls(scale, metrics, texname, vf=None) + font._path = Path(path) + font.subfont = subfont + font.effects = effects + return font + + @classmethod + def from_xetex(cls, scale, texname, subfont, effects): + # utf8 on Windows, not utf16! + path = texname.decode("utf8") if os.name == "nt" else os.fsdecode(texname) + metrics = TtfMetrics(path) + font = cls(scale, metrics, b"[" + texname + b"]", vf=None) + font._path = Path(path) + font.subfont = subfont + font.effects = effects + return font + + size = property(lambda self: self._scale * (72.0 / (72.27 * 2**16))) + + widths = _api.deprecated("3.11")(property(lambda self: [ + (1000 * self._tfm.width.get(char, 0)) >> 20 + for char in range(max(self._tfm.width, default=-1) + 1)])) + + @property + def fname(self): + """A fake filename""" + return self.texname.decode('latin-1') + + @property + def face_index(self): # For compatibility with FT2Font. + return 0 + + def _get_fontmap(self, string): + """Get the mapping from characters to the font that includes them. + + Each value maps to self; there is no fallback mechanism for DviFont. + """ + return {char: self for char in string} def __eq__(self, other): return (type(self) is type(other) @@ -591,32 +742,86 @@ def __repr__(self): def _width_of(self, char): """Width of char in dvi units.""" - width = self._tfm.width.get(char, None) - if width is not None: - return _mul2012(width, self._scale) - _log.debug('No width for char %d in font %s.', char, self.texname) - return 0 + metrics = self._metrics.get_metrics(char) + if metrics is None: + _log.debug('No width for char %d in font %s.', char, self.texname) + return 0 + return _mul1220(metrics.tex_width, self._scale) def _height_depth_of(self, char): """Height and depth of char in dvi units.""" - result = [] - for metric, name in ((self._tfm.height, "height"), - (self._tfm.depth, "depth")): - value = metric.get(char, None) - if value is None: - _log.debug('No %s for char %d in font %s', - name, char, self.texname) - result.append(0) - else: - result.append(_mul2012(value, self._scale)) + metrics = self._metrics.get_metrics(char) + if metrics is None: + _log.debug('No metrics for char %d in font %s', char, self.texname) + return [0, 0] + hd = [ + _mul1220(metrics.tex_height, self._scale), + _mul1220(metrics.tex_depth, self._scale), + ] # cmsyXX (symbols font) glyph 0 ("minus") has a nonzero descent # so that TeX aligns equations properly # (https://tex.stackexchange.com/q/526103/) # but we actually care about the rasterization depth to align # the dvipng-generated images. if re.match(br'^cmsy\d+$', self.texname) and char == 0: - result[-1] = 0 - return result + hd[-1] = 0 + return hd + + def resolve_path(self): + if self._path is None: + fontmap = PsfontsMap(find_tex_file("pdftex.map")) + try: + psfont = fontmap[self.texname] + except LookupError as exc: + try: + find_tex_file(f"{self.texname.decode('ascii')}.mf") + except FileNotFoundError: + raise exc from None + else: + self._path = Path(find_tex_file( + f"{self.texname.decode('ascii')}.600pk")) + else: + if psfont.filename is None: + raise ValueError("No usable font file found for {} ({}); " + "the font may lack a Type-1 version" + .format(psfont.psname.decode("ascii"), + psfont.texname.decode("ascii"))) + self._path = Path(psfont.filename) + return self._path + + @cached_property + def subfont(self): + return 0 + + @cached_property + def effects(self): + if self.resolve_path().match("*.600pk"): + return {} + return PsfontsMap(find_tex_file("pdftex.map"))[self.texname].effects + + def _index_dvi_to_freetype(self, idx): + """Convert dvi glyph indices to FreeType ones.""" + # Glyphs indices stored in the dvi file map to FreeType glyph indices + # (i.e., which can be passed to FT_Load_Glyph) in various ways: + # - for xetex & luatex "native fonts", dvi indices are directly equal + # to FreeType indices. + # - if pdftex.map specifies an ".enc" file for the font, that file maps + # dvi indices to Adobe glyph names, which can then be converted to + # FreeType glyph indices with FT_Get_Name_Index. + # - if no ".enc" file is specified, then the font must be a Type 1 + # font, and dvi indices directly index into the font's CharStrings + # vector. + if self.texname.startswith(b"["): + return idx + if self._encoding is None: + face = font_manager.get_font(self.resolve_path()) + psfont = PsfontsMap(find_tex_file("pdftex.map"))[self.texname] + if psfont.encoding: + self._encoding = [face.get_name_index(name) + for name in _parse_enc(psfont.encoding)] + else: + self._encoding = face._get_type1_encoding_vector() + return self._encoding[idx] class Vf(Dvi): @@ -632,7 +837,7 @@ class Vf(Dvi): The virtual font format is a derivative of dvi: http://mirrors.ctan.org/info/knuth/virtual-fonts This class reuses some of the machinery of `Dvi` - but replaces the `_read` loop and dispatch mechanism. + but replaces the `!_read` loop and dispatch mechanism. Examples -------- @@ -660,8 +865,8 @@ def _read(self): Read one page from the file. Return True if successful, False if there were no more pages. """ - packet_char, packet_ends = None, None - packet_len, packet_width = None, None + packet_char = packet_ends = None + packet_len = packet_width = None while True: byte = self.file.read(1)[0] # If we are in a packet, execute the dvi instructions @@ -669,74 +874,101 @@ def _read(self): byte_at = self.file.tell()-1 if byte_at == packet_ends: self._finalize_packet(packet_char, packet_width) - packet_len, packet_char, packet_width = None, None, None + packet_len = packet_char = packet_width = None # fall through to out-of-packet code elif byte_at > packet_ends: raise ValueError("Packet length mismatch in vf file") else: if byte in (139, 140) or byte >= 243: - raise ValueError( - "Inappropriate opcode %d in vf file" % byte) + raise ValueError(f"Inappropriate opcode {byte} in vf file") Dvi._dtable[byte](self, byte) continue # We are outside a packet if byte < 242: # a short packet (length given by byte) packet_len = byte - packet_char, packet_width = self._arg(1), self._arg(3) + packet_char = self._read_arg(1) + packet_width = self._read_arg(3) packet_ends = self._init_packet(byte) self.state = _dvistate.inpage elif byte == 242: # a long packet - packet_len, packet_char, packet_width = \ - [self._arg(x) for x in (4, 4, 4)] + packet_len = self._read_arg(4) + packet_char = self._read_arg(4) + packet_width = self._read_arg(4) self._init_packet(packet_len) elif 243 <= byte <= 246: - k = self._arg(byte - 242, byte == 246) - c, s, d, a, l = [self._arg(x) for x in (4, 4, 4, 1, 1)] + k = self._read_arg(byte - 242, byte == 246) + c = self._read_arg(4) + s = self._read_arg(4) + d = self._read_arg(4) + a = self._read_arg(1) + l = self._read_arg(1) self._fnt_def_real(k, c, s, d, a, l) if self._first_font is None: self._first_font = k elif byte == 247: # preamble - i, k = self._arg(1), self._arg(1) + i = self._read_arg(1) + k = self._read_arg(1) x = self.file.read(k) - cs, ds = self._arg(4), self._arg(4) + cs = self._read_arg(4) + ds = self._read_arg(4) self._pre(i, x, cs, ds) elif byte == 248: # postamble (just some number of 248s) break else: - raise ValueError("Unknown vf opcode %d" % byte) + raise ValueError(f"Unknown vf opcode {byte}") def _init_packet(self, pl): if self.state != _dvistate.outer: raise ValueError("Misplaced packet in vf file") - self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 - self.stack, self.text, self.boxes = [], [], [] + self.h = self.v = self.w = self.x = self.y = self.z = 0 + self.stack = [] + self.text = [] + self.boxes = [] self.f = self._first_font + self._missing_font = None return self.file.tell() + pl def _finalize_packet(self, packet_char, packet_width): - self._chars[packet_char] = Page( - text=self.text, boxes=self.boxes, width=packet_width, - height=None, descent=None) + if not self._missing_font: # Otherwise we don't have full glyph definition. + self._chars[packet_char] = Page( + text=self.text, boxes=self.boxes, width=packet_width, + height=None, descent=None) self.state = _dvistate.outer def _pre(self, i, x, cs, ds): if self.state is not _dvistate.pre: raise ValueError("pre command in middle of vf file") if i != 202: - raise ValueError("Unknown vf format %d" % i) + raise ValueError(f"Unknown vf format {i}") if len(x): _log.debug('vf file comment: %s', x) self.state = _dvistate.outer # cs = checksum, ds = design size -def _mul2012(num1, num2): - """Multiply two numbers in 20.12 fixed point format.""" +def _mul1220(num1, num2): + """Multiply two numbers in 12.20 fixed point format.""" # Separated into a function because >> has surprising precedence return (num1*num2) >> 20 +@dataclasses.dataclass(frozen=True, kw_only=True) +class TexMetrics: + """ + Metrics of a glyph, with TeX semantics. + + TeX metrics have different semantics from FreeType metrics: tex_width + corresponds to FreeType's ``advance`` (i.e., including whitespace padding); + tex_height to ``bearingY`` (how much the glyph extends over the baseline); + tex_depth to ``height - bearingY`` (how much the glyph extends under the + baseline, as a positive number). + """ + tex_width: int + tex_height: int + tex_depth: int + + class Tfm: """ A TeX Font Metric file. @@ -752,13 +984,9 @@ class Tfm: checksum : int Used for verifying against the dvi file. design_size : int - Design size of the font (unknown units) - width, height, depth : dict - Dimensions of each character, need to be scaled by the factor - specified in the dvi file. These are dicts because indexing may - not start from 0. + Design size of the font (in 12.20 TeX points); unused because it is + overridden by the scale factor specified in the dvi file. """ - __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth') def __init__(self, filename): _log.debug('opening tfm file %s', filename) @@ -774,13 +1002,47 @@ def __init__(self, filename): widths = struct.unpack(f'!{nw}i', file.read(4*nw)) heights = struct.unpack(f'!{nh}i', file.read(4*nh)) depths = struct.unpack(f'!{nd}i', file.read(4*nd)) - self.width, self.height, self.depth = {}, {}, {} + self._glyph_metrics = {} for idx, char in enumerate(range(bc, ec+1)): byte0 = char_info[4*idx] byte1 = char_info[4*idx+1] - self.width[char] = widths[byte0] - self.height[char] = heights[byte1 >> 4] - self.depth[char] = depths[byte1 & 0xf] + self._glyph_metrics[char] = TexMetrics( + tex_width=widths[byte0], + tex_height=heights[byte1 >> 4], + tex_depth=depths[byte1 & 0xf], + ) + + def get_metrics(self, idx): + """Return a glyph's TexMetrics, or None if unavailable.""" + return self._glyph_metrics.get(idx) + + width = _api.deprecated("3.11", alternative="get_metrics")( + property(lambda self: {c: m.tex_width for c, m in self._glyph_metrics})) + height = _api.deprecated("3.11", alternative="get_metrics")( + property(lambda self: {c: m.tex_height for c, m in self._glyph_metrics})) + depth = _api.deprecated("3.11", alternative="get_metrics")( + property(lambda self: {c: m.tex_depth for c, m in self._glyph_metrics})) + + +class TtfMetrics: + def __init__(self, filename): + self._face = font_manager.get_font(filename, hinting_factor=1) + + def get_metrics(self, idx): + # _mul1220 uses a truncating bitshift for compatibility with dvitype. + # When upem is 2048 the conversion to 12.20 is exact, but when + # upem is 1000 (e.g. lmroman10-regular.otf) the metrics themselves + # are not exactly representable as 12.20 fp. Manual testing via + # \sbox0{x}\count0=\wd0\typeout{\the\count0} suggests that metrics + # are rounded (not truncated) after conversion to 12.20 and before + # multiplication by the scale. + upem = self._face.units_per_EM # Usually 2048 or 1000. + g = self._face.load_glyph(idx, LoadFlags.NO_SCALE) + return TexMetrics( + tex_width=round(g.horiAdvance / upem * 2**20), + tex_height=round(g.horiBearingY / upem * 2**20), + tex_depth=round((g.height - g.horiBearingY) / upem * 2**20), + ) PsFont = namedtuple('PsFont', 'texname psname effects encoding filename') @@ -861,11 +1123,10 @@ def __getitem__(self, texname): return self._parsed[texname] except KeyError: raise LookupError( - f"An associated PostScript font (required by Matplotlib) " - f"could not be found for TeX font {texname.decode('ascii')!r} " - f"in {self._filename!r}; this problem can often be solved by " - f"installing a suitable PostScript font package in your TeX " - f"package manager") from None + f"The font map {self._filename!r} is missing a PostScript font " + f"associated to TeX font {texname.decode('ascii')!r}; this problem can " + f"often be solved by installing a suitable PostScript font package in " + f"your TeX package manager") from None def _parse_and_cache_line(self, line): """ @@ -976,8 +1237,7 @@ def _parse_enc(path): Returns ------- list - The nth entry of the list is the PostScript glyph name of the nth - glyph. + The nth list item is the PostScript glyph name of the nth glyph. """ no_comments = re.sub("%.*", "", Path(path).read_text(encoding="ascii")) array = re.search(r"(?s)\[(.*)\]", no_comments).group(1) @@ -989,7 +1249,7 @@ def _parse_enc(path): class _LuatexKpsewhich: - @lru_cache # A singleton. + @cache # A singleton. def __new__(cls): self = object.__new__(cls) self._proc = self._new_proc() @@ -997,9 +1257,12 @@ def __new__(cls): def _new_proc(self): return subprocess.Popen( - ["luatex", "--luaonly", - str(cbook._get_data_path("kpsewhich.lua"))], - stdin=subprocess.PIPE, stdout=subprocess.PIPE) + ["luatex", "--luaonly", str(cbook._get_data_path("kpsewhich.lua"))], + # mktexpk logs to stderr; suppress that. + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, + # Store generated pk fonts in our own cache. + env={"MT_VARTEXFONTS": str(Path(mpl.get_cachedir(), "vartexfonts")), + **os.environ}) def search(self, filename): if self._proc.poll() is not None: # Dead, restart it. @@ -1038,7 +1301,7 @@ def find_tex_file(filename): try: lk = _LuatexKpsewhich() - except FileNotFoundError: + except (FileNotFoundError, OSError): lk = None # Fallback to directly calling kpsewhich, as below. if lk: @@ -1051,14 +1314,17 @@ def find_tex_file(filename): kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'}, 'encoding': 'utf-8'} else: # On POSIX, run through the equivalent of os.fsdecode(). - kwargs = {'encoding': sys.getfilesystemencoding(), + kwargs = {'env': {**os.environ}, + 'encoding': sys.getfilesystemencoding(), 'errors': 'surrogateescape'} + kwargs['env'].update( + MT_VARTEXFONTS=str(Path(mpl.get_cachedir(), "vartexfonts"))) try: - path = (cbook._check_and_log_subprocess(['kpsewhich', filename], - _log, **kwargs) - .rstrip('\n')) - except (FileNotFoundError, RuntimeError): + path = cbook._check_and_log_subprocess( + ['kpsewhich', '-mktex=pk', filename], _log, **kwargs, + ).rstrip('\n') + except (FileNotFoundError, OSError, RuntimeError): path = None if path: @@ -1079,29 +1345,40 @@ def _fontfile(cls, suffix, texname): if __name__ == '__main__': - from argparse import ArgumentParser import itertools + from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument("filename") parser.add_argument("dpi", nargs="?", type=float, default=None) + parser.add_argument("-d", "--debug", action="store_true") args = parser.parse_args() + + if args.debug: + logging.basicConfig(level=logging.DEBUG) + + def _print_fields(*args): + print(" ".join(map("{:>11}".format, args))) + with Dvi(args.filename, args.dpi) as dvi: - fontmap = PsfontsMap(find_tex_file('pdftex.map')) for page in dvi: - print(f"=== new page === " + print(f"=== NEW PAGE === " f"(w: {page.width}, h: {page.height}, d: {page.descent})") - for font, group in itertools.groupby( - page.text, lambda text: text.font): - print(f"font: {font.texname.decode('latin-1')!r}\t" - f"scale: {font._scale / 2 ** 20}") - print("x", "y", "glyph", "chr", "w", "(glyphs)", sep="\t") + print("--- GLYPHS ---") + for font, group in itertools.groupby(page.text, lambda text: text.font): + font_name = (font.texname.decode("utf8") if os.name == "nt" + else os.fsdecode(font.texname)) + if isinstance(font._metrics, Tfm): + print(f"font: {font_name} at {font.resolve_path()}") + else: + print(f"font: {font_name}") + print(f"scale: {font._scale / 2 ** 20}") + _print_fields("x", "y", "glyph", "chr", "w") for text in group: - print(text.x, text.y, text.glyph, - chr(text.glyph) if chr(text.glyph).isprintable() - else ".", - text.width, sep="\t") + _print_fields(text.x, text.y, text.glyph, + text._as_unicode_or_name(), text.width) if page.boxes: - print("x", "y", "h", "w", "", "(boxes)", sep="\t") + print("--- BOXES ---") + _print_fields("x", "y", "h", "w") for box in page.boxes: - print(box.x, box.y, box.height, box.width, sep="\t") + _print_fields(box.x, box.y, box.height, box.width) diff --git a/lib/matplotlib/dviread.pyi b/lib/matplotlib/dviread.pyi index bf5cfcbe317a..de429bd0b7f1 100644 --- a/lib/matplotlib/dviread.pyi +++ b/lib/matplotlib/dviread.pyi @@ -1,3 +1,4 @@ +import dataclasses from pathlib import Path import io import os @@ -5,13 +6,17 @@ from enum import Enum from collections.abc import Generator from typing import NamedTuple +from typing import Self + +from .ft2font import CharacterCodeType, GlyphIndexType + class _dvistate(Enum): - pre: int - outer: int - inpage: int - post_post: int - finale: int + pre = ... + outer = ... + inpage = ... + post_post = ... + finale = ... class Page(NamedTuple): text: list[Text] @@ -30,7 +35,7 @@ class Text(NamedTuple): x: int y: int font: DviFont - glyph: int + glyph: CharacterCodeType width: int @property def font_path(self) -> Path: ... @@ -39,7 +44,9 @@ class Text(NamedTuple): @property def font_effects(self) -> dict[str, float]: ... @property - def glyph_name_or_index(self) -> int | str: ... + def index(self) -> GlyphIndexType: ... # type: ignore[override] + @property + def glyph_name_or_index(self) -> GlyphIndexType | str: ... class Dvi: file: io.BufferedReader @@ -47,33 +54,65 @@ class Dvi: fonts: dict[int, DviFont] state: _dvistate def __init__(self, filename: str | os.PathLike, dpi: float | None) -> None: ... - # Replace return with Self when py3.9 is dropped - def __enter__(self) -> Dvi: ... + def __enter__(self) -> Self: ... def __exit__(self, etype, evalue, etrace) -> None: ... def __iter__(self) -> Generator[Page, None, None]: ... def close(self) -> None: ... class DviFont: texname: bytes - size: float - widths: list[int] def __init__( - self, scale: float, tfm: Tfm, texname: bytes, vf: Vf | None + self, scale: float, metrics: Tfm | TtfMetrics, texname: bytes, vf: Vf | None ) -> None: ... + @classmethod + def from_luatex(cls, scale: float, texname: bytes) -> DviFont: ... + @classmethod + def from_xetex( + cls, scale: float, texname: bytes, subfont: int, effects: dict[str, float] + ) -> DviFont: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... + @property + def size(self) -> float: ... + @property + def widths(self) -> list[int]: ... + @property + def fname(self) -> str: ... + @property + def face_index(self) -> int: ... + def resolve_path(self) -> Path: ... + @property + def subfont(self) -> int: ... + @property + def effects(self) -> dict[str, float]: ... class Vf(Dvi): def __init__(self, filename: str | os.PathLike) -> None: ... def __getitem__(self, code: int) -> Page: ... +@dataclasses.dataclass(frozen=True, kw_only=True) +class TexMetrics: + tex_width: int + tex_height: int + tex_depth: int + # work around mypy not respecting kw_only=True in stub files + __match_args__ = () + class Tfm: checksum: int design_size: int - width: dict[int, int] - height: dict[int, int] - depth: dict[int, int] def __init__(self, filename: str | os.PathLike) -> None: ... + def get_metrics(self, idx: int) -> TexMetrics | None: ... + @property + def width(self) -> dict[int, int]: ... + @property + def height(self) -> dict[int, int]: ... + @property + def depth(self) -> dict[int, int]: ... + +class TtfMetrics: + def __init__(self, filename: str | os.PathLike) -> None: ... + def get_metrics(self, idx: int) -> TexMetrics: ... class PsFont(NamedTuple): texname: bytes @@ -83,8 +122,7 @@ class PsFont(NamedTuple): filename: str class PsfontsMap: - # Replace return with Self when py3.9 is dropped - def __new__(cls, filename: str | os.PathLike) -> PsfontsMap: ... + def __new__(cls, filename: str | os.PathLike) -> Self: ... def __getitem__(self, texname: bytes) -> PsFont: ... def find_tex_file(filename: str | os.PathLike) -> str: ... diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 0a0ff01a2571..ad0206e0db5c 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -6,9 +6,8 @@ Many methods are implemented in `FigureBase`. `SubFigure` - A logical figure inside a figure, usually added to a figure (or parent - `SubFigure`) with `Figure.add_subfigure` or `Figure.subfigures` methods - (provisional API v3.4). + A logical figure inside a figure, usually added to a figure (or parent `SubFigure`) + with `Figure.add_subfigure` or `Figure.subfigures` methods. Figures are typically created using pyplot methods `~.pyplot.figure`, `~.pyplot.subplots`, and `~.pyplot.subplot_mosaic`. @@ -30,6 +29,7 @@ from contextlib import ExitStack import inspect import itertools +import functools import logging from numbers import Integral import threading @@ -63,8 +63,8 @@ def _stale_figure_callback(self, val): - if self.figure: - self.figure.stale = val + if (fig := self.get_figure(root=False)) is not None: + fig.stale = val class _AxesStack: @@ -194,14 +194,15 @@ def autofmt_xdate( Selects which ticklabels to rotate. """ _api.check_in_list(['major', 'minor', 'both'], which=which) - allsubplots = all(ax.get_subplotspec() for ax in self.axes) - if len(self.axes) == 1: + axes = [ax for ax in self.axes if ax._label != ''] + allsubplots = all(ax.get_subplotspec() for ax in axes) + if len(axes) == 1: for label in self.axes[0].get_xticklabels(which=which): label.set_ha(ha) label.set_rotation(rotation) else: if allsubplots: - for ax in self.get_axes(): + for ax in axes: if ax.get_subplotspec().is_last_row(): for label in ax.get_xticklabels(which=which): label.set_ha(ha) @@ -211,7 +212,8 @@ def autofmt_xdate( label.set_visible(False) ax.set_xlabel('') - if allsubplots: + engine = self.get_layout_engine() + if allsubplots and (engine is None or engine.adjust_compatible): self.subplots_adjust(bottom=bottom) self.stale = True @@ -227,6 +229,67 @@ def get_children(self): *self.legends, *self.subfigs] + def get_figure(self, root=None): + """ + Return the `.Figure` or `.SubFigure` instance the (Sub)Figure belongs to. + + Parameters + ---------- + root : bool, default=True + If False, return the (Sub)Figure this artist is on. If True, + return the root Figure for a nested tree of SubFigures. + + .. deprecated:: 3.10 + + From version 3.12 *root* will default to False. + """ + if self._root_figure is self: + # Top level Figure + return self + + if self._parent is self._root_figure: + # Return early to prevent the deprecation warning when *root* does not + # matter + return self._parent + + if root is None: + # When deprecation expires, consider removing the docstring and just + # inheriting the one from Artist. + message = ('From Matplotlib 3.12 SubFigure.get_figure will by default ' + 'return the direct parent figure, which may be a SubFigure. ' + 'To suppress this warning, pass the root parameter. Pass ' + '`True` to maintain the old behavior and `False` to opt-in to ' + 'the future behavior.') + _api.warn_deprecated('3.10', message=message) + root = True + + if root: + return self._root_figure + + return self._parent + + def set_figure(self, fig): + """ + .. deprecated:: 3.10 + Currently this method will raise an exception if *fig* is anything other + than the root `.Figure` this (Sub)Figure is on. In future it will always + raise an exception. + """ + no_switch = ("The parent and root figures of a (Sub)Figure are set at " + "instantiation and cannot be changed.") + if fig is self._root_figure: + _api.warn_deprecated( + "3.10", + message=(f"{no_switch} From Matplotlib 3.12 this operation will raise " + "an exception.")) + return + + raise ValueError(no_switch) + + figure = property(functools.partial(get_figure, root=True), set_figure, + doc=("The root `Figure`. To get the parent of a `SubFigure`, " + "use the `get_figure` method.")) + def contains(self, mouseevent): """ Test whether the mouse event occurred on the figure. @@ -313,11 +376,18 @@ def _suplabels(self, t, info, **kwargs): else: suplab = self.text(x, y, t, **kwargs) setattr(self, info['name'], suplab) + suplab._remove_method = functools.partial(self._remove_suplabel, + name=info['name']) + suplab._autopos = autopos self.stale = True return suplab - @_docstring.Substitution(x0=0.5, y0=0.98, name='suptitle', ha='center', + def _remove_suplabel(self, label, name): + self.texts.remove(label) + setattr(self, name, None) + + @_docstring.Substitution(x0=0.5, y0=0.98, name='super title', ha='center', va='top', rc='title') @_docstring.copy(_suplabels) def suptitle(self, t, **kwargs): @@ -332,7 +402,7 @@ def get_suptitle(self): text_obj = self._suptitle return "" if text_obj is None else text_obj.get_text() - @_docstring.Substitution(x0=0.5, y0=0.01, name='supxlabel', ha='center', + @_docstring.Substitution(x0=0.5, y0=0.01, name='super xlabel', ha='center', va='bottom', rc='label') @_docstring.copy(_suplabels) def supxlabel(self, t, **kwargs): @@ -347,7 +417,7 @@ def get_supxlabel(self): text_obj = self._supxlabel return "" if text_obj is None else text_obj.get_text() - @_docstring.Substitution(x0=0.02, y0=0.5, name='supylabel', ha='left', + @_docstring.Substitution(x0=0.02, y0=0.5, name='super ylabel', ha='left', va='center', rc='label') @_docstring.copy(_suplabels) def supylabel(self, t, **kwargs): @@ -465,7 +535,7 @@ def add_artist(self, artist, clip=False): self.stale = True return artist - @_docstring.dedent_interpd + @_docstring.interpd def add_axes(self, *args, **kwargs): """ Add an `~.axes.Axes` to the figure. @@ -552,22 +622,22 @@ def add_axes(self, *args, **kwargs): """ if not len(args) and 'rect' not in kwargs: - raise TypeError( - "add_axes() missing 1 required positional argument: 'rect'") + raise TypeError("add_axes() missing 1 required positional argument: 'rect'") elif 'rect' in kwargs: if len(args): - raise TypeError( - "add_axes() got multiple values for argument 'rect'") + raise TypeError("add_axes() got multiple values for argument 'rect'") args = (kwargs.pop('rect'), ) + if len(args) != 1: + raise _api.nargs_error("add_axes", 1, len(args)) if isinstance(args[0], Axes): - a, *extra_args = args + a, = args key = a._projection_init - if a.get_figure() is not self: + if a.get_figure(root=False) is not self: raise ValueError( "The Axes must have been created in the present figure") else: - rect, *extra_args = args + rect, = args if not np.isfinite(rect).all(): raise ValueError(f'all entries in rect must be finite not {rect}') projection_class, pkw = self._process_projection_requirements(**kwargs) @@ -576,14 +646,9 @@ def add_axes(self, *args, **kwargs): a = projection_class(self, rect, **pkw) key = (projection_class, pkw) - if extra_args: - _api.warn_deprecated( - "3.8", - name="Passing more than one positional argument to Figure.add_axes", - addendum="Any additional positional arguments are currently ignored.") return self._add_axes_internal(a, key) - @_docstring.dedent_interpd + @_docstring.interpd def add_subplot(self, *args, **kwargs): """ Add an `~.axes.Axes` to the figure as part of a subplot arrangement. @@ -694,7 +759,7 @@ def add_subplot(self, *args, **kwargs): and args[0].get_subplotspec()): ax = args[0] key = ax._projection_init - if ax.get_figure() is not self: + if ax.get_figure(root=False) is not self: raise ValueError("The Axes must have been created in " "the present figure") else: @@ -755,6 +820,8 @@ def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False, When subplots have a shared axis that has units, calling `.Axis.set_units` will update each axis with the new units. + Note that it is not possible to unshare axes. + squeeze : bool, default: True - If True, extra dimensions are squeezed out from the returned array of Axes: @@ -883,11 +950,11 @@ def _remove_axes(self, ax, owners): self._axobservers.process("_axes_change_event", self) self.stale = True - self.canvas.release_mouse(ax) + self._root_figure.canvas.release_mouse(ax) for name in ax._axis_names: # Break link between any shared Axes grouper = ax._shared_axes[name] - siblings = [other for other in grouper.get_siblings(ax) if other is not ax] + siblings = grouper.get_siblings(ax, include_self=False) if not siblings: # Axes was not shared along this axis; we're done. continue grouper.remove(ax) @@ -929,6 +996,7 @@ def clear(self, keep_observers=False): self.texts = [] self.images = [] self.legends = [] + self.subplotpars.reset() if not keep_observers: self._axobservers = cbook.CallbackRegistry() self._suptitle = None @@ -960,7 +1028,7 @@ def clf(self, keep_observers=False): # " legend(" -> " figlegend(" for the signatures # "fig.legend(" -> "plt.figlegend" for the code examples # "ax.plot" -> "plt.plot" for consistency in using pyplot when able - @_docstring.dedent_interpd + @_docstring.interpd def legend(self, *args, **kwargs): """ Place a legend on the figure. @@ -1080,7 +1148,7 @@ def legend(self, *args, **kwargs): self.stale = True return l - @_docstring.dedent_interpd + @_docstring.interpd def text(self, x, y, s, fontdict=None, **kwargs): """ Add text to figure. @@ -1130,7 +1198,7 @@ def text(self, x, y, s, fontdict=None, **kwargs): self.stale = True return text - @_docstring.dedent_interpd + @_docstring.interpd def colorbar( self, mappable, cax=None, ax=None, use_gridspec=True, **kwargs): """ @@ -1139,17 +1207,18 @@ def colorbar( Parameters ---------- mappable - The `matplotlib.cm.ScalarMappable` (i.e., `.AxesImage`, + The `matplotlib.colorizer.ColorizingArtist` (i.e., `.AxesImage`, `.ContourSet`, etc.) described by this colorbar. This argument is mandatory for the `.Figure.colorbar` method but optional for the `.pyplot.colorbar` function, which sets the default to the current image. - Note that one can create a `.ScalarMappable` "on-the-fly" to - generate colorbars not attached to a previously drawn artist, e.g. + Note that one can create a `.colorizer.ColorizingArtist` "on-the-fly" + to generate colorbars not attached to a previously drawn artist, e.g. :: - fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax) + cr = colorizer.Colorizer(norm=norm, cmap=cmap) + fig.colorbar(colorizer.ColorizingArtist(cr), ax=ax) cax : `~matplotlib.axes.Axes`, optional Axes into which the colorbar will be drawn. If `None`, then a new @@ -1218,7 +1287,7 @@ def colorbar( fig = ( # Figure of first Axes; logic copied from make_axes. [*ax.flat] if isinstance(ax, np.ndarray) else [*ax] if np.iterable(ax) - else [ax])[0].figure + else [ax])[0].get_figure(root=False) current_ax = fig.gca() if (fig.get_layout_engine() is not None and not fig.get_layout_engine().colorbar_gridspec): @@ -1233,24 +1302,21 @@ def colorbar( fig.sca(current_ax) cax.grid(visible=False, which='both', axis='both') - if hasattr(mappable, "figure") and mappable.figure is not None: - # Get top level artists - mappable_host_fig = mappable.figure - if isinstance(mappable_host_fig, mpl.figure.SubFigure): - mappable_host_fig = mappable_host_fig.figure + if (hasattr(mappable, "get_figure") and + (mappable_host_fig := mappable.get_figure(root=True)) is not None): # Warn in case of mismatch - if mappable_host_fig is not self.figure: + if mappable_host_fig is not self._root_figure: _api.warn_external( f'Adding colorbar to a different Figure ' - f'{repr(mappable.figure)} than ' - f'{repr(self.figure)} which ' + f'{repr(mappable_host_fig)} than ' + f'{repr(self._root_figure)} which ' f'fig.colorbar is called on.') NON_COLORBAR_KEYS = [ # remove kws that cannot be passed to Colorbar 'fraction', 'pad', 'shrink', 'aspect', 'anchor', 'panchor'] cb = cbar.Colorbar(cax, mappable, **{ k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS}) - cax.figure.stale = True + cax.get_figure(root=False).stale = True return cb def subplots_adjust(self, left=None, bottom=None, right=None, top=None, @@ -1261,6 +1327,8 @@ def subplots_adjust(self, left=None, bottom=None, right=None, top=None, Unset parameters are left unmodified; initial values are given by :rc:`figure.subplot.[name]`. + .. plot:: _embedded_plots/figure_subplots_adjust.py + Parameters ---------- left : float, optional @@ -1323,16 +1391,15 @@ def align_xlabels(self, axs=None): Notes ----- - This assumes that ``axs`` are from the same `.GridSpec`, so that - their `.SubplotSpec` positions correspond to figure positions. + This assumes that all Axes in ``axs`` are from the same `.GridSpec`, + so that their `.SubplotSpec` positions correspond to figure positions. Examples -------- Example with rotated xtick labels:: fig, axs = plt.subplots(1, 2) - for tick in axs[0].get_xticklabels(): - tick.set_rotation(55) + axs[0].tick_params(axis='x', rotation=55) axs[0].set_xlabel('XLabel 0') axs[1].set_xlabel('XLabel 1') fig.align_xlabels() @@ -1385,8 +1452,8 @@ def align_ylabels(self, axs=None): Notes ----- - This assumes that ``axs`` are from the same `.GridSpec`, so that - their `.SubplotSpec` positions correspond to figure positions. + This assumes that all Axes in ``axs`` are from the same `.GridSpec`, + so that their `.SubplotSpec` positions correspond to figure positions. Examples -------- @@ -1441,8 +1508,8 @@ def align_titles(self, axs=None): Notes ----- - This assumes that ``axs`` are from the same `.GridSpec`, so that - their `.SubplotSpec` positions correspond to figure positions. + This assumes that all Axes in ``axs`` are from the same `.GridSpec`, + so that their `.SubplotSpec` positions correspond to figure positions. Examples -------- @@ -1485,6 +1552,11 @@ def align_labels(self, axs=None): matplotlib.figure.Figure.align_xlabels matplotlib.figure.Figure.align_ylabels matplotlib.figure.Figure.align_titles + + Notes + ----- + This assumes that all Axes in ``axs`` are from the same `.GridSpec`, + so that their `.SubplotSpec` positions correspond to figure positions. """ self.align_xlabels(axs=axs) self.align_ylabels(axs=axs) @@ -1547,8 +1619,8 @@ def subfigures(self, nrows=1, ncols=1, squeeze=True, the same as a figure, but cannot print itself. See :doc:`/gallery/subplots_axes_and_figures/subfigures`. - .. note:: - The *subfigure* concept is new in v3.4, and the API is still provisional. + .. versionchanged:: 3.10 + subfigures are now added in row-major order. Parameters ---------- @@ -1583,9 +1655,9 @@ def subfigures(self, nrows=1, ncols=1, squeeze=True, left=0, right=1, bottom=0, top=1) sfarr = np.empty((nrows, ncols), dtype=object) - for i in range(ncols): - for j in range(nrows): - sfarr[j, i] = self.add_subfigure(gs[j, i], **kwargs) + for i in range(nrows): + for j in range(ncols): + sfarr[i, j] = self.add_subfigure(gs[i, j], **kwargs) if self.get_layout_engine() is None and (wspace is not None or hspace is not None): @@ -1631,6 +1703,8 @@ def add_subfigure(self, subplotspec, **kwargs): sf = SubFigure(self, subplotspec, **kwargs) self.subfigs += [sf] sf._remove_method = self.subfigs.remove + sf.stale_callback = _stale_figure_callback + self.stale = True return sf def sca(self, a): @@ -1731,8 +1805,7 @@ def get_default_bbox_extra_artists(self): bbox_artists.extend(ax.get_default_bbox_extra_artists()) return bbox_artists - @_api.make_keyword_only("3.8", "bbox_extra_artists") - def get_tightbbox(self, renderer=None, bbox_extra_artists=None): + def get_tightbbox(self, renderer=None, *, bbox_extra_artists=None): """ Return a (tight) bounding box of the figure *in inches*. @@ -1760,7 +1833,7 @@ def get_tightbbox(self, renderer=None, bbox_extra_artists=None): """ if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() bb = [] if bbox_extra_artists is None: @@ -2058,9 +2131,9 @@ def _do_layout(gs, mosaic, unique_ids, nested): # go through the unique keys, for name in unique_ids: # sort out where each axes starts/ends - indx = np.argwhere(mosaic == name) - start_row, start_col = np.min(indx, axis=0) - end_row, end_col = np.max(indx, axis=0) + 1 + index = np.argwhere(mosaic == name) + start_row, start_col = np.min(index, axis=0) + end_row, end_col = np.max(index, axis=0) + 1 # and construct the slice object slc = (slice(start_row, end_row), slice(start_col, end_col)) # some light error checking @@ -2164,9 +2237,6 @@ class SubFigure(FigureBase): axsR = sfigs[1].subplots(2, 1) See :doc:`/gallery/subplots_axes_and_figures/subfigures` - - .. note:: - The *subfigure* concept is new in v3.4, and the API is still provisional. """ def __init__(self, parent, subplotspec, *, @@ -2208,21 +2278,18 @@ def __init__(self, parent, subplotspec, *, super().__init__(**kwargs) if facecolor is None: facecolor = "none" - if edgecolor is None: - edgecolor = mpl.rcParams['figure.edgecolor'] - if frameon is None: - frameon = mpl.rcParams['figure.frameon'] + edgecolor = mpl._val_or_rc(edgecolor, 'figure.edgecolor') + frameon = mpl._val_or_rc(frameon, 'figure.frameon') self._subplotspec = subplotspec self._parent = parent - self.figure = parent.figure + self._root_figure = parent._root_figure # subfigures use the parent axstack self._axstack = parent._axstack self.subplotpars = parent.subplotpars self.dpi_scale_trans = parent.dpi_scale_trans self._axobservers = parent._axobservers - self.canvas = parent.canvas self.transFigure = parent.transFigure self.bbox_relative = Bbox.null() self._redo_transform_rel_fig() @@ -2239,6 +2306,10 @@ def __init__(self, parent, subplotspec, *, self._set_artist_props(self.patch) self.patch.set_antialiased(False) + @property + def canvas(self): + return self._parent.canvas + @property def dpi(self): return self._parent.dpi @@ -2349,7 +2420,7 @@ def draw(self, renderer): renderer.open_group('subfigure', gid=self.get_gid()) self.patch.draw(renderer) mimage._draw_list_compositing_images( - renderer, self, artists, self.figure.suppressComposite) + renderer, self, artists, self.get_figure(root=True).suppressComposite) renderer.close_group('subfigure') finally: @@ -2413,8 +2484,16 @@ def __init__(self, """ Parameters ---------- - figsize : 2-tuple of floats, default: :rc:`figure.figsize` - Figure dimension ``(width, height)`` in inches. + figsize : (float, float) or (float, float, str), default: :rc:`figure.figsize` + The figure dimensions. This can be + + - a tuple ``(width, height, unit)``, where *unit* is one of "in" (inch), + "cm" (centimenter), "px" (pixel). + - a tuple ``(width, height)``, which is interpreted in inches, i.e. as + ``(width, height, "in")``. + + One of *width* or *height* may be ``None``; the respective value is + taken from :rc:`figure.figsize`. dpi : float, default: :rc:`figure.dpi` Dots per inch. @@ -2493,7 +2572,7 @@ def __init__(self, %(Figure:kwdoc)s """ super().__init__(**kwargs) - self.figure = self + self._root_figure = self self._layout_engine = None if layout is not None: @@ -2544,16 +2623,13 @@ def __init__(self, self._button_pick_id = connect('button_press_event', self.pick) self._scroll_pick_id = connect('scroll_event', self.pick) - if figsize is None: - figsize = mpl.rcParams['figure.figsize'] - if dpi is None: - dpi = mpl.rcParams['figure.dpi'] - if facecolor is None: - facecolor = mpl.rcParams['figure.facecolor'] - if edgecolor is None: - edgecolor = mpl.rcParams['figure.edgecolor'] - if frameon is None: - frameon = mpl.rcParams['figure.frameon'] + figsize = mpl._val_or_rc(figsize, 'figure.figsize') + dpi = mpl._val_or_rc(dpi, 'figure.dpi') + facecolor = mpl._val_or_rc(facecolor, 'figure.facecolor') + edgecolor = mpl._val_or_rc(edgecolor, 'figure.edgecolor') + frameon = mpl._val_or_rc(frameon, 'figure.frameon') + + figsize = _parse_figsize(figsize, dpi) if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any(): raise ValueError('figure size must be positive finite not ' @@ -2576,7 +2652,7 @@ def __init__(self, self._set_artist_props(self.patch) self.patch.set_antialiased(False) - FigureCanvasBase(self) # Set self.canvas. + self._set_base_canvas() if subplotpars is None: subplotpars = SubplotParams() @@ -2700,7 +2776,7 @@ def show(self, warn=True): .. warning:: - This does not manage an GUI event loop. Consequently, the figure + This does not manage a GUI event loop. Consequently, the figure may only be shown briefly or not shown at all if you or your environment are not managing an event loop. @@ -2750,6 +2826,36 @@ def axes(self): get_axes = axes.fget + @property + def number(self): + """The figure id, used to identify figures in `.pyplot`.""" + # Historically, pyplot dynamically added a number attribute to figure. + # However, this number must stay in sync with the figure manager. + # AFAICS overwriting the number attribute does not have the desired + # effect for pyplot. But there are some repos in GitHub that do change + # number. So let's take it slow and properly migrate away from writing. + # + # Making the dynamic attribute private and wrapping it in a property + # allows to maintain current behavior and deprecate write-access. + # + # When the deprecation expires, there's no need for duplicate state + # anymore and the private _number attribute can be replaced by + # `self.canvas.manager.num` if that exists and None otherwise. + if hasattr(self, '_number'): + return self._number + else: + raise AttributeError( + "'Figure' object has no attribute 'number'. In the future this" + "will change to returning 'None' instead.") + + @number.setter + def number(self, num): + _api.warn_deprecated( + "3.10", + message="Changing 'Figure.number' is deprecated since %(since)s and " + "will raise an error starting %(removal)s") + self._number = num + def _get_renderer(self): if hasattr(self.canvas, 'get_renderer'): return self.canvas.get_renderer() @@ -2796,8 +2902,7 @@ def set_tight_layout(self, tight): If a dict, pass it as kwargs to `.Figure.tight_layout`, overriding the default paddings. """ - if tight is None: - tight = mpl.rcParams['figure.autolayout'] + tight = mpl._val_or_rc(tight, 'figure.autolayout') _tight = 'tight' if bool(tight) else 'none' _tight_parameters = tight if isinstance(tight, dict) else {} self.set_layout_engine(_tight, **_tight_parameters) @@ -2828,8 +2933,7 @@ def set_constrained_layout(self, constrained): ---------- constrained : bool or dict or None """ - if constrained is None: - constrained = mpl.rcParams['figure.constrained_layout.use'] + constrained = mpl._val_or_rc(constrained, 'figure.constrained_layout.use') _constrained = 'constrained' if bool(constrained) else 'none' _parameters = constrained if isinstance(constrained, dict) else {} self.set_layout_engine(_constrained, **_parameters) @@ -2902,6 +3006,18 @@ def get_constrained_layout_pads(self, relative=False): return w_pad, h_pad, wspace, hspace + def _set_base_canvas(self): + """ + Initialize self.canvas with a FigureCanvasBase instance. + + This is used upon initialization of the Figure, but also + to reset the canvas when decoupling from pyplot. + """ + FigureCanvasBase(self) # Set self.canvas as a side-effect + # undo any high-dpi scaling + if self._dpi != self._original_dpi: + self.dpi = self._original_dpi + def set_canvas(self, canvas): """ Set the canvas that contains the figure @@ -2914,7 +3030,8 @@ def set_canvas(self, canvas): @_docstring.interpd def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, - vmin=None, vmax=None, origin=None, resize=False, **kwargs): + vmin=None, vmax=None, origin=None, resize=False, *, + colorizer=None, **kwargs): """ Add a non-resampled image to the figure. @@ -2957,6 +3074,10 @@ def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, resize : bool If *True*, resize the figure to match the given image size. + %(colorizer_doc)s + + This parameter is ignored if *X* is RGB(A). + Returns ------- `matplotlib.image.FigureImage` @@ -2990,6 +3111,7 @@ def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, self.set_size_inches(figsize, forward=True) im = mimage.FigureImage(self, cmap=cmap, norm=norm, + colorizer=colorizer, offsetx=xo, offsety=yo, origin=origin, **kwargs) im.stale_callback = _stale_figure_callback @@ -2997,6 +3119,7 @@ def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, im.set_array(X) im.set_alpha(alpha) if norm is None: + im._check_exclusionary_keywords(colorizer, vmin=vmin, vmax=vmax) im.set_clim(vmin, vmax) self.images.append(im) im._remove_method = self.images.remove @@ -3035,6 +3158,10 @@ def set_size_inches(self, w, h=None, forward=True): """ if h is None: # Got called with a single pair as argument. w, h = w + if w is None or h is None: + raise ValueError( + "Figure.set_size_inches does not accept None; provide both " + "width and height explicitly") size = np.array([w, h]) if not np.isfinite(size).all() or (size < 0).any(): raise ValueError(f'figure size must be positive finite not {size}') @@ -3208,8 +3335,9 @@ def __setstate__(self, state): self.__dict__ = state # re-initialise some of the unstored state information - FigureCanvasBase(self) # Set self.canvas. - + self._set_base_canvas() + # force the bounding boxes to respect current dpi + self.dpi_scale_trans.clear().scale(self._dpi) if restore_to_pylab: # lazy import to avoid circularity import matplotlib.pyplot as plt @@ -3349,8 +3477,7 @@ def savefig(self, fname, *, transparent=None, **kwargs): """ kwargs.setdefault('dpi', mpl.rcParams['savefig.dpi']) - if transparent is None: - transparent = mpl.rcParams['savefig.transparent'] + transparent = mpl._val_or_rc(transparent, 'savefig.transparent') with ExitStack() as stack: if transparent: @@ -3563,8 +3690,8 @@ def figaspect(arg): Returns ------- - width, height : float - The figure size in inches. + size : (2,) array + The width and height of the figure in inches. Notes ----- @@ -3580,7 +3707,7 @@ def figaspect(arg): w, h = figaspect(2.) fig = Figure(figsize=(w, h)) - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8)) ax.imshow(A, **kwargs) Make a figure with the proper aspect for an array:: @@ -3588,7 +3715,7 @@ def figaspect(arg): A = rand(5, 3) w, h = figaspect(A) fig = Figure(figsize=(w, h)) - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8)) ax.imshow(A, **kwargs) """ @@ -3622,3 +3749,61 @@ def figaspect(arg): # the min/max dimensions (we don't want figures 10 feet tall!) newsize = np.clip(newsize, figsize_min, figsize_max) return newsize + + +def _parse_figsize(figsize, dpi): + """ + Convert a figsize expression to (width, height) in inches. + + Parameters + ---------- + figsize : (float, float) or (float, float, str) + This can be + + - a tuple ``(width, height, unit)``, where *unit* is one of "in" (inch), + "cm" (centimenter), "px" (pixel). + - a tuple ``(width, height)``, which is interpreted in inches, i.e. as + ``(width, height, "in")``. + + dpi : float + The dots-per-inch; used for converting 'px' to 'in'. + """ + num_parts = len(figsize) + if num_parts == 2: + x, y = figsize + elif num_parts == 3: + x, y, unit = figsize + if unit == 'in': + pass + elif unit == 'cm': + if x is not None: + x /= 2.54 + if y is not None: + y /= 2.54 + elif unit == 'px': + if x is not None: + x /= dpi + if y is not None: + y /= dpi + else: + raise ValueError( + f"Invalid unit {unit!r} in 'figsize'; " + "supported units are 'in', 'cm', 'px'" + ) + else: + raise ValueError( + "Invalid figsize format, expected (x, y) or (x, y, unit) but got " + f"{figsize!r}" + ) + + if x is None and y is None: + raise ValueError( + "figsize=(None, None) is invalid; at least one of width or " + "height must be provided") + + default_width, default_height = mpl.rcParams["figure.figsize"] + if x is None: + x = default_width + if y is None: + y = default_height + return x, y diff --git a/lib/matplotlib/figure.pyi b/lib/matplotlib/figure.pyi index 21de9159d56c..59d276362dc5 100644 --- a/lib/matplotlib/figure.pyi +++ b/lib/matplotlib/figure.pyi @@ -1,6 +1,6 @@ -from collections.abc import Callable, Hashable, Iterable +from collections.abc import Callable, Hashable, Iterable, Sequence import os -from typing import Any, IO, Literal, Sequence, TypeVar, overload +from typing import Any, IO, Literal, TypeVar, overload import numpy as np from numpy.typing import ArrayLike @@ -15,6 +15,7 @@ from matplotlib.backend_bases import ( ) from matplotlib.colors import Colormap, Normalize from matplotlib.colorbar import Colorbar +from matplotlib.colorizer import ColorizingArtist, Colorizer from matplotlib.cm import ScalarMappable from matplotlib.gridspec import GridSpec, SubplotSpec, SubplotParams as SubplotParams from matplotlib.image import _ImageBase, FigureImage @@ -24,7 +25,9 @@ from matplotlib.lines import Line2D from matplotlib.patches import Rectangle, Patch from matplotlib.text import Text from matplotlib.transforms import Affine2D, Bbox, BboxBase, Transform -from .typing import ColorType, HashableList +from mpl_toolkits.mplot3d import Axes3D + +from .typing import ColorType, HashableList, LegendLocType _T = TypeVar("_T") @@ -61,6 +64,12 @@ class FigureBase(Artist): def get_linewidth(self) -> float: ... def set_edgecolor(self, color: ColorType) -> None: ... def set_facecolor(self, color: ColorType) -> None: ... + @overload + def get_figure(self, root: Literal[True]) -> Figure: ... + @overload + def get_figure(self, root: Literal[False]) -> Figure | SubFigure: ... + @overload + def get_figure(self, root: bool = ...) -> Figure | SubFigure: ... def set_frameon(self, b: bool) -> None: ... @property def frameon(self) -> bool: ... @@ -80,17 +89,20 @@ class FigureBase(Artist): # TODO: docstring indicates SubplotSpec a valid arg, but none of the listed signatures appear to be that @overload + def add_subplot(self, *args: Any, projection: Literal["3d"], **kwargs: Any) -> Axes3D: ... + @overload def add_subplot( - self, nrows: int, ncols: int, index: int | tuple[int, int], **kwargs + self, nrows: int, ncols: int, index: int | tuple[int, int], **kwargs: Any ) -> Axes: ... @overload - def add_subplot(self, pos: int, **kwargs) -> Axes: ... + def add_subplot(self, pos: int, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, ax: Axes, **kwargs) -> Axes: ... + def add_subplot(self, ax: Axes, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, ax: SubplotSpec, **kwargs) -> Axes: ... + def add_subplot(self, ax: SubplotSpec, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, **kwargs) -> Axes: ... + def add_subplot(self, **kwargs: Any) -> Axes: ... + @overload def subplots( self, @@ -132,7 +144,7 @@ class FigureBase(Artist): height_ratios: Sequence[float] | None = ..., subplot_kw: dict[str, Any] | None = ..., gridspec_kw: dict[str, Any] | None = ..., - ) -> Axes | np.ndarray: ... + ) -> Any: ... def delaxes(self, ax: Axes) -> None: ... def clear(self, keep_observers: bool = ...) -> None: ... def clf(self, keep_observers: bool = ...) -> None: ... @@ -140,13 +152,16 @@ class FigureBase(Artist): @overload def legend(self) -> Legend: ... @overload - def legend(self, handles: Iterable[Artist], labels: Iterable[str], **kwargs) -> Legend: ... + def legend(self, handles: Iterable[Artist], labels: Iterable[str], + *, loc: LegendLocType | None = ..., **kwargs) -> Legend: ... @overload - def legend(self, *, handles: Iterable[Artist], **kwargs) -> Legend: ... + def legend(self, *, handles: Iterable[Artist], + loc: LegendLocType | None = ..., **kwargs) -> Legend: ... @overload - def legend(self, labels: Iterable[str], **kwargs) -> Legend: ... + def legend(self, labels: Iterable[str], + *, loc: LegendLocType | None = ..., **kwargs) -> Legend: ... @overload - def legend(self, **kwargs) -> Legend: ... + def legend(self, *, loc: LegendLocType | None = ..., **kwargs) -> Legend: ... def text( self, @@ -158,7 +173,7 @@ class FigureBase(Artist): ) -> Text: ... def colorbar( self, - mappable: ScalarMappable, + mappable: ScalarMappable | ColorizingArtist, cax: Axes | None = ..., ax: Axes | Iterable[Axes] | None = ..., use_gridspec: bool = ..., @@ -179,11 +194,24 @@ class FigureBase(Artist): def align_labels(self, axs: Iterable[Axes] | None = ...) -> None: ... def add_gridspec(self, nrows: int = ..., ncols: int = ..., **kwargs) -> GridSpec: ... @overload + def subfigures( + self, + nrows: int, + ncols: int, + squeeze: Literal[False], + wspace: float | None = ..., + hspace: float | None = ..., + width_ratios: ArrayLike | None = ..., + height_ratios: ArrayLike | None = ..., + **kwargs + ) -> np.ndarray: ... + @overload def subfigures( self, nrows: int = ..., ncols: int = ..., - squeeze: Literal[False] = ..., + *, + squeeze: Literal[False], wspace: float | None = ..., hspace: float | None = ..., width_ratios: ArrayLike | None = ..., @@ -205,7 +233,7 @@ class FigureBase(Artist): def add_subfigure(self, subplotspec: SubplotSpec, **kwargs) -> SubFigure: ... def sca(self, a: Axes) -> Axes: ... def gca(self) -> Axes: ... - def _gci(self) -> ScalarMappable | None: ... + def _gci(self) -> ColorizingArtist | None: ... def _process_projection_requirements( self, *, axes_class=None, polar=False, projection=None, **kwargs ) -> tuple[type[Axes], dict[str, Any]]: ... @@ -260,10 +288,10 @@ class FigureBase(Artist): ) -> dict[Hashable, Axes]: ... class SubFigure(FigureBase): - figure: Figure + @property + def figure(self) -> Figure: ... subplotpars: SubplotParams dpi_scale_trans: Affine2D - canvas: FigureCanvasBase transFigure: Transform bbox_relative: Bbox figbbox: BboxBase @@ -282,6 +310,8 @@ class SubFigure(FigureBase): **kwargs ) -> None: ... @property + def canvas(self) -> FigureCanvasBase: ... + @property def dpi(self) -> float: ... @dpi.setter def dpi(self, value: float) -> None: ... @@ -297,7 +327,8 @@ class SubFigure(FigureBase): def get_axes(self) -> list[Axes]: ... class Figure(FigureBase): - figure: Figure + @property + def figure(self) -> Figure: ... bbox_inches: Bbox dpi_scale_trans: Affine2D bbox: BboxBase @@ -308,7 +339,9 @@ class Figure(FigureBase): subplotpars: SubplotParams def __init__( self, - figsize: tuple[float, float] | None = ..., + figsize: tuple[float, float] + | tuple[float, float, Literal["in", "cm", "px"]] + | None = ..., dpi: float | None = ..., *, facecolor: ColorType | None = ..., @@ -334,6 +367,10 @@ class Figure(FigureBase): def get_layout_engine(self) -> LayoutEngine | None: ... def _repr_html_(self) -> str | None: ... def show(self, warn: bool = ...) -> None: ... + @property + def number(self) -> int | str: ... + @number.setter + def number(self, num: int | str) -> None: ... @property # type: ignore[misc] def axes(self) -> list[Axes]: ... # type: ignore[override] def get_axes(self) -> list[Axes]: ... @@ -360,6 +397,8 @@ class Figure(FigureBase): vmax: float | None = ..., origin: Literal["upper", "lower"] | None = ..., resize: bool = ..., + *, + colorizer: Colorizer | None = ..., **kwargs ) -> FigureImage: ... def set_size_inches( @@ -402,4 +441,11 @@ class Figure(FigureBase): rect: tuple[float, float, float, float] | None = ... ) -> None: ... -def figaspect(arg: float | ArrayLike) -> tuple[float, float]: ... +def figaspect( + arg: float | ArrayLike, +) -> np.ndarray[tuple[Literal[2]], np.dtype[np.float64]]: ... + +def _parse_figsize( + figsize: tuple[float, float] | tuple[float, float, Literal["in", "cm", "px"]], + dpi: float +) -> tuple[float, float]: ... diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 312e8ee97246..90aa778cb292 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -28,14 +28,13 @@ from __future__ import annotations from base64 import b64encode -from collections import namedtuple -import copy import dataclasses -from functools import lru_cache +from functools import cache, lru_cache +import functools from io import BytesIO import json import logging -from numbers import Number +from numbers import Integral import os from pathlib import Path import plistlib @@ -132,8 +131,6 @@ 'sans', } -_ExceptionProxy = namedtuple('_ExceptionProxy', ['klass', 'message']) - # OS Font paths try: _HOME = Path.home() @@ -174,6 +171,10 @@ ] +def _normalize_weight(weight): + return weight if isinstance(weight, Integral) else weight_dict[weight] + + def get_fontext_synonyms(fontext): """ Return a list of file extensions that are synonyms for @@ -249,7 +250,7 @@ def _get_win32_installed_fonts(): return items -@lru_cache +@cache def _get_fontconfig_fonts(): """Cache and list the font paths known to ``fc-list``.""" try: @@ -263,11 +264,14 @@ def _get_fontconfig_fonts(): return [Path(os.fsdecode(fname)) for fname in out.split(b'\n')] -@lru_cache +@cache def _get_macos_fonts(): """Cache and list the font paths known to ``system_profiler SPFontsDataType``.""" - d, = plistlib.loads( - subprocess.check_output(["system_profiler", "-xml", "SPFontsDataType"])) + try: + d, = plistlib.loads( + subprocess.check_output(["system_profiler", "-xml", "SPFontsDataType"])) + except (OSError, subprocess.CalledProcessError, plistlib.InvalidFileException): + return [] return [Path(entry["path"]) for entry in d["_items"]] @@ -286,6 +290,9 @@ def findSystemFonts(fontpaths=None, fontext='ttf'): if sys.platform == 'win32': installed_fonts = _get_win32_installed_fonts() fontpaths = [] + elif sys.platform == 'emscripten': + installed_fonts = [] + fontpaths = [] else: installed_fonts = _get_fontconfig_fonts() if sys.platform == 'darwin': @@ -305,6 +312,69 @@ def findSystemFonts(fontpaths=None, fontext='ttf'): return [fname for fname in fontfiles if os.path.exists(fname)] +# To maintain backwards-compatibility with the current code we need to continue to +# return a str. However to support indexing into the file we need to return both the +# path and the index. Thus, we sub-class str to maintain compatibility and extend it to +# carry the index. +# +# The other alternative would be to create a completely new API and deprecate the +# existing one. In this case, sub-classing str is the simpler and less-disruptive +# option. +class FontPath(str): + """ + A class to describe a path to a font with a face index. + + Parameters + ---------- + path : str + The path to a font. + face_index : int + The face index in the font. + """ + + __match_args__ = ('path', 'face_index') + + def __new__(cls, path, face_index): + ret = super().__new__(cls, path) + ret._face_index = face_index + return ret + + @property + def path(self): + """The path to a font.""" + return str(self) + + @property + def face_index(self): + """The face index in a font.""" + return self._face_index + + def _as_tuple(self): + return (self.path, self.face_index) + + def __eq__(self, other): + if isinstance(other, FontPath): + return self._as_tuple() == other._as_tuple() + return super().__eq__(other) + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + if isinstance(other, FontPath): + return self._as_tuple() < other._as_tuple() + return super().__lt__(other) + + def __gt__(self, other): + return not (self == other or self < other) + + def __hash__(self): + return hash(self._as_tuple()) + + def __repr__(self): + return f'FontPath{self._as_tuple()}' + + @dataclasses.dataclass(frozen=True) class FontEntry: """ @@ -314,6 +384,7 @@ class FontEntry: """ fname: str = '' + index: int = 0 name: str = '' style: str = 'normal' variant: str = 'normal' @@ -378,7 +449,7 @@ def ttfFontProperty(font): style = 'italic' elif sfnt2.find('regular') >= 0: style = 'normal' - elif font.style_flags & ft2font.ITALIC: + elif ft2font.StyleFlags.ITALIC in font.style_flags: style = 'italic' else: style = 'normal' @@ -427,7 +498,7 @@ def get_weight(): # From fontconfig's FcFreeTypeQueryFaceInternal. for regex, weight in _weight_regexes: if re.search(regex, style, re.I): return weight - if font.style_flags & ft2font.BOLD: + if ft2font.StyleFlags.BOLD in font.style_flags: return 700 # "bold" return 500 # "medium", not "regular"! @@ -460,7 +531,84 @@ def get_weight(): # From fontconfig's FcFreeTypeQueryFaceInternal. raise NotImplementedError("Non-scalable fonts are not supported") size = 'scalable' - return FontEntry(font.fname, name, style, variant, weight, stretch, size) + return FontEntry(font.fname, font.face_index, name, + style, variant, weight, stretch, size) + + +def _get_font_alt_names(font, primary_name): + """ + Return ``(name, weight)`` pairs for alternate family names of *font*. + + A font file can advertise its family name in several places. FreeType + exposes ``font.family_name``, which is typically derived from the + Macintosh-platform Name ID 1 entry. However, other entries may carry + different (equally valid) names that users reasonably expect to work: + + - **Name ID 1, other platform** — some fonts store a different family name + on the Microsoft platform than on the Macintosh platform. + - **Name ID 16** — "Typographic Family" (a.k.a. preferred family): groups + more than the traditional four styles under one name. + - **Name ID 21** — "WWS Family": an even narrower grouping used by some + fonts (weight/width/slope only). + + Each name is paired with a weight derived from the corresponding subfamily + entry on the *same* platform. This ensures that the weight of the alternate entry + reflects the font's role *within that named family* rather than its absolute + typographic weight. + + Parameters + ---------- + font : `.FT2Font` + primary_name : str + The family name already extracted from the font (``font.family_name``). + + Returns + ------- + list of (str, int) + ``(alternate_family_name, weight)`` pairs, not including *primary_name*. + """ + try: + sfnt = font.get_sfnt() + except ValueError: + return [] + + mac_key = (1, # platform: macintosh + 0, # id: roman + 0) # langid: english + ms_key = (3, # platform: microsoft + 1, # id: unicode_cs + 0x0409) # langid: english_united_states + + seen = {primary_name} + result = [] + + def _weight_from_subfam(subfam): + subfam = subfam.replace(" ", "") + for regex, weight in _weight_regexes: + if re.search(regex, subfam, re.I): + return weight + return 400 # "Regular" or unrecognised + + def _try_add(name, subfam): + name = name.strip() + if not name or name in seen: + return + seen.add(name) + result.append((name, _weight_from_subfam(subfam.strip()))) + + # Each family-name ID is paired with its corresponding subfamily ID on the + # same platform: (family_id, subfamily_id). + for fam_id, subfam_id in ((1, 2), (16, 17), (21, 22)): + _try_add( + sfnt.get((*mac_key, fam_id), b'').decode('latin-1'), + sfnt.get((*mac_key, subfam_id), b'').decode('latin-1'), + ) + _try_add( + sfnt.get((*ms_key, fam_id), b'').decode('utf-16-be'), + sfnt.get((*ms_key, subfam_id), b'').decode('utf-16-be'), + ) + + return result def afmFontProperty(fontpath, font): @@ -530,7 +678,58 @@ def afmFontProperty(fontpath, font): size = 'scalable' - return FontEntry(fontpath, name, style, variant, weight, stretch, size) + return FontEntry(fontpath, 0, name, style, variant, weight, stretch, size) + + +def _cleanup_fontproperties_init(init_method): + """ + A decorator to limit the call signature to a single positional argument + or alternatively only keyword arguments. + + We still accept but deprecate all other call signatures. + + When the deprecation expires we can switch the signature to:: + + __init__(self, pattern=None, /, *, family=None, style=None, ...) + + plus a runtime check that pattern is not used alongside with the + keyword arguments. This results eventually in the two possible + call signatures:: + + FontProperties(pattern) + FontProperties(family=..., size=..., ...) + + """ + @functools.wraps(init_method) + def wrapper(self, *args, **kwargs): + # multiple args with at least some positional ones + if len(args) > 1 or len(args) == 1 and kwargs: + # Note: Both cases were previously handled as individual properties. + # Therefore, we do not mention the case of font properties here. + _api.warn_deprecated( + "3.10", + message="Passing individual properties to FontProperties() " + "positionally was deprecated in Matplotlib %(since)s and " + "will be removed in %(removal)s. Please pass all properties " + "via keyword arguments." + ) + # single non-string arg -> clearly a family not a pattern + if len(args) == 1 and not kwargs and not cbook.is_scalar_or_string(args[0]): + # Case font-family list passed as single argument + _api.warn_deprecated( + "3.10", + message="Passing family as positional argument to FontProperties() " + "was deprecated in Matplotlib %(since)s and will be removed " + "in %(removal)s. Please pass family names as keyword" + "argument." + ) + # Note on single string arg: + # This has been interpreted as pattern so far. We are already raising if a + # non-pattern compatible family string was given. Therefore, we do not need + # to warn for this case. + return init_method(self, *args, **kwargs) + + return wrapper class FontProperties: @@ -582,9 +781,14 @@ class FontProperties: approach allows all text sizes to be made larger or smaller based on the font manager's default font size. - This class will also accept a fontconfig_ pattern_, if it is the only - argument provided. This support does not depend on fontconfig; we are - merely borrowing its pattern syntax for use here. + This class accepts a single positional string as fontconfig_ pattern_, + or alternatively individual properties as keyword arguments:: + + FontProperties(pattern) + FontProperties(*, family=None, style=None, variant=None, ...) + + This support does not depend on fontconfig; we are merely borrowing its + pattern syntax for use here. .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ .. _pattern: @@ -596,6 +800,7 @@ class FontProperties: fontconfig. """ + @_cleanup_fontproperties_init def __init__(self, family=None, style=None, variant=None, weight=None, stretch=None, size=None, fname=None, # if set, it's a hardcoded filename to use @@ -640,15 +845,7 @@ def _from_any(cls, arg): return cls(**arg) def __hash__(self): - l = (tuple(self.get_family()), - self.get_slant(), - self.get_variant(), - self.get_weight(), - self.get_stretch(), - self.get_size(), - self.get_file(), - self.get_math_fontfamily()) - return hash(l) + return hash(tuple(self.__dict__.values())) def __eq__(self, other): return hash(self) == hash(other) @@ -664,7 +861,7 @@ def get_family(self): from their respective rcParams when searching for a matching font) in the order of preference. """ - return self._family + return list(self._family) def get_name(self): """ @@ -686,7 +883,7 @@ def get_variant(self): def get_weight(self): """ - Set the font weight. Options are: A numeric value in the + Get the font weight. Options are: A numeric value in the range 0-1000 or one of 'light', 'normal', 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black' @@ -731,11 +928,10 @@ def set_family(self, family): font names. Real font names are not supported when :rc:`text.usetex` is `True`. Default: :rc:`font.family` """ - if family is None: - family = mpl.rcParams['font.family'] + family = mpl._val_or_rc(family, 'font.family') if isinstance(family, str): - family = [family] - self._family = family + family = (family,) + self._family = tuple(family) def set_style(self, style): """ @@ -745,8 +941,7 @@ def set_style(self, style): ---------- style : {'normal', 'italic', 'oblique'}, default: :rc:`font.style` """ - if style is None: - style = mpl.rcParams['font.style'] + style = mpl._val_or_rc(style, 'font.style') _api.check_in_list(['normal', 'italic', 'oblique'], style=style) self._slant = style @@ -758,8 +953,7 @@ def set_variant(self, variant): ---------- variant : {'normal', 'small-caps'}, default: :rc:`font.variant` """ - if variant is None: - variant = mpl.rcParams['font.variant'] + variant = mpl._val_or_rc(variant, 'font.variant') _api.check_in_list(['normal', 'small-caps'], variant=variant) self._variant = variant @@ -774,8 +968,7 @@ def set_weight(self, weight): 'extra bold', 'black'}, default: :rc:`font.weight` If int, must be in the range 0-1000. """ - if weight is None: - weight = mpl.rcParams['font.weight'] + weight = mpl._val_or_rc(weight, 'font.weight') if weight in weight_dict: self._weight = weight return @@ -800,8 +993,7 @@ def set_stretch(self, stretch): 'ultra-expanded'}, default: :rc:`font.stretch` If int, must be in the range 0-1000. """ - if stretch is None: - stretch = mpl.rcParams['font.stretch'] + stretch = mpl._val_or_rc(stretch, 'font.stretch') if stretch in stretch_dict: self._stretch = stretch return @@ -826,8 +1018,7 @@ def set_size(self, size): If a float, the font size in points. The string values denote sizes relative to the default font size. """ - if size is None: - size = mpl.rcParams['font.size'] + size = mpl._val_or_rc(size, 'font.size') try: size = float(size) except ValueError: @@ -900,9 +1091,15 @@ def set_math_fontfamily(self, fontfamily): _api.check_in_list(valid_fonts, math_fontfamily=fontfamily) self._math_fontfamily = fontfamily + def __copy__(self): + # Bypass __init__ for speed, since values are already validated + new = FontProperties.__new__(FontProperties) + new.__dict__.update(self.__dict__) + return new + def copy(self): """Return a copy of self.""" - return copy.copy(self) + return self.__copy__() # Aliases set_name = set_family @@ -962,11 +1159,11 @@ def json_dump(data, filename): This function temporarily locks the output file to prevent multiple processes from overwriting one another's output. """ - with cbook._lock_path(filename), open(filename, 'w') as fh: - try: + try: + with cbook._lock_path(filename), open(filename, 'w') as fh: json.dump(data, fh, cls=_JSONEncoder, indent=2) - except OSError as e: - _log.warning('Could not save font_manager cache %s', e) + except OSError as e: + _log.warning('Could not save font_manager cache %s', e) def json_load(filename): @@ -1013,7 +1210,7 @@ class FontManager: # Increment this version number whenever the font cache data # format or behavior has changed and requires an existing font # cache files to be rebuilt. - __version__ = 390 + __version__ = '3.11.0a4' def __init__(self, size=None, weight='normal'): self._version = self.__version__ @@ -1034,9 +1231,12 @@ def __init__(self, size=None, weight='normal'): self.ttflist = [] # Delay the warning by 5s. - timer = threading.Timer(5, lambda: _log.warning( - 'Matplotlib is building the font cache; this may take a moment.')) - timer.start() + try: + timer = threading.Timer(5, lambda: _log.warning( + 'Matplotlib is building the font cache; this may take a moment.')) + timer.start() + except RuntimeError: + timer = None try: for fontext in ["afm", "ttf"]: for path in [*findSystemFonts(paths, fontext=fontext), @@ -1049,7 +1249,8 @@ def __init__(self, size=None, weight='normal'): _log.info("Failed to extract font properties from %s: " "%s", path, exc) finally: - timer.cancel() + if timer: + timer.cancel() def addfont(self, path): """ @@ -1078,6 +1279,18 @@ def addfont(self, path): font = ft2font.FT2Font(path) prop = ttfFontProperty(font) self.ttflist.append(prop) + for alt_name, alt_weight in _get_font_alt_names(font, prop.name): + self.ttflist.append( + dataclasses.replace(prop, name=alt_name, weight=alt_weight)) + + for face_index in range(1, font.num_faces): + subfont = ft2font.FT2Font(path, face_index=face_index) + prop = ttfFontProperty(subfont) + self.ttflist.append(prop) + for alt_name, alt_weight in _get_font_alt_names(subfont, prop.name): + self.ttflist.append( + dataclasses.replace(prop, name=alt_name, weight=alt_weight)) + self._findfont_cached.cache_clear() @property @@ -1204,8 +1417,8 @@ def score_weight(self, weight1, weight2): # exact match of the weight names, e.g. weight1 == weight2 == "regular" if cbook._str_equal(weight1, weight2): return 0.0 - w1 = weight1 if isinstance(weight1, Number) else weight_dict[weight1] - w2 = weight2 if isinstance(weight2, Number) else weight_dict[weight2] + w1 = _normalize_weight(weight1) + w2 = _normalize_weight(weight2) return 0.95 * (abs(w1 - w2) / 1000) + 0.05 def score_size(self, size1, size2): @@ -1264,7 +1477,7 @@ def findfont(self, prop, fontext='ttf', directory=None, Returns ------- - str + FontPath The filename of the best matching font. Notes @@ -1288,14 +1501,16 @@ def findfont(self, prop, fontext='ttf', directory=None, # Pass the relevant rcParams (and the font manager, as `self`) to # _findfont_cached so to prevent using a stale cache entry after an # rcParam was changed. - rc_params = tuple(tuple(mpl.rcParams[key]) for key in [ - "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", - "font.monospace"]) + rc_params = [mpl.rcParams[f"font.{key}"] for key in [ + "family", "style", "variant", "weight", "stretch", "size", + "serif", "sans-serif", "cursive", "fantasy", "monospace"]] + rc_params = tuple(tuple(e) if isinstance(e, list) else e + for e in rc_params) # Make this hashable. ret = self._findfont_cached( prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params) - if isinstance(ret, _ExceptionProxy): - raise ret.klass(ret.message) + if isinstance(ret, cbook._ExceptionInfo): + raise ret.to_exception() return ret def get_font_names(self): @@ -1334,7 +1549,7 @@ def _find_fonts_by_props(self, prop, fontext='ttf', directory=None, Returns ------- - list[str] + list[FontPath] The paths of the fonts found. Notes @@ -1428,6 +1643,10 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default, best_font = font if score == 0: break + if best_font is not None and (_normalize_weight(prop.get_weight()) != + _normalize_weight(best_font.weight)): + _log.warning('findfont: Failed to find font weight %s, now using %s.', + prop.get_weight(), best_font.weight) if best_font is None or best_score >= 10.0: if fallback_to_default: @@ -1448,7 +1667,7 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default, # This return instead of raise is intentional, as we wish to # cache that it was not found, which will not occur if it was # actually raised. - return _ExceptionProxy( + return cbook._ExceptionInfo( ValueError, f"Failed to find font {prop}, and fallback to the default font was " f"disabled" @@ -1474,12 +1693,12 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default, # This return instead of raise is intentional, as we wish to # cache that it was not found, which will not occur if it was # actually raised. - return _ExceptionProxy(ValueError, "No valid font could be found") + return cbook._ExceptionInfo(ValueError, "No valid font could be found") - return _cached_realpath(result) + return FontPath(_cached_realpath(result), best_font.index) -@lru_cache +@_api.deprecated("3.11") def is_opentype_cff_font(filename): """ Return whether the given font is a Postscript Compact Font Format Font @@ -1494,19 +1713,40 @@ def is_opentype_cff_font(filename): @lru_cache(64) -def _get_font(font_filepaths, hinting_factor, *, _kerning_factor, thread_id): - first_fontpath, *rest = font_filepaths - return ft2font.FT2Font( - first_fontpath, hinting_factor, - _fallback_list=[ - ft2font.FT2Font( - fpath, hinting_factor, - _kerning_factor=_kerning_factor - ) - for fpath in rest - ], +def _get_font(font_filepaths, hinting_factor, *, _kerning_factor, thread_id, + enable_last_resort): + (first_fontpath, first_fontindex), *rest = font_filepaths + fallback_list = [ + ft2font.FT2Font(fpath, hinting_factor, face_index=index, + _kerning_factor=_kerning_factor) + for fpath, index in rest + ] + last_resort_path = _cached_realpath( + cbook._get_data_path('fonts', 'ttf', 'LastResortHE-Regular.ttf')) + try: + last_resort_index = font_filepaths.index((last_resort_path, 0)) + except ValueError: + last_resort_index = -1 + # Add Last Resort font so we always have glyphs regardless of font, unless we're + # already in the list. + if enable_last_resort: + fallback_list.append( + ft2font.FT2Font(last_resort_path, hinting_factor, + _kerning_factor=_kerning_factor, + _warn_if_used=True)) + last_resort_index = len(fallback_list) + font = ft2font.FT2Font( + first_fontpath, hinting_factor, face_index=first_fontindex, + _fallback_list=fallback_list, _kerning_factor=_kerning_factor ) + # Ensure we are using the right charmap for the Last Resort font; FreeType picks the + # Unicode one by default, but this exists only for Windows, and is empty. + if last_resort_index == 0: + font.set_charmap(0) + elif last_resort_index > 0: + fallback_list[last_resort_index - 1].set_charmap(0) + return font # FT2Font objects cannot be used across fork()s because they reference the same @@ -1531,10 +1771,11 @@ def get_font(font_filepaths, hinting_factor=None): Parameters ---------- - font_filepaths : Iterable[str, Path, bytes], str, Path, bytes + font_filepaths : Iterable[str, bytes, os.PathLike, FontPath], \ +str, bytes, os.PathLike, FontPath Relative or absolute paths to the font files to be used. - If a single string, bytes, or `pathlib.Path`, then it will be treated + If a single string, bytes, or `os.PathLike`, then it will be treated as a list with that entry only. If more than one filepath is passed, then the returned FT2Font object @@ -1546,22 +1787,32 @@ def get_font(font_filepaths, hinting_factor=None): `.ft2font.FT2Font` """ - if isinstance(font_filepaths, (str, Path, bytes)): - paths = (_cached_realpath(font_filepaths),) - else: - paths = tuple(_cached_realpath(fname) for fname in font_filepaths) - - if hinting_factor is None: - hinting_factor = mpl.rcParams['text.hinting_factor'] - - return _get_font( + match font_filepaths: + case FontPath(path, index): + paths = ((_cached_realpath(path), index), ) + case str() | bytes() | os.PathLike() as path: + paths = ((_cached_realpath(path), 0), ) + case _: + paths = tuple( + (_cached_realpath(fname.path), fname.face_index) + if isinstance(fname, FontPath) else (_cached_realpath(fname), 0) + for fname in font_filepaths) + + hinting_factor = mpl._val_or_rc(hinting_factor, 'text.hinting_factor') + + font = _get_font( # must be a tuple to be cached paths, hinting_factor, _kerning_factor=mpl.rcParams['text.kerning_factor'], # also key on the thread ID to prevent segfaults with multi-threading - thread_id=threading.get_ident() + thread_id=threading.get_ident(), + enable_last_resort=mpl.rcParams['font.enable_last_resort'], ) + # Ensure the transform is always consistent. + font._set_transform([[round(0x10000 / font._hinting_factor), 0], [0, 0x10000]], + [0, 0]) + return font def _load_fontmanager(*, try_read_cache=True): diff --git a/lib/matplotlib/font_manager.pyi b/lib/matplotlib/font_manager.pyi index 48d0e362d599..22d925ea9273 100644 --- a/lib/matplotlib/font_manager.pyi +++ b/lib/matplotlib/font_manager.pyi @@ -1,14 +1,13 @@ +from collections.abc import Iterable from dataclasses import dataclass +from numbers import Integral import os +from pathlib import Path +from typing import Any, Final, Literal from matplotlib._afm import AFM from matplotlib import ft2font -from pathlib import Path - -from collections.abc import Iterable -from typing import Any, Literal - font_scalings: dict[str | None, float] stretch_dict: dict[str, int] weight_dict: dict[str, int] @@ -19,16 +18,37 @@ MSUserFontDirectories: list[str] X11FontDirectories: list[str] OSXFontDirectories: list[str] +def _normalize_weight(weight: str | Integral) -> Integral: ... def get_fontext_synonyms(fontext: str) -> list[str]: ... def list_fonts(directory: str, extensions: Iterable[str]) -> list[str]: ... def win32FontDirectory() -> str: ... def _get_fontconfig_fonts() -> list[Path]: ... +def _get_font_alt_names( + font: ft2font.FT2Font, primary_name: str +) -> list[tuple[str, int]]: ... def findSystemFonts( - fontpaths: Iterable[str | os.PathLike | Path] | None = ..., fontext: str = ... + fontpaths: Iterable[str | os.PathLike] | None = ..., fontext: str = ... ) -> list[str]: ... + +class FontPath(str): + __match_args__: Final[tuple[str, ...]] + def __new__(cls: type[str], path: str, face_index: int) -> FontPath: ... + @property + def path(self) -> str: ... + @property + def face_index(self) -> int: ... + def _as_tuple(self) -> tuple[str, int]: ... + def __eq__(self, other: Any) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + def __lt__(self, other: Any) -> bool: ... + def __gt__(self, other: Any) -> bool: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + @dataclass class FontEntry: fname: str = ... + index: int = ... name: str = ... style: str = ... variant: str = ... @@ -50,10 +70,11 @@ class FontProperties: weight: int | str | None = ..., stretch: int | str | None = ..., size: float | str | None = ..., - fname: str | os.PathLike | Path | None = ..., + fname: str | os.PathLike | None = ..., math_fontfamily: str | None = ..., ) -> None: ... def __hash__(self) -> int: ... + def __copy__(self) -> FontProperties: ... def __eq__(self, other: object) -> bool: ... def get_family(self) -> list[str]: ... def get_name(self) -> str: ... @@ -72,7 +93,7 @@ class FontProperties: def set_weight(self, weight: int | str | None) -> None: ... def set_stretch(self, stretch: int | str | None) -> None: ... def set_size(self, size: float | str | None) -> None: ... - def set_file(self, file: str | os.PathLike | Path | None) -> None: ... + def set_file(self, file: str | os.PathLike | None) -> None: ... def set_fontconfig_pattern(self, pattern: str) -> None: ... def get_math_fontfamily(self) -> str: ... def set_math_fontfamily(self, fontfamily: str | None) -> None: ... @@ -83,17 +104,17 @@ class FontProperties: set_slant = set_style get_size_in_points = get_size -def json_dump(data: FontManager, filename: str | Path | os.PathLike) -> None: ... -def json_load(filename: str | Path | os.PathLike) -> FontManager: ... +def json_dump(data: FontManager, filename: str | os.PathLike) -> None: ... +def json_load(filename: str | os.PathLike) -> FontManager: ... class FontManager: - __version__: int + __version__: str default_size: float | None defaultFamily: dict[str, str] afmlist: list[FontEntry] ttflist: list[FontEntry] def __init__(self, size: float | None = ..., weight: str = ...) -> None: ... - def addfont(self, path: str | Path | os.PathLike) -> None: ... + def addfont(self, path: str | os.PathLike) -> None: ... @property def defaultFont(self) -> dict[str, str]: ... def get_default_weight(self) -> str: ... @@ -115,12 +136,12 @@ class FontManager: directory: str | None = ..., fallback_to_default: bool = ..., rebuild_if_missing: bool = ..., - ) -> str: ... + ) -> FontPath: ... def get_font_names(self) -> list[str]: ... def is_opentype_cff_font(filename: str) -> bool: ... def get_font( - font_filepaths: Iterable[str | Path | bytes] | str | Path | bytes, + font_filepaths: Iterable[str | bytes | os.PathLike | FontPath] | str | bytes | os.PathLike | FontPath, hinting_factor: int | None = ..., ) -> ft2font.FT2Font: ... diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index 6a0716e993a5..3003f83932bc 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -1,46 +1,87 @@ -from typing import BinaryIO, Literal, TypedDict, overload +from enum import Enum, Flag +from os import PathLike +import sys +from typing import BinaryIO, Literal, NewType, NotRequired, TypeAlias, TypedDict, cast, final, overload +from typing_extensions import Buffer # < Py 3.12 import numpy as np from numpy.typing import NDArray __freetype_build_type__: str __freetype_version__: str -BOLD: int -EXTERNAL_STREAM: int -FAST_GLYPHS: int -FIXED_SIZES: int -FIXED_WIDTH: int -GLYPH_NAMES: int -HORIZONTAL: int -ITALIC: int -KERNING: int -KERNING_DEFAULT: int -KERNING_UNFITTED: int -KERNING_UNSCALED: int -LOAD_CROP_BITMAP: int -LOAD_DEFAULT: int -LOAD_FORCE_AUTOHINT: int -LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH: int -LOAD_IGNORE_TRANSFORM: int -LOAD_LINEAR_DESIGN: int -LOAD_MONOCHROME: int -LOAD_NO_AUTOHINT: int -LOAD_NO_BITMAP: int -LOAD_NO_HINTING: int -LOAD_NO_RECURSE: int -LOAD_NO_SCALE: int -LOAD_PEDANTIC: int -LOAD_RENDER: int -LOAD_TARGET_LCD: int -LOAD_TARGET_LCD_V: int -LOAD_TARGET_LIGHT: int -LOAD_TARGET_MONO: int -LOAD_TARGET_NORMAL: int -LOAD_VERTICAL_LAYOUT: int -MULTIPLE_MASTERS: int -SCALABLE: int -SFNT: int -VERTICAL: int +__libraqm_version__: str + +# We can't change the type hints for standard library chr/ord, so character codes are a +# simple type alias. +CharacterCodeType: TypeAlias = int +# But glyph indices are internal, so use a distinct type hint. +GlyphIndexType = NewType('GlyphIndexType', int) + +class FaceFlags(Flag): + SCALABLE = cast(int, ...) + FIXED_SIZES = cast(int, ...) + FIXED_WIDTH = cast(int, ...) + SFNT = cast(int, ...) + HORIZONTAL = cast(int, ...) + VERTICAL = cast(int, ...) + KERNING = cast(int, ...) + FAST_GLYPHS = cast(int, ...) + MULTIPLE_MASTERS = cast(int, ...) + GLYPH_NAMES = cast(int, ...) + EXTERNAL_STREAM = cast(int, ...) + HINTER = cast(int, ...) + CID_KEYED = cast(int, ...) + TRICKY = cast(int, ...) + COLOR = cast(int, ...) + VARIATION = cast(int, ...) + SVG = cast(int, ...) + SBIX = cast(int, ...) + SBIX_OVERLAY = cast(int, ...) + +class Kerning(Enum): + DEFAULT = cast(int, ...) + UNFITTED = cast(int, ...) + UNSCALED = cast(int, ...) + +class LoadFlags(Flag): + DEFAULT = cast(int, ...) + NO_SCALE = cast(int, ...) + NO_HINTING = cast(int, ...) + RENDER = cast(int, ...) + NO_BITMAP = cast(int, ...) + VERTICAL_LAYOUT = cast(int, ...) + FORCE_AUTOHINT = cast(int, ...) + CROP_BITMAP = cast(int, ...) + PEDANTIC = cast(int, ...) + IGNORE_GLOBAL_ADVANCE_WIDTH = cast(int, ...) + NO_RECURSE = cast(int, ...) + IGNORE_TRANSFORM = cast(int, ...) + MONOCHROME = cast(int, ...) + LINEAR_DESIGN = cast(int, ...) + NO_AUTOHINT = cast(int, ...) + COLOR = cast(int, ...) + COMPUTE_METRICS = cast(int, ...) + BITMAP_METRICS_ONLY = cast(int, ...) + NO_SVG = cast(int, ...) + # The following should be unique, but the above can be OR'd together. + TARGET_NORMAL = cast(int, ...) + TARGET_LIGHT = cast(int, ...) + TARGET_MONO = cast(int, ...) + TARGET_LCD = cast(int, ...) + TARGET_LCD_V = cast(int, ...) + +class RenderMode(Enum): + NORMAL = cast(int, ...) + LIGHT = cast(int, ...) + MONO = cast(int, ...) + LCD = cast(int, ...) + LCD_V = cast(int, ...) + SDF = cast(int, ...) + +class StyleFlags(Flag): + NORMAL = cast(int, ...) + ITALIC = cast(int, ...) + BOLD = cast(int, ...) class _SfntHeadDict(TypedDict): version: tuple[int, int] @@ -96,11 +137,27 @@ class _SfntOs2Dict(TypedDict): yStrikeoutPosition: int sFamilyClass: int panose: bytes - ulCharRange: tuple[int, int, int, int] + ulUnicodeRange: tuple[int, int, int, int] achVendID: bytes fsSelection: int - fsFirstCharIndex: int - fsLastCharIndex: int + usFirstCharIndex: int + usLastCharIndex: int + sTypoAscender: int + sTypoDescender: int + sTypoLineGap: int + usWinAscent: int + usWinDescent: int + # version >= 1 + ulCodePageRange: NotRequired[tuple[int, int]] + # version >= 2 + sxHeight: NotRequired[int] + sCapHeight: NotRequired[int] + usDefaultChar: NotRequired[int] + usBreakChar: NotRequired[int] + usMaxContext: NotRequired[int] + # version >= 5 + usLowerOpticalPointSize: NotRequired[int] + usUpperOpticalPointSize: NotRequired[int] class _SfntHheaDict(TypedDict): version: tuple[int, int] @@ -124,7 +181,7 @@ class _SfntVheaDict(TypedDict): vertTypoLineGap: int advanceHeightMax: int minTopSideBearing: int - minBottomSizeBearing: int + minBottomSideBearing: int yMaxExtent: int caretSlopeRise: int caretSlopeRun: int @@ -158,50 +215,55 @@ class _SfntPcltDict(TypedDict): widthType: int serifStyle: int -class FT2Font: - ascender: int - bbox: tuple[int, int, int, int] - descender: int - face_flags: int - family_name: str - fname: str - height: int - max_advance_height: int - max_advance_width: int - num_charmaps: int - num_faces: int - num_fixed_sizes: int - num_glyphs: int - postscript_name: str - scalable: bool - style_flags: int - style_name: str - underline_position: int - underline_thickness: int - units_per_EM: int +@final +class LayoutItem: + @property + def ft_object(self) -> FT2Font: ... + @property + def char(self) -> str: ... + @property + def glyph_index(self) -> GlyphIndexType: ... + @property + def x(self) -> float: ... + @property + def y(self) -> float: ... + @property + def prev_kern(self) -> float: ... + def __str__(self) -> str: ... +@final +class FT2Font(Buffer): def __init__( self, - filename: str | BinaryIO, + filename: str | bytes | PathLike | BinaryIO, hinting_factor: int = ..., *, + face_index: int = ..., _fallback_list: list[FT2Font] | None = ..., - _kerning_factor: int = ... + _kerning_factor: int | None = ... ) -> None: ... - def _get_fontmap(self, string: str) -> dict[str, FT2Font]: ... + if sys.version_info[:2] >= (3, 12): + def __buffer__(self, /, flags: int) -> memoryview: ... + def _layout( + self, + text: str, + flags: LoadFlags, + features: tuple[str, ...] | None = ..., + language: str | tuple[tuple[str, int, int], ...] | None = ..., + ) -> list[LayoutItem]: ... def clear(self) -> None: ... def draw_glyph_to_bitmap( - self, image: FT2Image, x: float, y: float, glyph: Glyph, antialiased: bool = ... + self, image: NDArray[np.uint8], x: int, y: int, glyph: Glyph, antialiased: bool = ... ) -> None: ... def draw_glyphs_to_bitmap(self, antialiased: bool = ...) -> None: ... def get_bitmap_offset(self) -> tuple[int, int]: ... - def get_char_index(self, codepoint: int) -> int: ... - def get_charmap(self) -> dict[int, int]: ... + def get_char_index(self, codepoint: CharacterCodeType) -> GlyphIndexType: ... + def get_charmap(self) -> dict[CharacterCodeType, GlyphIndexType]: ... def get_descent(self) -> int: ... - def get_glyph_name(self, index: int) -> str: ... + def get_glyph_name(self, index: GlyphIndexType) -> str: ... def get_image(self) -> NDArray[np.uint8]: ... - def get_kerning(self, left: int, right: int, mode: int) -> int: ... - def get_name_index(self, name: str) -> int: ... + def get_kerning(self, left: GlyphIndexType, right: GlyphIndexType, mode: Kerning) -> int: ... + def get_name_index(self, name: str) -> GlyphIndexType: ... def get_num_glyphs(self) -> int: ... def get_path(self) -> tuple[NDArray[np.float64], NDArray[np.int8]]: ... def get_ps_font_info( @@ -223,31 +285,91 @@ class FT2Font: @overload def get_sfnt_table(self, name: Literal["pclt"]) -> _SfntPcltDict | None: ... def get_width_height(self) -> tuple[int, int]: ... - def get_xys(self, antialiased: bool = ...) -> NDArray[np.float64]: ... - def load_char(self, charcode: int, flags: int = ...) -> Glyph: ... - def load_glyph(self, glyphindex: int, flags: int = ...) -> Glyph: ... + def load_char(self, charcode: CharacterCodeType, flags: LoadFlags = ...) -> Glyph: ... + def load_glyph(self, glyphindex: GlyphIndexType, flags: LoadFlags = ...) -> Glyph: ... def select_charmap(self, i: int) -> None: ... def set_charmap(self, i: int) -> None: ... def set_size(self, ptsize: float, dpi: float) -> None: ... def set_text( - self, string: str, angle: float = ..., flags: int = ... + self, + string: str, + angle: float = ..., + flags: LoadFlags = ..., + *, + features: tuple[str] | None = ..., + language: str | list[tuple[str, int, int]] | None = ..., ) -> NDArray[np.float64]: ... + @property + def ascender(self) -> int: ... + @property + def bbox(self) -> tuple[int, int, int, int]: ... + @property + def descender(self) -> int: ... + @property + def face_flags(self) -> FaceFlags: ... + @property + def face_index(self) -> int: ... + @property + def family_name(self) -> str: ... + @property + def fname(self) -> str | bytes: ... + @property + def height(self) -> int: ... + @property + def max_advance_height(self) -> int: ... + @property + def max_advance_width(self) -> int: ... + @property + def num_charmaps(self) -> int: ... + @property + def num_faces(self) -> int: ... + @property + def num_fixed_sizes(self) -> int: ... + @property + def num_glyphs(self) -> int: ... + @property + def num_named_instances(self) -> int: ... + @property + def postscript_name(self) -> str: ... + @property + def scalable(self) -> bool: ... + @property + def style_flags(self) -> StyleFlags: ... + @property + def style_name(self) -> str: ... + @property + def underline_position(self) -> int: ... + @property + def underline_thickness(self) -> int: ... + @property + def units_per_EM(self) -> int: ... -class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer. - def __init__(self, width: float, height: float) -> None: ... - def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ... - def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ... +@final +class FT2Image(Buffer): + def __init__(self, width: int, height: int) -> None: ... + def draw_rect_filled(self, x0: int, y0: int, x1: int, y1: int) -> None: ... + if sys.version_info[:2] >= (3, 12): + def __buffer__(self, /, flags: int) -> memoryview: ... +@final class Glyph: - width: int - height: int - horiBearingX: int - horiBearingY: int - horiAdvance: int - linearHoriAdvance: int - vertBearingX: int - vertBearingY: int - vertAdvance: int - + @property + def width(self) -> int: ... + @property + def height(self) -> int: ... + @property + def horiBearingX(self) -> int: ... + @property + def horiBearingY(self) -> int: ... + @property + def horiAdvance(self) -> int: ... + @property + def linearHoriAdvance(self) -> int: ... + @property + def vertBearingX(self) -> int: ... + @property + def vertBearingY(self) -> int: ... + @property + def vertAdvance(self) -> int: ... @property def bbox(self) -> tuple[int, int, int, int]: ... diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index c6b363d36efa..5cd05bc167bb 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -18,6 +18,7 @@ import matplotlib as mpl from matplotlib import _api, _pylab_helpers, _tight_layout +from matplotlib._api import UNSET as _UNSET from matplotlib.transforms import Bbox _log = logging.getLogger(__name__) @@ -366,7 +367,8 @@ def __init__(self, nrows, ncols, figure=None, _AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"] - def update(self, **kwargs): + def update(self, *, left=_UNSET, bottom=_UNSET, right=_UNSET, top=_UNSET, + wspace=_UNSET, hspace=_UNSET): """ Update the subplot parameters of the grid. @@ -377,22 +379,30 @@ def update(self, **kwargs): ---------- left, right, top, bottom : float or None, optional Extent of the subplots as a fraction of figure width or height. - wspace, hspace : float, optional + wspace, hspace : float or None, optional Spacing between the subplots as a fraction of the average subplot width / height. """ - for k, v in kwargs.items(): - if k in self._AllowedKeys: - setattr(self, k, v) - else: - raise AttributeError(f"{k} is an unknown keyword") + if left is not _UNSET: + self.left = left + if bottom is not _UNSET: + self.bottom = bottom + if right is not _UNSET: + self.right = right + if top is not _UNSET: + self.top = top + if wspace is not _UNSET: + self.wspace = wspace + if hspace is not _UNSET: + self.hspace = hspace + for figmanager in _pylab_helpers.Gcf.figs.values(): for ax in figmanager.canvas.figure.axes: if ax.get_subplotspec() is not None: ss = ax.get_subplotspec().get_topmost_subplotspec() if ss.get_gridspec() == self: - ax._set_position( - ax.get_subplotspec().get_position(ax.figure)) + fig = ax.get_figure(root=False) + ax._set_position(ax.get_subplotspec().get_position(fig)) def get_subplot_params(self, figure=None): """ @@ -740,22 +750,22 @@ def __init__(self, left=None, bottom=None, right=None, top=None, Parameters ---------- - left : float + left : float, optional The position of the left edge of the subplots, as a fraction of the figure width. - right : float + right : float, optional The position of the right edge of the subplots, as a fraction of the figure width. - bottom : float + bottom : float, optional The position of the bottom edge of the subplots, as a fraction of the figure height. - top : float + top : float, optional The position of the top edge of the subplots, as a fraction of the figure height. - wspace : float + wspace : float, optional The width of the padding between subplots, as a fraction of the average Axes width. - hspace : float + hspace : float, optional The height of the padding between subplots, as a fraction of the average Axes height. """ @@ -786,3 +796,12 @@ def update(self, left=None, bottom=None, right=None, top=None, self.wspace = wspace if hspace is not None: self.hspace = hspace + + def reset(self): + """Restore the subplot positioning parameters to the default rcParams values""" + for key in self.to_dict(): + setattr(self, key, mpl.rcParams[f'figure.subplot.{key}']) + + def to_dict(self): + """Return a copy of the subplot parameters as a dict.""" + return self.__dict__.copy() diff --git a/lib/matplotlib/gridspec.pyi b/lib/matplotlib/gridspec.pyi index b6732ad8fafa..2a9068635c46 100644 --- a/lib/matplotlib/gridspec.pyi +++ b/lib/matplotlib/gridspec.pyi @@ -3,7 +3,8 @@ from typing import Any, Literal, overload from numpy.typing import ArrayLike import numpy as np -from matplotlib.axes import Axes, SubplotBase +from matplotlib._api import _Unset +from matplotlib.axes import Axes from matplotlib.backend_bases import RendererBase from matplotlib.figure import Figure from matplotlib.transforms import Bbox @@ -78,7 +79,16 @@ class GridSpec(GridSpecBase): width_ratios: ArrayLike | None = ..., height_ratios: ArrayLike | None = ..., ) -> None: ... - def update(self, **kwargs: float | None) -> None: ... + def update( + self, + *, + left: float | None | _Unset = ..., + bottom: float | None | _Unset = ..., + right: float | None | _Unset = ..., + top: float | None | _Unset = ..., + wspace: float | None | _Unset = ..., + hspace: float | None | _Unset = ..., + ) -> None: ... def locally_modified_subplot_params(self) -> list[str]: ... def tight_layout( self, @@ -115,7 +125,7 @@ class SubplotSpec: def num2(self) -> int: ... @num2.setter def num2(self, value: int) -> None: ... - def get_gridspec(self) -> GridSpec: ... + def get_gridspec(self) -> GridSpecBase: ... def get_geometry(self) -> tuple[int, int, int, int]: ... @property def rowspan(self) -> range: ... @@ -158,3 +168,7 @@ class SubplotParams: wspace: float | None = ..., hspace: float | None = ..., ) -> None: ... + def to_dict( + self, + ) -> dict[str, float]: ... + def reset(self) -> None: ... diff --git a/lib/matplotlib/hatch.py b/lib/matplotlib/hatch.py index 7a4b283c1dbe..5e0b6d761a98 100644 --- a/lib/matplotlib/hatch.py +++ b/lib/matplotlib/hatch.py @@ -1,4 +1,21 @@ -"""Contains classes for generating hatch patterns.""" +""" +Module for generating hatch patterns. + +For examples of using the hatch API, see +:ref:`sphx_glr_gallery_shapes_and_collections_hatch_style_reference.py`. + +The following hatching patterns are available, shown here at level 2 density: + +.. plot:: _embedded_plots/hatch_classes.py + :include-source: false + :alt: + 8 squares, each showing the pattern corresponding to the hatch symbol: + symbol '/' makes right leaning diagonals, '\\' makes left leaning diagonals, + '|' makes vertical lines, '-' makes horizontal lines, '+' makes a grid, + 'X' makes a grid rotated 90 degrees, 'o' makes small unfilled circles, + 'O' makes large unfilled circles, '.' makes small filled circles, and '*' makes + a star with 5 points +""" import numpy as np @@ -165,6 +182,7 @@ def __init__(self, hatch, density): self.shape_codes = np.full(len(self.shape_vertices), Path.LINETO, dtype=Path.code_type) self.shape_codes[0] = Path.MOVETO + self.shape_codes[-1] = Path.CLOSEPOLY super().__init__(hatch, density) _hatch_types = [ @@ -192,7 +210,7 @@ def _validate_hatch_pattern(hatch): message=f'hatch must consist of a string of "{valid}" or ' 'None, but found the following invalid values ' f'"{invalids}". Passing invalid values is deprecated ' - 'since %(since)s and will become an error %(removal)s.' + 'since %(since)s and will become an error in %(removal)s.' ) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 61b22cf519c7..25e6a3bd5ee8 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -3,7 +3,6 @@ operations. """ -import math import os import logging from pathlib import Path @@ -14,13 +13,14 @@ import PIL.PngImagePlugin import matplotlib as mpl -from matplotlib import _api, cbook, cm +from matplotlib import _api, cbook # For clarity, names from _image are given explicitly in this module from matplotlib import _image # For user convenience, the names from _image are also imported into # the image namespace from matplotlib._image import * # noqa: F401, F403 import matplotlib.artist as martist +import matplotlib.colorizer as mcolorizer from matplotlib.backend_bases import FigureCanvasBase import matplotlib.colors as mcolors from matplotlib.transforms import ( @@ -31,7 +31,7 @@ # map interpolation strings to module constants _interpd_ = { - 'antialiased': _image.NEAREST, # this will use nearest or Hanning... + 'auto': _image.NEAREST, # this will use nearest or Hanning... 'none': _image.NEAREST, # fall back to nearest when not supported 'nearest': _image.NEAREST, 'bilinear': _image.BILINEAR, @@ -50,6 +50,7 @@ 'sinc': _image.SINC, 'lanczos': _image.LANCZOS, 'blackman': _image.BLACKMAN, + 'antialiased': _image.NEAREST, # this will use nearest or Hanning... } interpolations_names = set(_interpd_) @@ -63,8 +64,8 @@ def composite_images(images, renderer, magnification=1.0): Parameters ---------- images : list of Images - Each must have a `make_image` method. For each image, - `can_composite` should return `True`, though this is not + Each must have a `!make_image` method. For each image, + `!can_composite` should return `True`, though this is not enforced by this function. Each image must have a purely affine transformation with no shear. @@ -186,7 +187,7 @@ def _resample( # compare the number of displayed pixels to the number of # the data pixels. interpolation = image_obj.get_interpolation() - if interpolation == 'antialiased': + if interpolation in ['antialiased', 'auto']: # don't antialias if upsampling by an integer number or # if zooming in more than a factor of 3 pos = np.array([[0, 0], [data.shape[1], data.shape[0]]]) @@ -205,12 +206,26 @@ def _resample( out = np.zeros(out_shape + data.shape[2:], data.dtype) # 2D->2D, 3D->3D. if resample is None: resample = image_obj.get_resample() + + # When an output pixel falls exactly on the edge between two input pixels, the Agg + # resampler will use the right input pixel as the nearest neighbor. We want the + # left input pixel to be chosen instead, so we flip the input data and the supplied + # transform. If origin != 'upper', the transform will already include a flip in the + # vertical direction. + if interpolation == 'nearest': + transform = Affine2D().translate(-data.shape[1], 0).scale(-1, 1) + transform + data = np.flip(data, axis=1) + if image_obj.origin == 'upper': + transform = Affine2D().translate(0, -data.shape[0]).scale(1, -1) + transform + data = np.flip(data, axis=0) + _image.resample(data, out, transform, _interpd_[interpolation], resample, alpha, image_obj.get_filternorm(), image_obj.get_filterrad()) + return out @@ -228,26 +243,28 @@ def _rgb_to_rgba(A): return rgba -class _ImageBase(martist.Artist, cm.ScalarMappable): +class _ImageBase(mcolorizer.ColorizingArtist): """ Base class for images. - interpolation and cmap default to their rc settings + *interpolation* and *cmap* default to their rc settings. - cmap is a colors.Colormap instance - norm is a colors.Normalize instance to map luminance to 0-1 + *cmap* is a `.colors.Colormap` instance. + *norm* is a `.colors.Normalize` instance to map luminance to 0-1. - extent is data axes (left, right, bottom, top) for making image plots - registered with data plots. Default is to label the pixel - centers with the zero-based row and column indices. + *extent* is a ``(left, right, bottom, top)`` tuple in data coordinates, for + making image plots registered with data plots; the default is to label the + pixel centers with the zero-based row and column indices. - Additional kwargs are matplotlib.artist properties + Additional kwargs are `.Artist` properties. """ + zorder = 0 def __init__(self, ax, cmap=None, norm=None, + colorizer=None, interpolation=None, origin=None, filternorm=True, @@ -257,10 +274,8 @@ def __init__(self, ax, interpolation_stage=None, **kwargs ): - martist.Artist.__init__(self) - cm.ScalarMappable.__init__(self, norm, cmap) - if origin is None: - origin = mpl.rcParams['image.origin'] + super().__init__(self._get_colorizer(cmap, norm, colorizer)) + origin = mpl._val_or_rc(origin, 'image.origin') _api.check_in_list(["upper", "lower"], origin=origin) self.origin = origin self.set_filternorm(filternorm) @@ -330,7 +345,7 @@ def changed(self): Call this whenever the mappable is changed so observers can update. """ self._imcache = None - cm.ScalarMappable.changed(self) + super().changed() def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, unsampled=False, round_to_pixel_border=True): @@ -340,18 +355,33 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, the given *clip_bbox* (also in pixel space), and magnified by the *magnification* factor. - *A* may be a greyscale image (M, N) with a dtype of `~numpy.float32`, - `~numpy.float64`, `~numpy.float128`, `~numpy.uint16` or `~numpy.uint8`, - or an (M, N, 4) RGBA image with a dtype of `~numpy.float32`, - `~numpy.float64`, `~numpy.float128`, or `~numpy.uint8`. + Parameters + ---------- + A : ndarray - If *unsampled* is True, the image will not be scaled, but an - appropriate affine transformation will be returned instead. + - a (M, N) array interpreted as scalar (greyscale) image, + with one of the dtypes `~numpy.float32`, `~numpy.float64`, + `~numpy.float128`, `~numpy.uint16` or `~numpy.uint8`. + - (M, N, 4) RGBA image with a dtype of `~numpy.float32`, + `~numpy.float64`, `~numpy.float128`, or `~numpy.uint8`. - If *round_to_pixel_border* is True, the output image size will be - rounded to the nearest pixel boundary. This makes the images align - correctly with the Axes. It should not be used if exact scaling is - needed, such as for `FigureImage`. + in_bbox : `~matplotlib.transforms.Bbox` + + out_bbox : `~matplotlib.transforms.Bbox` + + clip_bbox : `~matplotlib.transforms.Bbox` + + magnification : float, default: 1 + + unsampled : bool, default: False + If True, the image will not be scaled, but an appropriate + affine transformation will be returned instead. + + round_to_pixel_border : bool, default: True + If True, the output image size will be rounded to the nearest pixel + boundary. This makes the images align correctly with the Axes. + It should not be used if exact scaling is needed, such as for + `.FigureImage`. Returns ------- @@ -376,10 +406,21 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, if clipped_bbox is None: return None, 0, 0, None - out_width_base = clipped_bbox.width * magnification - out_height_base = clipped_bbox.height * magnification + # Define the magnified bbox after clipping + magnified_extents = clipped_bbox.extents * magnification + if ((not unsampled) and round_to_pixel_border): + # Round to the nearest output pixel + # Add a tiny fudge amount to account for numerical precision loss + # on the two sides away from the Agg anchor point (x0, y1) + x0 = np.floor(magnified_extents[0] + 0.5) # round half up + y0 = np.ceil(magnified_extents[1] - 0.5 - 1e-8) # round half down + x1 = np.floor(magnified_extents[2] + 0.5 + 1e-8) # round half up + y1 = np.ceil(magnified_extents[3] - 0.5) # round half down + magnified_bbox = Bbox.from_extents([x0, y0, x1, y1]) + else: + magnified_bbox = Bbox.from_extents(magnified_extents) - if out_width_base == 0 or out_height_base == 0: + if magnified_bbox.width == 0 or magnified_bbox.height == 0: return None, 0, 0, None if self.origin == 'upper': @@ -400,120 +441,51 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, t = (t0 + (Affine2D() - .translate(-clipped_bbox.x0, -clipped_bbox.y0) - .scale(magnification))) - - # So that the image is aligned with the edge of the Axes, we want to - # round up the output width to the next integer. This also means - # scaling the transform slightly to account for the extra subpixel. - if ((not unsampled) and t.is_affine and round_to_pixel_border and - (out_width_base % 1.0 != 0.0 or out_height_base % 1.0 != 0.0)): - out_width = math.ceil(out_width_base) - out_height = math.ceil(out_height_base) - extra_width = (out_width - out_width_base) / out_width_base - extra_height = (out_height - out_height_base) / out_height_base - t += Affine2D().scale(1.0 + extra_width, 1.0 + extra_height) - else: - out_width = int(out_width_base) - out_height = int(out_height_base) - out_shape = (out_height, out_width) + .scale(magnification) + .translate(-magnified_bbox.x0, -magnified_bbox.y0))) + + out_shape = (int(magnified_bbox.height), int(magnified_bbox.width)) if not unsampled: if not (A.ndim == 2 or A.ndim == 3 and A.shape[-1] in (3, 4)): raise ValueError(f"Invalid shape {A.shape} for image data") - if A.ndim == 2 and self._interpolation_stage != 'rgba': + + float_rgba_in = A.ndim == 3 and A.shape[-1] == 4 and A.dtype.kind == 'f' + + # if antialiased, this needs to change as window sizes + # change: + interpolation_stage = self._interpolation_stage + if interpolation_stage in ['antialiased', 'auto']: + pos = np.array([[0, 0], [A.shape[1], A.shape[0]]]) + disp = t.transform(pos) + dispx = np.abs(np.diff(disp[:, 0])) / A.shape[1] + dispy = np.abs(np.diff(disp[:, 1])) / A.shape[0] + if (dispx < 3) or (dispy < 3): + interpolation_stage = 'rgba' + else: + interpolation_stage = 'data' + + if A.ndim == 2 and interpolation_stage == 'data': # if we are a 2D array, then we are running through the # norm + colormap transformation. However, in general the # input data is not going to match the size on the screen so we # have to resample to the correct number of pixels - # TODO slice input array first - a_min = A.min() - a_max = A.max() - if a_min is np.ma.masked: # All masked; values don't matter. - a_min, a_max = np.int32(0), np.int32(1) if A.dtype.kind == 'f': # Float dtype: scale to same dtype. - scaled_dtype = np.dtype( - np.float64 if A.dtype.itemsize > 4 else np.float32) + scaled_dtype = np.dtype("f8" if A.dtype.itemsize > 4 else "f4") if scaled_dtype.itemsize < A.dtype.itemsize: _api.warn_external(f"Casting input data from {A.dtype}" f" to {scaled_dtype} for imshow.") else: # Int dtype, likely. + # TODO slice input array first # Scale to appropriately sized float: use float32 if the # dynamic range is small, to limit the memory footprint. - da = a_max.astype(np.float64) - a_min.astype(np.float64) - scaled_dtype = np.float64 if da > 1e8 else np.float32 - - # Scale the input data to [.1, .9]. The Agg interpolators clip - # to [0, 1] internally, and we use a smaller input scale to - # identify the interpolated points that need to be flagged as - # over/under. This may introduce numeric instabilities in very - # broadly scaled data. - - # Always copy, and don't allow array subtypes. - A_scaled = np.array(A, dtype=scaled_dtype) - # Clip scaled data around norm if necessary. This is necessary - # for big numbers at the edge of float64's ability to represent - # changes. Applying a norm first would be good, but ruins the - # interpolation of over numbers. - self.norm.autoscale_None(A) - dv = np.float64(self.norm.vmax) - np.float64(self.norm.vmin) - vmid = np.float64(self.norm.vmin) + dv / 2 - fact = 1e7 if scaled_dtype == np.float64 else 1e4 - newmin = vmid - dv * fact - if newmin < a_min: - newmin = None - else: - a_min = np.float64(newmin) - newmax = vmid + dv * fact - if newmax > a_max: - newmax = None - else: - a_max = np.float64(newmax) - if newmax is not None or newmin is not None: - np.clip(A_scaled, newmin, newmax, out=A_scaled) - - # Rescale the raw data to [offset, 1-offset] so that the - # resampling code will run cleanly. Using dyadic numbers here - # could reduce the error, but would not fully eliminate it and - # breaks a number of tests (due to the slightly different - # error bouncing some pixels across a boundary in the (very - # quantized) colormapping step). - offset = .1 - frac = .8 - # Run vmin/vmax through the same rescaling as the raw data; - # otherwise, data values close or equal to the boundaries can - # end up on the wrong side due to floating point error. - vmin, vmax = self.norm.vmin, self.norm.vmax - if vmin is np.ma.masked: - vmin, vmax = a_min, a_max - vrange = np.array([vmin, vmax], dtype=scaled_dtype) - - A_scaled -= a_min - vrange -= a_min - # .item() handles a_min/a_max being ndarray subclasses. - a_min = a_min.astype(scaled_dtype).item() - a_max = a_max.astype(scaled_dtype).item() - - if a_min != a_max: - A_scaled /= ((a_max - a_min) / frac) - vrange /= ((a_max - a_min) / frac) - A_scaled += offset - vrange += offset + da = A.max().astype("f8") - A.min().astype("f8") + scaled_dtype = "f8" if da > 1e8 else "f4" + # resample the input data to the correct resolution and shape - A_resampled = _resample(self, A_scaled, out_shape, t) - del A_scaled # Make sure we don't use A_scaled anymore! - # Un-scale the resampled data to approximately the original - # range. Things that interpolated to outside the original range - # will still be outside, but possibly clipped in the case of - # higher order interpolation + drastically changing data. - A_resampled -= offset - vrange -= offset - if a_min != a_max: - A_resampled *= ((a_max - a_min) / frac) - vrange *= ((a_max - a_min) / frac) - A_resampled += a_min - vrange += a_min + A_resampled = _resample(self, A.astype(scaled_dtype), out_shape, t) + # if using NoNorm, cast back to the original datatype if isinstance(self.norm, mcolors.NoNorm): A_resampled = A_resampled.astype(A.dtype) @@ -534,41 +506,48 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # Apply the pixel-by-pixel alpha values if present alpha = self.get_alpha() if alpha is not None and np.ndim(alpha) > 0: - out_alpha *= _resample(self, alpha, out_shape, - t, resample=True) + out_alpha *= _resample(self, alpha, out_shape, t, resample=True) # mask and run through the norm resampled_masked = np.ma.masked_array(A_resampled, out_mask) - # we have re-set the vmin/vmax to account for small errors - # that may have moved input values in/out of range - s_vmin, s_vmax = vrange - if isinstance(self.norm, mcolors.LogNorm) and s_vmin <= 0: - # Don't give 0 or negative values to LogNorm - s_vmin = np.finfo(scaled_dtype).eps - # Block the norm from sending an update signal during the - # temporary vmin/vmax change - with self.norm.callbacks.blocked(), \ - cbook._setattr_cm(self.norm, vmin=s_vmin, vmax=s_vmax): - output = self.norm(resampled_masked) + res = self.norm(resampled_masked) else: - if A.ndim == 2: # _interpolation_stage == 'rgba' + if A.ndim == 2: # interpolation_stage = 'rgba' self.norm.autoscale_None(A) A = self.to_rgba(A) - alpha = self._get_scalar_alpha() - if A.shape[2] == 3: - # No need to resample alpha or make a full array; NumPy will expand - # this out and cast to uint8 if necessary when it's assigned to the - # alpha channel below. - output_alpha = (255 * alpha) if A.dtype == np.uint8 else alpha - else: - output_alpha = _resample( # resample alpha channel - self, A[..., 3], out_shape, t, alpha=alpha) - output = _resample( # resample rgb channels - self, _rgb_to_rgba(A[..., :3]), out_shape, t, alpha=alpha) - output[..., 3] = output_alpha # recombine rgb and alpha - - # output is now either a 2D array of normed (int or float) data + if A.dtype == np.uint8: + # uint8 is too imprecise for premultiplied alpha roundtrips. + A = np.divide(A, 0xff, dtype=np.float32) + alpha = self.get_alpha() + post_apply_alpha = False + if alpha is None: # alpha parameter not specified + if A.shape[2] == 3: # image has no alpha channel + A = np.dstack([A, np.ones(A.shape[:2])]) + elif np.ndim(alpha) > 0: # Array alpha + if A.shape[2] == 3: # RGB: use array alpha directly + A = np.dstack([A, alpha]) + else: # RGBA: multiply existing alpha by array alpha + A = np.dstack([A[..., :3], A[..., 3] * alpha]) + else: # Scalar alpha + if A.shape[2] == 3: # broadcast scalar alpha + A = np.dstack([A, np.full(A.shape[:2], alpha, np.float32)]) + else: # or apply scalar alpha to existing alpha channel + post_apply_alpha = True + # Resample in premultiplied alpha space. (TODO: Consider + # implementing premultiplied-space resampling in + # span_image_resample_rgba_affine::generate?) + if float_rgba_in and np.ndim(alpha) == 0 and np.any(A[..., 3] < 1): + # Do not modify original RGBA input + A = A.copy() + A[..., :3] *= A[..., 3:] + res = _resample(self, A, out_shape, t) + np.divide(res[..., :3], res[..., 3:], out=res[..., :3], + where=res[..., 3:] != 0) + if post_apply_alpha: + res[..., 3] *= alpha + + # res is now either a 2D array of normed (int or float) data # or an RGBA array of re-sampled input - output = self.to_rgba(output, bytes=True, norm=False) + output = self.to_rgba(res, bytes=True, norm=False) # output is now a correctly sized RGBA array of uint8 # Apply alpha *after* if the input was greyscale without a mask @@ -594,7 +573,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, t = Affine2D().translate( int(max(subset.xmin, 0)), int(max(subset.ymin, 0))) + t - return output, clipped_bbox.x0, clipped_bbox.y0, t + return (output, + magnified_bbox.x0 / magnification, + magnified_bbox.y0 / magnification, + t) def make_image(self, renderer, magnification=1.0, unsampled=False): """ @@ -744,9 +726,9 @@ def get_interpolation(self): """ Return the interpolation method the image uses when resizing. - One of 'antialiased', 'nearest', 'bilinear', 'bicubic', 'spline16', - 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', - 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', + One of 'auto', 'antialiased', 'nearest', 'bilinear', 'bicubic', + 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', + 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', or 'none'. """ return self._interpolation @@ -762,7 +744,7 @@ def set_interpolation(self, s): Parameters ---------- - s : {'antialiased', 'nearest', 'bilinear', 'bicubic', 'spline16', \ + s : {'auto', 'nearest', 'bilinear', 'bicubic', 'spline16', \ 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', \ 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', 'none'} or None """ @@ -775,7 +757,7 @@ def get_interpolation_stage(self): """ Return when interpolation happens during the transform to RGBA. - One of 'data', 'rgba'. + One of 'data', 'rgba', 'auto'. """ return self._interpolation_stage @@ -785,12 +767,13 @@ def set_interpolation_stage(self, s): Parameters ---------- - s : {'data', 'rgba'} or None - Whether to apply up/downsampling interpolation in data or RGBA - space. If None, use :rc:`image.interpolation_stage`. + s : {'data', 'rgba', 'auto'}, default: :rc:`image.interpolation_stage` + Whether to apply resampling interpolation in data or RGBA space. + If 'auto', 'rgba' is used if the upsampling rate is less than 3, + otherwise 'data' is used. """ s = mpl._val_or_rc(s, 'image.interpolation_stage') - _api.check_in_list(['data', 'rgba'], s=s) + _api.check_in_list(['data', 'rgba', 'auto'], s=s) self._interpolation_stage = s self.stale = True @@ -808,8 +791,7 @@ def set_resample(self, v): Parameters ---------- - v : bool or None - If None, use :rc:`image.resample`. + v : bool, default: :rc:`image.resample` """ v = mpl._val_or_rc(v, 'image.resample') self._resample = v @@ -838,8 +820,10 @@ def get_filternorm(self): def set_filterrad(self, filterrad): """ - Set the resize filter radius only applicable to some - interpolation schemes -- see help for imshow + Set the resize filter radius (only applicable to some + interpolation schemes). + + See help for `~.Axes.imshow`. Parameters ---------- @@ -858,7 +842,7 @@ def get_filterrad(self): class AxesImage(_ImageBase): """ - An image attached to an Axes. + An image with pixels on a regular grid, attached to an Axes. Parameters ---------- @@ -870,7 +854,7 @@ class AxesImage(_ImageBase): norm : str or `~matplotlib.colors.Normalize` Maps luminance to 0-1. interpolation : str, default: :rc:`image.interpolation` - Supported values are 'none', 'antialiased', 'nearest', 'bilinear', + Supported values are 'none', 'auto', 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', 'blackman'. @@ -908,6 +892,7 @@ def __init__(self, ax, *, cmap=None, norm=None, + colorizer=None, interpolation=None, origin=None, extent=None, @@ -924,6 +909,7 @@ def __init__(self, ax, ax, cmap=cmap, norm=norm, + colorizer=colorizer, interpolation=interpolation, origin=origin, filternorm=filternorm, @@ -946,7 +932,7 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): bbox = Bbox(np.array([[x1, y1], [x2, y2]])) transformed_bbox = TransformedBbox(bbox, trans) clip = ((self.get_clip_box() or self.axes.bbox) if self.get_clip_on() - else self.figure.bbox) + else self.get_figure(root=True).bbox) return self._make_image(self._A, bbox, transformed_bbox, clip, magnification, unsampled=unsampled) @@ -970,10 +956,10 @@ def set_extent(self, extent, **kwargs): Notes ----- - This updates ``ax.dataLim``, and, if autoscaling, sets ``ax.viewLim`` - to tightly fit the image, regardless of ``dataLim``. Autoscaling - state is not changed, so following this with ``ax.autoscale_view()`` - will redo the autoscaling in accord with ``dataLim``. + This updates `.Axes.dataLim`, and, if autoscaling, sets `.Axes.viewLim` + to tightly fit the image, regardless of `~.Axes.dataLim`. Autoscaling + state is not changed, so a subsequent call to `.Axes.autoscale_view` + will redo the autoscaling in accord with `~.Axes.dataLim`. """ (xmin, xmax), (ymin, ymax) = self.axes._process_unit_info( [("x", [extent[0], extent[1]]), @@ -997,9 +983,9 @@ def set_extent(self, extent, **kwargs): self.sticky_edges.x[:] = [xmin, xmax] self.sticky_edges.y[:] = [ymin, ymax] if self.axes.get_autoscalex_on(): - self.axes.set_xlim((xmin, xmax), auto=None) + self.axes.set_xlim(xmin, xmax, auto=None) if self.axes.get_autoscaley_on(): - self.axes.set_ylim((ymin, ymax), auto=None) + self.axes.set_ylim(ymin, ymax, auto=None) self.stale = True def get_extent(self): @@ -1043,6 +1029,14 @@ def get_cursor_data(self, event): class NonUniformImage(AxesImage): + """ + An image with pixels on a rectilinear grid. + + In contrast to `.AxesImage`, where pixels are on a regular grid, + NonUniformImage allows rows and columns with individual heights / widths. + + See also :doc:`/gallery/images_contours_and_fields/image_nonuniform`. + """ def __init__(self, ax, *, interpolation='nearest', **kwargs): """ @@ -1083,21 +1077,24 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): B[:, :, 0:3] = A B[:, :, 3] = 255 A = B - l, b, r, t = self.axes.bbox.extents - width = int(((round(r) + 0.5) - (round(l) - 0.5)) * magnification) - height = int(((round(t) + 0.5) - (round(b) - 0.5)) * magnification) + magnified_extents = (self.axes.bbox.extents * magnification + 0.5).astype(int) + l, b, r, t = magnified_extents / magnification + width = int((r - l) * magnification) + height = int((t - b) * magnification) invertedTransform = self.axes.transData.inverted() - x_pix = invertedTransform.transform( - [(x, b) for x in np.linspace(l, r, width)])[:, 0] - y_pix = invertedTransform.transform( - [(l, y) for y in np.linspace(b, t, height)])[:, 1] + x_pix_edges = invertedTransform.transform( + [(x, b) for x in np.linspace(l, r, width + 1)])[:, 0] + y_pix_edges = invertedTransform.transform( + [(l, y) for y in np.linspace(b, t, height + 1)])[:, 1] + x_pix_centers = (x_pix_edges[:-1] + x_pix_edges[1:]) / 2 + y_pix_centers = (y_pix_edges[:-1] + y_pix_edges[1:]) / 2 if self._interpolation == "nearest": x_mid = (self._Ax[:-1] + self._Ax[1:]) / 2 y_mid = (self._Ay[:-1] + self._Ay[1:]) / 2 - x_int = x_mid.searchsorted(x_pix) - y_int = y_mid.searchsorted(y_pix) + x_int = x_mid.searchsorted(x_pix_centers) + y_int = y_mid.searchsorted(y_pix_centers) # The following is equal to `A[y_int[:, None], x_int[None, :]]`, # but many times faster. Both casting to uint32 (to have an # effectively 1D array) and manual index flattening matter. @@ -1108,16 +1105,16 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): else: # self._interpolation == "bilinear" # Use np.interp to compute x_int/x_float has similar speed. x_int = np.clip( - self._Ax.searchsorted(x_pix) - 1, 0, len(self._Ax) - 2) + self._Ax.searchsorted(x_pix_centers) - 1, 0, len(self._Ax) - 2) y_int = np.clip( - self._Ay.searchsorted(y_pix) - 1, 0, len(self._Ay) - 2) + self._Ay.searchsorted(y_pix_centers) - 1, 0, len(self._Ay) - 2) idx_int = np.add.outer(y_int * A.shape[1], x_int) x_frac = np.clip( - np.divide(x_pix - self._Ax[x_int], np.diff(self._Ax)[x_int], + np.divide(x_pix_centers - self._Ax[x_int], np.diff(self._Ax)[x_int], dtype=np.float32), # Downcasting helps with speed. 0, 1) y_frac = np.clip( - np.divide(y_pix - self._Ay[y_int], np.diff(self._Ay)[y_int], + np.divide(y_pix_centers - self._Ay[y_int], np.diff(self._Ay)[y_int], dtype=np.float32), 0, 1) f00 = np.outer(1 - y_frac, 1 - x_frac) @@ -1180,11 +1177,9 @@ def get_extent(self): raise RuntimeError('Must set data first') return self._Ax[0], self._Ax[-1], self._Ay[0], self._Ay[-1] - @_api.rename_parameter("3.8", "s", "filternorm") def set_filternorm(self, filternorm): pass - @_api.rename_parameter("3.8", "s", "filterrad") def set_filterrad(self, filterrad): pass @@ -1224,6 +1219,7 @@ def __init__(self, ax, *, cmap=None, norm=None, + colorizer=None, **kwargs ): """ @@ -1250,7 +1246,7 @@ def __init__(self, ax, Maps luminance to 0-1. **kwargs : `~matplotlib.artist.Artist` properties """ - super().__init__(ax, norm=norm, cmap=cmap) + super().__init__(ax, norm=norm, cmap=cmap, colorizer=colorizer) self._internal_update(kwargs) if A is not None: self.set_data(x, y, A) @@ -1271,22 +1267,24 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): if (padded_A[0, 0] != bg).all(): padded_A[[0, -1], :] = padded_A[:, [0, -1]] = bg - l, b, r, t = self.axes.bbox.extents - width = (round(r) + 0.5) - (round(l) - 0.5) - height = (round(t) + 0.5) - (round(b) - 0.5) - width = round(width * magnification) - height = round(height * magnification) + # Round to the nearest output pixels after magnification + l, b, r, t = (self.axes.bbox.extents * magnification + 0.5).astype(int) + width = r - l + height = t - b + vl = self.axes.viewLim - x_pix = np.linspace(vl.x0, vl.x1, width) - y_pix = np.linspace(vl.y0, vl.y1, height) - x_int = self._Ax.searchsorted(x_pix) - y_int = self._Ay.searchsorted(y_pix) + x_pix_edges = np.linspace(vl.x0, vl.x1, width + 1) + y_pix_edges = np.linspace(vl.y0, vl.y1, height + 1) + x_pix_centers = (x_pix_edges[:-1] + x_pix_edges[1:]) / 2 + y_pix_centers = (y_pix_edges[:-1] + y_pix_edges[1:]) / 2 + x_int = self._Ax.searchsorted(x_pix_centers) + y_int = self._Ay.searchsorted(y_pix_centers) im = ( # See comment in NonUniformImage.make_image re: performance. padded_A.view(np.uint32).ravel()[ np.add.outer(y_int * padded_A.shape[1], x_int)] .view(np.uint8).reshape((height, width, 4))) - return im, l, b, IdentityTransform() + return im, l / magnification, b / magnification, IdentityTransform() def _check_unsampled_image(self): return False @@ -1354,6 +1352,7 @@ def __init__(self, fig, *, cmap=None, norm=None, + colorizer=None, offsetx=0, offsety=0, origin=None, @@ -1369,9 +1368,10 @@ def __init__(self, fig, None, norm=norm, cmap=cmap, + colorizer=colorizer, origin=origin ) - self.figure = fig + self.set_figure(fig) self.ox = offsetx self.oy = offsety self._internal_update(kwargs) @@ -1385,14 +1385,15 @@ def get_extent(self): def make_image(self, renderer, magnification=1.0, unsampled=False): # docstring inherited - fac = renderer.dpi/self.figure.dpi + fig = self.get_figure(root=True) + fac = renderer.dpi/fig.dpi # fac here is to account for pdf, eps, svg backends where # figure.dpi is set to 72. This means we need to scale the # image (using magnification) and offset it appropriately. bbox = Bbox([[self.ox/fac, self.oy/fac], [(self.ox/fac + self._A.shape[1]), (self.oy/fac + self._A.shape[0])]]) - width, height = self.figure.get_size_inches() + width, height = fig.get_size_inches() width *= renderer.dpi height *= renderer.dpi clip = Bbox([[0, 0], [width, height]]) @@ -1402,17 +1403,62 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): def set_data(self, A): """Set the image array.""" - cm.ScalarMappable.set_array(self, A) + super().set_data(A) self.stale = True class BboxImage(_ImageBase): - """The Image class whose size is determined by the given bbox.""" + """ + The Image class whose size is determined by the given bbox. + + Parameters + ---------- + bbox : BboxBase or Callable[RendererBase, BboxBase] + The bbox or a function to generate the bbox + + .. warning :: + + If using `matplotlib.artist.Artist.get_window_extent` as the + callable ensure that the other artist is drawn first (lower zorder) + or you may need to renderer the figure twice to ensure that the + computed bbox is accurate. + + cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` + The Colormap instance or registered colormap name used to map scalar + data to colors. This parameter is ignored if X is RGB(A). + norm : str or `~matplotlib.colors.Normalize` + Maps luminance to 0-1. This parameter is ignored if X is RGB(A). + interpolation : str, default: :rc:`image.interpolation` + Supported values are 'none', 'auto', 'nearest', 'bilinear', + 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', + 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', + 'sinc', 'lanczos', 'blackman'. + origin : {'upper', 'lower'}, default: :rc:`image.origin` + Place the [0, 0] index of the array in the upper left or lower left + corner of the Axes. The convention 'upper' is typically used for + matrices and images. + filternorm : bool, default: True + A parameter for the antigrain image resize filter + (see the antigrain documentation). + If filternorm is set, the filter normalizes integer values and corrects + the rounding errors. It doesn't do anything with the source floating + point values, it corrects only integers according to the rule of 1.0 + which means that any sum of pixel weights must be equal to 1.0. So, + the filter function must produce a graph of the proper shape. + filterrad : float > 0, default: 4 + The filter radius for filters that have a radius parameter, i.e. when + interpolation is one of: 'sinc', 'lanczos' or 'blackman'. + resample : bool, default: False + When True, use a full resampling method. When False, only resample when + the output image is larger than the input image. + **kwargs : `~matplotlib.artist.Artist` properties + """ def __init__(self, bbox, *, cmap=None, norm=None, + colorizer=None, interpolation=None, origin=None, filternorm=True, @@ -1420,16 +1466,12 @@ def __init__(self, bbox, resample=False, **kwargs ): - """ - cmap is a colors.Colormap instance - norm is a colors.Normalize instance to map luminance to 0-1 - kwargs are an optional list of Artist keyword args - """ super().__init__( None, cmap=cmap, norm=norm, + colorizer=colorizer, interpolation=interpolation, origin=origin, filternorm=filternorm, @@ -1440,12 +1482,11 @@ def __init__(self, bbox, self.bbox = bbox def get_window_extent(self, renderer=None): - if renderer is None: - renderer = self.get_figure()._get_renderer() - if isinstance(self.bbox, BboxBase): return self.bbox elif callable(self.bbox): + if renderer is None: + renderer = self.get_figure()._get_renderer() return self.bbox(renderer) else: raise ValueError("Unknown type of bbox") @@ -1570,7 +1611,8 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, extension of *fname*, if any, and from :rc:`savefig.format` otherwise. If *format* is set, it determines the output format. arr : array-like - The image data. The shape can be one of + The image data. Accepts NumPy arrays or sequences + (e.g., lists or tuples). The shape can be one of MxN (luminance), MxNx3 (RGB) or MxNx4 (RGBA). vmin, vmax : float, optional *vmin* and *vmax* set the color scaling for the image by fixing the @@ -1601,6 +1643,10 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, default 'Software' key. """ from matplotlib.figure import Figure + + # Normalizing input (e.g., list or tuples) to NumPy array if needed + arr = np.asanyarray(arr) + if isinstance(fname, os.PathLike): fname = os.fspath(fname) if format is None: @@ -1619,10 +1665,8 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, else: # Don't bother creating an image; this avoids rounding errors on the # size when dividing and then multiplying by dpi. - if origin is None: - origin = mpl.rcParams["image.origin"] - else: - _api.check_in_list(('upper', 'lower'), origin=origin) + origin = mpl._val_or_rc(origin, "image.origin") + _api.check_in_list(('upper', 'lower'), origin=origin) if origin == "lower": arr = arr[::-1] if (isinstance(arr, memoryview) and arr.format == "B" @@ -1633,7 +1677,7 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, # as is, saving a few operations. rgba = arr else: - sm = cm.ScalarMappable(cmap=cmap) + sm = mcolorizer.Colorizer(cmap=cmap) sm.set_clim(vmin, vmax) rgba = sm.to_rgba(arr, bytes=True) if pil_kwargs is None: @@ -1743,12 +1787,14 @@ def _pil_png_to_float_array(pil_png): raise ValueError(f"Unknown PIL rawmode: {rawmode}") +@_api.deprecated('3.11', alternative="Pillow's `PIL.Image.Image.thumbnail`") def thumbnail(infile, thumbfile, scale=0.1, interpolation='bilinear', preview=False): """ Make a thumbnail of image in *infile* with output filename *thumbfile*. - See :doc:`/gallery/misc/image_thumbnail_sgskip`. + See `Pillow for a replacement + `_. Parameters ---------- @@ -1757,7 +1803,7 @@ def thumbnail(infile, thumbfile, scale=0.1, interpolation='bilinear', thus supports a wide range of file formats, including PNG, JPG, TIFF and others. - .. _Pillow: https://python-pillow.org/ + .. _Pillow: https://python-pillow.github.io thumbfile : str or file-like The thumbnail filename. @@ -1800,7 +1846,7 @@ def thumbnail(infile, thumbfile, scale=0.1, interpolation='bilinear', fig = Figure(figsize=(width, height), dpi=dpi) FigureCanvasBase(fig) - ax = fig.add_axes([0, 0, 1, 1], aspect='auto', + ax = fig.add_axes((0, 0, 1, 1), aspect='auto', frameon=False, xticks=[], yticks=[]) ax.imshow(im, aspect='auto', resample=True, interpolation=interpolation) fig.savefig(thumbfile, dpi=dpi) diff --git a/lib/matplotlib/image.pyi b/lib/matplotlib/image.pyi index 4b684f693845..1fcc1a710bfd 100644 --- a/lib/matplotlib/image.pyi +++ b/lib/matplotlib/image.pyi @@ -7,10 +7,10 @@ import numpy as np from numpy.typing import ArrayLike, NDArray import PIL.Image -import matplotlib.artist as martist from matplotlib.axes import Axes -from matplotlib import cm +from matplotlib import colorizer from matplotlib.backend_bases import RendererBase, MouseEvent +from matplotlib.colorizer import Colorizer from matplotlib.colors import Colormap, Normalize from matplotlib.figure import Figure from matplotlib.transforms import Affine2D, BboxBase, Bbox, Transform @@ -58,7 +58,7 @@ def composite_images( images: Sequence[_ImageBase], renderer: RendererBase, magnification: float = ... ) -> tuple[np.ndarray, float, float]: ... -class _ImageBase(martist.Artist, cm.ScalarMappable): +class _ImageBase(colorizer.ColorizingArtist): zorder: float origin: Literal["upper", "lower"] axes: Axes @@ -67,13 +67,14 @@ class _ImageBase(martist.Artist, cm.ScalarMappable): ax: Axes, cmap: str | Colormap | None = ..., norm: str | Normalize | None = ..., + colorizer: Colorizer | None = ..., interpolation: str | None = ..., origin: Literal["upper", "lower"] | None = ..., filternorm: bool = ..., filterrad: float = ..., resample: bool | None = ..., *, - interpolation_stage: Literal["data", "rgba"] | None = ..., + interpolation_stage: Literal["data", "rgba", "auto"] | None = ..., **kwargs ) -> None: ... def get_size(self) -> tuple[int, int]: ... @@ -89,8 +90,8 @@ class _ImageBase(martist.Artist, cm.ScalarMappable): def get_shape(self) -> tuple[int, int, int]: ... def get_interpolation(self) -> str: ... def set_interpolation(self, s: str | None) -> None: ... - def get_interpolation_stage(self) -> Literal["data", "rgba"]: ... - def set_interpolation_stage(self, s: Literal["data", "rgba"]) -> None: ... + def get_interpolation_stage(self) -> Literal["data", "rgba", "auto"]: ... + def set_interpolation_stage(self, s: Literal["data", "rgba", "auto"]) -> None: ... def can_composite(self) -> bool: ... def set_resample(self, v: bool | None) -> None: ... def get_resample(self) -> bool: ... @@ -106,13 +107,14 @@ class AxesImage(_ImageBase): *, cmap: str | Colormap | None = ..., norm: str | Normalize | None = ..., + colorizer: Colorizer | None = ..., interpolation: str | None = ..., origin: Literal["upper", "lower"] | None = ..., extent: tuple[float, float, float, float] | None = ..., filternorm: bool = ..., filterrad: float = ..., resample: bool = ..., - interpolation_stage: Literal["data", "rgba"] | None = ..., + interpolation_stage: Literal["data", "rgba", "auto"] | None = ..., **kwargs ) -> None: ... def get_window_extent(self, renderer: RendererBase | None = ...) -> Bbox: ... @@ -144,6 +146,7 @@ class PcolorImage(AxesImage): *, cmap: str | Colormap | None = ..., norm: str | Normalize | None = ..., + colorizer: Colorizer | None = ..., **kwargs ) -> None: ... def set_data(self, x: ArrayLike, y: ArrayLike, A: ArrayLike) -> None: ... # type: ignore[override] @@ -160,6 +163,7 @@ class FigureImage(_ImageBase): *, cmap: str | Colormap | None = ..., norm: str | Normalize | None = ..., + colorizer: Colorizer | None = ..., offsetx: int = ..., offsety: int = ..., origin: Literal["upper", "lower"] | None = ..., @@ -175,6 +179,7 @@ class BboxImage(_ImageBase): *, cmap: str | Colormap | None = ..., norm: str | Normalize | None = ..., + colorizer: Colorizer | None = ..., interpolation: str | None = ..., origin: Literal["upper", "lower"] | None = ..., filternorm: bool = ..., diff --git a/lib/matplotlib/inset.py b/lib/matplotlib/inset.py new file mode 100644 index 000000000000..aae640db6f81 --- /dev/null +++ b/lib/matplotlib/inset.py @@ -0,0 +1,290 @@ +""" +The inset module defines the InsetIndicator class, which draws the rectangle and +connectors required for `.Axes.indicate_inset` and `.Axes.indicate_inset_zoom`. +""" + +from . import _api, artist, transforms +from matplotlib.patches import ConnectionPatch, PathPatch, Rectangle +from matplotlib.path import Path + + +_shared_properties = ('alpha', 'edgecolor', 'linestyle', 'linewidth') + + +class InsetIndicator(artist.Artist): + """ + An artist to highlight an area of interest. + + An inset indicator is a rectangle on the plot at the position indicated by + *bounds* that optionally has lines that connect the rectangle to an inset + Axes (`.Axes.inset_axes`). + + .. versionadded:: 3.10 + """ + zorder = 4.99 + + def __init__(self, bounds=None, inset_ax=None, zorder=None, **kwargs): + """ + Parameters + ---------- + bounds : [x0, y0, width, height], optional + Lower-left corner of rectangle to be marked, and its width + and height. If not set, the bounds will be calculated from the + data limits of inset_ax, which must be supplied. + + inset_ax : `~.axes.Axes`, optional + An optional inset Axes to draw connecting lines to. Two lines are + drawn connecting the indicator box to the inset Axes on corners + chosen so as to not overlap with the indicator box. + + zorder : float, default: 4.99 + Drawing order of the rectangle and connector lines. The default, + 4.99, is just below the default level of inset Axes. + + **kwargs + Other keyword arguments are passed on to the `.Rectangle` patch. + """ + if bounds is None and inset_ax is None: + raise ValueError("At least one of bounds or inset_ax must be supplied") + + self._inset_ax = inset_ax + + if bounds is None: + # Work out bounds from inset_ax + self._auto_update_bounds = True + bounds = self._bounds_from_inset_ax() + else: + self._auto_update_bounds = False + + x, y, width, height = bounds + + self._rectangle = Rectangle((x, y), width, height, clip_on=False, **kwargs) + + # Connector positions cannot be calculated till the artist has been added + # to an axes, so just make an empty list for now. + self._connectors = [] + + super().__init__() + self.set_zorder(zorder) + + # Initial style properties for the artist should match the rectangle. + for prop in _shared_properties: + setattr(self, f'_{prop}', artist.getp(self._rectangle, prop)) + + def _shared_setter(self, prop, val): + """ + Helper function to set the same style property on the artist and its children. + """ + setattr(self, f'_{prop}', val) + + artist.setp([self._rectangle, *self._connectors], prop, val) + + @artist.Artist.axes.setter + def axes(self, new_axes): + # Set axes on the rectangle (required for some external transforms to work) as + # well as the InsetIndicator artist. + self.rectangle.axes = new_axes + artist.Artist.axes.fset(self, new_axes) + + def set_alpha(self, alpha): + # docstring inherited + self._shared_setter('alpha', alpha) + + def set_edgecolor(self, color): + """ + Set the edge color of the rectangle and the connectors. + + Parameters + ---------- + color : :mpltype:`color` or None + """ + self._shared_setter('edgecolor', color) + + def set_color(self, c): + """ + Set the edgecolor of the rectangle and the connectors, and the + facecolor for the rectangle. + + Parameters + ---------- + c : :mpltype:`color` + """ + self._shared_setter('edgecolor', c) + self._shared_setter('facecolor', c) + + def set_linewidth(self, w): + """ + Set the linewidth in points of the rectangle and the connectors. + + Parameters + ---------- + w : float or None + """ + self._shared_setter('linewidth', w) + + def set_linestyle(self, ls): + """ + Set the linestyle of the rectangle and the connectors. + + Parameters + ---------- + ls : {'-', '--', '-.', ':', '', ...} or (offset, on-off-seq) + Possible values: + + - A string: + + ======================================================= ================ + linestyle description + ======================================================= ================ + ``'-'`` or ``'solid'`` solid line + ``'--'`` or ``'dashed'`` dashed line + ``'-.'`` or ``'dashdot'`` dash-dotted line + ``':'`` or ``'dotted'`` dotted line + ``''`` or ``'none'`` (discouraged: ``'None'``, ``' '``) draw nothing + ======================================================= ================ + + - A tuple describing the start position and lengths of dashes and spaces: + + (offset, onoffseq) + + where + + - *offset* is a float specifying the offset (in points); i.e. how much + is the dash pattern shifted. + - *onoffseq* is a sequence of on and off ink in points. There can be + arbitrary many pairs of on and off values. + + Example: The tuple ``(0, (10, 5, 1, 5))`` means that the pattern starts + at the beginning of the line. It draws a 10 point long dash, + then a 5 point long space, then a 1 point long dash, followed by a 5 point + long space, and then the pattern repeats. + + For examples see :doc:`/gallery/lines_bars_and_markers/linestyles`. + """ + self._shared_setter('linestyle', ls) + + def _bounds_from_inset_ax(self): + xlim = self._inset_ax.get_xlim() + ylim = self._inset_ax.get_ylim() + return (xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]) + + def _update_connectors(self): + (x, y) = self._rectangle.get_xy() + width = self._rectangle.get_width() + height = self._rectangle.get_height() + + existing_connectors = self._connectors or [None] * 4 + + # connect the inset_axes to the rectangle + for xy_inset_ax, existing in zip([(0, 0), (0, 1), (1, 0), (1, 1)], + existing_connectors): + # inset_ax positions are in axes coordinates + # The 0, 1 values define the four edges if the inset_ax + # lower_left, upper_left, lower_right upper_right. + ex, ey = xy_inset_ax + if self.axes.xaxis.get_inverted(): + ex = 1 - ex + if self.axes.yaxis.get_inverted(): + ey = 1 - ey + xy_data = x + ex * width, y + ey * height + if existing is None: + # Create new connection patch with styles inherited from the + # parent artist. + p = ConnectionPatch( + xyA=xy_inset_ax, coordsA=self._inset_ax.transAxes, + xyB=xy_data, coordsB=self.rectangle.get_data_transform(), + arrowstyle="-", + edgecolor=self._edgecolor, alpha=self.get_alpha(), + linestyle=self._linestyle, linewidth=self._linewidth) + self._connectors.append(p) + else: + # Only update positioning of existing connection patch. We + # do not want to override any style settings made by the user. + existing.xy1 = xy_inset_ax + existing.xy2 = xy_data + existing.coords1 = self._inset_ax.transAxes + existing.coords2 = self.rectangle.get_data_transform() + + if existing is None: + # decide which two of the lines to keep visible.... + pos = self._inset_ax.get_position() + bboxins = pos.transformed(self.get_figure(root=False).transSubfigure) + rectbbox = transforms.Bbox.from_bounds(x, y, width, height).transformed( + self._rectangle.get_transform()) + x0 = rectbbox.x0 < bboxins.x0 + x1 = rectbbox.x1 < bboxins.x1 + y0 = rectbbox.y0 < bboxins.y0 + y1 = rectbbox.y1 < bboxins.y1 + self._connectors[0].set_visible(x0 ^ y0) + self._connectors[1].set_visible(x0 == y1) + self._connectors[2].set_visible(x1 == y0) + self._connectors[3].set_visible(x1 ^ y1) + + @property + def rectangle(self): + """`.Rectangle`: the indicator frame.""" + return self._rectangle + + @property + def connectors(self): + """ + 4-tuple of `.patches.ConnectionPatch` or None + The four connector lines connecting to (lower_left, upper_left, + lower_right upper_right) corners of *inset_ax*. Two lines are + set with visibility to *False*, but the user can set the + visibility to True if the automatic choice is not deemed correct. + """ + if self._inset_ax is None: + return + + if self._auto_update_bounds: + self._rectangle.set_bounds(self._bounds_from_inset_ax()) + self._update_connectors() + return tuple(self._connectors) + + def draw(self, renderer): + # docstring inherited + conn_same_style = [] + + # Figure out which connectors have the same style as the box, so should + # be drawn as a single path. + for conn in self.connectors or []: + if conn.get_visible(): + drawn = False + for s in _shared_properties: + if artist.getp(self._rectangle, s) != artist.getp(conn, s): + # Draw this connector by itself + conn.draw(renderer) + drawn = True + break + + if not drawn: + # Connector has same style as box. + conn_same_style.append(conn) + + if conn_same_style: + # Since at least one connector has the same style as the rectangle, draw + # them as a compound path. + artists = [self._rectangle] + conn_same_style + paths = [a.get_transform().transform_path(a.get_path()) for a in artists] + path = Path.make_compound_path(*paths) + + # Create a temporary patch to draw the path. + p = PathPatch(path) + p.update_from(self._rectangle) + p.set_transform(transforms.IdentityTransform()) + p.draw(renderer) + + return + + # Just draw the rectangle + self._rectangle.draw(renderer) + + @_api.deprecated( + '3.10', + message=('Since Matplotlib 3.10 indicate_inset_[zoom] returns a single ' + 'InsetIndicator artist with a rectangle property and a connectors ' + 'property. From 3.12 it will no longer be possible to unpack the ' + 'return value into two elements.')) + def __getitem__(self, key): + return [self._rectangle, self.connectors][key] diff --git a/lib/matplotlib/inset.pyi b/lib/matplotlib/inset.pyi new file mode 100644 index 000000000000..e895fd7be27c --- /dev/null +++ b/lib/matplotlib/inset.pyi @@ -0,0 +1,25 @@ +from . import artist +from .axes import Axes +from .backend_bases import RendererBase +from .patches import ConnectionPatch, Rectangle + +from .typing import ColorType, LineStyleType + +class InsetIndicator(artist.Artist): + def __init__( + self, + bounds: tuple[float, float, float, float] | None = ..., + inset_ax: Axes | None = ..., + zorder: float | None = ..., + **kwargs + ) -> None: ... + def set_alpha(self, alpha: float | None) -> None: ... + def set_edgecolor(self, color: ColorType | None) -> None: ... + def set_color(self, c: ColorType | None) -> None: ... + def set_linewidth(self, w: float | None) -> None: ... + def set_linestyle(self, ls: LineStyleType | None) -> None: ... + @property + def rectangle(self) -> Rectangle: ... + @property + def connectors(self) -> tuple[ConnectionPatch, ConnectionPatch, ConnectionPatch, ConnectionPatch] | None: ... + def draw(self, renderer: RendererBase) -> None: ... diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py index 5a96745d0697..8a3276b53371 100644 --- a/lib/matplotlib/layout_engine.py +++ b/lib/matplotlib/layout_engine.py @@ -10,9 +10,14 @@ layout engine while the figure is being created. In particular, colorbars are made differently with different layout engines (for historical reasons). -Matplotlib supplies two layout engines, `.TightLayoutEngine` and -`.ConstrainedLayoutEngine`. Third parties can create their own layout engine -by subclassing `.LayoutEngine`. +Matplotlib has two built-in layout engines: + +- `.TightLayoutEngine` was the first layout engine added to Matplotlib. + See also :ref:`tight_layout_guide`. +- `.ConstrainedLayoutEngine` is more modern and generally gives better results. + See also :ref:`constrainedlayout_guide`. + +Third parties can create their own layout engine by subclassing `.LayoutEngine`. """ from contextlib import nullcontext diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 9033fc23c1a1..e25c3525821c 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -37,7 +37,7 @@ from matplotlib.patches import (Patch, Rectangle, Shadow, FancyBboxPatch, StepPatch) from matplotlib.collections import ( - Collection, CircleCollection, LineCollection, PathCollection, + Collection, CircleCollection, LineCollection, PatchCollection, PathCollection, PolyCollection, RegularPolyCollection) from matplotlib.text import Text from matplotlib.transforms import Bbox, BboxBase, TransformedBbox @@ -98,9 +98,11 @@ def _update_bbox_to_anchor(self, loc_in_canvas): _legend_kw_doc_base = """ bbox_to_anchor : `.BboxBase`, 2-tuple, or 4-tuple of floats Box that is used to position the legend in conjunction with *loc*. - Defaults to `axes.bbox` (if called as a method to `.Axes.legend`) or - `figure.bbox` (if `.Figure.legend`). This argument allows arbitrary - placement of the legend. + This is an advanced option for free placement of the legend. For + most use cases, *loc* alone is sufficient. + + Defaults to ``axes.bbox`` (if called as a method to `.Axes.legend`) or + ``figure.bbox`` (if ``figure.legend``). Bbox coordinates are interpreted in the coordinate system given by *bbox_transform*, with the default transform @@ -119,6 +121,9 @@ def _update_bbox_to_anchor(self, loc_in_canvas): loc='upper right', bbox_to_anchor=(0.5, 0.5) + For more details on legend positioning, see the + :ref:`legend_guide`. + ncols : int, default: 1 The number of columns that the legend has. @@ -196,6 +201,12 @@ def _update_bbox_to_anchor(self, loc_in_canvas): The legend's background patch edge color. If ``"inherit"``, use :rc:`axes.edgecolor`. +linewidth : float or None, default: :rc:`legend.linewidth` + The legend's background patch edge linewidth. + If ``None``, use :rc:`patch.linewidth`. + + .. versionadded:: 3.11 + mode : {"expand", None} If *mode* is set to ``"expand"`` the legend will be horizontally expanded to fill the Axes area (or *bbox_to_anchor* if defines @@ -204,7 +215,7 @@ def _update_bbox_to_anchor(self, loc_in_canvas): bbox_transform : None or `~matplotlib.transforms.Transform` The transform for the bounding box (*bbox_to_anchor*). For a value of ``None`` (default) the Axes' - :data:`~matplotlib.axes.Axes.transAxes` transform will be used. + :data:`!matplotlib.axes.Axes.transAxes` transform will be used. title : str or None The legend's title. Default is no title (``None``). @@ -259,15 +270,19 @@ def _update_bbox_to_anchor(self, loc_in_canvas): loc : str or pair of floats, default: {default} The location of the legend. - The strings ``'upper left'``, ``'upper right'``, ``'lower left'``, - ``'lower right'`` place the legend at the corresponding corner of the - {parent}. + The string locations place the legend at the corresponding position + within the bounding box, which by default is the full {parent} area. + The bounding box can be changed via *bbox_to_anchor*. - The strings ``'upper center'``, ``'lower center'``, ``'center left'``, - ``'center right'`` place the legend at the center of the corresponding edge - of the {parent}. + The positions are visualized below:: - The string ``'center'`` places the legend at the center of the {parent}. + +--------------+--------------+---------------+ + | 'upper left' |'upper center'| 'upper right' | + +--------------+--------------+---------------+ + |'center left' | 'center' |'center right' | + +--------------+--------------+---------------+ + | 'lower left' |'lower center'| 'lower right' | + +--------------+--------------+---------------+ {best} The location can also be a 2-tuple giving the coordinates of the lower-left corner of the legend in {parent} coordinates (in which case *bbox_to_anchor* @@ -297,15 +312,20 @@ def _update_bbox_to_anchor(self, loc_in_canvas): _loc_doc_best = """ The string ``'best'`` places the legend at the location, among the nine locations defined so far, with the minimum overlap with other drawn - artists. This option can be quite slow for plots with large amounts of - data; your plotting speed may benefit from providing a specific location. + artists. This currently takes into account most, but not all, artists + added to the Axes via plotting functions. In particular it does not consider + inset axes, titles, or axis labels. + + The computation of the best position can be expensive for plots with large + amounts of data. If speed becomes a concern, you may may benefit from + providing a specific location. """ _legend_kw_axes_st = ( _loc_doc_base.format(parent='axes', default=':rc:`legend.loc`', best=_loc_doc_best, outside='') + _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_axes=_legend_kw_axes_st) +_docstring.interpd.register(_legend_kw_axes=_legend_kw_axes_st) _outside_doc = """ If a figure is using the constrained layout manager, the string codes @@ -323,20 +343,20 @@ def _update_bbox_to_anchor(self, loc_in_canvas): _loc_doc_base.format(parent='figure', default="'upper right'", best='', outside=_outside_doc) + _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_figure=_legend_kw_figure_st) +_docstring.interpd.register(_legend_kw_figure=_legend_kw_figure_st) _legend_kw_both_st = ( _loc_doc_base.format(parent='axes/figure', default=":rc:`legend.loc` for Axes, 'upper right' for Figure", best=_loc_doc_best, outside=_outside_doc) + _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_doc=_legend_kw_both_st) +_docstring.interpd.register(_legend_kw_doc=_legend_kw_both_st) _legend_kw_set_loc_st = ( _loc_doc_base.format(parent='axes/figure', default=":rc:`legend.loc` for Axes, 'upper right' for Figure", best=_loc_doc_best, outside=_outside_doc)) -_docstring.interpd.update(_legend_kw_set_loc_doc=_legend_kw_set_loc_st) +_docstring.interpd.register(_legend_kw_set_loc_doc=_legend_kw_set_loc_st) class Legend(Artist): @@ -351,7 +371,7 @@ class Legend(Artist): def __str__(self): return "Legend" - @_docstring.dedent_interpd + @_docstring.interpd def __init__( self, parent, handles, labels, *, @@ -385,6 +405,7 @@ def __init__( framealpha=None, # set frame alpha edgecolor=None, # frame patch edgecolor facecolor=None, # frame patch facecolor + linewidth=None, # frame patch linewidth bbox_to_anchor=None, # bbox to which the legend will be anchored bbox_transform=None, # transform for the bbox @@ -454,25 +475,12 @@ def __init__( self.borderaxespad = mpl._val_or_rc(borderaxespad, 'legend.borderaxespad') self.columnspacing = mpl._val_or_rc(columnspacing, 'legend.columnspacing') self.shadow = mpl._val_or_rc(shadow, 'legend.shadow') - # trim handles and labels if illegal label... - _lab, _hand = [], [] - for label, handle in zip(labels, handles): - if isinstance(label, str) and label.startswith('_'): - _api.warn_deprecated("3.8", message=( - "An artist whose label starts with an underscore was passed to " - "legend(); such artists will no longer be ignored in the future. " - "To suppress this warning, explicitly filter out such artists, " - "e.g. with `[art for art in artists if not " - "art.get_label().startswith('_')]`.")) - else: - _lab.append(label) - _hand.append(handle) - labels, handles = _lab, _hand if reverse: - labels.reverse() - handles.reverse() + labels = [*reversed(labels)] + handles = [*reversed(handles)] + handles = list(handles) if len(handles) < 2: ncols = 1 self._ncols = ncols if ncols != 1 else ncol @@ -497,7 +505,7 @@ def __init__( if isinstance(parent, Axes): self.isaxes = True self.axes = parent - self.set_figure(parent.figure) + self.set_figure(parent.get_figure(root=False)) elif isinstance(parent, FigureBase): self.isaxes = False self.set_figure(parent) @@ -539,9 +547,12 @@ def __init__( fancybox = mpl._val_or_rc(fancybox, "legend.fancybox") + linewidth = mpl._val_or_rc(linewidth, "legend.linewidth") + self.legendPatch = FancyBboxPatch( xy=(0, 0), width=1, height=1, facecolor=facecolor, edgecolor=edgecolor, + linewidth=linewidth, # If shadow is used, default to alpha=1 (#8943). alpha=(framealpha if framealpha is not None else 1 if shadow @@ -589,15 +600,18 @@ def __init__( # set the text color color_getters = { # getter function depends on line or patch - 'linecolor': ['get_color', 'get_facecolor'], + 'linecolor': ['get_markerfacecolor', + 'get_facecolor', + 'get_markeredgecolor', + 'get_edgecolor', + 'get_color'], 'markerfacecolor': ['get_markerfacecolor', 'get_facecolor'], 'mfc': ['get_markerfacecolor', 'get_facecolor'], 'markeredgecolor': ['get_markeredgecolor', 'get_edgecolor'], 'mec': ['get_markeredgecolor', 'get_edgecolor'], } - labelcolor = mpl._val_or_rc(labelcolor, 'legend.labelcolor') - if labelcolor is None: - labelcolor = mpl.rcParams['text.color'] + labelcolor = mpl._val_or_rc(mpl._val_or_rc(labelcolor, 'legend.labelcolor'), + 'text.color') if isinstance(labelcolor, str) and labelcolor in color_getters: getter_names = color_getters[labelcolor] for handle, text in zip(self.legend_handles, self.texts): @@ -609,19 +623,22 @@ def __init__( for getter_name in getter_names: try: color = getattr(handle, getter_name)() - if isinstance(color, np.ndarray): - if ( - color.shape[0] == 1 - or np.isclose(color, color[0]).all() - ): - text.set_color(color[0]) - else: - pass - else: - text.set_color(color) - break except AttributeError: - pass + continue + if isinstance(color, np.ndarray): + if color.size == 0: + continue + elif (color.shape[0] == 1 or np.isclose(color, color[0]).all()): + text.set_color(color[0]) + else: + pass + elif cbook._str_lower_equal(color, 'none'): + continue + elif mpl.colors.to_rgba(color)[3] == 0: + continue + else: + text.set_color(color) + break elif cbook._str_equal(labelcolor, 'none'): for text in self.texts: text.set_color(labelcolor) @@ -637,13 +654,13 @@ def _set_artist_props(self, a): """ Set the boilerplate props for artists added to Axes. """ - a.set_figure(self.figure) + a.set_figure(self.get_figure(root=False)) if self.isaxes: a.axes = self.axes a.set_transform(self.get_transform()) - @_docstring.dedent_interpd + @_docstring.interpd def set_loc(self, loc=None): """ Set the location of the legend. @@ -681,7 +698,7 @@ def set_loc(self, loc=None): locs = locs[::-1] loc = locs[0] + ' ' + locs[1] # check that loc is in acceptable strings - loc = _api.check_getitem(self.codes, loc=loc) + loc = _api.getitem_checked(self.codes, loc=loc) elif np.iterable(loc): # coerce iterable into tuple loc = tuple(loc) @@ -794,6 +811,7 @@ def draw(self, renderer): BarContainer: legend_handler.HandlerPatch( update_func=legend_handler.update_from_first_child), tuple: legend_handler.HandlerTuple(), + PatchCollection: legend_handler.HandlerPolyCollection(), PathCollection: legend_handler.HandlerPathCollection(), PolyCollection: legend_handler.HandlerPolyCollection() } @@ -943,12 +961,12 @@ def _init_legend_box(self, handles, labels, markerfirst=True): align=self._alignment, children=[self._legend_title_box, self._legend_handle_box]) - self._legend_box.set_figure(self.figure) + self._legend_box.set_figure(self.get_figure(root=False)) self._legend_box.axes = self.axes self.texts = text_list self.legend_handles = handle_list - def _auto_legend_data(self): + def _auto_legend_data(self, renderer): """ Return display coordinates for hit testing for "best" positioning. @@ -983,7 +1001,7 @@ def _auto_legend_data(self): if len(hoffsets): offsets.extend(transOffset.transform(hoffsets)) elif isinstance(artist, Text): - bboxes.append(artist.get_window_extent()) + bboxes.append(artist.get_window_extent(renderer)) return bboxes, lines, offsets @@ -1065,7 +1083,7 @@ def get_title(self): def get_window_extent(self, renderer=None): # docstring inherited if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() return self._legend_box.get_window_extent(renderer=renderer) def get_tightbbox(self, renderer=None): @@ -1154,9 +1172,10 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer): parentbbox : `~matplotlib.transforms.Bbox` A parent box which will contain the bbox, in display coordinates. """ + pad = self.borderaxespad * renderer.points_to_pixels(self._fontsize) return offsetbox._get_anchored_bbox( loc, bbox, parentbbox, - self.borderaxespad * renderer.points_to_pixels(self._fontsize)) + pad, pad) def _find_best_position(self, width, height, renderer): """Determine the best location to place the legend.""" @@ -1164,7 +1183,7 @@ def _find_best_position(self, width, height, renderer): start_time = time.perf_counter() - bboxes, lines, offsets = self._auto_legend_data() + bboxes, lines, offsets = self._auto_legend_data(renderer) bbox = Bbox.from_bounds(0, 0, width, height) @@ -1196,7 +1215,6 @@ def _find_best_position(self, width, height, renderer): return l, b - @_api.rename_parameter("3.8", "event", "mouseevent") def contains(self, mouseevent): return self.legendPatch.contains(mouseevent) @@ -1302,7 +1320,7 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): legend(handles=handles, labels=labels) The behavior for a mixture of positional and keyword handles and labels - is undefined and issues a warning; it will be an error in the future. + is undefined and raises an error. Parameters ---------- @@ -1335,10 +1353,8 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): handlers = kwargs.get('handler_map') if (handles is not None or labels is not None) and args: - _api.warn_deprecated("3.9", message=( - "You have mixed positional and keyword arguments, some input may " - "be discarded. This is deprecated since %(since)s and will " - "become an error %(removal)s.")) + raise TypeError("When passing handles and labels, they must both be " + "passed positionally or both as keywords.") if (hasattr(handles, "__len__") and hasattr(labels, "__len__") and @@ -1347,7 +1363,7 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): f"len(handles) = {len(handles)} " f"len(labels) = {len(labels)}") # if got both handles and labels as kwargs, make same length - if handles and labels: + if handles is not None and labels is not None: handles, labels = zip(*zip(handles, labels)) elif handles is not None and labels is None: @@ -1378,6 +1394,11 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): elif len(args) == 2: # 2 args: user defined handles and labels. handles, labels = args[:2] + if (hasattr(handles, "__len__") and hasattr(labels, "__len__") + and len(handles) != len(labels)): + _api.warn_external(f"Mismatched number of handles and labels: " + f"len(handles) = {len(handles)} " + f"len(labels) = {len(labels)}") else: raise _api.nargs_error('legend', '0-2', len(args)) diff --git a/lib/matplotlib/legend.pyi b/lib/matplotlib/legend.pyi index dde5882da69d..e17738c76161 100644 --- a/lib/matplotlib/legend.pyi +++ b/lib/matplotlib/legend.pyi @@ -14,12 +14,13 @@ from matplotlib.transforms import ( BboxBase, Transform, ) +from matplotlib.typing import ColorType, LegendLocType import pathlib from collections.abc import Iterable from typing import Any, Literal, overload -from .typing import ColorType + class DraggableLegend(DraggableOffsetBox): legend: Legend @@ -55,7 +56,7 @@ class Legend(Artist): handles: Iterable[Artist | tuple[Artist, ...]], labels: Iterable[str], *, - loc: str | tuple[float, float] | int | None = ..., + loc: LegendLocType | None = ..., numpoints: int | None = ..., markerscale: float | None = ..., markerfirst: bool = ..., @@ -84,6 +85,7 @@ class Legend(Artist): framealpha: float | None = ..., edgecolor: Literal["inherit"] | ColorType | None = ..., facecolor: Literal["inherit"] | ColorType | None = ..., + linewidth: float | None = ..., bbox_to_anchor: BboxBase | tuple[float, float] | tuple[float, float, float, float] @@ -118,7 +120,7 @@ class Legend(Artist): def get_texts(self) -> list[Text]: ... def set_alignment(self, alignment: Literal["center", "left", "right"]) -> None: ... def get_alignment(self) -> Literal["center", "left", "right"]: ... - def set_loc(self, loc: str | tuple[float, float] | int | None = ...) -> None: ... + def set_loc(self, loc: LegendLocType | None = ...) -> None: ... def set_title( self, title: str, prop: FontProperties | str | pathlib.Path | None = ... ) -> None: ... diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 5a929070e32d..65a78891b17f 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -466,7 +466,7 @@ def update_prop(self, legend_handle, orig_handle, legend): self._update_prop(legend_handle, orig_handle) - legend_handle.set_figure(legend.figure) + legend_handle.set_figure(legend.get_figure(root=False)) # legend._set_artist_props(legend_handle) legend_handle.set_clip_box(None) legend_handle.set_clip_path(None) @@ -790,17 +790,15 @@ def get_first(prop_array): # Directly set Patch color attributes (must be RGBA tuples). legend_handle._facecolor = first_color(orig_handle.get_facecolor()) legend_handle._edgecolor = first_color(orig_handle.get_edgecolor()) + legend_handle._hatch_color = first_color(orig_handle.get_hatchcolor()) legend_handle._original_facecolor = orig_handle._original_facecolor legend_handle._original_edgecolor = orig_handle._original_edgecolor legend_handle._fill = orig_handle.get_fill() legend_handle._hatch = orig_handle.get_hatch() - # Hatch color is anomalous in having no getters and setters. - legend_handle._hatch_color = orig_handle._hatch_color # Setters are fine for the remaining attributes. legend_handle.set_linewidth(get_first(orig_handle.get_linewidths())) legend_handle.set_linestyle(get_first(orig_handle.get_linestyles())) legend_handle.set_transform(get_first(orig_handle.get_transforms())) - legend_handle.set_figure(orig_handle.get_figure()) # Alpha is already taken into account by the color attributes. def create_artists(self, legend, orig_handle, diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 72e74f4eb9c5..69ad36fb768b 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -60,6 +60,20 @@ def _get_dash_pattern(style): return offset, dashes +def _get_dash_patterns(styles): + """Convert linestyle or sequence of linestyles to list of dash patterns.""" + try: + patterns = [_get_dash_pattern(styles)] + except ValueError: + try: + patterns = [_get_dash_pattern(x) for x in styles] + except ValueError as err: + emsg = f'Do not know how to convert {styles!r} to dashes' + raise ValueError(emsg) from err + + return patterns + + def _get_inverse_dash_pattern(offset, dashes): """Return the inverse of the given dash pattern, for filling the gaps.""" # Define the inverse pattern by moving the last gap to the start of the @@ -169,7 +183,7 @@ def _slice_or_none(in_v, slc): if ax is None: raise ValueError( "markevery is specified relative to the Axes size, but " - "the line does not have a Axes as parent") + "the line does not have an Axes as parent") # calc cumulative distance along path (in display coords): fin = np.isfinite(verts).all(axis=1) @@ -327,28 +341,16 @@ def __init__(self, xdata, ydata, *, if not np.iterable(ydata): raise RuntimeError('ydata must be a sequence') - if linewidth is None: - linewidth = mpl.rcParams['lines.linewidth'] - - if linestyle is None: - linestyle = mpl.rcParams['lines.linestyle'] - if marker is None: - marker = mpl.rcParams['lines.marker'] - if color is None: - color = mpl.rcParams['lines.color'] - - if markersize is None: - markersize = mpl.rcParams['lines.markersize'] - if antialiased is None: - antialiased = mpl.rcParams['lines.antialiased'] - if dash_capstyle is None: - dash_capstyle = mpl.rcParams['lines.dash_capstyle'] - if dash_joinstyle is None: - dash_joinstyle = mpl.rcParams['lines.dash_joinstyle'] - if solid_capstyle is None: - solid_capstyle = mpl.rcParams['lines.solid_capstyle'] - if solid_joinstyle is None: - solid_joinstyle = mpl.rcParams['lines.solid_joinstyle'] + linewidth = mpl._val_or_rc(linewidth, 'lines.linewidth') + linestyle = mpl._val_or_rc(linestyle, 'lines.linestyle') + marker = mpl._val_or_rc(marker, 'lines.marker') + color = mpl._val_or_rc(color, 'lines.color') + markersize = mpl._val_or_rc(markersize, 'lines.markersize') + antialiased = mpl._val_or_rc(antialiased, 'lines.antialiased') + dash_capstyle = mpl._val_or_rc(dash_capstyle, 'lines.dash_capstyle') + dash_joinstyle = mpl._val_or_rc(dash_joinstyle, 'lines.dash_joinstyle') + solid_capstyle = mpl._val_or_rc(solid_capstyle, 'lines.solid_capstyle') + solid_joinstyle = mpl._val_or_rc(solid_joinstyle, 'lines.solid_joinstyle') if drawstyle is None: drawstyle = 'default' @@ -467,11 +469,12 @@ def contains(self, mouseevent): yt = xy[:, 1] # Convert pick radius from points to pixels - if self.figure is None: + fig = self.get_figure(root=True) + if fig is None: _log.warning('no figure set when check if mouse is on line') pixels = self._pickradius else: - pixels = self.figure.dpi / 72. * self._pickradius + pixels = fig.dpi / 72. * self._pickradius # The math involved in checking for containment (here and inside of # segment_hits) assumes that it is OK to overflow, so temporarily set @@ -640,7 +643,7 @@ def get_window_extent(self, renderer=None): ignore=True) # correct for marker size, if any if self._marker: - ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5 + ms = (self._markersize / 72.0 * self.get_figure(root=True).dpi) * 0.5 bbox = bbox.padded(ms) return bbox @@ -681,7 +684,8 @@ def recache(self, always=False): y = self._y self._xy = np.column_stack(np.broadcast_arrays(x, y)).astype(float) - self._x, self._y = self._xy.T # views + self._x = self._xy[:, 0] # views of the x and y data + self._y = self._xy[:, 1] self._subslice = False if (self.axes @@ -706,9 +710,12 @@ def recache(self, always=False): interpolation_steps = self._path._interpolation_steps else: interpolation_steps = 1 - xy = STEP_LOOKUP_MAP[self._drawstyle](*self._xy.T) - self._path = Path(np.asarray(xy).T, - _interpolation_steps=interpolation_steps) + if self._drawstyle == 'default': + vertices = self._xy + else: + step_func = STEP_LOOKUP_MAP[self._drawstyle] + vertices = np.asarray(step_func(*self._xy.T)).T + self._path = Path(vertices, _interpolation_steps=interpolation_steps) self._transformed_path = None self._invalidx = False self._invalidy = False @@ -721,8 +728,12 @@ def _transform_path(self, subslice=None): """ # Masked arrays are now handled by the Path class itself if subslice is not None: - xy = STEP_LOOKUP_MAP[self._drawstyle](*self._xy[subslice, :].T) - _path = Path(np.asarray(xy).T, + if self._drawstyle == 'default': + vertices = self._xy[subslice] + else: + step_func = STEP_LOOKUP_MAP[self._drawstyle] + vertices = np.asarray(step_func(*self._xy[subslice, :].T)).T + _path = Path(vertices, _interpolation_steps=self._path._interpolation_steps) else: _path = self._path @@ -788,8 +799,11 @@ def draw(self, renderer): if self.get_sketch_params() is not None: gc.set_sketch_params(*self.get_sketch_params()) - # We first draw a path within the gaps if needed. - if self.is_dashed() and self._gapcolor is not None: + # We first draw a path within the gaps if needed, but only for + # visible dashed lines; zero-width lines would otherwise yield + # all-zero dashes. + if (self._linewidth > 0 and self.is_dashed() + and self._gapcolor is not None): lc_rgba = mcolors.to_rgba(self._gapcolor, self._alpha) gc.set_foreground(lc_rgba, isRGBA=True) @@ -802,7 +816,10 @@ def draw(self, renderer): lc_rgba = mcolors.to_rgba(self._color, self._alpha) gc.set_foreground(lc_rgba, isRGBA=True) - gc.set_dashes(*self._dash_pattern) + if self._linewidth > 0: + gc.set_dashes(*self._dash_pattern) + else: + gc.set_dashes(0, None) renderer.draw_path(gc, tpath, affine.frozen()) gc.restore() @@ -1146,28 +1163,38 @@ def set_linestyle(self, ls): Parameters ---------- - ls : {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} + ls : {'-', '--', '-.', ':', '', ...} or (offset, on-off-seq) Possible values: - A string: - ========================================== ================= - linestyle description - ========================================== ================= - ``'-'`` or ``'solid'`` solid line - ``'--'`` or ``'dashed'`` dashed line - ``'-.'`` or ``'dashdot'`` dash-dotted line - ``':'`` or ``'dotted'`` dotted line - ``'none'``, ``'None'``, ``' '``, or ``''`` draw nothing - ========================================== ================= + ======================================================= ================ + linestyle description + ======================================================= ================ + ``'-'`` or ``'solid'`` solid line + ``'--'`` or ``'dashed'`` dashed line + ``'-.'`` or ``'dashdot'`` dash-dotted line + ``':'`` or ``'dotted'`` dotted line + ``''`` or ``'none'`` (discouraged: ``'None'``, ``' '``) draw nothing + ======================================================= ================ - - Alternatively a dash tuple of the following form can be - provided:: + - A tuple describing the start position and lengths of dashes and spaces: (offset, onoffseq) - where ``onoffseq`` is an even length tuple of on and off ink - in points. See also :meth:`set_dashes`. + where + + - *offset* is a float specifying the offset (in points); i.e. how much + is the dash pattern shifted. + - *onoffseq* is a sequence of on and off ink in points. There can be + arbitrary many pairs of on and off values. + + Example: The tuple ``(0, (10, 5, 1, 5))`` means that the pattern starts + at the beginning of the line. It draws a 10 point long dash, + then a 5 point long space, then a 1 point long dash, followed by a 5 point + long space, and then the pattern repeats. + + See also :meth:`set_dashes`. For examples see :doc:`/gallery/lines_bars_and_markers/linestyles`. """ @@ -1252,8 +1279,7 @@ def set_markeredgewidth(self, ew): ew : float Marker edge width, in points. """ - if ew is None: - ew = mpl.rcParams['lines.markeredgewidth'] + ew = mpl._val_or_rc(ew, 'lines.markeredgewidth') if self._markeredgewidth != ew: self.stale = True self._markeredgewidth = ew @@ -1353,7 +1379,6 @@ def update_from(self, other): self._solidcapstyle = other._solidcapstyle self._solidjoinstyle = other._solidjoinstyle - self._linestyle = other._linestyle self._marker = MarkerStyle(marker=other._marker) self._drawstyle = other._drawstyle @@ -1505,8 +1530,8 @@ def get_transform(self): points_transform.transform([self._xy1, self._xy2]) dx = x2 - x1 dy = y2 - y1 - if np.allclose(x1, x2): - if np.allclose(y1, y2): + if dx == 0: + if dy == 0: raise ValueError( f"Cannot draw a line through two identical points " f"(x={(x1, x2)}, y={(y1, y2)})") @@ -1520,7 +1545,7 @@ def get_transform(self): (vxlo, vylo), (vxhi, vyhi) = ax.transScale.transform(ax.viewLim) # General case: find intersections with view limits in either # direction, and draw between the middle two points. - if np.isclose(slope, 0): + if slope == 0: start = vxlo, y1 stop = vxhi, y1 elif np.isinf(slope): @@ -1541,45 +1566,65 @@ def draw(self, renderer): super().draw(renderer) def get_xy1(self): - """ - Return the *xy1* value of the line. - """ + """Return the *xy1* value of the line.""" return self._xy1 def get_xy2(self): - """ - Return the *xy2* value of the line. - """ + """Return the *xy2* value of the line.""" return self._xy2 def get_slope(self): - """ - Return the *slope* value of the line. - """ + """Return the *slope* value of the line.""" return self._slope - def set_xy1(self, x, y): + def set_xy1(self, *args, **kwargs): """ Set the *xy1* value of the line. Parameters ---------- - x, y : float + xy1 : tuple[float, float] Points for the line to pass through. """ - self._xy1 = x, y + params = _api.select_matching_signature([ + lambda self, x, y: locals(), lambda self, xy1: locals(), + ], self, *args, **kwargs) + if "x" in params: + _api.warn_deprecated("3.10", message=( + "Passing x and y separately to AxLine.set_xy1 is deprecated since " + "%(since)s; pass them as a single tuple instead.")) + xy1 = params["x"], params["y"] + else: + xy1 = params["xy1"] + self._xy1 = xy1 - def set_xy2(self, x, y): + def set_xy2(self, *args, **kwargs): """ Set the *xy2* value of the line. + .. note:: + + You can only set *xy2* if the line was created using the *xy2* + parameter. If the line was created using *slope*, please use + `~.AxLine.set_slope`. + Parameters ---------- - x, y : float + xy2 : tuple[float, float] Points for the line to pass through. """ if self._slope is None: - self._xy2 = x, y + params = _api.select_matching_signature([ + lambda self, x, y: locals(), lambda self, xy2: locals(), + ], self, *args, **kwargs) + if "x" in params: + _api.warn_deprecated("3.10", message=( + "Passing x and y separately to AxLine.set_xy2 is deprecated since " + "%(since)s; pass them as a single tuple instead.")) + xy2 = params["x"], params["y"] + else: + xy2 = params["xy2"] + self._xy2 = xy2 else: raise ValueError("Cannot set an 'xy2' value while 'slope' is set;" " they differ but their functionalities overlap") @@ -1588,6 +1633,12 @@ def set_slope(self, slope): """ Set the *slope* value of the line. + .. note:: + + You can only set *slope* if the line was created using the *slope* + parameter. If the line was created using *xy2*, please use + `~.AxLine.set_xy2`. + Parameters ---------- slope : float @@ -1648,7 +1699,7 @@ def __init__(self, line): 'pick_event', self.onpick) self.ind = set() - canvas = property(lambda self: self.axes.figure.canvas) + canvas = property(lambda self: self.axes.get_figure(root=True).canvas) def process_selected(self, ind, xs, ys): """ diff --git a/lib/matplotlib/lines.pyi b/lib/matplotlib/lines.pyi index c91e457e3301..7989a03dae3a 100644 --- a/lib/matplotlib/lines.pyi +++ b/lib/matplotlib/lines.pyi @@ -2,7 +2,7 @@ from .artist import Artist from .axes import Axes from .backend_bases import MouseEvent, FigureCanvasBase from .path import Path -from .transforms import Bbox, Transform +from .transforms import Bbox from collections.abc import Callable, Sequence from typing import Any, Literal, overload @@ -130,8 +130,8 @@ class AxLine(Line2D): def get_xy1(self) -> tuple[float, float] | None: ... def get_xy2(self) -> tuple[float, float] | None: ... def get_slope(self) -> float: ... - def set_xy1(self, x: float, y: float) -> None: ... - def set_xy2(self, x: float, y: float) -> None: ... + def set_xy1(self, xy1: tuple[float, float]) -> None: ... + def set_xy2(self, xy2: tuple[float, float]) -> None: ... def set_slope(self, slope: float) -> None: ... class VertexSelector: diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index fa5e66e73ade..52fa0797536a 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -129,7 +129,7 @@ """ import copy -from collections.abc import Sized +from collections.abc import Hashable, Sized import numpy as np @@ -282,8 +282,7 @@ def _set_fillstyle(self, fillstyle): The part of the marker surface that is colored with markerfacecolor. """ - if fillstyle is None: - fillstyle = mpl.rcParams['markers.fillstyle'] + fillstyle = mpl._val_or_rc(fillstyle, 'markers.fillstyle') _api.check_in_list(self.fillstyles, fillstyle=fillstyle) self._fillstyle = fillstyle @@ -309,7 +308,7 @@ def _set_marker(self, marker): """ if isinstance(marker, str) and cbook.is_math_text(marker): self._marker_function = self._set_mathtext_path - elif isinstance(marker, (int, str)) and marker in self.markers: + elif isinstance(marker, Hashable) and marker in self.markers: self._marker_function = getattr(self, '_set_' + self.markers[marker]) elif (isinstance(marker, np.ndarray) and marker.ndim == 2 and marker.shape[1] == 2): diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index e25b76c4037c..42d4b5ef1fa8 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -20,9 +20,9 @@ import matplotlib as mpl from matplotlib import _api, _mathtext -from matplotlib.ft2font import LOAD_NO_HINTING +from matplotlib.ft2font import LoadFlags from matplotlib.font_manager import FontProperties -from ._mathtext import ( # noqa: reexported API +from ._mathtext import ( # noqa: F401, reexported API RasterParse, VectorParse, get_unicode_index) _log = logging.getLogger(__name__) @@ -55,7 +55,7 @@ def __init__(self, output): Whether to return a `VectorParse` ("path") or a `RasterParse` ("agg", or its synonym "macosx"). """ - self._output_type = _api.check_getitem( + self._output_type = _api.getitem_checked( {"path": "vector", "agg": "raster", "macosx": "raster"}, output=output.lower()) @@ -71,27 +71,27 @@ def parse(self, s, dpi=72, prop=None, *, antialiased=None): Depending on the *output* type, this returns either a `VectorParse` or a `RasterParse`. """ - # lru_cache can't decorate parse() directly because prop - # is mutable; key the cache using an internal copy (see - # text._get_text_metrics_with_cache for a similar case). + # lru_cache can't decorate parse() directly because prop is + # mutable, so we key the cache using an internal copy (see + # Text._get_text_metrics_with_cache for a similar case); likewise, + # we need to check the mutable state of the text.antialiased and + # text.hinting rcParams. prop = prop.copy() if prop is not None else None antialiased = mpl._val_or_rc(antialiased, 'text.antialiased') - return self._parse_cached(s, dpi, prop, antialiased) - - @functools.lru_cache(50) - def _parse_cached(self, s, dpi, prop, antialiased): from matplotlib.backends import backend_agg + load_glyph_flags = { + "vector": LoadFlags.NO_HINTING, + "raster": backend_agg.get_hinting_flag(), + }[self._output_type] + return self._parse_cached(s, dpi, prop, antialiased, load_glyph_flags) + @functools.lru_cache(50) + def _parse_cached(self, s, dpi, prop, antialiased, load_glyph_flags): if prop is None: prop = FontProperties() - fontset_class = _api.check_getitem( + fontset_class = _api.getitem_checked( self._font_type_mapping, fontset=prop.get_math_fontfamily()) - load_glyph_flags = { - "vector": LOAD_NO_HINTING, - "raster": backend_agg.get_hinting_flag(), - }[self._output_type] fontset = fontset_class(prop, load_glyph_flags) - fontsize = prop.get_size_in_points() if self._parser is None: # Cache the parser globally. diff --git a/lib/matplotlib/meson.build b/lib/matplotlib/meson.build index c4b66fc1b336..c0bfdb227e2e 100644 --- a/lib/matplotlib/meson.build +++ b/lib/matplotlib/meson.build @@ -4,7 +4,9 @@ python_sources = [ '_animation_data.py', '_blocking_input.py', '_cm.py', + '_cm_bivar.py', '_cm_listed.py', + '_cm_multivar.py', '_color_data.py', '_constrained_layout.py', '_docstring.py', @@ -15,6 +17,7 @@ python_sources = [ '_mathtext.py', '_mathtext_data.py', '_pylab_helpers.py', + '_style_helpers.py', '_text_helpers.py', '_tight_bbox.py', '_tight_layout.py', @@ -31,6 +34,7 @@ python_sources = [ 'cm.py', 'collections.py', 'colorbar.py', + 'colorizer.py', 'colors.py', 'container.py', 'contour.py', @@ -41,6 +45,7 @@ python_sources = [ 'gridspec.py', 'hatch.py', 'image.py', + 'inset.py', 'layout_engine.py', 'legend_handler.py', 'legend.py', @@ -87,7 +92,6 @@ typing_sources = [ '_enums.pyi', '_path.pyi', '_pylab_helpers.pyi', - '_ttconv.pyi', 'animation.pyi', 'artist.pyi', 'axis.pyi', @@ -99,6 +103,7 @@ typing_sources = [ 'cm.pyi', 'collections.pyi', 'colorbar.pyi', + 'colorizer.pyi', 'colors.pyi', 'container.pyi', 'contour.pyi', @@ -108,6 +113,7 @@ typing_sources = [ 'gridspec.pyi', 'hatch.pyi', 'image.pyi', + 'inset.pyi', 'layout_engine.pyi', 'legend_handler.pyi', 'legend.pyi', diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index e1f08c0da5ce..a694308384c1 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -48,7 +48,8 @@ """ import functools -from numbers import Number +from numbers import Integral, Number +import sys import numpy as np @@ -210,6 +211,15 @@ def detrend_linear(y): return y - (b*x + a) +def _stride_windows(x, n, noverlap=0): + _api.check_isinstance(Integral, n=n, noverlap=noverlap) + x = np.asarray(x) + step = n - noverlap + shape = (n, (x.shape[-1]-noverlap)//step) + strides = (x.strides[0], step*x.strides[0]) + return np.lib.stride_tricks.as_strided(x, shape=shape, strides=strides) + + def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): @@ -239,7 +249,7 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, if NFFT is None: NFFT = 256 - if noverlap >= NFFT: + if not (0 <= noverlap < NFFT): raise ValueError('noverlap must be less than NFFT') if mode is None or mode == 'default': @@ -304,8 +314,12 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, raise ValueError( "The window length must match the data's first dimension") - result = np.lib.stride_tricks.sliding_window_view( - x, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: + result = np.lib.stride_tricks.sliding_window_view( + x, NFFT, axis=0)[::NFFT - noverlap].T + else: + # The NumPy version on 32-bit will OOM, so use old implementation. + result = _stride_windows(x, NFFT, noverlap=noverlap) result = detrend(result, detrend_func, axis=0) result = result * window.reshape((-1, 1)) result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] @@ -313,8 +327,12 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, if not same_data: # if same_data is False, mode must be 'psd' - resultY = np.lib.stride_tricks.sliding_window_view( - y, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: + resultY = np.lib.stride_tricks.sliding_window_view( + y, NFFT, axis=0)[::NFFT - noverlap].T + else: + # The NumPy version on 32-bit will OOM, so use old implementation. + resultY = _stride_windows(y, NFFT, noverlap=noverlap) resultY = detrend(resultY, detrend_func, axis=0) resultY = resultY * window.reshape((-1, 1)) resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] @@ -335,7 +353,7 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, # the sampling frequency, if desired. Scale everything, except the DC # component and the NFFT/2 component: - # if we have a even number of frequencies, don't scale NFFT/2 + # if we have an even number of frequencies, don't scale NFFT/2 if not NFFT % 2: slc = slice(1, -1, None) # if we have an odd number, just don't scale DC @@ -400,7 +418,7 @@ def _single_spectrum_helper( # Split out these keyword docs so that they can be used elsewhere -_docstring.interpd.update( +_docstring.interpd.register( Spectral="""\ Fs : float, default: 2 The sampling frequency (samples per time unit). It is used to calculate @@ -458,7 +476,7 @@ def _single_spectrum_helper( MATLAB compatibility.""") -@_docstring.dedent_interpd +@_docstring.interpd def psd(x, NFFT=None, Fs=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None): r""" @@ -514,7 +532,7 @@ def psd(x, NFFT=None, Fs=None, detrend=None, window=None, return Pxx.real, freqs -@_docstring.dedent_interpd +@_docstring.interpd def csd(x, y, NFFT=None, Fs=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None): """ @@ -634,7 +652,7 @@ def csd(x, y, NFFT=None, Fs=None, detrend=None, window=None, **_docstring.interpd.params) -@_docstring.dedent_interpd +@_docstring.interpd def specgram(x, NFFT=None, Fs=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): @@ -717,7 +735,7 @@ def specgram(x, NFFT=None, Fs=None, detrend=None, window=None, return spec, freqs, t -@_docstring.dedent_interpd +@_docstring.interpd def cohere(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0, pad_to=None, sides='default', scale_by_freq=None): r""" @@ -778,7 +796,7 @@ class GaussianKDE: array, otherwise a 2D array with shape (# of dims, # of data). bw_method : {'scott', 'silverman'} or float or callable, optional The method used to calculate the estimator bandwidth. If a - float, this will be used directly as `kde.factor`. If a + float, this will be used directly as `!kde.factor`. If a callable, it should take a `GaussianKDE` instance as only parameter and return a float. If None (default), 'scott' is used. @@ -791,11 +809,11 @@ class GaussianKDE: num_dp : int Number of datapoints. factor : float - The bandwidth factor, obtained from `kde.covariance_factor`, with which + The bandwidth factor, obtained from `~GaussianKDE.covariance_factor`, with which the covariance matrix is multiplied. covariance : ndarray The covariance matrix of *dataset*, scaled by the calculated bandwidth - (`kde.factor`). + (`!kde.factor`). inv_cov : ndarray The inverse of *covariance*. diff --git a/lib/matplotlib/mpl-data/fonts/afm/cmti10.afm b/lib/matplotlib/mpl-data/fonts/afm/cmti10.afm new file mode 100644 index 000000000000..ac9e89f676b8 --- /dev/null +++ b/lib/matplotlib/mpl-data/fonts/afm/cmti10.afm @@ -0,0 +1,333 @@ +StartFontMetrics 2.0 +FontName cmti10 +FullName cmti10 +FamilyName cmti10 +Weight Medium +ItalicAngle 0.000000 +IsFixedPitch false +UnderlinePosition -133 +UnderlineThickness 20 +Version 1.1/12-Nov-94 +FontBBox -35, -250, 1125, 750 +Notice Copyright \(C\) 1994, Basil K. Malyshev. All Rights Reserved.\nBaKoMa Fonts Collection, Level-B. +EncodingScheme FontSpecific +CapHeight 683 +XHeight 431 +Descender -194 +Ascender 694 +StartCharMetrics 129 +C 0 ; WX 627.22 ; N Gamma ; B 59 0 706 683 ; +C 1 ; WX 817.78 ; N Delta ; B 70 0 752 716 ; +C 2 ; WX 766.67 ; N Theta ; B 148 -22 788 705 ; +C 3 ; WX 692.22 ; N Lambda ; B 58 0 643 716 ; +C 4 ; WX 664.44 ; N Xi ; B 75 0 755 683 ; +C 5 ; WX 743.33 ; N Pi ; B 59 0 854 683 ; +C 6 ; WX 715.56 ; N Sigma ; B 80 0 782 683 ; +C 7 ; WX 766.67 ; N Upsilon ; B 213 0 833 705 ; +C 8 ; WX 715.56 ; N Phi ; B 158 0 729 683 ; +C 9 ; WX 766.67 ; N Psi ; B 211 0 825 683 ; +C 10 ; WX 715.56 ; N Omega ; B 100 0 759 705 ; +C 11 ; WX 613.33 ; N ff ; B -25 -205 758 705 ; L i ffi ; L l ffl ; +C 12 ; WX 562.22 ; N fi ; B -25 -205 597 705 ; +C 13 ; WX 587.78 ; N fl ; B -25 -205 639 705 ; +C 14 ; WX 881.67 ; N ffi ; B -25 -205 917 705 ; +C 15 ; WX 894.44 ; N ffl ; B -25 -205 945 705 ; +C 16 ; WX 306.67 ; N dotlessi ; B 81 -11 334 442 ; +C 17 ; WX 332.22 ; N dotlessj ; B -35 -205 322 442 ; +C 18 ; WX 511.11 ; N grave ; B 291 503 433 696 ; +C 19 ; WX 511.11 ; N acute ; B 339 503 551 696 ; +C 20 ; WX 511.11 ; N caron ; B 279 505 538 633 ; +C 21 ; WX 511.11 ; N breve ; B 280 521 567 694 ; +C 22 ; WX 511.11 ; N macron ; B 232 555 566 589 ; +C 23 ; WX 831.28 ; N ring ; B 476 541 670 716 ; +C 24 ; WX 460 ; N cedilla ; B 99 -194 338 0 ; +C 25 ; WX 536.67 ; N germandbls ; B -20 -205 578 705 ; +C 26 ; WX 715.56 ; N ae ; B 90 -11 721 442 ; +C 27 ; WX 715.56 ; N oe ; B 106 -15 721 446 ; +C 28 ; WX 511.11 ; N oslash ; B 66 -109 553 540 ; +C 29 ; WX 882.78 ; N AE ; B 59 0 949 683 ; +C 30 ; WX 985 ; N OE ; B 162 -22 1051 705 ; +C 31 ; WX 766.67 ; N Oslash ; B 120 -62 818 745 ; +C 32 ; WX 255.56 ; N polishlcross ; B 91 280 345 393 ; +C 33 ; WX 306.67 ; N exclam ; B 111 0 376 716 ; L quoteleft exclamdown ; +C 34 ; WX 514.44 ; N quotedblright ; B 176 390 520 694 ; +C 35 ; WX 817.78 ; N numbersign ; B 115 -194 828 694 ; +C 36 ; WX 769.11 ; N dollar ; B 88 -11 698 709 ; +C 37 ; WX 817.78 ; N percent ; B 145 -56 847 750 ; +C 38 ; WX 766.67 ; N ampersand ; B 127 -22 803 716 ; +C 39 ; WX 306.67 ; N quoteright ; B 218 390 375 694 ; L quoteright quotedblright ; +C 40 ; WX 408.89 ; N parenleft ; B 150 -250 517 750 ; +C 41 ; WX 408.89 ; N parenright ; B 17 -250 384 750 ; +C 42 ; WX 511.11 ; N asterisk ; B 195 319 584 750 ; +C 43 ; WX 766.67 ; N plus ; B 140 -57 753 557 ; +C 44 ; WX 306.67 ; N comma ; B 72 -194 226 110 ; +C 45 ; WX 357.78 ; N hyphen ; B 85 185 340 245 ; L hyphen endash ; +C 46 ; WX 306.67 ; N period ; B 111 0 224 110 ; +C 47 ; WX 511.11 ; N slash ; B 20 -250 617 750 ; +C 48 ; WX 511.11 ; N zero ; B 115 -22 556 666 ; +C 49 ; WX 511.11 ; N one ; B 115 0 463 666 ; +C 50 ; WX 511.11 ; N two ; B 82 -22 551 666 ; +C 51 ; WX 511.11 ; N three ; B 95 -22 562 666 ; +C 52 ; WX 511.11 ; N four ; B 44 -194 475 666 ; +C 53 ; WX 511.11 ; N five ; B 107 -22 567 666 ; +C 54 ; WX 511.11 ; N six ; B 120 -22 567 666 ; +C 55 ; WX 511.11 ; N seven ; B 142 -22 628 666 ; +C 56 ; WX 511.11 ; N eight ; B 97 -22 554 666 ; +C 57 ; WX 511.11 ; N nine ; B 105 -22 553 666 ; +C 58 ; WX 306.67 ; N colon ; B 111 0 305 431 ; +C 59 ; WX 306.67 ; N semicolon ; B 72 -194 305 431 ; +C 60 ; WX 306.67 ; N exclamdown ; B 57 -216 322 500 ; +C 61 ; WX 766.67 ; N equal ; B 115 133 776 367 ; +C 62 ; WX 511.11 ; N questiondown ; B 85 -216 442 500 ; +C 63 ; WX 511.11 ; N question ; B 194 0 551 716 ; L quoteleft questiondown ; +C 64 ; WX 766.67 ; N at ; B 151 -11 789 705 ; +C 65 ; WX 743.33 ; N A ; B 58 0 693 716 ; +C 66 ; WX 703.89 ; N B ; B 62 0 734 683 ; +C 67 ; WX 715.56 ; N C ; B 150 -22 813 705 ; +C 68 ; WX 755 ; N D ; B 60 0 775 683 ; +C 69 ; WX 678.33 ; N E ; B 59 0 744 683 ; +C 70 ; WX 652.78 ; N F ; B 59 0 732 683 ; +C 71 ; WX 773.61 ; N G ; B 150 -22 813 705 ; +C 72 ; WX 743.33 ; N H ; B 59 0 854 683 ; +C 73 ; WX 385.56 ; N I ; B 55 0 503 683 ; +C 74 ; WX 525 ; N J ; B 90 -22 622 683 ; +C 75 ; WX 768.89 ; N K ; B 59 0 860 683 ; +C 76 ; WX 627.22 ; N L ; B 59 0 626 683 ; +C 77 ; WX 896.67 ; N M ; B 63 0 1004 683 ; +C 78 ; WX 743.33 ; N N ; B 59 0 854 683 ; +C 79 ; WX 766.67 ; N O ; B 148 -22 788 705 ; +C 80 ; WX 678.33 ; N P ; B 60 0 730 683 ; +C 81 ; WX 766.67 ; N Q ; B 148 -194 788 705 ; +C 82 ; WX 729.44 ; N R ; B 60 -22 723 683 ; +C 83 ; WX 562.22 ; N S ; B 74 -22 633 705 ; +C 84 ; WX 715.56 ; N T ; B 175 0 808 683 ; +C 85 ; WX 743.33 ; N U ; B 200 -22 854 683 ; +C 86 ; WX 743.33 ; N V ; B 208 -22 868 683 ; +C 87 ; WX 998.89 ; N W ; B 207 -22 1125 683 ; +C 88 ; WX 743.33 ; N X ; B 50 0 825 683 ; +C 89 ; WX 743.33 ; N Y ; B 201 0 875 683 ; +C 90 ; WX 613.33 ; N Z ; B 79 0 704 683 ; +C 91 ; WX 306.67 ; N bracketleft ; B 73 -250 446 750 ; +C 92 ; WX 514.44 ; N quotedblleft ; B 265 390 609 694 ; +C 93 ; WX 306.67 ; N bracketright ; B -14 -250 359 750 ; +C 94 ; WX 511.11 ; N circumflex ; B 264 533 524 694 ; +C 95 ; WX 306.67 ; N dotaccent ; B 251 559 364 669 ; +C 96 ; WX 306.67 ; N quoteleft ; B 204 390 361 694 ; L quoteleft quotedblleft ; +C 97 ; WX 511.11 ; N a ; B 107 -11 538 442 ; +C 98 ; WX 460 ; N b ; B 113 -11 461 694 ; +C 99 ; WX 460 ; N c ; B 108 -11 470 442 ; +C 100 ; WX 511.11 ; N d ; B 107 -11 562 694 ; +C 101 ; WX 460 ; N e ; B 112 -11 468 442 ; +C 102 ; WX 306.67 ; N f ; B -25 -205 452 705 ; L i fi ; L f ff ; L l fl ; +C 103 ; WX 460 ; N g ; B 51 -205 489 442 ; +C 104 ; WX 511.11 ; N h ; B 74 -11 538 694 ; +C 105 ; WX 306.67 ; N i ; B 81 -11 334 656 ; +C 106 ; WX 306.67 ; N j ; B -35 -205 359 656 ; +C 107 ; WX 460 ; N k ; B 74 -11 501 694 ; +C 108 ; WX 255.56 ; N l ; B 92 -11 308 694 ; +C 109 ; WX 817.78 ; N m ; B 81 -11 845 442 ; +C 110 ; WX 562.22 ; N n ; B 81 -11 589 442 ; +C 111 ; WX 511.11 ; N o ; B 108 -11 511 442 ; +C 112 ; WX 511.11 ; N p ; B 12 -194 512 442 ; +C 113 ; WX 460 ; N q ; B 107 -194 499 442 ; +C 114 ; WX 421.67 ; N r ; B 81 -11 488 442 ; +C 115 ; WX 408.89 ; N s ; B 76 -11 419 442 ; +C 116 ; WX 332.22 ; N t ; B 90 -11 373 626 ; +C 117 ; WX 536.67 ; N u ; B 81 -11 564 442 ; +C 118 ; WX 460 ; N v ; B 81 -11 492 443 ; +C 119 ; WX 664.44 ; N w ; B 81 -11 696 443 ; +C 120 ; WX 463.89 ; N x ; B 55 -11 517 442 ; +C 121 ; WX 485.56 ; N y ; B 81 -205 517 442 ; +C 122 ; WX 408.89 ; N z ; B 60 -11 465 442 ; +C 123 ; WX 511.11 ; N endash ; B 92 253 552 279 ; L hyphen emdash ; +C 124 ; WX 1022.22 ; N emdash ; B 118 253 1037 279 ; +C 125 ; WX 511.11 ; N hungarumlaut ; B 267 505 576 697 ; +C 126 ; WX 511.11 ; N tilde ; B 248 565 572 668 ; +C 127 ; WX 511.11 ; N dieresis ; B 268 565 552 669 ; +C -1 ; WX 357.78 ; N space ; B 357 0 358 0 ; +EndCharMetrics +StartKernData +StartKernPairs 180 +KPX A C -25.56 +KPX A G -25.56 +KPX A O -25.56 +KPX A Q -25.56 +KPX A T -76.67 +KPX A U -25.56 +KPX A V -102.22 +KPX A W -102.22 +KPX A Y -76.67 +KPX A a -51.11 +KPX A b -25.56 +KPX A c -51.11 +KPX A d -51.11 +KPX A e -51.11 +KPX A g -51.11 +KPX A h -25.56 +KPX A i -25.56 +KPX A k -25.56 +KPX A l -25.56 +KPX A m -25.56 +KPX A n -25.56 +KPX A o -51.11 +KPX A q -51.11 +KPX A r -25.56 +KPX A t -25.56 +KPX A u -25.56 +KPX A v -25.56 +KPX A w -25.56 +KPX D A -25.56 +KPX D V -25.56 +KPX D W -25.56 +KPX D X -25.56 +KPX D Y -25.56 +KPX F A -102.22 +KPX F C -25.56 +KPX F G -25.56 +KPX F O -25.56 +KPX F Q -25.56 +KPX F a -76.67 +KPX F e -76.67 +KPX F o -76.67 +KPX F r -76.67 +KPX F u -76.67 +KPX K C -25.56 +KPX K G -25.56 +KPX K O -25.56 +KPX K Q -25.56 +KPX L T -76.67 +KPX L V -102.22 +KPX L W -102.22 +KPX L Y -76.67 +KPX L a -51.11 +KPX L c -51.11 +KPX L d -51.11 +KPX L e -51.11 +KPX L g -51.11 +KPX L o -51.11 +KPX L q -51.11 +KPX O A -25.56 +KPX O V -25.56 +KPX O W -25.56 +KPX O X -25.56 +KPX O Y -25.56 +KPX P A -76.67 +KPX R C -25.56 +KPX R G -25.56 +KPX R O -25.56 +KPX R Q -25.56 +KPX R T -76.67 +KPX R U -25.56 +KPX R V -102.22 +KPX R W -102.22 +KPX R Y -76.67 +KPX R a -51.11 +KPX R b -25.56 +KPX R c -51.11 +KPX R d -51.11 +KPX R e -51.11 +KPX R g -51.11 +KPX R h -25.56 +KPX R i -25.56 +KPX R k -25.56 +KPX R l -25.56 +KPX R m -25.56 +KPX R n -25.56 +KPX R o -51.11 +KPX R q -51.11 +KPX R r -25.56 +KPX R t -25.56 +KPX R u -25.56 +KPX R v -25.56 +KPX R w -25.56 +KPX T A -76.67 +KPX T a -76.67 +KPX T e -76.67 +KPX T o -76.67 +KPX T r -76.67 +KPX T u -76.67 +KPX T y -76.67 +KPX V A -102.22 +KPX V C -25.56 +KPX V G -25.56 +KPX V O -25.56 +KPX V Q -25.56 +KPX V a -76.67 +KPX V e -76.67 +KPX V o -76.67 +KPX V r -76.67 +KPX V u -76.67 +KPX W A -76.67 +KPX X C -25.56 +KPX X G -25.56 +KPX X O -25.56 +KPX X Q -25.56 +KPX Y A -76.67 +KPX Y a -76.67 +KPX Y e -76.67 +KPX Y o -76.67 +KPX Y r -76.67 +KPX Y u -76.67 +KPX b a -51.11 +KPX b c -51.11 +KPX b d -51.11 +KPX b e -51.11 +KPX b g -51.11 +KPX b o -51.11 +KPX b q -51.11 +KPX c a -51.11 +KPX c c -51.11 +KPX c d -51.11 +KPX c e -51.11 +KPX c g -51.11 +KPX c o -51.11 +KPX c q -51.11 +KPX d l 51.11 +KPX e a -51.11 +KPX e c -51.11 +KPX e d -51.11 +KPX e e -51.11 +KPX e g -51.11 +KPX e o -51.11 +KPX e q -51.11 +KPX f bracketright 104.31 +KPX f exclam 104.31 +KPX f parenright 104.31 +KPX f question 104.31 +KPX f quoteright 104.31 +KPX ff bracketright 104.31 +KPX ff exclam 104.31 +KPX ff parenright 104.31 +KPX ff question 104.31 +KPX ff quoteright 104.31 +KPX l l 51.11 +KPX n quoteright -102.22 +KPX o a -51.11 +KPX o c -51.11 +KPX o d -51.11 +KPX o e -51.11 +KPX o g -51.11 +KPX o o -51.11 +KPX o q -51.11 +KPX p a -51.11 +KPX p c -51.11 +KPX p d -51.11 +KPX p e -51.11 +KPX p g -51.11 +KPX p o -51.11 +KPX p q -51.11 +KPX polishlcross L -320.55 +KPX polishlcross l -255.55 +KPX quoteright exclam 102.22 +KPX quoteright question 102.22 +KPX r a -51.11 +KPX r c -51.11 +KPX r d -51.11 +KPX r e -51.11 +KPX r g -51.11 +KPX r o -51.11 +KPX r q -51.11 +KPX w l 51.11 +EndKernPairs +EndKernData +EndFontMetrics diff --git a/lib/matplotlib/mpl-data/fonts/ttf/LastResortHE-Regular.ttf b/lib/matplotlib/mpl-data/fonts/ttf/LastResortHE-Regular.ttf new file mode 100644 index 000000000000..69ad694fa5d2 Binary files /dev/null and b/lib/matplotlib/mpl-data/fonts/ttf/LastResortHE-Regular.ttf differ diff --git a/lib/matplotlib/mpl-data/fonts/ttf/cmti10.ttf b/lib/matplotlib/mpl-data/fonts/ttf/cmti10.ttf new file mode 100644 index 000000000000..8311a98ac9e3 Binary files /dev/null and b/lib/matplotlib/mpl-data/fonts/ttf/cmti10.ttf differ diff --git a/lib/matplotlib/mpl-data/kpsewhich.lua b/lib/matplotlib/mpl-data/kpsewhich.lua index 8e9172a45082..dc526effeebe 100644 --- a/lib/matplotlib/mpl-data/kpsewhich.lua +++ b/lib/matplotlib/mpl-data/kpsewhich.lua @@ -1,3 +1,4 @@ -- see dviread._LuatexKpsewhich kpse.set_program_name("latex") -while true do print(kpse.lookup(io.read():gsub("\r", ""))); io.flush(); end +kpse.init_prog("", 600, "ljfour") +while true do print(kpse.lookup(io.read():gsub("\r", ""), {mktexpk=true})); io.flush(); end diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index 29ffb20f4280..67fd6c0d18be 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -1,6 +1,6 @@ #### MATPLOTLIBRC FORMAT -## NOTE FOR END USERS: DO NOT EDIT THIS FILE! +## DO NOT EDIT THIS FILE, MAKE A COPY FIRST ## ## This is a sample Matplotlib configuration file - you can find a copy ## of it on your system in site-packages/matplotlib/mpl-data/matplotlibrc @@ -148,15 +148,21 @@ ## for more information on patch properties. #patch.linewidth: 1.0 # edge width in points. #patch.facecolor: C0 -#patch.edgecolor: black # if forced, or patch is not filled -#patch.force_edgecolor: False # True to always use edgecolor +#patch.edgecolor: black # By default, Patches and Collections do not draw edges. + # This value is only used if facecolor is "none" + # (an Artist without facecolor and edgecolor would be + # invisible) or if patch.force_edgecolor is True. +#patch.force_edgecolor: False # By default, Patches and Collections do not draw edges. + # Set this to True to draw edges with patch.edgedcolor + # as the default edgecolor. + # This is mainly relevant for styles. #patch.antialiased: True # render patches in antialiased (no jaggies) ## *************************************************************************** ## * HATCHES * ## *************************************************************************** -#hatch.color: black +#hatch.color: edge #hatch.linewidth: 1.0 @@ -237,7 +243,7 @@ ## ## The font.variant property has two values: normal or small-caps. For ## TrueType fonts, which are scalable fonts, small-caps is equivalent -## to using a font size of 'smaller', or about 83 % of the current font +## to using a font size of 'small', or about 83 % of the current font ## size. ## ## The font.weight property has effectively 13 values: normal, bold, @@ -257,7 +263,7 @@ ## special text sizes tick labels, axes, labels, title, etc., see the rc ## settings for axes and ticks. Special text sizes can be defined ## relative to font.size, using the following values: xx-small, x-small, -## small, medium, large, x-large, xx-large, larger, or smaller +## small, medium, large, x-large, xx-large #font.family: sans-serif #font.style: normal @@ -272,6 +278,11 @@ #font.fantasy: Chicago, Charcoal, Impact, Western, xkcd script, fantasy #font.monospace: DejaVu Sans Mono, Bitstream Vera Sans Mono, Computer Modern Typewriter, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace +## If font.enable_last_resort is True, then Unicode Consortium's Last Resort +## font will be appended to all font selections. This ensures that there will +## always be a glyph displayed. +#font.enable_last_resort: true + ## *************************************************************************** ## * TEXT * @@ -281,6 +292,11 @@ ## for more information on text properties #text.color: black +## The language of the text in a format accepted by libraqm, namely `a BCP47 language +## code `_. If None, then no +## particular language will be implied, and default font settings will be used. +#text.language: None + ## FreeType hinting flag ("foo" corresponds to FT_LOAD_FOO); may be one of the ## following (Proprietary Matplotlib-specific synonyms are given in parentheses, ## but their use is discouraged): @@ -290,15 +306,14 @@ ## ("native" is a synonym.) ## - force_autohint: Use FreeType's auto-hinter. ("auto" is a synonym.) ## - no_hinting: Disable hinting. ("none" is a synonym.) -#text.hinting: force_autohint +#text.hinting: default -#text.hinting_factor: 8 # Specifies the amount of softness for hinting in the +#text.hinting_factor: 1 # Specifies the amount of softness for hinting in the # horizontal direction. A value of 1 will hint to full # pixels. A value of 2 will hint to half pixels etc. -#text.kerning_factor: 0 # Specifies the scaling factor for kerning values. This - # is provided solely to allow old test images to remain - # unchanged. Set to 6 to obtain previous behavior. - # Values other than 0 or 6 have no defined meaning. +#text.kerning_factor: None # Specifies the scaling factor for kerning values. Values + # other than 0, 6, or None have no defined meaning. + # This setting is deprecated. #text.antialiased: True # If True (default), the text will be antialiased. # This only affects raster outputs. #text.parse_math: True # Use mathtext if there is an even number of unescaped @@ -316,6 +331,16 @@ # zapf chancery, charter, serif, sans-serif, helvetica, # avant garde, courier, monospace, computer modern roman, # computer modern sans serif, computer modern typewriter + +## The TeX engine/format to use. The following values are supported: +## - "latex": The classic TeX engine (the current default). All backends render +## TeX's output by parsing the DVI output into glyphs and boxes and emitting +## those one by one. +## - "latex+dvipng": The same as "latex", with the exception that Agg-based +## backends rely on dvipng to rasterize TeX's output. This value was the +## default up to Matplotlib 3.10. +#text.latex.engine: latex + #text.latex.preamble: # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES # AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP # IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO. @@ -328,7 +353,7 @@ # become quite long. # The following packages are always loaded with usetex, # so beware of package collisions: - # geometry, inputenc, type1cm. + # color, fix-cm, geometry, graphicx, textcomp. # PostScript (PSNFSS) font packages may also be # loaded, depending on your font settings. @@ -350,10 +375,10 @@ # 'stixsans'] when a symbol cannot be found in one of the # custom math fonts. Select 'None' to not perform fallback # and replace the missing character by a dummy symbol. -#mathtext.default: it # The default font to use for math. - # Can be any of the LaTeX font names, including - # the special name "regular" for the same font - # used in regular text. +#mathtext.default: normal # The default font to use for math. + # Can be any of the LaTeX font names (normal, it, bf, + # etc.), including the special name "regular" for the + # same font used in regular text. ## *************************************************************************** @@ -411,9 +436,9 @@ #axes.unicode_minus: True # use Unicode for the minus symbol rather than hyphen. See # https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes -#axes.prop_cycle: cycler('color', ['1f77b4', 'ff7f0e', '2ca02c', 'd62728', '9467bd', '8c564b', 'e377c2', '7f7f7f', 'bcbd22', '17becf']) - # color cycle for plot lines as list of string color specs: - # single letter, long name, or web-style hex +#axes.prop_cycle: cycler(color='tab10') + # color cycle for plot lines as either a named color sequence or a list + # of string color specs: single letter, long name, or web-style hex # As opposed to all other parameters in this file, the color # values must be enclosed in quotes for this parameter, # e.g. '1f77b4', instead of 1f77b4. @@ -433,6 +458,14 @@ #axes3d.yaxis.panecolor: (0.90, 0.90, 0.90, 0.5) # background pane on 3D axes #axes3d.zaxis.panecolor: (0.925, 0.925, 0.925, 0.5) # background pane on 3D axes +#axes3d.depthshade: True # depth shade for 3D scatter plots +#axes3d.depthshade_minalpha: 0.3 # minimum alpha value for depth shading + +#axes3d.mouserotationstyle: arcball # {azel, trackball, sphere, arcball} + # See also https://matplotlib.org/stable/api/toolkits/mplot3d/view_angles.html#rotation-with-mouse +#axes3d.trackballsize: 0.667 # trackball diameter, in units of the Axes bbox +#axes3d.trackballborder: 0.2 # trackball border width, in units of the Axes bbox (only for 'sphere' and 'arcball' style) +#axes3d.snap_rotation: 5.0 # Snap angle (degrees) for 3D rotation when holding Control. ## *************************************************************************** ## * AXIS * ## *************************************************************************** @@ -524,6 +557,16 @@ #grid.linewidth: 0.8 # in points #grid.alpha: 1.0 # transparency, between 0.0 and 1.0 +#grid.major.color: None # If None defaults to grid.color +#grid.major.linestyle: None # If None defaults to grid.linestyle +#grid.major.linewidth: None # If None defaults to grid.linewidth +#grid.major.alpha: None # If None defaults to grid.alpha + +#grid.minor.color: None # If None defaults to grid.color +#grid.minor.linestyle: None # If None defaults to grid.linestyle +#grid.minor.linewidth: None # If None defaults to grid.linewidth +#grid.minor.alpha: None # If None defaults to grid.alpha + ## *************************************************************************** ## * LEGEND * @@ -533,6 +576,7 @@ #legend.framealpha: 0.8 # legend patch transparency #legend.facecolor: inherit # inherit from axes.facecolor; or color spec #legend.edgecolor: 0.8 # background patch boundary color +#legend.linewidth: None # line width of the legend frame, None means inherit from patch.linewidth #legend.fancybox: True # if True, use a rounded box for the # legend background, else a rectangle #legend.shadow: False # if True, give background a shadow effect @@ -570,6 +614,9 @@ # the pyplot interface before emitting a warning. # If less than one this feature is disabled. #figure.raise_window : True # Raise the GUI window to front when show() is called. + # If set to False, we currently do not take any further + # actions and whether the window appears on the front + # may depend on the GUI framework and window manager. ## The figure subplot parameters. All dimensions are a fraction of the figure width and height. #figure.subplot.left: 0.125 # the left side of the subplots of the figure @@ -602,8 +649,8 @@ ## * IMAGES * ## *************************************************************************** #image.aspect: equal # {equal, auto} or a number -#image.interpolation: antialiased # see help(imshow) for options -#image.interpolation_stage: data # see help(imshow) for options +#image.interpolation: auto # see help(imshow) for options +#image.interpolation_stage: auto # see help(imshow) for options #image.cmap: viridis # A colormap name (plasma, magma, etc.) #image.lut: 256 # the size of the colormap lookup table #image.origin: upper # {lower, upper} @@ -629,6 +676,8 @@ ## * ERRORBAR PLOTS * ## *************************************************************************** #errorbar.capsize: 0 # length of end cap on error bars in pixels +#errorbar.capthick: None # thickness of end cap on error bars in points +#errorbar.elinewidth: None # line width of error bar lines in points ## *************************************************************************** @@ -671,7 +720,7 @@ # to the nearest pixel when certain criteria are met. # When False, paths will never be snapped. #path.sketch: None # May be None, or a tuple of the form: - # path.sketch: (scale, length, randomness) + # path.sketch: (scale, length, randomness) # - *scale* is the amplitude of the wiggle # perpendicular to the line (in pixels). # - *length* is the length of the wiggle along the @@ -732,9 +781,11 @@ #svg.fonttype: path # How to handle SVG fonts: # path: Embed characters as paths -- supported # by most SVG renderers - # None: Assume fonts are installed on the + # none: Assume fonts are installed on the # machine where the SVG will be viewed. #svg.hashsalt: None # If not None, use this string as hash salt instead of uuid4 +#svg.id: None # If not None, use this string as the value for the `id` + # attribute in the top tag ### pgf parameter ## See https://matplotlib.org/stable/tutorials/text/pgf.html for more information. diff --git a/lib/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle b/lib/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle index 418721314335..03a97a7b3cd9 100644 --- a/lib/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle @@ -1,7 +1,7 @@ # Solarized color palette taken from https://ethanschoonover.com/solarized/ # Inspired by, and copied from ggthemes https://github.com/jrnold/ggthemes -#TODO: +# TODO: # 1. Padding to title from face # 2. Remove top & right ticks # 3. Give Title a Magenta Color(?) diff --git a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle index 96f62f4ba592..3dc92f832b20 100644 --- a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle @@ -1,6 +1,9 @@ # This patch should go on top of the "classic" style and exists solely to avoid # changing baseline images. -text.kerning_factor : 6 - ytick.alignment: center_baseline + +hatch.color: edge + +text.hinting: default +text.hinting_factor: 1 diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle index 50516d831ae4..302a25ca29a9 100644 --- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle @@ -61,7 +61,7 @@ hist.bins : 10 # # The font.variant property has two values: normal or small-caps. For # TrueType fonts, which are scalable fonts, small-caps is equivalent -# to using a font size of 'smaller', or about 83% of the current font +# to using a font size of 'small', or about 83% of the current font # size. # # The font.weight property has effectively 13 values: normal, bold, @@ -86,7 +86,7 @@ font.stretch : normal # special text sizes tick labels, axes, labels, title, etc, see the rc # settings for axes and ticks. Special text sizes can be defined # relative to font.size, using the following values: xx-small, x-small, -# small, medium, large, x-large, xx-large, larger, or smaller +# small, medium, large, x-large, xx-large font.size : 12.0 font.serif : DejaVu Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif font.sans-serif: DejaVu Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif @@ -122,8 +122,8 @@ text.latex.preamble : # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURE # Note that it has to be put on a single line, which may # become quite long. # The following packages are always loaded with usetex, so - # beware of package collisions: color, geometry, graphicx, - # type1cm, textcomp. + # beware of package collisions: + # color, fix-cm, geometry, graphicx, textcomp. # Adobe Postscript (PSSNFS) font packages may also be # loaded, depending on your font settings. @@ -162,10 +162,10 @@ mathtext.fallback: cm # Select fallback font from ['cm' (Computer Modern), 'sti # custom math fonts. Select 'None' to not perform fallback # and replace the missing character by a dummy. -mathtext.default : it # The default font to use for math. - # Can be any of the LaTeX font names, including - # the special name "regular" for the same font - # used in regular text. +mathtext.default: normal # The default font to use for math. + # Can be any of the LaTeX font names (normal, it, bf, + # etc.), including the special name "regular" for the + # same font used in regular text. ### AXES # default face and edge color, default tick sizes, @@ -225,6 +225,8 @@ axes.spines.top : True polaraxes.grid : True # display grid on polar axes axes3d.grid : True # display grid on 3D axes axes3d.automargin : False # automatically add margin when manually setting 3D axis limits +axes3d.depthshade : False # depth shade for 3D scatter plots +axes3d.depthshade_minalpha : 0.3 # minimum alpha value for depth shading date.autoformatter.year : %Y date.autoformatter.month : %b %Y diff --git a/lib/matplotlib/mpl-data/stylelib/petroff10.mplstyle b/lib/matplotlib/mpl-data/stylelib/petroff10.mplstyle new file mode 100644 index 000000000000..62d1262a09cd --- /dev/null +++ b/lib/matplotlib/mpl-data/stylelib/petroff10.mplstyle @@ -0,0 +1,5 @@ +# Color cycle survey palette from Petroff (2021): +# https://arxiv.org/abs/2107.02270 +# https://github.com/mpetroff/accessible-color-cycles +axes.prop_cycle: cycler('color', ['3f90da', 'ffa90e', 'bd1f01', '94a4a2', '832db6', 'a96b59', 'e76300', 'b9ac70', '717581', '92dadd']) +patch.facecolor: 3f90da diff --git a/lib/matplotlib/mpl-data/stylelib/petroff6.mplstyle b/lib/matplotlib/mpl-data/stylelib/petroff6.mplstyle new file mode 100644 index 000000000000..ff227eba45ba --- /dev/null +++ b/lib/matplotlib/mpl-data/stylelib/petroff6.mplstyle @@ -0,0 +1,5 @@ +# Color cycle survey palette from Petroff (2021): +# https://arxiv.org/abs/2107.02270 +# https://github.com/mpetroff/accessible-color-cycles +axes.prop_cycle: cycler('color', ['5790fc', 'f89c20', 'e42536', '964a8b', '9c9ca1', '7a21dd']) +patch.facecolor: 5790fc diff --git a/lib/matplotlib/mpl-data/stylelib/petroff8.mplstyle b/lib/matplotlib/mpl-data/stylelib/petroff8.mplstyle new file mode 100644 index 000000000000..0228f736ddea --- /dev/null +++ b/lib/matplotlib/mpl-data/stylelib/petroff8.mplstyle @@ -0,0 +1,5 @@ +# Color cycle survey palette from Petroff (2021): +# https://arxiv.org/abs/2107.02270 +# https://github.com/mpetroff/accessible-color-cycles +axes.prop_cycle: cycler('color', ['1845fb', 'ff5e02', 'c91f16', 'c849a9', 'adad7d', '86c8dd', '578dff', '656364']) +patch.facecolor: 1845fb diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 32c5bafcde1d..ca19a26f2b17 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -201,15 +201,15 @@ def _get_aligned_offsets(yspans, height, align="baseline"): class OffsetBox(martist.Artist): """ - The OffsetBox is a simple container artist. + A simple container artist. The child artists are meant to be drawn at a relative position to its parent. - Being an artist itself, all parameters are passed on to `.Artist`. + Being an artist itself, all keyword arguments are passed on to `.Artist`. """ - def __init__(self, *args, **kwargs): - super().__init__(*args) + def __init__(self, **kwargs): + super().__init__() self._internal_update(kwargs) # Clipping has not been implemented in the OffsetBox family, so # disable the clip flag for consistency. It can always be turned back @@ -363,7 +363,7 @@ def get_bbox(self, renderer): def get_window_extent(self, renderer=None): # docstring inherited if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() bbox = self.get_bbox(renderer) try: # Some subclasses redefine get_offset to take no args. px, py = self.get_offset(bbox, renderer) @@ -436,6 +436,14 @@ class VPacker(PackerBase): """ VPacker packs its children vertically, automatically adjusting their relative positions at draw time. + + .. code-block:: none + + +---------+ + | Child 1 | + | Child 2 | + | Child 3 | + +---------+ """ def _get_bbox_and_child_offsets(self, renderer): @@ -468,6 +476,12 @@ class HPacker(PackerBase): """ HPacker packs its children horizontally, automatically adjusting their relative positions at draw time. + + .. code-block:: none + + +-------------------------------+ + | Child 1 Child 2 Child 3 | + +-------------------------------+ """ def _get_bbox_and_child_offsets(self, renderer): @@ -498,6 +512,26 @@ class PaddedBox(OffsetBox): The `.PaddedBox` contains a `.FancyBboxPatch` that is used to visualize it when rendering. + + .. code-block:: none + + +----------------------------+ + | | + | | + | | + | <--pad--> Artist | + | ^ | + | pad | + | v | + +----------------------------+ + + Attributes + ---------- + pad : float + The padding in points. + patch : `.FancyBboxPatch` + When *draw_frame* is True, this `.FancyBboxPatch` is made visible and + creates a border around the box. """ def __init__(self, child, pad=0., *, draw_frame=False, patch_attrs=None): @@ -644,7 +678,7 @@ def add_artist(self, a): a.set_transform(self.get_transform()) if self.axes is not None: a.axes = self.axes - fig = self.figure + fig = self.get_figure(root=False) if fig is not None: a.set_figure(fig) @@ -760,27 +794,29 @@ def get_offset(self): return self._offset def get_bbox(self, renderer): - _, h_, d_ = renderer.get_text_width_height_descent( - "lp", self._text._fontproperties, - ismath="TeX" if self._text.get_usetex() else False) + _, h_, d_ = mtext._get_text_metrics_with_cache( + renderer, "lp", self._text._fontproperties, + ismath="TeX" if self._text.get_usetex() else False, + dpi=self.get_figure(root=True).dpi) - bbox, info, yd = self._text._get_layout(renderer) + bbox, info, _ = self._text._get_layout(renderer) + _last_line, (_last_width, _last_ascent, last_descent), _last_xy = info[-1] w, h = bbox.size self._baseline_transform.clear() if len(info) > 1 and self._multilinebaseline: yd_new = 0.5 * h - 0.5 * (h_ - d_) - self._baseline_transform.translate(0, yd - yd_new) - yd = yd_new + self._baseline_transform.translate(0, last_descent - yd_new) + last_descent = yd_new else: # single line - h_d = max(h_ - d_, h - yd) - h = h_d + yd + h_d = max(h_ - d_, h - last_descent) + h = h_d + last_descent ha = self._text.get_horizontalalignment() x0 = {"left": 0, "center": -w / 2, "right": -w}[ha] - return Bbox.from_bounds(x0, -yd, w, h) + return Bbox.from_bounds(x0, -last_descent, w, h) def draw(self, renderer): # docstring inherited @@ -791,17 +827,18 @@ def draw(self, renderer): class AuxTransformBox(OffsetBox): """ - Offset Box with the aux_transform. Its children will be - transformed with the aux_transform first then will be - offsetted. The absolute coordinate of the aux_transform is meaning - as it will be automatically adjust so that the left-lower corner - of the bounding box of children will be set to (0, 0) before the - offset transform. - - It is similar to drawing area, except that the extent of the box - is not predetermined but calculated from the window extent of its - children. Furthermore, the extent of the children will be - calculated in the transformed coordinate. + An OffsetBox with an auxiliary transform. + + All child artists are first transformed with *aux_transform*, then + translated with an offset (the same for all children) so the bounding + box of the children matches the drawn box. (In other words, adding an + arbitrary translation to *aux_transform* has no effect as it will be + cancelled out by the later offsetting.) + + `AuxTransformBox` is similar to `.DrawingArea`, except that the extent of + the box is not predetermined but calculated from the window extent of its + children, and the extent of the children will be calculated in the + transformed coordinate. """ def __init__(self, aux_transform): self.aux_transform = aux_transform @@ -818,10 +855,7 @@ def add_artist(self, a): self.stale = True def get_transform(self): - """ - Return the :class:`~matplotlib.transforms.Transform` applied - to the children - """ + """Return the `.Transform` applied to the children.""" return (self.aux_transform + self.ref_offset_transform + self.offset_transform) @@ -873,7 +907,7 @@ def draw(self, renderer): class AnchoredOffsetbox(OffsetBox): """ - An offset box placed according to location *loc*. + An OffsetBox placed according to location *loc*. AnchoredOffsetbox has a single child. When multiple children are needed, use an extra OffsetBox to enclose them. By default, the offset box is @@ -913,8 +947,13 @@ def __init__(self, loc, *, See the parameter *loc* of `.Legend` for details. pad : float, default: 0.4 Padding around the child as fraction of the fontsize. - borderpad : float, default: 0.5 + borderpad : float or (float, float), default: 0.5 Padding between the offsetbox frame and the *bbox_to_anchor*. + If a float, the same padding is used for both x and y. + If a tuple of two floats, it specifies the (x, y) padding. + + .. versionadded:: 3.11 + The *borderpad* parameter now accepts a tuple of (x, y) paddings. child : `.OffsetBox` The box that will be anchored. prop : `.FontProperties` @@ -939,7 +978,7 @@ def __init__(self, loc, *, self.set_child(child) if isinstance(loc, str): - loc = _api.check_getitem(self.codes, loc=loc) + loc = _api.getitem_checked(self.codes, loc=loc) self.loc = loc self.borderpad = borderpad @@ -1021,12 +1060,22 @@ def set_bbox_to_anchor(self, bbox, transform=None): @_compat_get_offset def get_offset(self, bbox, renderer): # docstring inherited - pad = (self.borderpad - * renderer.points_to_pixels(self.prop.get_size_in_points())) + fontsize_in_pixels = renderer.points_to_pixels(self.prop.get_size_in_points()) + try: + borderpad_x, borderpad_y = self.borderpad + except TypeError: + borderpad_x = self.borderpad + borderpad_y = self.borderpad + pad_x_pixels = borderpad_x * fontsize_in_pixels + pad_y_pixels = borderpad_y * fontsize_in_pixels bbox_to_anchor = self.get_bbox_to_anchor() x0, y0 = _get_anchored_bbox( - self.loc, Bbox.from_bounds(0, 0, bbox.width, bbox.height), - bbox_to_anchor, pad) + self.loc, + Bbox.from_bounds(0, 0, bbox.width, bbox.height), + bbox_to_anchor, + pad_x_pixels, + pad_y_pixels + ) return x0 - bbox.x0, y0 - bbox.y0 def update_frame(self, bbox, fontsize=None): @@ -1051,15 +1100,15 @@ def draw(self, renderer): self.stale = False -def _get_anchored_bbox(loc, bbox, parentbbox, borderpad): +def _get_anchored_bbox(loc, bbox, parentbbox, pad_x, pad_y): """ Return the (x, y) position of the *bbox* anchored at the *parentbbox* with - the *loc* code with the *borderpad*. + the *loc* code with the *borderpad* and padding *pad_x*, *pad_y*. """ # This is only called internally and *loc* should already have been # validated. If 0 (None), we just let ``bbox.anchored`` raise. c = [None, "NE", "NW", "SW", "SE", "E", "W", "E", "S", "N", "C"][loc] - container = parentbbox.padded(-borderpad) + container = parentbbox.padded(-pad_x, -pad_y) return bbox.anchored(c, container=container).p0 @@ -1107,7 +1156,70 @@ def __init__(self, s, loc, *, pad=0.4, borderpad=0.5, prop=None, **kwargs): class OffsetImage(OffsetBox): + """ + Container artist for images. + + Image data is displayed using `.BboxImage`. This image is meant to be positioned + relative to a parent artist. + + Parameters + ---------- + arr: array-like or `PIL.Image.Image` + The data to be color-coded. The interpretation depends on the + shape: + + - (M, N) `~numpy.ndarray` or masked array: values to be colormapped + - (M, N, 3): RGB array + - (M, N, 4): RGBA array + zoom: float, default: 1 + zoom factor: + + - no zoom: factor =1 + - zoom in: factor > 1 + - zoom out: 0< factor < 1 + + cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` + The Colormap instance or registered colormap name used to map scalar + data to colors. This parameter is ignored if X is RGB(A). + + norm : str or `~matplotlib.colors.Normalize`, default: None + Maps luminance to 0-1. This parameter is ignored if X is RGB(A). + + interpolation : str, default: :rc:`image.interpolation` + Supported values are 'none', 'auto', 'nearest', 'bilinear', + 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', + 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', + 'sinc', 'lanczos', 'blackman'. + + origin : {'upper', 'lower'}, default: :rc:`image.origin` + Place the [0, 0] index of the array in the upper left or lower left + corner of the Axes. The convention 'upper' is typically used for + matrices and images. + + filternorm : bool, default: True + A parameter for the antigrain image resize filter + (see the antigrain documentation). + If filternorm is set, the filter normalizes integer values and corrects + the rounding errors. It doesn't do anything with the source floating + point values, it corrects only integers according to the rule of 1.0 + which means that any sum of pixel weights must be equal to 1.0. So, + the filter function must produce a graph of the proper shape. + + filterrad : float > 0, default: 4 + The filter radius for filters that have a radius parameter, i.e. when + interpolation is one of: 'sinc', 'lanczos' or 'blackman'. + + resample : bool, default: False + When True, use a full resampling method. When False, only resample when + the output image is larger than the input image. + + dpi_cor: bool, default: True + Correct for the backend DPI setting + + **kwargs : `.BboxImage` properties + + """ def __init__(self, arr, *, zoom=1, cmap=None, @@ -1120,7 +1232,6 @@ def __init__(self, arr, *, dpi_cor=True, **kwargs ): - super().__init__() self._dpi_cor = dpi_cor @@ -1191,7 +1302,7 @@ class AnnotationBbox(martist.Artist, mtext._AnnotationBase): def __str__(self): return f"AnnotationBbox({self.xy[0]:g},{self.xy[1]:g})" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, offsetbox, xy, xybox=None, xycoords='data', boxcoords=None, *, frameon=True, pad=0.4, # FancyBboxPatch boxstyle. annotation_clip=None, @@ -1343,9 +1454,7 @@ def set_fontsize(self, s=None): If *s* is not given, reset to :rc:`legend.fontsize`. """ - if s is None: - s = mpl.rcParams["legend.fontsize"] - + s = mpl._val_or_rc(s, "legend.fontsize") self.prop = FontProperties(size=s) self.stale = True @@ -1356,7 +1465,7 @@ def get_fontsize(self): def get_window_extent(self, renderer=None): # docstring inherited if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() self.update_positions(renderer) return Bbox.union([child.get_window_extent(renderer) for child in self.get_children()]) @@ -1364,7 +1473,7 @@ def get_window_extent(self, renderer=None): def get_tightbbox(self, renderer=None): # docstring inherited if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() self.update_positions(renderer) return Bbox.union([child.get_tightbbox(renderer) for child in self.get_children()]) @@ -1412,8 +1521,9 @@ def draw(self, renderer): renderer.open_group(self.__class__.__name__, gid=self.get_gid()) self.update_positions(renderer) if self.arrow_patch is not None: - if self.arrow_patch.figure is None and self.figure is not None: - self.arrow_patch.figure = self.figure + if (self.arrow_patch.get_figure(root=False) is None and + (fig := self.get_figure(root=False)) is not None): + self.arrow_patch.set_figure(fig) self.arrow_patch.draw(renderer) self.patch.draw(renderer) self.offsetbox.draw(renderer) @@ -1453,7 +1563,7 @@ def finalize_offset(self): def __init__(self, ref_artist, use_blit=False): self.ref_artist = ref_artist if not ref_artist.pickable(): - ref_artist.set_picker(True) + ref_artist.set_picker(self._picker) self.got_artist = False self._use_blit = use_blit and self.canvas.supports_blit callbacks = self.canvas.callbacks @@ -1467,8 +1577,15 @@ def __init__(self, ref_artist, use_blit=False): ] ] + @staticmethod + def _picker(artist, mouseevent): + # A custom picker to prevent dragging on mouse scroll events + if mouseevent.name == "scroll_event": + return False, {} + return artist.contains(mouseevent) + # A property, not an attribute, to maintain picklability. - canvas = property(lambda self: self.ref_artist.figure.canvas) + canvas = property(lambda self: self.ref_artist.get_figure(root=True).canvas) cids = property(lambda self: [ disconnect.args[0] for disconnect in self._disconnectors[:2]]) @@ -1480,35 +1597,38 @@ def on_motion(self, evt): if self._use_blit: self.canvas.restore_region(self.background) self.ref_artist.draw( - self.ref_artist.figure._get_renderer()) + self.ref_artist.get_figure(root=True)._get_renderer()) self.canvas.blit() else: self.canvas.draw() def on_pick(self, evt): - if self._check_still_parented() and evt.artist == self.ref_artist: - self.mouse_x = evt.mouseevent.x - self.mouse_y = evt.mouseevent.y - self.got_artist = True - if self._use_blit: + if self._check_still_parented(): + if evt.artist == self.ref_artist: + self.mouse_x = evt.mouseevent.x + self.mouse_y = evt.mouseevent.y + self.save_offset() + self.got_artist = True + if self.got_artist and self._use_blit: self.ref_artist.set_animated(True) self.canvas.draw() - self.background = \ - self.canvas.copy_from_bbox(self.ref_artist.figure.bbox) - self.ref_artist.draw( - self.ref_artist.figure._get_renderer()) + fig = self.ref_artist.get_figure(root=False) + self.background = self.canvas.copy_from_bbox(fig.bbox) + self.ref_artist.draw(fig._get_renderer()) self.canvas.blit() - self.save_offset() def on_release(self, event): if self._check_still_parented() and self.got_artist: self.finalize_offset() self.got_artist = False if self._use_blit: + self.canvas.restore_region(self.background) + self.ref_artist.draw(self.ref_artist.figure._get_renderer()) + self.canvas.blit() self.ref_artist.set_animated(False) def _check_still_parented(self): - if self.ref_artist.figure is None: + if self.ref_artist.get_figure(root=False) is None: self.disconnect() return False else: @@ -1536,7 +1656,7 @@ def __init__(self, ref_artist, offsetbox, use_blit=False): def save_offset(self): offsetbox = self.offsetbox - renderer = offsetbox.figure._get_renderer() + renderer = offsetbox.get_figure(root=True)._get_renderer() offset = offsetbox.get_offset(offsetbox.get_bbox(renderer), renderer) self.offsetbox_x, self.offsetbox_y = offset self.offsetbox.set_offset(offset) @@ -1547,7 +1667,7 @@ def update_offset(self, dx, dy): def get_loc_in_canvas(self): offsetbox = self.offsetbox - renderer = offsetbox.figure._get_renderer() + renderer = offsetbox.get_figure(root=True)._get_renderer() bbox = offsetbox.get_bbox(renderer) ox, oy = offsetbox._offset loc_in_canvas = (ox + bbox.x0, oy + bbox.y0) diff --git a/lib/matplotlib/offsetbox.pyi b/lib/matplotlib/offsetbox.pyi index c222a9b2973e..36f31908eebf 100644 --- a/lib/matplotlib/offsetbox.pyi +++ b/lib/matplotlib/offsetbox.pyi @@ -2,11 +2,12 @@ import matplotlib.artist as martist from matplotlib.backend_bases import RendererBase, Event, FigureCanvasBase from matplotlib.colors import Colormap, Normalize import matplotlib.text as mtext -from matplotlib.figure import Figure +from matplotlib.figure import Figure, SubFigure from matplotlib.font_manager import FontProperties from matplotlib.image import BboxImage from matplotlib.patches import FancyArrowPatch, FancyBboxPatch from matplotlib.transforms import Bbox, BboxBase, Transform +from matplotlib.typing import CoordsType import numpy as np from numpy.typing import ArrayLike @@ -25,8 +26,8 @@ def _get_packed_offsets( class OffsetBox(martist.Artist): width: float | None height: float | None - def __init__(self, *args, **kwargs) -> None: ... - def set_figure(self, fig: Figure) -> None: ... + def __init__(self, **kwargs) -> None: ... + def set_figure(self, fig: Figure | SubFigure) -> None: ... def set_offset( self, xy: tuple[float, float] @@ -156,7 +157,7 @@ class AnchoredOffsetbox(OffsetBox): loc: str, *, pad: float = ..., - borderpad: float = ..., + borderpad: float | tuple[float, float] = ..., child: OffsetBox | None = ..., prop: FontProperties | None = ..., frameon: bool = ..., @@ -184,7 +185,7 @@ class AnchoredText(AnchoredOffsetbox): loc: str, *, pad: float = ..., - borderpad: float = ..., + borderpad: float | tuple[float, float] = ..., prop: dict[str, Any] | None = ..., **kwargs ) -> None: ... @@ -219,9 +220,7 @@ class AnnotationBbox(martist.Artist, mtext._AnnotationBase): offsetbox: OffsetBox arrowprops: dict[str, Any] | None xybox: tuple[float, float] - boxcoords: str | tuple[str, str] | martist.Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ] + boxcoords: CoordsType arrow_patch: FancyArrowPatch | None patch: FancyBboxPatch prop: FontProperties @@ -230,17 +229,8 @@ class AnnotationBbox(martist.Artist, mtext._AnnotationBase): offsetbox: OffsetBox, xy: tuple[float, float], xybox: tuple[float, float] | None = ..., - xycoords: str - | tuple[str, str] - | martist.Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] = ..., - boxcoords: str - | tuple[str, str] - | martist.Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] - | None = ..., + xycoords: CoordsType = ..., + boxcoords: CoordsType | None = ..., *, frameon: bool = ..., pad: float = ..., @@ -258,20 +248,14 @@ class AnnotationBbox(martist.Artist, mtext._AnnotationBase): @property def anncoords( self, - ) -> str | tuple[str, str] | martist.Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ]: ... + ) -> CoordsType: ... @anncoords.setter def anncoords( self, - coords: str - | tuple[str, str] - | martist.Artist - | Transform - | Callable[[RendererBase], Bbox | Transform], + coords: CoordsType, ) -> None: ... def get_children(self) -> list[martist.Artist]: ... - def set_figure(self, fig: Figure) -> None: ... + def set_figure(self, fig: Figure | SubFigure) -> None: ... def set_fontsize(self, s: str | float | None = ...) -> None: ... def get_fontsize(self) -> float: ... def get_tightbbox(self, renderer: RendererBase | None = ...) -> Bbox: ... diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 2899952634a9..cdd03bcd7e89 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -56,6 +56,8 @@ def __init__(self, *, fill=True, capstyle=None, joinstyle=None, + hatchcolor=None, + edgegapcolor=None, **kwargs): """ The following kwarg properties are supported @@ -71,7 +73,7 @@ def __init__(self, *, if joinstyle is None: joinstyle = JoinStyle.miter - self._hatch_color = colors.to_rgba(mpl.rcParams['hatch.color']) + self._hatch_linewidth = mpl.rcParams['hatch.linewidth'] self._fill = bool(fill) # needed for set_facecolor call if color is not None: if edgecolor is not None or facecolor is not None: @@ -81,11 +83,13 @@ def __init__(self, *, self.set_color(color) else: self.set_edgecolor(edgecolor) + self.set_hatchcolor(hatchcolor) self.set_facecolor(facecolor) self._linewidth = 0 self._unscaled_dash_pattern = (0, None) # offset, dash self._dash_pattern = (0, None) # offset, dash (scaled by linewidth) + self._gapcolor = None self.set_linestyle(linestyle) self.set_linewidth(linewidth) @@ -93,6 +97,7 @@ def __init__(self, *, self.set_hatch(hatch) self.set_capstyle(capstyle) self.set_joinstyle(joinstyle) + self.set_edgegapcolor(edgegapcolor) if len(kwargs): self._internal_update(kwargs) @@ -290,7 +295,9 @@ def update_from(self, other): self._fill = other._fill self._hatch = other._hatch self._hatch_color = other._hatch_color + self._original_hatchcolor = other._original_hatchcolor self._unscaled_dash_pattern = other._unscaled_dash_pattern + self._gapcolor = other._gapcolor self.set_linewidth(other._linewidth) # also sets scaled dashes self.set_transform(other.get_data_transform()) # If the transform of other needs further initialization, then it will @@ -337,6 +344,14 @@ def get_facecolor(self): """Return the face color.""" return self._facecolor + def get_hatchcolor(self): + """Return the hatch color.""" + if self._hatch_color == 'edge': + if self._edgecolor[3] == 0: # fully transparent + return colors.to_rgba(mpl.rcParams['patch.edgecolor']) + return self.get_edgecolor() + return self._hatch_color + def get_linewidth(self): """Return the line width in points.""" return self._linewidth @@ -353,24 +368,18 @@ def set_antialiased(self, aa): ---------- aa : bool or None """ - if aa is None: - aa = mpl.rcParams['patch.antialiased'] - self._antialiased = aa + self._antialiased = mpl._val_or_rc(aa, 'patch.antialiased') self.stale = True def _set_edgecolor(self, color): - set_hatch_color = True if color is None: if (mpl.rcParams['patch.force_edgecolor'] or not self._fill or self._edge_default): color = mpl.rcParams['patch.edgecolor'] else: color = 'none' - set_hatch_color = False self._edgecolor = colors.to_rgba(color, self._alpha) - if set_hatch_color: - self._hatch_color = self._edgecolor self.stale = True def set_edgecolor(self, color): @@ -385,8 +394,7 @@ def set_edgecolor(self, color): self._set_edgecolor(color) def _set_facecolor(self, color): - if color is None: - color = mpl.rcParams['patch.facecolor'] + color = mpl._val_or_rc(color, 'patch.facecolor') alpha = self._alpha if self._fill else 0 self._facecolor = colors.to_rgba(color, alpha) self.stale = True @@ -415,14 +423,71 @@ def set_color(self, c): Patch.set_facecolor, Patch.set_edgecolor For setting the edge or face color individually. """ - self.set_facecolor(c) self.set_edgecolor(c) + self.set_hatchcolor(c) + self.set_facecolor(c) + + def _set_hatchcolor(self, color): + color = mpl._val_or_rc(color, 'hatch.color') + if cbook._str_equal(color, 'edge'): + self._hatch_color = 'edge' + else: + self._hatch_color = colors.to_rgba(color, self._alpha) + self.stale = True + + def set_hatchcolor(self, color): + """ + Set the patch hatch color. + + Parameters + ---------- + color : :mpltype:`color` or 'edge' or None + """ + self._original_hatchcolor = color + self._set_hatchcolor(color) + + def get_edgegapcolor(self): + """ + Return the edge gap color. + + .. versionadded:: 3.11 + + See also `~.Patch.set_edgegapcolor`. + """ + return self._gapcolor + + def set_edgegapcolor(self, edgegapcolor): + """ + Set a color to fill the gaps in the dashed edge style. + + .. versionadded:: 3.11 + + .. note:: + + Striped edges are created by drawing two interleaved dashed lines. + There can be overlaps between those two, which may result in + artifacts when using transparency. + + This functionality is experimental and may change. + + Parameters + ---------- + edgegapcolor : :mpltype:`color` or None + The color with which to fill the gaps. If None, the gaps are + unfilled. + """ + if edgegapcolor is not None: + self._gapcolor = colors.to_rgba(edgegapcolor, self._alpha) + else: + self._gapcolor = None + self.stale = True def set_alpha(self, alpha): # docstring inherited super().set_alpha(alpha) self._set_facecolor(self._original_facecolor) self._set_edgecolor(self._original_edgecolor) + self._set_hatchcolor(self._original_hatchcolor) # stale is already True def set_linewidth(self, w): @@ -433,37 +498,50 @@ def set_linewidth(self, w): ---------- w : float or None """ - if w is None: - w = mpl.rcParams['patch.linewidth'] - self._linewidth = float(w) - self._dash_pattern = mlines._scale_dashes( - *self._unscaled_dash_pattern, w) + w = mpl._val_or_rc(w, 'patch.linewidth') + w = float(w) + self._linewidth = w + self._dash_pattern = mlines._scale_dashes(*self._unscaled_dash_pattern, w) self.stale = True def set_linestyle(self, ls): """ Set the patch linestyle. - ========================================== ================= - linestyle description - ========================================== ================= - ``'-'`` or ``'solid'`` solid line - ``'--'`` or ``'dashed'`` dashed line - ``'-.'`` or ``'dashdot'`` dash-dotted line - ``':'`` or ``'dotted'`` dotted line - ``'none'``, ``'None'``, ``' '``, or ``''`` draw nothing - ========================================== ================= + Parameters + ---------- + ls : {'-', '--', '-.', ':', '', ...} or (offset, on-off-seq) + Possible values: - Alternatively a dash tuple of the following form can be provided:: + - A string: - (offset, onoffseq) + ======================================================= ================ + linestyle description + ======================================================= ================ + ``'-'`` or ``'solid'`` solid line + ``'--'`` or ``'dashed'`` dashed line + ``'-.'`` or ``'dashdot'`` dash-dotted line + ``':'`` or ``'dotted'`` dotted line + ``''`` or ``'none'`` (discouraged: ``'None'``, ``' '``) draw nothing + ======================================================= ================ - where ``onoffseq`` is an even length tuple of on and off ink in points. + - A tuple describing the start position and lengths of dashes and spaces: - Parameters - ---------- - ls : {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} - The line style. + (offset, onoffseq) + + where + + - *offset* is a float specifying the offset (in points); i.e. how much + is the dash pattern shifted. + - *onoffseq* is a sequence of on and off ink in points. There can be + arbitrary many pairs of on and off values. + + Example: The tuple ``(0, (10, 5, 1, 5))`` means that the pattern starts + at the beginning of the line. It draws a 10 point long dash, + then a 5 point long space, then a 1 point long dash, followed by a 5 point + long space, and then the pattern repeats. + + For examples see :doc:`/gallery/lines_bars_and_markers/linestyles`. """ if ls is None: ls = "solid" @@ -486,6 +564,7 @@ def set_fill(self, b): self._fill = bool(b) self._set_facecolor(self._original_facecolor) self._set_edgecolor(self._original_edgecolor) + self._set_hatchcolor(self._original_hatchcolor) self.stale = True def get_fill(self): @@ -571,6 +650,25 @@ def get_hatch(self): """Return the hatching pattern.""" return self._hatch + def set_hatch_linewidth(self, lw): + """Set the hatch linewidth.""" + self._hatch_linewidth = lw + + def get_hatch_linewidth(self): + """Return the hatch linewidth.""" + return self._hatch_linewidth + + def _has_dashed_edge(self): + """ + Return whether the patch edge has a dashed linestyle. + + A custom linestyle is assumed to be dashed, we do not inspect the + ``onoffseq`` directly. + + See also `~.Patch.set_linestyle`. + """ + return self._linestyle not in ('solid', '-') + def _draw_paths_with_artist_properties( self, renderer, draw_path_args_list): """ @@ -585,13 +683,10 @@ def _draw_paths_with_artist_properties( renderer.open_group('patch', self.get_gid()) gc = renderer.new_gc() - gc.set_foreground(self._edgecolor, isRGBA=True) - lw = self._linewidth if self._edgecolor[3] == 0 or self._linestyle == 'None': lw = 0 gc.set_linewidth(lw) - gc.set_dashes(*self._dash_pattern) gc.set_capstyle(self._capstyle) gc.set_joinstyle(self._joinstyle) @@ -604,7 +699,8 @@ def _draw_paths_with_artist_properties( if self._hatch: gc.set_hatch(self._hatch) - gc.set_hatch_color(self._hatch_color) + gc.set_hatch_color(self.get_hatchcolor()) + gc.set_hatch_linewidth(self._hatch_linewidth) if self.get_sketch_params() is not None: gc.set_sketch_params(*self.get_sketch_params()) @@ -613,6 +709,22 @@ def _draw_paths_with_artist_properties( from matplotlib.patheffects import PathEffectRenderer renderer = PathEffectRenderer(self.get_path_effects(), renderer) + # We first draw a path within the gaps if needed, but only for visible + # dashed edges; zero-width edges would otherwise yield all-zero dashes. + if lw > 0 and self._has_dashed_edge() and self._gapcolor is not None: + gc.set_foreground(self._gapcolor, isRGBA=True) + offset_gaps, gaps = mlines._get_inverse_dash_pattern( + *self._dash_pattern) + gc.set_dashes(offset_gaps, gaps) + for draw_path_args in draw_path_args_list: + renderer.draw_path(gc, *draw_path_args) + + # Draw the main edge + gc.set_foreground(self._edgecolor, isRGBA=True) + if lw > 0: + gc.set_dashes(*self._dash_pattern) + else: + gc.set_dashes(0, None) for draw_path_args in draw_path_args_list: renderer.draw_path(gc, *draw_path_args) @@ -655,7 +767,7 @@ class Shadow(Patch): def __str__(self): return f"Shadow({self.patch})" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, patch, ox, oy, *, shade=0.7, **kwargs): """ Create a shadow of the given *patch*. @@ -735,7 +847,7 @@ def __str__(self): fmt = "Rectangle(xy=(%g, %g), width=%g, height=%g, angle=%g)" return fmt % pars - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, width, height, *, angle=0.0, rotation_point='xy', **kwargs): """ @@ -758,6 +870,10 @@ def __init__(self, xy, width, height, *, ---------------- **kwargs : `~matplotlib.patches.Patch` properties %(Patch:kwdoc)s + + See Also + -------- + FancyBboxPatch : A rectangle with a fancy box style, e.g. rounded corners. """ super().__init__(**kwargs) self._x0 = xy[0] @@ -936,7 +1052,7 @@ def __str__(self): return s % (self.xy[0], self.xy[1], self.numvertices, self.radius, self.orientation) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, numVertices, *, radius=5, orientation=0, **kwargs): """ @@ -986,7 +1102,7 @@ def __str__(self): s = "PathPatch%d((%g, %g) ...)" return s % (len(self._path.vertices), *tuple(self._path.vertices[0])) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, path, **kwargs): """ *path* is a `.Path` object. @@ -1015,7 +1131,7 @@ class StepPatch(PathPatch): _edge_default = False - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, values, edges, *, orientation='vertical', baseline=0, **kwargs): """ @@ -1124,7 +1240,7 @@ def __str__(self): else: return "Polygon0()" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, *, closed=True, **kwargs): """ Parameters @@ -1222,7 +1338,7 @@ def __str__(self): fmt = "Wedge(center=(%g, %g), r=%g, theta1=%g, theta2=%g, width=%s)" return fmt % pars - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, center, r, theta1, theta2, *, width=None, **kwargs): """ A wedge centered at *x*, *y* center with radius *r* that @@ -1310,7 +1426,7 @@ def __str__(self): [0.0, 0.1], [0.0, -0.1], [0.8, -0.1], [0.8, -0.3], [1.0, 0.0], [0.8, 0.3], [0.8, 0.1]]) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, x, y, dx, dy, *, width=1.0, **kwargs): """ Draws an arrow from (*x*, *y*) to (*x* + *dx*, *y* + *dy*). @@ -1393,7 +1509,7 @@ class FancyArrow(Polygon): def __str__(self): return "FancyArrow()" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, x, y, dx, dy, *, width=0.001, length_includes_head=False, head_width=None, head_length=None, shape='full', overhang=0, @@ -1505,7 +1621,7 @@ def _make_verts(self): length = distance else: length = distance + head_length - if not length: + if np.size(length) == 0: self.verts = np.empty([0, 2]) # display nothing if empty else: # start by drawing horizontal arrow, point at (0, 0) @@ -1552,7 +1668,7 @@ def _make_verts(self): ] -_docstring.interpd.update( +_docstring.interpd.register( FancyArrow="\n".join( (inspect.getdoc(FancyArrow.__init__) or "").splitlines()[2:])) @@ -1564,7 +1680,7 @@ def __str__(self): s = "CirclePolygon((%g, %g), radius=%g, resolution=%d)" return s % (self.xy[0], self.xy[1], self.radius, self.numvertices) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, radius=5, *, resolution=20, # the number of vertices ** kwargs): @@ -1591,7 +1707,7 @@ def __str__(self): fmt = "Ellipse(xy=(%s, %s), width=%s, height=%s, angle=%s)" return fmt % pars - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, width, height, *, angle=0, **kwargs): """ Parameters @@ -1767,7 +1883,7 @@ class Annulus(Patch): An elliptical annulus. """ - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, r, width, angle=0.0, **kwargs): """ Parameters @@ -1958,7 +2074,7 @@ def __str__(self): fmt = "Circle(xy=(%g, %g), radius=%g)" return fmt % pars - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, radius=5, **kwargs): """ Create a true circle at center *xy* = (*x*, *y*) with given *radius*. @@ -2005,7 +2121,7 @@ def __str__(self): "height=%g, angle=%g, theta1=%g, theta2=%g)") return fmt % pars - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, width, height, *, angle=0.0, theta1=0.0, theta2=360.0, **kwargs): """ @@ -2161,7 +2277,7 @@ def segment_circle_intersect(x0, y0, x1, y1): # the unit circle in the same way that it is relative to the desired # ellipse. box_path_transform = ( - transforms.BboxTransformTo((self.axes or self.figure).bbox) + transforms.BboxTransformTo((self.axes or self.get_figure(root=False)).bbox) - self.get_transform()) box_path = Path.unit_rectangle().transformed(box_path_transform) @@ -2290,7 +2406,7 @@ def __init_subclass__(cls): # - %(BoxStyle:table_and_accepts)s # - %(ConnectionStyle:table_and_accepts)s # - %(ArrowStyle:table_and_accepts)s - _docstring.interpd.update({ + _docstring.interpd.register(**{ f"{cls.__name__}:table": cls.pprint_styles(), f"{cls.__name__}:table_and_accepts": ( cls.pprint_styles() @@ -2325,7 +2441,7 @@ def get_styles(cls): @classmethod def pprint_styles(cls): """Return the available styles as pretty-printed string.""" - table = [('Class', 'Name', 'Attrs'), + table = [('Class', 'Name', 'Parameters'), *[(cls.__name__, # Add backquotes, as - and | have special meaning in reST. f'``{name}``', @@ -2347,6 +2463,10 @@ def pprint_styles(cls): return textwrap.indent(rst_table, prefix=' ' * 4) @classmethod + @_api.deprecated( + '3.10', + message="This method is never used internally.", + alternative="No replacement. Please open an issue if you use this.") def register(cls, name, style): """Register a new style.""" if not issubclass(style, cls._Base): @@ -2362,7 +2482,7 @@ def _register_style(style_list, cls=None, *, name=None): return cls -@_docstring.dedent_interpd +@_docstring.interpd class BoxStyle(_Style): """ `BoxStyle` is a container class which defines several @@ -2467,83 +2587,124 @@ def __call__(self, x0, y0, width, height, mutation_size): return trans.transform_path(Path.unit_circle()) @_register_style(_style_list) - class LArrow: - """A box in the shape of a left-pointing arrow.""" + class RArrow: + """A box in the shape of a right-pointing arrow.""" - def __init__(self, pad=0.3): + def __init__(self, pad=0.3, head_width=1.5, head_angle=90): """ Parameters ---------- pad : float, default: 0.3 The amount of padding around the original box. + head_width : float, default: 1.5 + The head width, relative to the arrow shaft width; must be + nonnegative. + head_angle : float, default: 90 + The angle at the tip of the arrow, in degrees; must be nonzero + (modulo 360). Negative angles result in arrow heads pointing + backwards. """ self.pad = pad + if head_width < 0: + raise ValueError("'head_width' must be nonnegative") + self.head_width = head_width + if head_angle % 360 == 0: + raise ValueError("'head_angle' must be nonzero") + self.head_angle = head_angle def __call__(self, x0, y0, width, height, mutation_size): - # padding + # padding & padded dimensions pad = mutation_size * self.pad - # width and height with padding added. - width, height = width + 2 * pad, height + 2 * pad - # boundary of the padded box + dx, dy = width + 2 * pad, height + 2 * pad x0, y0 = x0 - pad, y0 - pad, - x1, y1 = x0 + width, y0 + height - - dx = (y1 - y0) / 2 - dxx = dx / 2 - x0 = x0 + pad / 1.4 # adjust by ~sqrt(2) - - return Path._create_closed( - [(x0 + dxx, y0), (x1, y0), (x1, y1), (x0 + dxx, y1), - (x0 + dxx, y1 + dxx), (x0 - dx, y0 + dx), - (x0 + dxx, y0 - dxx), # arrow - (x0 + dxx, y0)]) + x1, y1 = x0 + dx, y0 + dy + + head_dy = self.head_width * dy + mid_y = (y0 + y1) / 2 + shaft_y0 = mid_y - head_dy / 2 + shaft_y1 = mid_y + head_dy / 2 + + cot = 1 / math.tan(math.radians(self.head_angle / 2)) + + if cot > 0: + # tip_x is chosen s.t. the angled line moving back from the tip hits + # i) if head_width > 1: the box corner, or ii) if head_width < + # 1 the box edge at the point giving the correct shaft width. + tip_x = x1 + cot * min(dy, head_dy) / 2 + shaft_x = tip_x - cot * head_dy / 2 + return Path._create_closed([ + (x0, y0), (shaft_x, y0), (shaft_x, shaft_y0), + (tip_x, mid_y), + (shaft_x, shaft_y1), (shaft_x, y1), (x0, y1), + ]) + else: # Reverse arrowhead. + # Make the long (outer) side of the arrowhead flush with the + # original box, and move back accordingly (but clipped to no + # more than the box length). If this clipping is necessary, + # the y positions at the short (inner) side of the arrowhead + # will be thicker than the original box, hence the need to + # recompute mid_y0 & mid_y1. + # If head_width < 1 no arrowhead is drawn. + dx = min(-cot * max(head_dy - dy, 0) / 2, dx) # cot < 0! + mid_y0 = min(shaft_y0, y0) - dx / cot + mid_y1 = max(shaft_y1, y1) + dx / cot + return Path._create_closed([ + (x0, y0), (x1 - dx, mid_y0), (x1, shaft_y0), + (x1, shaft_y1), (x1 - dx, mid_y1), (x0, y1), + ]) @_register_style(_style_list) - class RArrow(LArrow): - """A box in the shape of a right-pointing arrow.""" + class LArrow(RArrow): + """A box in the shape of a left-pointing arrow.""" def __call__(self, x0, y0, width, height, mutation_size): - p = BoxStyle.LArrow.__call__( - self, x0, y0, width, height, mutation_size) + p = super().__call__(x0, y0, width, height, mutation_size) p.vertices[:, 0] = 2 * x0 + width - p.vertices[:, 0] return p @_register_style(_style_list) - class DArrow: + class DArrow(RArrow): """A box in the shape of a two-way arrow.""" - # Modified from LArrow to add a right arrow to the bbox. - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad + # Modified from RArrow to have arrows on both sides; see comments above. def __call__(self, x0, y0, width, height, mutation_size): - # padding + # padding & padded dimensions pad = mutation_size * self.pad - # width and height with padding added. - # The width is padded by the arrows, so we don't need to pad it. - height = height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad - x1, y1 = x0 + width, y0 + height - - dx = (y1 - y0) / 2 - dxx = dx / 2 - x0 = x0 + pad / 1.4 # adjust by ~sqrt(2) - - return Path._create_closed([ - (x0 + dxx, y0), (x1, y0), # bot-segment - (x1, y0 - dxx), (x1 + dx + dxx, y0 + dx), - (x1, y1 + dxx), # right-arrow - (x1, y1), (x0 + dxx, y1), # top-segment - (x0 + dxx, y1 + dxx), (x0 - dx, y0 + dx), - (x0 + dxx, y0 - dxx), # left-arrow - (x0 + dxx, y0)]) + dx, dy = width + 2 * pad, height + 2 * pad + x0, y0 = x0 - pad, y0 - pad, + x1, y1 = x0 + dx, y0 + dy + + head_dy = self.head_width * dy + mid_y = (y0 + y1) / 2 + shaft_y0 = mid_y - head_dy / 2 + shaft_y1 = mid_y + head_dy / 2 + + cot = 1 / math.tan(math.radians(self.head_angle / 2)) + + if cot > 0: + tip_x0 = x0 - cot * min(dy, head_dy) / 2 + shaft_x0 = tip_x0 + cot * head_dy / 2 + tip_x1 = x1 + cot * min(dy, head_dy) / 2 + shaft_x1 = tip_x1 - cot * head_dy / 2 + return Path._create_closed([ + (shaft_x0, y1), (shaft_x0, shaft_y1), + (tip_x0, mid_y), + (shaft_x0, shaft_y0), (shaft_x0, y0), + (shaft_x1, y0), (shaft_x1, shaft_y0), + (tip_x1, mid_y), + (shaft_x1, shaft_y1), (shaft_x1, y1), + ]) + else: + # Don't move back by more than half the box length. + dx = min(-cot * max(head_dy - dy, 0) / 2, dx / 2) # cot < 0! + mid_y0 = min(shaft_y0, y0) - dx / cot + mid_y1 = max(shaft_y1, y1) + dx / cot + return Path._create_closed([ + (x0, shaft_y0), (x0 + dx, mid_y0), + (x1 - dx, mid_y0), (x1, shaft_y0), + (x1, shaft_y1), (x1 - dx, mid_y1), + (x0 + dx, mid_y1), (x0, shaft_y1), + ]) @_register_style(_style_list) class Round: @@ -2727,7 +2888,7 @@ def __call__(self, x0, y0, width, height, mutation_size): return Path(saw_vertices, codes) -@_docstring.dedent_interpd +@_docstring.interpd class ConnectionStyle(_Style): """ `ConnectionStyle` is a container class which defines @@ -3149,7 +3310,7 @@ def _point_along_a_line(x0, y0, x1, y1, d): return x2, y2 -@_docstring.dedent_interpd +@_docstring.interpd class ArrowStyle(_Style): """ `ArrowStyle` is a container class which defines several @@ -3886,7 +4047,7 @@ def __str__(self): s = self.__class__.__name__ + "((%g, %g), width=%g, height=%g)" return s % (self._x, self._y, self._width, self._height) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xy, width, height, boxstyle="round", *, mutation_scale=1, mutation_aspect=1, **kwargs): """ @@ -3938,7 +4099,7 @@ def __init__(self, xy, width, height, boxstyle="round", *, self._mutation_aspect = mutation_aspect self.stale = True - @_docstring.dedent_interpd + @_docstring.interpd def set_boxstyle(self, boxstyle=None, **kwargs): """ Set the box style, possibly with further attributes. @@ -4138,55 +4299,96 @@ def __str__(self): else: return f"{type(self).__name__}({self._path_original})" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, posA=None, posB=None, *, path=None, arrowstyle="simple", connectionstyle="arc3", patchA=None, patchB=None, shrinkA=2, shrinkB=2, mutation_scale=1, mutation_aspect=1, **kwargs): """ - There are two ways for defining an arrow: + **Defining the arrow position and path** + + There are two ways to define the arrow position and path: + + - **Start, end and connection**: + The typical approach is to define the start and end points of the + arrow using *posA* and *posB*. The curve between these two can + further be configured using *connectionstyle*. + + If given, the arrow curve is clipped by *patchA* and *patchB*, + allowing it to start/end at the border of these patches. + Additionally, the arrow curve can be shortened by *shrinkA* and *shrinkB* + to create a margin between start/end (after possible clipping) and the + drawn arrow. + + - **path**: Alternatively if *path* is provided, an arrow is drawn along + this Path. In this case, *connectionstyle*, *patchA*, *patchB*, + *shrinkA*, and *shrinkB* are ignored. - - If *posA* and *posB* are given, a path connecting two points is - created according to *connectionstyle*. The path will be - clipped with *patchA* and *patchB* and further shrunken by - *shrinkA* and *shrinkB*. An arrow is drawn along this - resulting path using the *arrowstyle* parameter. + **Styling** - - Alternatively if *path* is provided, an arrow is drawn along this - path and *patchA*, *patchB*, *shrinkA*, and *shrinkB* are ignored. + The *arrowstyle* defines the styling of the arrow head, tail and shaft. + The resulting arrows can be styled further by setting the `.Patch` + properties such as *linewidth*, *color*, *facecolor*, *edgecolor* + etc. via keyword arguments. Parameters ---------- - posA, posB : (float, float), default: None - (x, y) coordinates of arrow tail and arrow head respectively. + posA, posB : (float, float), optional + (x, y) coordinates of start and end point of the arrow. + The actually drawn start and end positions may be modified + through *patchA*, *patchB*, *shrinkA*, and *shrinkB*. - path : `~matplotlib.path.Path`, default: None + *posA*, *posB* are exclusive of *path*. + + path : `~matplotlib.path.Path`, optional If provided, an arrow is drawn along this path and *patchA*, *patchB*, *shrinkA*, and *shrinkB* are ignored. + *path* is exclusive of *posA*, *posB*. + arrowstyle : str or `.ArrowStyle`, default: 'simple' - The `.ArrowStyle` with which the fancy arrow is drawn. If a - string, it should be one of the available arrowstyle names, with - optional comma-separated attributes. The optional attributes are - meant to be scaled with the *mutation_scale*. The following arrow - styles are available: + The styling of arrow head, tail and shaft. This can be + + - `.ArrowStyle` or one of its subclasses + - The shorthand string name (e.g. "->") as given in the table below, + optionally containing a comma-separated list of style parameters, + e.g. "->, head_length=10, head_width=5". + + The style parameters are scaled by *mutation_scale*. + + The following arrow styles are available. See also + :doc:`/gallery/text_labels_and_annotations/fancyarrow_demo`. %(ArrowStyle:table)s + Only the styles ``<|-``, ``-|>``, ``<|-|>`` ``simple``, ``fancy`` + and ``wedge`` contain closed paths and can be filled. + connectionstyle : str or `.ConnectionStyle` or None, optional, \ default: 'arc3' - The `.ConnectionStyle` with which *posA* and *posB* are connected. - If a string, it should be one of the available connectionstyle - names, with optional comma-separated attributes. The following - connection styles are available: + `.ConnectionStyle` with which *posA* and *posB* are connected. + This can be + + - `.ConnectionStyle` or one of its subclasses + - The shorthand string name as given in the table below, e.g. "arc3". %(ConnectionStyle:table)s + Ignored if *path* is provided. + patchA, patchB : `~matplotlib.patches.Patch`, default: None - Head and tail patches, respectively. + Optional Patches at *posA* and *posB*, respectively. If given, + the arrow path is clipped by these patches such that head and tail + are at the border of the patches. + + Ignored if *path* is provided. shrinkA, shrinkB : float, default: 2 - Shrink amount, in points, of the tail and head of the arrow respectively. + Shorten the arrow path at *posA* and *posB* by this amount in points. + This allows to add a margin between the intended start/end points and + the arrow. + + Ignored if *path* is provided. mutation_scale : float, default: 1 Value with which attributes of *arrowstyle* (e.g., *head_length*) @@ -4277,7 +4479,7 @@ def set_patchB(self, patchB): self.patchB = patchB self.stale = True - @_docstring.dedent_interpd + @_docstring.interpd def set_connectionstyle(self, connectionstyle=None, **kwargs): """ Set the connection style, possibly with further attributes. @@ -4321,6 +4523,7 @@ def get_connectionstyle(self): """Return the `ConnectionStyle` used.""" return self._connector + @_docstring.interpd def set_arrowstyle(self, arrowstyle=None, **kwargs): """ Set the arrow style, possibly with further attributes. @@ -4464,7 +4667,7 @@ def __str__(self): return "ConnectionPatch((%g, %g), (%g, %g))" % \ (self.xy1[0], self.xy1[1], self.xy2[0], self.xy2[1]) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, xyA, xyB, coordsA, coordsB=None, *, axesA=None, axesB=None, arrowstyle="-", @@ -4573,29 +4776,34 @@ def _get_xy(self, xy, s, axes=None): s0 = s # For the error message, if needed. if axes is None: axes = self.axes - xy = np.array(xy) + + # preserve mixed type input (such as str, int) + x = np.array(xy[0]) + y = np.array(xy[1]) + + fig = self.get_figure(root=False) if s in ["figure points", "axes points"]: - xy *= self.figure.dpi / 72 + x = x * fig.dpi / 72 + y = y * fig.dpi / 72 s = s.replace("points", "pixels") elif s == "figure fraction": - s = self.figure.transFigure + s = fig.transFigure elif s == "subfigure fraction": - s = self.figure.transSubfigure + s = fig.transSubfigure elif s == "axes fraction": s = axes.transAxes - x, y = xy if s == 'data': trans = axes.transData - x = float(self.convert_xunits(x)) - y = float(self.convert_yunits(y)) + x = cbook._to_unmasked_float_array(axes.xaxis.convert_units(x)) + y = cbook._to_unmasked_float_array(axes.yaxis.convert_units(y)) return trans.transform((x, y)) elif s == 'offset points': if self.xycoords == 'offset points': # prevent recursion return self._get_xy(self.xy, 'data') return ( self._get_xy(self.xy, self.xycoords) # converted data point - + xy * self.figure.dpi / 72) # converted offset + + xy * self.get_figure(root=True).dpi / 72) # converted offset elif s == 'polar': theta, r = x, y x = r * np.cos(theta) @@ -4604,13 +4812,13 @@ def _get_xy(self, xy, s, axes=None): return trans.transform((x, y)) elif s == 'figure pixels': # pixels from the lower left corner of the figure - bb = self.figure.figbbox + bb = self.get_figure(root=False).figbbox x = bb.x0 + x if x >= 0 else bb.x1 + x y = bb.y0 + y if y >= 0 else bb.y1 + y return x, y elif s == 'subfigure pixels': # pixels from the lower left corner of the figure - bb = self.figure.bbox + bb = self.get_figure(root=False).bbox x = bb.x0 + x if x >= 0 else bb.x1 + x y = bb.y0 + y if y >= 0 else bb.y1 + y return x, y diff --git a/lib/matplotlib/patches.pyi b/lib/matplotlib/patches.pyi index f6c9ddf75839..862eccf4da64 100644 --- a/lib/matplotlib/patches.pyi +++ b/lib/matplotlib/patches.pyi @@ -25,6 +25,8 @@ class Patch(artist.Artist): fill: bool = ..., capstyle: CapStyleType | None = ..., joinstyle: JoinStyleType | None = ..., + hatchcolor: ColorType | Literal["edge"] | None = ..., + edgegapcolor: ColorType | None = ..., **kwargs, ) -> None: ... def get_verts(self) -> ArrayLike: ... @@ -42,12 +44,16 @@ class Patch(artist.Artist): def get_antialiased(self) -> bool: ... def get_edgecolor(self) -> ColorType: ... def get_facecolor(self) -> ColorType: ... + def get_edgegapcolor(self) -> ColorType | None: ... + def get_hatchcolor(self) -> ColorType: ... def get_linewidth(self) -> float: ... def get_linestyle(self) -> LineStyleType: ... def set_antialiased(self, aa: bool | None) -> None: ... def set_edgecolor(self, color: ColorType | None) -> None: ... def set_facecolor(self, color: ColorType | None) -> None: ... def set_color(self, c: ColorType | None) -> None: ... + def set_edgegapcolor(self, edgegapcolor: ColorType | None) -> None: ... + def set_hatchcolor(self, color: ColorType | Literal["edge"] | None) -> None: ... def set_alpha(self, alpha: float | None) -> None: ... def set_linewidth(self, w: float | None) -> None: ... def set_linestyle(self, ls: LineStyleType | None) -> None: ... @@ -59,6 +65,8 @@ class Patch(artist.Artist): def set_joinstyle(self, s: JoinStyleType) -> None: ... def get_joinstyle(self) -> Literal["miter", "round", "bevel"]: ... def set_hatch(self, hatch: str) -> None: ... + def set_hatch_linewidth(self, lw: float) -> None: ... + def get_hatch_linewidth(self) -> float: ... def get_hatch(self) -> str: ... def get_path(self) -> Path: ... @@ -371,9 +379,13 @@ class BoxStyle(_Style): mutation_size: float, ) -> Path: ... - class LArrow(BoxStyle): + class RArrow(BoxStyle): pad: float - def __init__(self, pad: float = ...) -> None: ... + head_width: float + head_angle: float + def __init__( + self, pad: float = ..., head_width: float = ..., head_angle: float = ... + ) -> None: ... def __call__( self, x0: float, @@ -383,7 +395,7 @@ class BoxStyle(_Style): mutation_size: float, ) -> Path: ... - class RArrow(LArrow): + class LArrow(RArrow): def __call__( self, x0: float, @@ -393,9 +405,13 @@ class BoxStyle(_Style): mutation_size: float, ) -> Path: ... - class DArrow(BoxStyle): + class DArrow(RArrow): pad: float - def __init__(self, pad: float = ...) -> None: ... + head_width: float + head_angle: float + def __init__( + self, pad: float = ..., head_width: float = ..., head_angle: float = ... + ) -> None: ... def __call__( self, x0: float, diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index e72eb1a9ca73..f65ade669167 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -76,7 +76,6 @@ class Path: made up front in the constructor that will not change when the data changes. """ - code_type = np.uint8 # Path codes @@ -129,7 +128,7 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, vertices = _to_unmasked_float_array(vertices) _api.check_shape((None, 2), vertices=vertices) - if codes is not None: + if codes is not None and len(vertices): codes = np.asarray(codes, self.code_type) if codes.ndim != 1 or len(codes) != len(vertices): raise ValueError("'codes' must be a 1D list or array with the " @@ -276,17 +275,37 @@ def copy(self): """ return copy.copy(self) - def __deepcopy__(self, memo=None): + def __deepcopy__(self, memo): """ Return a deepcopy of the `Path`. The `Path` will not be readonly, even if the source `Path` is. """ # Deepcopying arrays (vertices, codes) strips the writeable=False flag. - p = copy.deepcopy(super(), memo) + cls = type(self) + memo[id(self)] = p = cls.__new__(cls) + + for k, v in self.__dict__.items(): + setattr(p, k, copy.deepcopy(v, memo)) + p._readonly = False return p - deepcopy = __deepcopy__ + def deepcopy(self, memo=None): + """ + Return a deep copy of the `Path`. The `Path` will not be readonly, + even if the source `Path` is. + + Parameters + ---------- + memo : dict, optional + A dictionary to use for memoizing, passed to `copy.deepcopy`. + + Returns + ------- + Path + A deep copy of the `Path`, but not readonly. + """ + return copy.deepcopy(self, memo) @classmethod def make_compound_path_from_polys(cls, XY): @@ -668,14 +687,35 @@ def intersects_bbox(self, bbox, filled=True): def interpolated(self, steps): """ - Return a new path resampled to length N x *steps*. + Return a new path with each segment divided into *steps* parts. - Codes other than `LINETO` are not handled correctly. + Codes other than `LINETO`, `MOVETO`, and `CLOSEPOLY` are not handled correctly. + + Parameters + ---------- + steps : int + The number of segments in the new path for each in the original. + + Returns + ------- + Path + The interpolated path. """ - if steps == 1: + if steps == 1 or len(self) == 0: return self - vertices = simple_linear_interpolation(self.vertices, steps) + if self.codes is not None and self.MOVETO in self.codes[1:]: + return self.make_compound_path( + *(p.interpolated(steps) for p in self._iter_connected_components())) + + if self.codes is not None and self.CLOSEPOLY in self.codes and not np.all( + self.vertices[self.codes == self.CLOSEPOLY] == self.vertices[0]): + vertices = self.vertices.copy() + vertices[self.codes == self.CLOSEPOLY] = vertices[0] + else: + vertices = self.vertices + + vertices = simple_linear_interpolation(vertices, steps) codes = self.codes if codes is not None: new_codes = np.full((len(codes) - 1) * steps + 1, Path.LINETO, @@ -697,6 +737,9 @@ def to_polygons(self, transform=None, width=0, height=0, closed_only=True): be simplified so that vertices outside of (0, 0), (width, height) will be clipped. + The resulting polygons will be simplified if the + :attr:`Path.should_simplify` attribute of the path is `True`. + If *closed_only* is `True` (default), only closed polygons, with the last point being the same as the first point, will be returned. Any unclosed polylines in the path will be @@ -1083,10 +1126,7 @@ def get_path_collection_extents( if len(paths) == 0: raise ValueError("No paths provided") if len(offsets) == 0: - _api.warn_deprecated( - "3.8", message="Calling get_path_collection_extents() with an" - " empty offsets list is deprecated since %(since)s. Support will" - " be removed %(removal)s.") + raise ValueError("No offsets provided") extents, minpos = _path.get_path_collection_extents( master_transform, paths, np.atleast_3d(transforms), offsets, offset_transform) diff --git a/lib/matplotlib/path.pyi b/lib/matplotlib/path.pyi index 464fc6d9a912..8a5a5c03792e 100644 --- a/lib/matplotlib/path.pyi +++ b/lib/matplotlib/path.pyi @@ -44,8 +44,8 @@ class Path: @property def readonly(self) -> bool: ... def copy(self) -> Path: ... - def __deepcopy__(self, memo: dict[int, Any] | None = ...) -> Path: ... - deepcopy = __deepcopy__ + def __deepcopy__(self, memo: dict[int, Any]) -> Path: ... + def deepcopy(self, memo: dict[int, Any] | None = None) -> Path: ... @classmethod def make_compound_path_from_polys(cls, XY: ArrayLike) -> Path: ... diff --git a/lib/matplotlib/patheffects.py b/lib/matplotlib/patheffects.py index cf159ddc4023..ae808eb62fbe 100644 --- a/lib/matplotlib/patheffects.py +++ b/lib/matplotlib/patheffects.py @@ -96,6 +96,13 @@ def __init__(self, path_effects, renderer): def copy_with_path_effect(self, path_effects): return self.__class__(path_effects, self._renderer) + def __getattribute__(self, name): + if name in ['flipy', 'get_canvas_width_height', 'new_gc', + 'points_to_pixels', '_text2path', 'height', 'width']: + return getattr(self._renderer, name) + else: + return object.__getattribute__(self, name) + def draw_path(self, gc, tpath, affine, rgbFace=None): for path_effect in self._path_effects: path_effect.draw_path(self._renderer, gc, tpath, affine, @@ -137,21 +144,6 @@ def draw_path_collection(self, gc, master_transform, paths, *args, renderer.draw_path_collection(gc, master_transform, paths, *args, **kwargs) - def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath): - # Implements the naive text drawing as is found in RendererBase. - path, transform = self._get_text_path_transform(x, y, s, prop, - angle, ismath) - color = gc.get_rgb() - gc.set_linewidth(0.0) - self.draw_path(gc, path, transform, rgbFace=color) - - def __getattribute__(self, name): - if name in ['flipy', 'get_canvas_width_height', 'new_gc', - 'points_to_pixels', '_text2path', 'height', 'width']: - return getattr(self._renderer, name) - else: - return object.__getattribute__(self, name) - def open_group(self, s, gid=None): return self._renderer.open_group(s, gid) @@ -243,7 +235,7 @@ def __init__(self, offset=(2, -2), is not specified. **kwargs Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. + :meth:`!AbstractPathEffect._update_gc`. """ super().__init__(offset) @@ -277,7 +269,7 @@ def draw_path(self, renderer, gc, tpath, affine, rgbFace): else: shadow_rgbFace = self._shadow_rgbFace - gc0.set_foreground("none") + gc0.set_foreground(mcolors.to_rgba("none"), isRGBA=True) gc0.set_alpha(self._alpha) gc0.set_linewidth(0) @@ -312,7 +304,7 @@ def __init__(self, offset=(2, -2), is ``None``. **kwargs Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. + :meth:`!AbstractPathEffect._update_gc`. """ super().__init__(offset) if shadow_color is None: @@ -333,13 +325,13 @@ def draw_path(self, renderer, gc, tpath, affine, rgbFace): gc0.copy_properties(gc) if self._shadow_color is None: - r, g, b = (gc0.get_foreground() or (1., 1., 1.))[:3] + r, g, b = (gc0.get_rgb() or (1., 1., 1.))[:3] # Scale the colors by a factor to improve the shadow effect. shadow_rgbFace = (r * self._rho, g * self._rho, b * self._rho) else: shadow_rgbFace = self._shadow_color - gc0.set_foreground(shadow_rgbFace) + gc0.set_foreground(mcolors.to_rgba(shadow_rgbFace), isRGBA=True) gc0.set_alpha(self._alpha) gc0 = self._update_gc(gc0, self._gc) @@ -417,7 +409,7 @@ def __init__(self, offset=(0, 0), when angle=90 and length=2.0 when angle=60. **kwargs Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. + :meth:`!AbstractPathEffect._update_gc`. Examples -------- diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index b58d1ceb754d..f7b46192a84e 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -123,4 +123,4 @@ def get_projection_class(projection=None): get_projection_names = projection_registry.get_projection_names -_docstring.interpd.update(projection_names=get_projection_names()) +_docstring.interpd.register(projection_names=get_projection_names()) diff --git a/lib/matplotlib/projections/geo.py b/lib/matplotlib/projections/geo.py index 498b2f72ebb4..d5ab3c746dea 100644 --- a/lib/matplotlib/projections/geo.py +++ b/lib/matplotlib/projections/geo.py @@ -151,6 +151,15 @@ def set_xlim(self, *args, **kwargs): "not supported. Please consider using Cartopy.") set_ylim = set_xlim + set_xbound = set_xlim + set_ybound = set_ylim + + def invert_xaxis(self): + """Not supported. Please consider using Cartopy.""" + raise TypeError("Changing axes limits of a geographic projection is " + "not supported. Please consider using Cartopy.") + + invert_yaxis = invert_xaxis def format_coord(self, lon, lat): """Return a format string formatting the coordinate.""" @@ -249,7 +258,6 @@ class AitoffAxes(GeoAxes): class AitoffTransform(_GeoTransform): """The base Aitoff transform.""" - @_api.rename_parameter("3.8", "ll", "values") def transform_non_affine(self, values): # docstring inherited longitude, latitude = values.T @@ -271,7 +279,6 @@ def inverted(self): class InvertedAitoffTransform(_GeoTransform): - @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): # docstring inherited # MGDTODO: Math is hard ;( @@ -297,7 +304,6 @@ class HammerAxes(GeoAxes): class HammerTransform(_GeoTransform): """The base Hammer transform.""" - @_api.rename_parameter("3.8", "ll", "values") def transform_non_affine(self, values): # docstring inherited longitude, latitude = values.T @@ -315,7 +321,6 @@ def inverted(self): class InvertedHammerTransform(_GeoTransform): - @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): # docstring inherited x, y = values.T @@ -344,7 +349,6 @@ class MollweideAxes(GeoAxes): class MollweideTransform(_GeoTransform): """The base Mollweide transform.""" - @_api.rename_parameter("3.8", "ll", "values") def transform_non_affine(self, values): # docstring inherited def d(theta): @@ -385,7 +389,6 @@ def inverted(self): class InvertedMollweideTransform(_GeoTransform): - @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): # docstring inherited x, y = values.T @@ -426,7 +429,6 @@ def __init__(self, center_longitude, center_latitude, resolution): self._center_longitude = center_longitude self._center_latitude = center_latitude - @_api.rename_parameter("3.8", "ll", "values") def transform_non_affine(self, values): # docstring inherited longitude, latitude = values.T @@ -460,7 +462,6 @@ def __init__(self, center_longitude, center_latitude, resolution): self._center_longitude = center_longitude self._center_latitude = center_latitude - @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): # docstring inherited x, y = values.T diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 8d3e03f64e7c..75e1295f77f1 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -15,20 +15,6 @@ from matplotlib.spines import Spine -def _apply_theta_transforms_warn(): - _api.warn_deprecated( - "3.9", - message=( - "Passing `apply_theta_transforms=True` (the default) " - "is deprecated since Matplotlib %(since)s. " - "Support for this will be removed in Matplotlib %(removal)s. " - "To prevent this warning, set `apply_theta_transforms=False`, " - "and make sure to shift theta values before being passed to " - "this transform." - ) - ) - - class PolarTransform(mtransforms.Transform): r""" The base polar transform. @@ -48,8 +34,7 @@ class PolarTransform(mtransforms.Transform): input_dims = output_dims = 2 - def __init__(self, axis=None, use_rmin=True, *, - apply_theta_transforms=True, scale_transform=None): + def __init__(self, axis=None, use_rmin=True, *, scale_transform=None): """ Parameters ---------- @@ -64,30 +49,21 @@ def __init__(self, axis=None, use_rmin=True, *, super().__init__() self._axis = axis self._use_rmin = use_rmin - self._apply_theta_transforms = apply_theta_transforms self._scale_transform = scale_transform - if apply_theta_transforms: - _apply_theta_transforms_warn() __str__ = mtransforms._make_str_method( "_axis", - use_rmin="_use_rmin", - apply_theta_transforms="_apply_theta_transforms") + use_rmin="_use_rmin" + ) def _get_rorigin(self): # Get lower r limit after being scaled by the radial scale transform return self._scale_transform.transform( (0, self._axis.get_rorigin()))[1] - @_api.rename_parameter("3.8", "tr", "values") def transform_non_affine(self, values): # docstring inherited theta, r = np.transpose(values) - # PolarAxes does not use the theta transforms here, but apply them for - # backwards-compatibility if not being used by it. - if self._apply_theta_transforms and self._axis is not None: - theta *= self._axis.get_theta_direction() - theta += self._axis.get_theta_offset() if self._use_rmin and self._axis is not None: r = (r - self._get_rorigin()) * self._axis.get_rsign() r = np.where(r >= 0, r, np.nan) @@ -149,10 +125,7 @@ def transform_path_non_affine(self, path): def inverted(self): # docstring inherited - return PolarAxes.InvertedPolarTransform( - self._axis, self._use_rmin, - apply_theta_transforms=self._apply_theta_transforms - ) + return PolarAxes.InvertedPolarTransform(self._axis, self._use_rmin) class PolarAffine(mtransforms.Affine2DBase): @@ -210,8 +183,7 @@ class InvertedPolarTransform(mtransforms.Transform): """ input_dims = output_dims = 2 - def __init__(self, axis=None, use_rmin=True, - *, apply_theta_transforms=True): + def __init__(self, axis=None, use_rmin=True): """ Parameters ---------- @@ -226,27 +198,16 @@ def __init__(self, axis=None, use_rmin=True, super().__init__() self._axis = axis self._use_rmin = use_rmin - self._apply_theta_transforms = apply_theta_transforms - if apply_theta_transforms: - _apply_theta_transforms_warn() __str__ = mtransforms._make_str_method( "_axis", - use_rmin="_use_rmin", - apply_theta_transforms="_apply_theta_transforms") + use_rmin="_use_rmin") - @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): # docstring inherited x, y = values.T r = np.hypot(x, y) - theta = (np.arctan2(y, x) + 2 * np.pi) % (2 * np.pi) - # PolarAxes does not use the theta transforms here, but apply them for - # backwards-compatibility if not being used by it. - if self._apply_theta_transforms and self._axis is not None: - theta -= self._axis.get_theta_offset() - theta *= self._axis.get_theta_direction() - theta %= 2 * np.pi + theta = np.arctan2(y, x) % (2 * np.pi) if self._use_rmin and self._axis is not None: r += self._axis.get_rorigin() r *= self._axis.get_rsign() @@ -254,10 +215,7 @@ def transform_non_affine(self, values): def inverted(self): # docstring inherited - return PolarAxes.PolarTransform( - self._axis, self._use_rmin, - apply_theta_transforms=self._apply_theta_transforms - ) + return PolarAxes.PolarTransform(self._axis, self._use_rmin) class ThetaFormatter(mticker.Formatter): @@ -341,9 +299,9 @@ class ThetaTick(maxis.XTick): def __init__(self, axes, *args, **kwargs): self._text1_translate = mtransforms.ScaledTranslation( - 0, 0, axes.figure.dpi_scale_trans) + 0, 0, axes.get_figure(root=False).dpi_scale_trans) self._text2_translate = mtransforms.ScaledTranslation( - 0, 0, axes.figure.dpi_scale_trans) + 0, 0, axes.get_figure(root=False).dpi_scale_trans) super().__init__(axes, *args, **kwargs) self.label1.set( rotation_mode='anchor', @@ -473,6 +431,7 @@ class RadialLocator(mticker.Locator): scale of the *r*-axis). """ + @_api.delete_parameter("3.11", "axes") def __init__(self, base, axes=None): self.base = base self._axes = axes @@ -482,11 +441,11 @@ def set_axis(self, axis): def __call__(self): # Ensure previous behaviour with full circle non-annular views. - if self._axes: - if _is_full_circle_rad(*self._axes.viewLim.intervalx): - rorigin = self._axes.get_rorigin() * self._axes.get_rsign() - if self._axes.get_rmin() <= rorigin: - return [tick for tick in self.base() if tick > rorigin] + ax = self.base.axis.axes + if _is_full_circle_rad(*ax.viewLim.intervalx): + rorigin = ax.get_rorigin() * ax.get_rsign() + if ax.get_rmin() <= rorigin: + return [tick for tick in self.base() if tick > rorigin] return self.base() def _zero_in_bounds(self): @@ -494,7 +453,7 @@ def _zero_in_bounds(self): Return True if zero is within the valid values for the scale of the radial axis. """ - vmin, vmax = self._axes.yaxis._scale.limit_range_for_scale(0, 1, 1e-5) + vmin, vmax = self.base.axis._scale.limit_range_for_scale(0, 1, 1e-5) return vmin == 0 def nonsingular(self, vmin, vmax): @@ -510,7 +469,7 @@ def view_limits(self, vmin, vmax): if self._zero_in_bounds() and vmax > vmin: # this allows inverted r/y-lims vmin = min(0, vmin) - return mtransforms.nonsingular(vmin, vmax) + return mtransforms._nonsingular(vmin, vmax) class _ThetaShift(mtransforms.ScaledTranslation): @@ -530,7 +489,7 @@ class _ThetaShift(mtransforms.ScaledTranslation): of the axes, or using the rlabel position (``'rlabel'``). """ def __init__(self, axes, pad, mode): - super().__init__(pad, pad, axes.figure.dpi_scale_trans) + super().__init__(pad, pad, axes.get_figure(root=False).dpi_scale_trans) self.set_children(axes._realViewLim) self.axes = axes self.mode = mode @@ -721,20 +680,15 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.sticky_edges.y.append(0) - def _wrap_locator_formatter(self): - self.set_major_locator(RadialLocator(self.get_major_locator(), - self.axes)) - self.isDefault_majloc = True + def set_major_locator(self, locator): + if not isinstance(locator, RadialLocator): + locator = RadialLocator(locator) + super().set_major_locator(locator) def clear(self): # docstring inherited super().clear() self.set_ticks_position('none') - self._wrap_locator_formatter() - - def _set_scale(self, value, **kwargs): - super()._set_scale(value, **kwargs) - self._wrap_locator_formatter() def _is_full_circle_deg(thetamin, thetamax): @@ -859,6 +813,10 @@ def _init_axis(self): self.xaxis = ThetaAxis(self, clear=False) self.yaxis = RadialAxis(self, clear=False) self.spines['polar'].register_axis(self.yaxis) + inner_spine = self.spines.get('inner', None) + if inner_spine is not None: + # Subclasses may not have inner spine. + inner_spine.register_axis(self.yaxis) def _set_lim_and_transforms(self): # A view limit where the minimum radius can be locked if the user @@ -897,7 +855,6 @@ def _set_lim_and_transforms(self): # data. This one is aware of rmin self.transProjection = self.PolarTransform( self, - apply_theta_transforms=False, scale_transform=self.transScale ) # Add dependency on rorigin. @@ -1004,7 +961,9 @@ def draw(self, renderer): thetamin, thetamax = np.rad2deg(self._realViewLim.intervalx) if thetamin > thetamax: thetamin, thetamax = thetamax, thetamin - rmin, rmax = ((self._realViewLim.intervaly - self.get_rorigin()) * + rscale_tr = self.yaxis.get_transform() + rmin, rmax = ((rscale_tr.transform(self._realViewLim.intervaly) - + rscale_tr.transform(self.get_rorigin())) * self.get_rsign()) if isinstance(self.patch, mpatches.Wedge): # Backwards-compatibility: Any subclassed Axes might override the @@ -1285,11 +1244,6 @@ def set_rlabel_position(self, value): """ self._r_label_position.clear().translate(np.deg2rad(value), 0.0) - def set_yscale(self, *args, **kwargs): - super().set_yscale(*args, **kwargs) - self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_major_locator(), self)) - def set_rscale(self, *args, **kwargs): return Axes.set_yscale(self, *args, **kwargs) @@ -1447,12 +1401,25 @@ def format_sig(value, delta, opt, fmt): cbook._g_sig_digits(value, delta)) return f"{value:-{opt}.{prec}{fmt}}" - return ('\N{GREEK SMALL LETTER THETA}={}\N{GREEK SMALL LETTER PI} ' - '({}\N{DEGREE SIGN}), r={}').format( + # In case fmt_xdata was not specified, resort to default + + if self.fmt_ydata is None: + r_label = format_sig(r, delta_r, "#", "g") + else: + r_label = self.format_ydata(r) + + if self.fmt_xdata is None: + return ('\N{GREEK SMALL LETTER THETA}={}\N{GREEK SMALL LETTER PI} ' + '({}\N{DEGREE SIGN}), r={}').format( format_sig(theta_halfturns, delta_t_halfturns, "", "f"), format_sig(theta_degrees, delta_t_degrees, "", "f"), - format_sig(r, delta_r, "#", "g"), + r_label ) + else: + return '\N{GREEK SMALL LETTER THETA}={}, r={}'.format( + self.format_xdata(theta), + r_label + ) def get_data_ratio(self): """ diff --git a/lib/matplotlib/projections/polar.pyi b/lib/matplotlib/projections/polar.pyi index de1cbc293900..fc1d508579b5 100644 --- a/lib/matplotlib/projections/polar.pyi +++ b/lib/matplotlib/projections/polar.pyi @@ -18,7 +18,6 @@ class PolarTransform(mtransforms.Transform): axis: PolarAxes | None = ..., use_rmin: bool = ..., *, - apply_theta_transforms: bool = ..., scale_transform: mtransforms.Transform | None = ..., ) -> None: ... def inverted(self) -> InvertedPolarTransform: ... @@ -35,8 +34,6 @@ class InvertedPolarTransform(mtransforms.Transform): self, axis: PolarAxes | None = ..., use_rmin: bool = ..., - *, - apply_theta_transforms: bool = ..., ) -> None: ... def inverted(self) -> PolarTransform: ... diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 76f0bb269264..09f37018cc5f 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -1,5 +1,9 @@ -# Note: The first part of this file can be modified in place, but the latter -# part is autogenerated by the boilerplate.py script. +# Note: The first part of this file is hand-written and must be edited +# in-place. The second part, starting with +# ### REMAINING CONTENT GENERATED BY boilerplate.py ### +# is generated by the script boilerplate.py. It must not be edited here +# because all changes will be overwritten by the next run of the script. +# For more information see the description in boilerplate.py. """ `matplotlib.pyplot` is a state-based interface to matplotlib. It provides @@ -15,6 +19,7 @@ x = np.arange(0, 5, 0.1) y = np.sin(x) plt.plot(x, y) + plt.show() The explicit object-oriented API is recommended for complex plots, though pyplot is still usually used to create the figure and often the Axes in the @@ -29,6 +34,7 @@ y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, y) + plt.show() See :ref:`api_interfaces` for an explanation of the tradeoffs between the @@ -48,15 +54,16 @@ import sys import threading import time -from typing import TYPE_CHECKING, cast, overload +from typing import IO, TYPE_CHECKING, cast, overload from cycler import cycler # noqa: F401 import matplotlib -import matplotlib.colorbar import matplotlib.image from matplotlib import _api -from matplotlib import ( # noqa: F401 Re-exported for typing. - cm as cm, get_backend as get_backend, rcParams as rcParams, style as style) +# Re-exported (import x as x) for typing. +from matplotlib import get_backend as get_backend, rcParams as rcParams +from matplotlib import cm as cm # noqa: F401 +from matplotlib import style as style # noqa: F401 from matplotlib import _pylab_helpers from matplotlib import interactive # noqa: F401 from matplotlib import cbook @@ -71,6 +78,7 @@ from matplotlib.axes import Subplot # noqa: F401 from matplotlib.backends import BackendFilter, backend_registry from matplotlib.projections import PolarAxes +from matplotlib.colorizer import _ColorizerInterface, ColorizingArtist, Colorizer from matplotlib import mlab # for detrend_none, window_hanning from matplotlib.scale import get_scale_names # noqa: F401 @@ -81,7 +89,6 @@ if TYPE_CHECKING: from collections.abc import Callable, Hashable, Iterable, Sequence - import datetime import pathlib import os from typing import Any, BinaryIO, Literal, TypeVar @@ -89,17 +96,26 @@ import PIL.Image from numpy.typing import ArrayLike + import pandas as pd import matplotlib.axes import matplotlib.artist import matplotlib.backend_bases from matplotlib.axis import Tick from matplotlib.axes._base import _AxesBase - from matplotlib.backend_bases import RendererBase, Event + from matplotlib.backend_bases import ( + CloseEvent, + DrawEvent, + KeyEvent, + MouseEvent, + PickEvent, + ResizeEvent, + ) from matplotlib.cm import ScalarMappable from matplotlib.contour import ContourSet, QuadContourSet from matplotlib.collections import ( Collection, + FillBetweenPolyCollection, LineCollection, PolyCollection, PathCollection, @@ -110,17 +126,32 @@ from matplotlib.container import ( BarContainer, ErrorbarContainer, + PieContainer, StemContainer, ) from matplotlib.figure import SubFigure from matplotlib.legend import Legend from matplotlib.mlab import GaussianKDE from matplotlib.image import AxesImage, FigureImage - from matplotlib.patches import FancyArrow, StepPatch, Wedge + from matplotlib.patches import FancyArrow, StepPatch from matplotlib.quiver import Barbs, Quiver, QuiverKey from matplotlib.scale import ScaleBase - from matplotlib.transforms import Transform, Bbox - from matplotlib.typing import ColorType, LineStyleType, MarkerType, HashableList + from matplotlib.typing import ( + CloseEventType, + ColorType, + CoordsType, + DrawEventType, + HashableList, + KeyEventType, + LineStyleType, + MarkerType, + MouseEventType, + PickEventType, + RcGroupKeyType, + RcKeyType, + ResizeEventType, + LogLevel + ) from matplotlib.widgets import SubplotTool _P = ParamSpec('_P') @@ -327,8 +358,8 @@ def uninstall_repl_displayhook() -> None: # Ensure this appears in the pyplot docs. @_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(*args, **kwargs) -> None: - return matplotlib.set_loglevel(*args, **kwargs) +def set_loglevel(level: LogLevel) -> None: + return matplotlib.set_loglevel(level) @_copy_docstring_and_deprecators(Artist.findobj) @@ -367,9 +398,6 @@ def switch_backend(newbackend: str) -> None: another interactive backend has started. Switching to and from non-interactive backends is always possible. - If the new backend is different than the current backend then all open - Figures will be closed via ``plt.close('all')``. - Parameters ---------- newbackend : str @@ -409,8 +437,7 @@ def switch_backend(newbackend: str) -> None: switch_backend("agg") rcParamsOrig["backend"] = "agg" return - # have to escape the switch on access logic - old_backend = dict.__getitem__(rcParams, 'backend') + old_backend = rcParams._get('backend') # get without triggering backend resolution module = backend_registry.load_backend_module(newbackend) canvas_class = module.FigureCanvas @@ -509,16 +536,12 @@ def draw_if_interactive() -> None: # See https://github.com/matplotlib/matplotlib/issues/6092 matplotlib.backends.backend = newbackend # type: ignore[attr-defined] - if not cbook._str_equal(old_backend, newbackend): - if get_fignums(): - _api.warn_deprecated("3.8", message=( - "Auto-close()ing of figures upon backend switching is deprecated since " - "%(since)s and will be removed %(removal)s. To suppress this warning, " - "explicitly call plt.close('all') first.")) - close("all") - # Make sure the repl display hook is installed in case we become interactive. - install_repl_displayhook() + try: + install_repl_displayhook() + except NotImplementedError as err: + _log.warning("Fallback to a different backend") + raise ImportError from err def _warn_if_gui_out_of_main_thread() -> None: @@ -563,6 +586,14 @@ def draw_if_interactive(*args, **kwargs): return _get_backend_mod().draw_if_interactive(*args, **kwargs) +@overload +def show(*, block: bool, **kwargs) -> None: ... + + +@overload +def show(*args: Any, **kwargs: Any) -> None: ... + + # This function's signature is rewritten upon backend-load by switch_backend. def show(*args, **kwargs) -> None: """ @@ -759,13 +790,13 @@ def pause(interval: float) -> None: @_copy_docstring_and_deprecators(matplotlib.rc) -def rc(group: str, **kwargs) -> None: +def rc(group: RcGroupKeyType, **kwargs) -> None: matplotlib.rc(group, **kwargs) @_copy_docstring_and_deprecators(matplotlib.rc_context) def rc_context( - rc: dict[str, Any] | None = None, + rc: dict[RcKeyType, Any] | None = None, fname: str | pathlib.Path | os.PathLike | None = None, ) -> AbstractContextManager[None]: return matplotlib.rc_context(rc, fname) @@ -818,8 +849,7 @@ def xkcd( Notes ----- - This function works by a number of rcParams, so it will probably - override others you have set before. + This function works by a number of rcParams, overriding those set before. If you want the effects of this function to be temporary, it can be used as a context manager, for example:: @@ -840,7 +870,7 @@ def xkcd( "xkcd mode is not compatible with text.usetex = True") stack = ExitStack() - stack.callback(dict.update, rcParams, rcParams.copy()) # type: ignore[arg-type] + stack.callback(rcParams._update_raw, rcParams.copy()) # type: ignore[arg-type] from matplotlib import patheffects rcParams.update({ @@ -871,7 +901,9 @@ def figure( # autoincrement if None, else integer from 1-N num: int | str | Figure | SubFigure | None = None, # defaults to rc figure.figsize - figsize: tuple[float, float] | None = None, + figsize: ArrayLike # a 2-element ndarray is accepted as well + | tuple[float, float, Literal["in", "cm", "px"]] + | None = None, # defaults to rc figure.dpi dpi: float | None = None, *, @@ -904,8 +936,19 @@ def figure( window title is set to this value. If num is a ``SubFigure``, its parent ``Figure`` is activated. - figsize : (float, float), default: :rc:`figure.figsize` - Width, height in inches. + If *num* is a Figure instance that is already tracked in pyplot, it is + activated. If *num* is a Figure instance that is not tracked in pyplot, + it is added to the tracked figures and activated. + + figsize : (float, float) or (float, float, str), default: :rc:`figure.figsize` + The figure dimensions. This can be + + - a tuple ``(width, height, unit)``, where *unit* is one of "inch", "cm", + "px". + - a tuple ``(x, y)``, which is interpreted as ``(x, y, "inch")``. + + One of *width* or *height* may be ``None``; the respective value is taken + from :rc:`figure.figsize`. dpi : float, default: :rc:`figure.dpi` The resolution of the figure in dots-per-inch. @@ -986,26 +1029,38 @@ def figure( in the matplotlibrc file. """ allnums = get_fignums() + next_num = max(allnums) + 1 if allnums else 1 if isinstance(num, FigureBase): # type narrowed to `Figure | SubFigure` by combination of input and isinstance - if num.canvas.manager is None: - raise ValueError("The passed figure is not managed by pyplot") - elif any([figsize, dpi, facecolor, edgecolor, not frameon, - kwargs]) and num.canvas.manager.num in allnums: + has_figure_property_parameters = ( + any(param is not None for param in [figsize, dpi, facecolor, edgecolor]) + or not frameon or kwargs + ) + + root_fig = num.get_figure(root=True) + if root_fig.canvas.manager is None: + if has_figure_property_parameters: + raise ValueError( + "You cannot pass figure properties when calling figure() with " + "an existing Figure instance") + backend = _get_backend_mod() + manager_ = backend.new_figure_manager_given_figure(next_num, root_fig) + _pylab_helpers.Gcf._set_new_active_manager(manager_) + return manager_.canvas.figure + elif has_figure_property_parameters and root_fig.canvas.manager.num in allnums: _api.warn_external( - "Ignoring specified arguments in this call " - f"because figure with num: {num.canvas.manager.num} already exists") - _pylab_helpers.Gcf.set_active(num.canvas.manager) - return num.figure + "Ignoring specified arguments in this call because figure " + f"with num: {root_fig.canvas.manager.num} already exists") + _pylab_helpers.Gcf.set_active(root_fig.canvas.manager) + return root_fig - next_num = max(allnums) + 1 if allnums else 1 fig_label = '' if num is None: num = next_num else: - if any([figsize, dpi, facecolor, edgecolor, not frameon, - kwargs]) and num in allnums: + if (any(param is not None for param in [figsize, dpi, facecolor, edgecolor]) + or not frameon or kwargs) and num in allnums: _api.warn_external( "Ignoring specified arguments in this call " f"because figure with num: {num} already exists") @@ -1126,6 +1181,25 @@ def fignum_exists(num: int | str) -> bool: ) +def _raise_if_figure_exists(num, func_name, clear=False): + """ + Raise a ValueError if the figure *num* already exists. + """ + if num is not None and not clear: + if isinstance(num, FigureBase): + raise ValueError( + f"num {num!r} cannot be a FigureBase instance. " + f"plt.{func_name}() is for creating new figures. " + f"To add to an existing figure, use fig.{func_name}() " + "instead.") + + if fignum_exists(num): + raise ValueError( + f"Figure {num!r} already exists. Use plt.figure({num!r}) " + f"to get it or plt.close({num!r}) to close it. " + f"Alternatively, pass 'clear=True' to {func_name}().") + + def get_fignums() -> list[int]: """Return a list of existing figure numbers.""" return sorted(_pylab_helpers.Gcf.figs) @@ -1155,8 +1229,32 @@ def get_current_fig_manager() -> FigureManagerBase | None: return gcf().canvas.manager +@overload +def connect(s: MouseEventType, func: Callable[[MouseEvent], Any]) -> int: ... + + +@overload +def connect(s: KeyEventType, func: Callable[[KeyEvent], Any]) -> int: ... + + +@overload +def connect(s: PickEventType, func: Callable[[PickEvent], Any]) -> int: ... + + +@overload +def connect(s: ResizeEventType, func: Callable[[ResizeEvent], Any]) -> int: ... + + +@overload +def connect(s: CloseEventType, func: Callable[[CloseEvent], Any]) -> int: ... + + +@overload +def connect(s: DrawEventType, func: Callable[[DrawEvent], Any]) -> int: ... + + @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s: str, func: Callable[[Event], Any]) -> int: +def connect(s, func) -> int: return gcf().canvas.mpl_connect(s, func) @@ -1167,7 +1265,7 @@ def disconnect(cid: int) -> None: def close(fig: None | int | str | Figure | Literal["all"] = None) -> None: """ - Close a figure window. + Close a figure window, and unregister it from pyplot. Parameters ---------- @@ -1180,6 +1278,14 @@ def close(fig: None | int | str | Figure | Literal["all"] = None) -> None: - ``str``: a figure name - 'all': all figures + Notes + ----- + pyplot maintains a reference to figures created with `figure()`. When + work on the figure is completed, it should be closed, i.e. deregistered + from pyplot, to free its memory (see also :rc:`figure.max_open_warning`). + Closing a figure window created by `show()` automatically deregisters the + figure. For all other use cases, most prominently `savefig()` without + `show()`, the figure must be deregistered explicitly using `close()`. """ if fig is None: manager = _pylab_helpers.Gcf.get_active() @@ -1191,9 +1297,7 @@ def close(fig: None | int | str | Figure | Literal["all"] = None) -> None: _pylab_helpers.Gcf.destroy_all() elif isinstance(fig, int): _pylab_helpers.Gcf.destroy(fig) - elif hasattr(fig, 'int'): - # if we are dealing with a type UUID, we - # can use its integer representation + elif hasattr(fig, 'int'): # UUIDs get converted to ints by figure(). _pylab_helpers.Gcf.destroy(fig.int) elif isinstance(fig, str): all_labels = get_figlabels() @@ -1203,8 +1307,8 @@ def close(fig: None | int | str | Figure | Literal["all"] = None) -> None: elif isinstance(fig, Figure): _pylab_helpers.Gcf.destroy_fig(fig) else: - raise TypeError("close() argument must be a Figure, an int, a string, " - "or None, not %s" % type(fig)) + _api.check_isinstance( # type: ignore[unreachable] + (Figure, int, str, None), fig=fig) def clf() -> None: @@ -1233,11 +1337,11 @@ def draw() -> None: @_copy_docstring_and_deprecators(Figure.savefig) -def savefig(*args, **kwargs) -> None: +def savefig(fname: str | os.PathLike | IO, **kwargs) -> None: fig = gcf() # savefig default implementation has no return, so mypy is unhappy # presumably this is here because subclasses can return? - res = fig.savefig(*args, **kwargs) # type: ignore[func-returns-value] + res = fig.savefig(fname, **kwargs) # type: ignore[func-returns-value] fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. return res @@ -1256,7 +1360,7 @@ def figlegend(*args, **kwargs) -> Legend: ## Axes ## -@_docstring.dedent_interpd +@_docstring.interpd def axes( arg: None | tuple[float, float, float, float] = None, **kwargs @@ -1277,7 +1381,7 @@ def axes( - *None*: A new full window Axes is added using ``subplot(**kwargs)``. - - 4-tuple of floats *rect* = ``(left, bottom, width, height)``. + - 4-tuple of float *rect* = ``(left, bottom, width, height)``. A new Axes is added with dimensions *rect* in normalized (0, 1) units using `~.Figure.add_axes` on the current figure. @@ -1362,8 +1466,9 @@ def sca(ax: Axes) -> None: # Mypy sees ax.figure as potentially None, # but if you are calling this, it won't be None # Additionally the slight difference between `Figure` and `FigureBase` mypy catches - figure(ax.figure) # type: ignore[arg-type] - ax.figure.sca(ax) # type: ignore[union-attr] + fig = ax.get_figure(root=False) + figure(fig) # type: ignore[arg-type] + fig.sca(ax) # type: ignore[union-attr] def cla() -> None: @@ -1374,7 +1479,19 @@ def cla() -> None: ## More ways of creating Axes ## -@_docstring.dedent_interpd +@overload +def subplot(nrows: int, ncols: int, index: int, /, **kwargs): ... + + +@overload +def subplot(pos: int | SubplotSpec, /, **kwargs): ... + + +@overload +def subplot(**kwargs): ... + + +@_docstring.interpd def subplot(*args, **kwargs) -> Axes: """ Add an Axes to the current figure or retrieve an existing Axes. @@ -1387,7 +1504,6 @@ def subplot(*args, **kwargs) -> Axes: subplot(nrows, ncols, index, **kwargs) subplot(pos, **kwargs) subplot(**kwargs) - subplot(ax) Parameters ---------- @@ -1445,16 +1561,10 @@ def subplot(*args, **kwargs) -> Axes: Notes ----- - Creating a new Axes will delete any preexisting Axes that - overlaps with it beyond sharing a boundary:: - - import matplotlib.pyplot as plt - # plot a line, implicitly creating a subplot(111) - plt.plot([1, 2, 3]) - # now create a subplot which represents the top plot of a grid - # with 2 rows and 1 column. Since this subplot will overlap the - # first, the plot (and its Axes) previously created, will be removed - plt.subplot(211) + .. versionchanged:: 3.8 + In versions prior to 3.8, any preexisting Axes that overlap with the new Axes + beyond sharing a boundary was deleted. Deletion does not happen in more + recent versions anymore. Use `.Axes.remove` explicitly if needed. If you do not want this behavior, use the `.Figure.add_subplot` method or the `.pyplot.axes` function instead. @@ -1574,7 +1684,7 @@ def subplots( height_ratios: Sequence[float] | None = ..., subplot_kw: dict[str, Any] | None = ..., gridspec_kw: dict[str, Any] | None = ..., - **fig_kw + **fig_kw: Any ) -> tuple[Figure, Axes]: ... @@ -1591,7 +1701,7 @@ def subplots( height_ratios: Sequence[float] | None = ..., subplot_kw: dict[str, Any] | None = ..., gridspec_kw: dict[str, Any] | None = ..., - **fig_kw + **fig_kw: Any ) -> tuple[Figure, np.ndarray]: # TODO numpy/numpy#24738 ... @@ -1608,8 +1718,8 @@ def subplots( height_ratios: Sequence[float] | None = ..., subplot_kw: dict[str, Any] | None = ..., gridspec_kw: dict[str, Any] | None = ..., - **fig_kw -) -> tuple[Figure, Axes | np.ndarray]: + **fig_kw: Any +) -> tuple[Figure, Any]: ... @@ -1622,7 +1732,7 @@ def subplots( height_ratios: Sequence[float] | None = None, subplot_kw: dict[str, Any] | None = None, gridspec_kw: dict[str, Any] | None = None, - **fig_kw + **fig_kw: Any ) -> tuple[Figure, Any]: """ Create a figure and a set of subplots. @@ -1651,8 +1761,9 @@ def subplots( on, use `~matplotlib.axes.Axes.tick_params`. When subplots have a shared axis that has units, calling - `~matplotlib.axis.Axis.set_units` will update each axis with the - new units. + `.Axis.set_units` will update each axis with the new units. + + Note that it is not possible to unshare axes. squeeze : bool, default: True - If True, extra dimensions are squeezed out from the returned @@ -1704,7 +1815,7 @@ def subplots( Typical idioms for handling the return value are:: - # using the variable ax for single a Axes + # using the variable ax for a single Axes fig, ax = plt.subplots() # using the variable axs for multiple Axes @@ -1750,7 +1861,7 @@ def subplots( axs[0, 0].plot(x, y) axs[1, 1].scatter(x, y) - # Share a X axis with each column of subplots + # Share an X axis with each column of subplots plt.subplots(2, 2, sharex='col') # Share a Y axis with each row of subplots @@ -1767,6 +1878,9 @@ def subplots( fig, ax = plt.subplots(num=10, clear=True) """ + num = fig_kw.get('num') + _raise_if_figure_exists(fig_kw.get('num'), "subplots", fig_kw.get('clear')) + fig = figure(**fig_kw) axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey, squeeze=squeeze, subplot_kw=subplot_kw, @@ -1940,6 +2054,9 @@ def subplot_mosaic( total layout. """ + num = fig_kw.get('num') + _raise_if_figure_exists(fig_kw.get('num'), "subplot_mosaic", fig_kw.get('clear')) + fig = figure(**fig_kw) ax_dict = fig.subplot_mosaic( # type: ignore[misc] mosaic, # type: ignore[arg-type] @@ -2082,6 +2199,24 @@ def box(on: bool | None = None) -> None: ## Axis ## +@overload +def xlim() -> tuple[float, float]: + ... + + +@overload +def xlim( + left: float | tuple[float, float] | None = None, + right: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + xmin: float | None = None, + xmax: float | None = None, +) -> tuple[float, float]: + ... + + def xlim(*args, **kwargs) -> tuple[float, float]: """ Get or set the x limits of the current Axes. @@ -2119,6 +2254,24 @@ def xlim(*args, **kwargs) -> tuple[float, float]: return ret +@overload +def ylim() -> tuple[float, float]: + ... + + +@overload +def ylim( + bottom: float | tuple[float, float] | None = None, + top: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + ymin: float | None = None, + ymax: float | None = None, +) -> tuple[float, float]: + ... + + def ylim(*args, **kwargs) -> tuple[float, float]: """ Get or set the y-limits of the current Axes. @@ -2181,6 +2334,21 @@ def xticks( **kwargs `.Text` properties can be used to control the appearance of the labels. + .. warning:: + + This only sets the properties of the current ticks, which is + only sufficient if you either pass *ticks*, resulting in a + fixed list of ticks, or if the plot is static. + + Ticks are not guaranteed to be persistent. Various operations + can create, delete and modify the Tick instances. There is an + imminent risk that these settings can get lost if you work on + the figure further (including also panning/zooming on a + displayed figure). + + Use `~.pyplot.tick_params` instead if possible. + + Returns ------- locs @@ -2252,6 +2420,20 @@ def yticks( **kwargs `.Text` properties can be used to control the appearance of the labels. + .. warning:: + + This only sets the properties of the current ticks, which is + only sufficient if you either pass *ticks*, resulting in a + fixed list of ticks, or if the plot is static. + + Ticks are not guaranteed to be persistent. Various operations + can create, delete and modify the Tick instances. There is an + imminent risk that these settings can get lost if you work on + the figure further (including also panning/zooming on a + displayed figure). + + Use `~.pyplot.tick_params` instead if possible. + Returns ------- locs @@ -2483,7 +2665,7 @@ def _get_pyplot_commands() -> list[str]: @_copy_docstring_and_deprecators(Figure.colorbar) def colorbar( - mappable: ScalarMappable | None = None, + mappable: ScalarMappable | ColorizingArtist | None = None, cax: matplotlib.axes.Axes | None = None, ax: matplotlib.axes.Axes | Iterable[matplotlib.axes.Axes] | None = None, **kwargs @@ -2634,9 +2816,13 @@ def matshow(A: ArrayLike, fignum: None | int = None, **kwargs) -> AxesImage: if fignum == 0: ax = gca() else: - # Extract actual aspect ratio of array and make appropriately sized - # figure. - fig = figure(fignum, figsize=figaspect(A)) + if fignum is not None and fignum_exists(fignum): + # Do not try to set a figure size. + figsize = None + else: + # Extract actual aspect ratio of array and make appropriately sized figure. + figsize = figaspect(A) + fig = figure(fignum, figsize=figsize) ax = fig.add_axes((0.15, 0.09, 0.775, 0.775)) im = ax.matshow(A, **kwargs) sci(im) @@ -2649,17 +2835,32 @@ def polar(*args, **kwargs) -> list[Line2D]: call signature:: - polar(theta, r, **kwargs) + polar(theta, r, [fmt], **kwargs) + + This is a convenience wrapper around `.pyplot.plot`. It ensures that the + current Axes is polar (or creates one if needed) and then passes all parameters + to ``.pyplot.plot``. - Multiple *theta*, *r* arguments are supported, with format strings, as in - `plot`. + .. note:: + When making polar plots using the :ref:`pyplot API `, + ``polar()`` should typically be the first command because that makes sure + a polar Axes is created. Using other commands such as ``plt.title()`` + before this can lead to the implicit creation of a rectangular Axes, in which + case a subsequent ``polar()`` call will fail. """ # If an axis already exists, check if it has a polar projection if gcf().get_axes(): ax = gca() if not isinstance(ax, PolarAxes): - _api.warn_external('Trying to create polar plot on an Axes ' - 'that does not have a polar projection.') + _api.warn_deprecated( + "3.10", + message="There exists a non-polar current Axes. Therefore, the " + "resulting plot from 'polar()' is non-polar. You likely " + "should call 'polar()' before any other pyplot plotting " + "commands. " + "Support for this scenario is deprecated in %(since)s and " + "will raise an error in %(removal)s" + ) else: ax = axes(projection="polar") return ax.plot(*args, **kwargs) @@ -2668,12 +2869,15 @@ def polar(*args, **kwargs) -> list[Line2D]: # If rcParams['backend_fallback'] is true, and an interactive backend is # requested, ignore rcParams['backend'] and force selection of a backend that # is compatible with the current running interactive framework. -if (rcParams["backend_fallback"] - and rcParams._get_backend_or_none() in ( # type: ignore[attr-defined] - set(backend_registry.list_builtin(BackendFilter.INTERACTIVE)) - - {'webagg', 'nbagg'}) - and cbook._get_running_interactive_framework()): - rcParams._set("backend", rcsetup._auto_backend_sentinel) +if rcParams["backend_fallback"]: + requested_backend = rcParams._get_backend_or_none() # type: ignore[attr-defined] + requested_backend = None if requested_backend is None else requested_backend.lower() + available_backends = backend_registry.list_builtin(BackendFilter.INTERACTIVE) + if ( + requested_backend in (set(available_backends) - {'webagg', 'nbagg'}) + and cbook._get_running_interactive_framework() + ): + rcParams._set("backend", rcsetup._auto_backend_sentinel) # fmt: on @@ -2693,6 +2897,8 @@ def figimage( vmax: float | None = None, origin: Literal["upper", "lower"] | None = None, resize: bool = False, + *, + colorizer: Colorizer | None = None, **kwargs, ) -> FigureImage: return gcf().figimage( @@ -2706,6 +2912,7 @@ def figimage( vmax=vmax, origin=origin, resize=resize, + colorizer=colorizer, **kwargs, ) @@ -2726,7 +2933,7 @@ def gca() -> Axes: # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure._gci) -def gci() -> ScalarMappable | None: +def gci() -> ColorizingArtist | None: return gcf()._gci() @@ -2826,19 +3033,10 @@ def angle_spectrum( @_copy_docstring_and_deprecators(Axes.annotate) def annotate( text: str, - xy: tuple[float, float], - xytext: tuple[float, float] | None = None, - xycoords: str - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] - | tuple[float, float] = "data", - textcoords: str - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] - | tuple[float, float] - | None = None, + xy: tuple[Any, Any], + xytext: tuple[Any, Any] | None = None, + xycoords: CoordsType = "data", + textcoords: CoordsType | None = None, arrowprops: dict[str, Any] | None = None, annotation_clip: bool | None = None, **kwargs, @@ -2881,7 +3079,7 @@ def axhline(y: float = 0, xmin: float = 0, xmax: float = 1, **kwargs) -> Line2D: @_copy_docstring_and_deprecators(Axes.axhspan) def axhspan( ymin: float, ymax: float, xmin: float = 0, xmax: float = 1, **kwargs -) -> Polygon: +) -> Rectangle: return gca().axhspan(ymin, ymax, xmin=xmin, xmax=xmax, **kwargs) @@ -2919,7 +3117,7 @@ def axvline(x: float = 0, ymin: float = 0, ymax: float = 1, **kwargs) -> Line2D: @_copy_docstring_and_deprecators(Axes.axvspan) def axvspan( xmin: float, xmax: float, ymin: float = 0, ymax: float = 1, **kwargs -) -> Polygon: +) -> Rectangle: return gca().axvspan(xmin, xmax, ymin=ymin, ymax=ymax, **kwargs) @@ -2983,7 +3181,7 @@ def bar_label( *, fmt: str | Callable[[float], str] = "%g", label_type: Literal["center", "edge"] = "edge", - padding: float = 0, + padding: float | ArrayLike = 0, **kwargs, ) -> list[Annotation]: return gca().bar_label( @@ -3070,12 +3268,17 @@ def boxplot( def broken_barh( xranges: Sequence[tuple[float, float]], yrange: tuple[float, float], + align: Literal["bottom", "center", "top"] = "bottom", *, data=None, **kwargs, ) -> PolyCollection: return gca().broken_barh( - xranges, yrange, **({"data": data} if data is not None else {}), **kwargs + xranges, + yrange, + align=align, + **({"data": data} if data is not None else {}), + **kwargs, ) @@ -3093,8 +3296,9 @@ def cohere( NFFT: int = 256, Fs: float = 2, Fc: int = 0, - detrend: Literal["none", "mean", "linear"] - | Callable[[ArrayLike], ArrayLike] = mlab.detrend_none, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] + ) = mlab.detrend_none, window: Callable[[ArrayLike], ArrayLike] | ArrayLike = mlab.window_hanning, noverlap: int = 0, pad_to: int | None = None, @@ -3151,9 +3355,9 @@ def csd( NFFT: int | None = None, Fs: float | None = None, Fc: int | None = None, - detrend: Literal["none", "mean", "linear"] - | Callable[[ArrayLike], ArrayLike] - | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, noverlap: int | None = None, pad_to: int | None = None, @@ -3189,7 +3393,7 @@ def ecdf( weights: ArrayLike | None = None, *, complementary: bool = False, - orientation: Literal["vertical", "horizonatal"] = "vertical", + orientation: Literal["vertical", "horizontal"] = "vertical", compress: bool = False, data=None, **kwargs, @@ -3223,6 +3427,7 @@ def errorbar( xuplims: bool | ArrayLike = False, errorevery: int | tuple[int, int] = 1, capthick: float | None = None, + elinestyle: LineStyleType | None = None, *, data=None, **kwargs, @@ -3243,6 +3448,7 @@ def errorbar( xuplims=xuplims, errorevery=errorevery, capthick=capthick, + elinestyle=elinestyle, **({"data": data} if data is not None else {}), **kwargs, ) @@ -3295,7 +3501,7 @@ def fill_between( *, data=None, **kwargs, -) -> PolyCollection: +) -> FillBetweenPolyCollection: return gca().fill_between( x, y1, @@ -3320,7 +3526,7 @@ def fill_betweenx( *, data=None, **kwargs, -) -> PolyCollection: +) -> FillBetweenPolyCollection: return gca().fill_betweenx( y, x1, @@ -3344,6 +3550,33 @@ def grid( gca().grid(visible=visible, which=which, axis=axis, **kwargs) +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.grouped_bar) +def grouped_bar( + heights: Sequence[ArrayLike] | dict[str, ArrayLike] | np.ndarray | pd.DataFrame, + *, + positions: ArrayLike | None = None, + group_spacing: float | None = 1.5, + bar_spacing: float | None = 0, + tick_labels: Sequence[str] | None = None, + labels: Sequence[str] | None = None, + orientation: Literal["vertical", "horizontal"] = "vertical", + colors: Iterable[ColorType] | None = None, + **kwargs, +) -> list[BarContainer]: + return gca().grouped_bar( + heights, + positions=positions, + group_spacing=group_spacing, + bar_spacing=bar_spacing, + tick_labels=tick_labels, + labels=labels, + orientation=orientation, + colors=colors, + **kwargs, + ) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.hexbin) def hexbin( @@ -3365,6 +3598,7 @@ def hexbin( reduce_C_function: Callable[[np.ndarray | list[float]], float] = np.mean, mincnt: int | None = None, marginals: bool = False, + colorizer: Colorizer | None = None, *, data=None, **kwargs, @@ -3388,6 +3622,7 @@ def hexbin( reduce_C_function=reduce_C_function, mincnt=mincnt, marginals=marginals, + colorizer=colorizer, **({"data": data} if data is not None else {}), **kwargs, ) @@ -3533,9 +3768,10 @@ def imshow( alpha: float | ArrayLike | None = None, vmin: float | None = None, vmax: float | None = None, + colorizer: Colorizer | None = None, origin: Literal["upper", "lower"] | None = None, extent: tuple[float, float, float, float] | None = None, - interpolation_stage: Literal["data", "rgba"] | None = None, + interpolation_stage: Literal["data", "rgba", "auto"] | None = None, filternorm: bool = True, filterrad: float = 4.0, resample: bool | None = None, @@ -3552,6 +3788,7 @@ def imshow( alpha=alpha, vmin=vmin, vmax=vmax, + colorizer=colorizer, origin=origin, extent=extent, interpolation_stage=interpolation_stage, @@ -3646,6 +3883,7 @@ def pcolor( cmap: str | Colormap | None = None, vmin: float | None = None, vmax: float | None = None, + colorizer: Colorizer | None = None, data=None, **kwargs, ) -> Collection: @@ -3657,6 +3895,7 @@ def pcolor( cmap=cmap, vmin=vmin, vmax=vmax, + colorizer=colorizer, **({"data": data} if data is not None else {}), **kwargs, ) @@ -3673,6 +3912,7 @@ def pcolormesh( cmap: str | Colormap | None = None, vmin: float | None = None, vmax: float | None = None, + colorizer: Colorizer | None = None, shading: Literal["flat", "nearest", "gouraud", "auto"] | None = None, antialiased: bool = False, data=None, @@ -3685,6 +3925,7 @@ def pcolormesh( cmap=cmap, vmin=vmin, vmax=vmax, + colorizer=colorizer, shading=shading, antialiased=antialiased, **({"data": data} if data is not None else {}), @@ -3742,7 +3983,7 @@ def pie( normalize: bool = True, hatch: str | Sequence[str] | None = None, data=None, -) -> tuple[list[Wedge], list[Text]] | tuple[list[Wedge], list[Text], list[Text]]: +) -> PieContainer: return gca().pie( x, explode=explode, @@ -3766,6 +4007,28 @@ def pie( ) +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.pie_label) +def pie_label( + container: PieContainer, + /, + labels: str | Sequence[str], + *, + distance: float = 0.6, + textprops: dict | None = None, + rotate: bool = False, + alignment: str = "auto", +) -> list[Text]: + return gca().pie_label( + container, + labels, + distance=distance, + textprops=textprops, + rotate=rotate, + alignment=alignment, + ) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.plot) def plot( @@ -3784,31 +4047,6 @@ def plot( ) -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.plot_date) -def plot_date( - x: ArrayLike, - y: ArrayLike, - fmt: str = "o", - tz: str | datetime.tzinfo | None = None, - xdate: bool = True, - ydate: bool = False, - *, - data=None, - **kwargs, -) -> list[Line2D]: - return gca().plot_date( - x, - y, - fmt=fmt, - tz=tz, - xdate=xdate, - ydate=ydate, - **({"data": data} if data is not None else {}), - **kwargs, - ) - - # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.psd) def psd( @@ -3816,9 +4054,9 @@ def psd( NFFT: int | None = None, Fs: float | None = None, Fc: int | None = None, - detrend: Literal["none", "mean", "linear"] - | Callable[[ArrayLike], ArrayLike] - | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, noverlap: int | None = None, pad_to: int | None = None, @@ -3880,6 +4118,7 @@ def scatter( linewidths: float | Sequence[float] | None = None, *, edgecolors: Literal["face", "none"] | ColorType | Sequence[ColorType] | None = None, + colorizer: Colorizer | None = None, plotnonfinite: bool = False, data=None, **kwargs, @@ -3897,6 +4136,7 @@ def scatter( alpha=alpha, linewidths=linewidths, edgecolors=edgecolors, + colorizer=colorizer, plotnonfinite=plotnonfinite, **({"data": data} if data is not None else {}), **kwargs, @@ -3924,9 +4164,9 @@ def specgram( NFFT: int | None = None, Fs: float | None = None, Fc: int | None = None, - detrend: Literal["none", "mean", "linear"] - | Callable[[ArrayLike], ArrayLike] - | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, noverlap: int | None = None, cmap: str | Colormap | None = None, @@ -3986,22 +4226,19 @@ def spy( origin=origin, **kwargs, ) - if isinstance(__ret, cm.ScalarMappable): - sci(__ret) # noqa + if isinstance(__ret, _ColorizerInterface): + sci(__ret) return __ret # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.stackplot) -def stackplot( - x, *args, labels=(), colors=None, hatch=None, baseline="zero", data=None, **kwargs -): +def stackplot(x, *args, labels=(), colors=None, baseline="zero", data=None, **kwargs): return gca().stackplot( x, *args, labels=labels, colors=colors, - hatch=hatch, baseline=baseline, **({"data": data} if data is not None else {}), **kwargs, @@ -4074,6 +4311,9 @@ def streamplot( integration_direction="both", broken_streamlines=True, *, + integration_max_step_scale=1.0, + integration_max_error_scale=1.0, + num_arrows=1, data=None, ): __ret = gca().streamplot( @@ -4095,6 +4335,9 @@ def streamplot( maxlength=maxlength, integration_direction=integration_direction, broken_streamlines=broken_streamlines, + integration_max_step_scale=integration_max_step_scale, + integration_max_error_scale=integration_max_error_scale, + num_arrows=num_arrows, **({"data": data} if data is not None else {}), ) sci(__ret.lines) @@ -4237,11 +4480,12 @@ def violinplot( showmedians: bool = False, quantiles: Sequence[float | Sequence[float]] | None = None, points: int = 100, - bw_method: Literal["scott", "silverman"] - | float - | Callable[[GaussianKDE], float] - | None = None, + bw_method: ( + Literal["scott", "silverman"] | float | Callable[[GaussianKDE], float] | None + ) = None, side: Literal["both", "low", "high"] = "both", + facecolor: Sequence[ColorType] | ColorType | None = None, + linecolor: Sequence[ColorType] | ColorType | None = None, *, data=None, ) -> dict[str, Collection]: @@ -4258,6 +4502,8 @@ def violinplot( points=points, bw_method=bw_method, side=side, + facecolor=facecolor, + linecolor=linecolor, **({"data": data} if data is not None else {}), ) @@ -4314,7 +4560,7 @@ def xcorr( # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes._sci) -def sci(im: ScalarMappable) -> None: +def sci(im: ColorizingArtist) -> None: gca()._sci(im) diff --git a/lib/matplotlib/quiver.py b/lib/matplotlib/quiver.py index 240d7737b516..da1e85950933 100644 --- a/lib/matplotlib/quiver.py +++ b/lib/matplotlib/quiver.py @@ -27,7 +27,7 @@ import matplotlib.transforms as transforms -_quiver_doc = """ +_quiver_doc = r""" Plot a 2D field of arrows. Call signature:: @@ -87,14 +87,19 @@ angles : {'uv', 'xy'} or array-like, default: 'uv' Method for determining the angle of the arrows. - - 'uv': Arrow direction in screen coordinates. Use this if the arrows - symbolize a quantity that is not based on *X*, *Y* data coordinates. + - 'uv': Arrow directions are based on + :ref:`display coordinates `; i.e. a 45° angle will + always show up as diagonal on the screen, irrespective of figure or Axes + aspect ratio or Axes data ranges. This is useful when the arrows represent + a quantity whose direction is not tied to the x and y data coordinates. If *U* == *V* the orientation of the arrow on the plot is 45 degrees counter-clockwise from the horizontal axis (positive to the right). - 'xy': Arrow direction in data coordinates, i.e. the arrows point from - (x, y) to (x+u, y+v). Use this e.g. for plotting a gradient field. + (x, y) to (x+u, y+v). This is ideal for vector fields or gradient plots + where the arrows should directly represent movements or gradients in the + x and y directions. - Arbitrary angles may be specified explicitly as an array of values in degrees, counter-clockwise from the horizontal axis. @@ -102,38 +107,74 @@ In this case *U*, *V* is only used to determine the length of the arrows. + For example, ``angles=[30, 60, 90]`` will orient the arrows at 30, 60, and 90 + degrees respectively, regardless of the *U* and *V* components. + Note: inverting a data axis will correspondingly invert the arrows only with ``angles='xy'``. -pivot : {'tail', 'mid', 'middle', 'tip'}, default: 'tail' +pivot : {'tail', 'middle', 'tip'}, default: 'tail' The part of the arrow that is anchored to the *X*, *Y* grid. The arrow rotates about this point. - 'mid' is a synonym for 'middle'. + .. admonition:: Discouraged + + 'mid' is a synonym for 'middle', which is retained for backwards + compatibility. New code should use 'middle'. scale : float, optional Scales the length of the arrow inversely. - Number of data units per arrow length unit, e.g., m/s per plot width; a - smaller scale parameter makes the arrow longer. Default is *None*. + Number of data values represented by one unit of arrow length on the plot. + For example, if the data represents velocity in meters per second (m/s), the + scale parameter determines how many meters per second correspond to one unit of + arrow length relative to the width of the plot. + Smaller scale parameter makes the arrow longer. + + By default, an autoscaling algorithm is used to scale the arrow length to a + reasonable size, which is based on the average vector length and the number of + vectors. - If *None*, a simple autoscaling algorithm is used, based on the average - vector length and the number of vectors. The arrow length unit is given by - the *scale_units* parameter. + The arrow length unit is given by the *scale_units* parameter. -scale_units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, optional - If the *scale* kwarg is *None*, the arrow length unit. Default is *None*. +scale_units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, default: 'width' + The physical image unit, which is used for rendering the scaled arrow data U, V. - e.g. *scale_units* is 'inches', *scale* is 2.0, and ``(u, v) = (1, 0)``, - then the vector will be 0.5 inches long. + The rendered arrow length is given by: + + - length in x direction = :math:`\frac{u}{\mathrm{scale}}\,\mathrm{scale\_unit}` + - length in y direction = :math:`\frac{v}{\mathrm{scale}}\,\mathrm{scale\_unit}` + For example, ``(u, v) = (0.5, 0)`` with ``scale=10, scale_units="width"`` results + in a horizontal arrow with a length of *0.5 / 10 * "width"*, i.e. 0.05 times the + Axes width. + + Supported values are: - If *scale_units* is 'width' or 'height', then the vector will be half the - width/height of the axes. + - 'width' or 'height': The arrow length is scaled relative to the width or height + of the Axes. + For example, ``scale_units='width', scale=1.0``, will result in an arrow length + of width of the Axes. - If *scale_units* is 'x' then the vector will be 0.5 x-axis - units. To plot vectors in the x-y plane, with u and v having - the same units as x and y, use - ``angles='xy', scale_units='xy', scale=1``. + - 'dots': The arrow length of the arrows is in measured in display dots (pixels). + + - 'inches': Arrow lengths are scaled based on the DPI (dots per inch) of the figure. + This ensures that the arrows have a consistent physical size on the figure, + in inches, regardless of data values or plot scaling. + For example, ``(u, v) = (1, 0)`` with ``scale_units='inches', scale=2`` results + in a 0.5 inch-long arrow. + + - 'x' or 'y': The arrow length is scaled relative to the x or y axis units. + For example, ``(u, v) = (0, 1)`` with ``scale_units='x', scale=1`` results + in a vertical arrow with the length of 1 x-axis unit. + + - 'xy': Arrow length will be same as 'x' or 'y' units. + This is useful for creating vectors in the x-y plane where u and v have + the same units as x and y. To plot vectors in the x-y plane with u and v having + the same units as x and y, use ``angles='xy', scale_units='xy', scale=1``. + + Note: Setting *scale_units* without setting scale does not have any effect because + the scale units only differ by a constant factor and that is rescaled through + autoscaling. units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, default: 'width' Affects the arrow size (except for the length). In particular, the shaft @@ -230,7 +271,7 @@ of the head in forward direction so that the arrow head looks broken. """ % _docstring.interpd.params -_docstring.interpd.update(quiver_doc=_quiver_doc) +_docstring.interpd.register(quiver_doc=_quiver_doc) class QuiverKey(martist.Artist): @@ -241,7 +282,8 @@ class QuiverKey(martist.Artist): def __init__(self, Q, X, Y, U, label, *, angle=0, coordinates='axes', color=None, labelsep=0.1, - labelpos='N', labelcolor=None, fontproperties=None, **kwargs): + labelpos='N', labelcolor=None, fontproperties=None, + zorder=None, **kwargs): """ Add a key to a quiver plot. @@ -285,6 +327,8 @@ def __init__(self, Q, X, Y, U, label, A dictionary with keyword arguments accepted by the `~matplotlib.font_manager.FontProperties` initializer: *family*, *style*, *variant*, *size*, *weight*. + zorder : float + The zorder of the key. The default is 0.1 above *Q*. **kwargs Any additional keyword arguments are used to override vector properties taken from *Q*. @@ -301,47 +345,57 @@ def __init__(self, Q, X, Y, U, label, self._labelsep_inches = labelsep self.labelpos = labelpos - self.labelcolor = labelcolor - self.fontproperties = fontproperties or dict() - self.kw = kwargs + self._kw = kwargs # Remove when kw deprecation elapses. + self.vector = mcollections.PolyCollection( + [], **{**self.Q.polykw, **kwargs}) self.text = mtext.Text( text=label, horizontalalignment=self.halign[self.labelpos], verticalalignment=self.valign[self.labelpos], - fontproperties=self.fontproperties) - if self.labelcolor is not None: - self.text.set_color(self.labelcolor) + fontproperties=fontproperties or {}) + if labelcolor is not None: + self.text.set_color(labelcolor) self._dpi_at_last_init = None - self.zorder = Q.zorder + 0.1 + self.zorder = zorder if zorder is not None else Q.zorder + 0.1 + + kw = _api.deprecated("3.11")( # Also remove self._kw when deprecation elapses. + property(lambda self: self._kw)) + fontproperties = _api.deprecated( + "3.11", alternative="quiverkey.text.get_fontproperties()")( + property(lambda self: self.text.get_fontproperties())) + labelcolor = _api.deprecated( + "3.11", alternative="quiverkey.text.get_color()")( + property(lambda self: self.text.get_color())) + verts = _api.deprecated( + "3.11", alternative="[p.vertices for p in quiverkey.vector.get_paths()]")( + property(lambda self: [p.vertices for p in self.vector.get_paths()])) @property def labelsep(self): - return self._labelsep_inches * self.Q.axes.figure.dpi + return self._labelsep_inches * self.Q.axes.get_figure(root=True).dpi def _init(self): - if True: # self._dpi_at_last_init != self.axes.figure.dpi - if self.Q._dpi_at_last_init != self.Q.axes.figure.dpi: - self.Q._init() - self._set_transform() - with cbook._setattr_cm(self.Q, pivot=self.pivot[self.labelpos], - # Hack: save and restore the Umask - Umask=ma.nomask): - u = self.U * np.cos(np.radians(self.angle)) - v = self.U * np.sin(np.radians(self.angle)) - self.verts = self.Q._make_verts([[0., 0.]], - np.array([u]), np.array([v]), 'uv') - kwargs = self.Q.polykw - kwargs.update(self.kw) - self.vector = mcollections.PolyCollection( - self.verts, - offsets=[(self.X, self.Y)], - offset_transform=self.get_transform(), - **kwargs) - if self.color is not None: - self.vector.set_color(self.color) - self.vector.set_transform(self.Q.get_transform()) - self.vector.set_figure(self.get_figure()) - self._dpi_at_last_init = self.Q.axes.figure.dpi + if False: # self._dpi_at_last_init == self.axes.get_figure().dpi + return + if self.Q._dpi_at_last_init != self.Q.axes.get_figure(root=True).dpi: + self.Q._init() + self._set_transform() + with cbook._setattr_cm(self.Q, pivot=self.pivot[self.labelpos], + # Hack: save and restore the Umask + Umask=ma.nomask): + u = self.U * np.cos(np.radians([self.angle])) + v = self.U * np.sin(np.radians([self.angle])) + verts = self.Q._make_verts([[0., 0.]], u, v, 'uv') + self.vector.set( + verts=verts, + offsets=[(self.X, self.Y)], + offset_transform=self.get_transform(), + transform=self.Q.get_transform(), + figure=self.get_figure(), + ) + if self.color is not None: + self.vector.set_color(self.color) + self._dpi_at_last_init = self.Q.axes.get_figure(root=True).dpi def _text_shift(self): return { @@ -361,11 +415,12 @@ def draw(self, renderer): self.stale = False def _set_transform(self): - self.set_transform(_api.check_getitem({ + fig = self.Q.axes.get_figure(root=False) + self.set_transform(_api.getitem_checked({ "data": self.Q.axes.transData, "axes": self.Q.axes.transAxes, - "figure": self.Q.axes.figure.transFigure, - "inches": self.Q.axes.figure.dpi_scale_trans, + "figure": fig.transFigure, + "inches": fig.dpi_scale_trans, }, coordinates=self.coord)) def set_figure(self, fig): @@ -424,13 +479,13 @@ def _parse_args(*args, caller_name='function'): X = X.ravel() Y = Y.ravel() if len(X) == nc and len(Y) == nr: - X, Y = [a.ravel() for a in np.meshgrid(X, Y)] + X, Y = (a.ravel() for a in np.meshgrid(X, Y)) elif len(X) != len(Y): raise ValueError('X and Y must be the same size, but ' f'X.size is {X.size} and Y.size is {Y.size}.') else: indexgrid = np.meshgrid(np.arange(nc), np.arange(nr)) - X, Y = [np.ravel(a) for a in indexgrid] + X, Y = (np.ravel(a) for a in indexgrid) # Size validation for U, V, C is left to the set_UVC method. return X, Y, U, V, C @@ -445,11 +500,8 @@ class Quiver(mcollections.PolyCollection): """ Specialized PolyCollection for arrows. - The only API method is set_UVC(), which can be used - to change the size, orientation, and color of the - arrows; their locations are fixed when the class is - instantiated. Possibly this method will be useful - in animations. + Use set_UVC to change the size, orientation, and color of the + arrows; their locations can be set using set_offsets(). Much of the work in this class is done in the draw() method so that as much information as possible is available @@ -518,11 +570,11 @@ def _init(self): self.width = 0.06 * self.span / sn # _make_verts sets self.scale if not already specified - if (self._dpi_at_last_init != self.axes.figure.dpi + if (self._dpi_at_last_init != self.axes.get_figure(root=True).dpi and self.scale is None): self._make_verts(self.XY, self.U, self.V, self.angles) - self._dpi_at_last_init = self.axes.figure.dpi + self._dpi_at_last_init = self.axes.get_figure(root=True).dpi def get_datalim(self, transData): trans = self.get_transform() @@ -572,14 +624,14 @@ def _dots_per_unit(self, units): """Return a scale factor for converting from units to pixels.""" bb = self.axes.bbox vl = self.axes.viewLim - return _api.check_getitem({ + return _api.getitem_checked({ 'x': bb.width / vl.width, 'y': bb.height / vl.height, 'xy': np.hypot(*bb.size) / np.hypot(*vl.size), 'width': bb.width, 'height': bb.height, 'dots': 1., - 'inches': self.axes.figure.dpi, + 'inches': self.axes.get_figure(root=True).dpi, }, units=units) def _set_transform(self): @@ -864,7 +916,7 @@ def _h_arrows(self, length): %(PolyCollection:kwdoc)s """ % _docstring.interpd.params -_docstring.interpd.update(barbs_doc=_barbs_doc) +_docstring.interpd.register(barbs_doc=_barbs_doc) class Barbs(mcollections.PolyCollection): @@ -876,9 +928,9 @@ class Barbs(mcollections.PolyCollection): are changed using the :meth:`set_offsets` collection method. Possibly this method will be useful in animations. - There is one internal function :meth:`_find_tails` which finds + There is one internal function :meth:`!_find_tails` which finds exactly what should be put on the barb given the vector magnitude. - From there :meth:`_make_barbs` is used to find the vertices of the + From there :meth:`!_make_barbs` is used to find the vertices of the polygon to represent the barb based on this information. """ diff --git a/lib/matplotlib/quiver.pyi b/lib/matplotlib/quiver.pyi index 2a043a92b4b5..02d622f6bb5c 100644 --- a/lib/matplotlib/quiver.pyi +++ b/lib/matplotlib/quiver.pyi @@ -1,7 +1,7 @@ import matplotlib.artist as martist import matplotlib.collections as mcollections from matplotlib.axes import Axes -from matplotlib.figure import Figure +from matplotlib.figure import Figure, SubFigure from matplotlib.text import Text from matplotlib.transforms import Transform, Bbox @@ -25,9 +25,6 @@ class QuiverKey(martist.Artist): color: ColorType | None label: str labelpos: Literal["N", "S", "E", "W"] - labelcolor: ColorType | None - fontproperties: dict[str, Any] - kw: dict[str, Any] text: Text zorder: float def __init__( @@ -45,11 +42,20 @@ class QuiverKey(martist.Artist): labelpos: Literal["N", "S", "E", "W"] = ..., labelcolor: ColorType | None = ..., fontproperties: dict[str, Any] | None = ..., + zorder: float | None = ..., **kwargs ) -> None: ... @property + def kw(self) -> dict[str, Any]: ... + @property + def fontproperties(self) -> dict[str, Any]: ... + @property + def labelcolor(self) -> ColorType | None: ... + @property + def verts(self) -> Sequence[ArrayLike]: ... + @property def labelsep(self) -> float: ... - def set_figure(self, fig: Figure) -> None: ... + def set_figure(self, fig: Figure | SubFigure) -> None: ... class Quiver(mcollections.PolyCollection): X: ArrayLike diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index b0cd22098489..eefbae5e68f5 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -10,53 +10,33 @@ The default values of the rc settings are set in the default matplotlibrc file. Any additions or deletions to the parameter set listed here should also be propagated to the :file:`lib/matplotlib/mpl-data/matplotlibrc` in Matplotlib's -root source directory. +root source directory. New rcparams also need to be added to the RcKeyType enum +in :file:`lib/matplotlib/typing.py`. """ + import ast +from dataclasses import dataclass from functools import lru_cache, reduce from numbers import Real import operator import os import re +from typing import Any +from collections.abc import Callable import numpy as np +import matplotlib as mpl from matplotlib import _api, cbook -from matplotlib.backends import BackendFilter, backend_registry +from matplotlib.backends import backend_registry from matplotlib.cbook import ls_mapper from matplotlib.colors import Colormap, is_color_like from matplotlib._fontconfig_pattern import parse_fontconfig_pattern from matplotlib._enums import JoinStyle, CapStyle # Don't let the original cycler collide with our validating cycler -from cycler import Cycler, cycler as ccycler - - -@_api.caching_module_getattr -class __getattr__: - @_api.deprecated( - "3.9", - alternative="``matplotlib.backends.backend_registry.list_builtin" - "(matplotlib.backends.BackendFilter.INTERACTIVE)``") - @property - def interactive_bk(self): - return backend_registry.list_builtin(BackendFilter.INTERACTIVE) - - @_api.deprecated( - "3.9", - alternative="``matplotlib.backends.backend_registry.list_builtin" - "(matplotlib.backends.BackendFilter.NON_INTERACTIVE)``") - @property - def non_interactive_bk(self): - return backend_registry.list_builtin(BackendFilter.NON_INTERACTIVE) - - @_api.deprecated( - "3.9", - alternative="``matplotlib.backends.backend_registry.list_builtin()``") - @property - def all_backends(self): - return backend_registry.list_builtin() +from cycler import Cycler, concat as cconcat, cycler as ccycler class ValidateInStrings: @@ -92,6 +72,55 @@ def __call__(self, s): msg += "; remove quotes surrounding your string" raise ValueError(msg) + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"key={self.key!r}, valid={[*self.valid.values()]}, " + f"ignorecase={self.ignorecase})") + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, ValidateInStrings): + return NotImplemented + return ( + self.key, + self.ignorecase, + self._deprecated_since, + tuple(sorted(self.valid.items())) + ) == ( + other.key, + other.ignorecase, + other._deprecated_since, + tuple(sorted(other.valid.items())) + ) + + def __hash__(self): + return hash(( + self.key, + self.ignorecase, + self._deprecated_since, + tuple(sorted(self.valid.items())) + )) + + +def _single_string_color_list(s, scalar_validator): + """ + Convert the string *s* to a list of colors interpreting it either as a + color sequence name, or a string containing single-letter colors. + """ + try: + colors = mpl.color_sequences[s] + except KeyError: + try: + # Sometimes, a list of colors might be a single string + # of single-letter colornames. So give that a shot. + colors = [scalar_validator(v.strip()) for v in s if v.strip()] + except ValueError: + raise ValueError(f'{s!r} is neither a color sequence name nor can ' + 'it be interpreted as a list of colors') + + return colors + @lru_cache def _listify_validator(scalar_validator, allow_stringlist=False, *, @@ -103,9 +132,8 @@ def f(s): if v.strip()] except Exception: if allow_stringlist: - # Sometimes, a list of colors might be a single string - # of single-letter colornames. So give that a shot. - val = [scalar_validator(v.strip()) for v in s if v.strip()] + # Special handling for colors + val = _single_string_color_list(s, scalar_validator) else: raise # Allow any ordered sequence type -- generators, np.ndarray, pd.Series @@ -191,6 +219,14 @@ def _make_type_validator(cls, *, allow_none=False): def validator(s): if (allow_none and (s is None or cbook._str_lower_equal(s, "none"))): + if cbook._str_lower_equal(s, "none") and s != "None": + _api.warn_deprecated( + "3.11", + message=f"Using the capitalization {s!r} in matplotlibrc for " + "*None* is deprecated in %(removal)s and will lead to an " + "error from version 3.13 onward. Please use 'None' " + "instead." + ) return None if cls is str and not isinstance(s, str): raise ValueError(f'Could not convert {s!r} to str') @@ -214,10 +250,11 @@ def validator(s): validate_string, doc='return a list of strings') validate_int = _make_type_validator(int) validate_int_or_None = _make_type_validator(int, allow_none=True) +validate_intlist = _listify_validator(validate_int, n=2) validate_float = _make_type_validator(float) validate_float_or_None = _make_type_validator(float, allow_none=True) validate_floatlist = _listify_validator( - validate_float, doc='return a list of floats') + validate_float) def _validate_marker(s): @@ -301,6 +338,12 @@ def validate_color_or_auto(s): return validate_color(s) +def _validate_color_or_edge(s): + if cbook._str_equal(s, 'edge'): + return s + return validate_color(s) + + def validate_color_for_prop_cycle(s): # N-th color cycle syntax can't go into the color cycle. if isinstance(s, str) and re.match("^C[0-9]$", s): @@ -354,6 +397,12 @@ def validate_color(s): raise ValueError(f'{s!r} does not look like a color arg') +def _validate_color_or_None(s): + if s is None or cbook._str_equal(s, "None"): + return None + return validate_color(s) + + validate_colorlist = _listify_validator( validate_color, allow_stringlist=True, doc='return a list of colorspecs') @@ -463,19 +512,6 @@ def validate_ps_distiller(s): return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s) -def _validate_papersize(s): - # Re-inline this validator when the 'auto' deprecation expires. - s = ValidateInStrings("ps.papersize", - ["figure", "auto", "letter", "legal", "ledger", - *[f"{ab}{i}" for ab in "ab" for i in range(11)]], - ignorecase=True)(s) - if s == "auto": - _api.warn_deprecated("3.8", name="ps.papersize='auto'", - addendum="Pass an explicit paper type, figure, or omit " - "the *ps.papersize* rcParam entirely.") - return s - - # A validator dedicated to the named line styles, based on the items in # ls_mapper, and a list of possible strings read from Line2D.set_linestyle _validate_named_linestyle = ValidateInStrings( @@ -521,6 +557,13 @@ def _is_iterable_not_string_like(x): raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") +def _validate_linestyle_or_None(s): + if s is None or cbook._str_equal(s, "None"): + return None + + return _validate_linestyle(s) + + validate_fillstyle = ValidateInStrings( 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none']) @@ -695,7 +738,7 @@ def cycler(*args, **kwargs): Call signatures:: cycler(cycler) - cycler(label=values[, label2=values2[, ...]]) + cycler(label=values, label2=values2, ...) cycler(label, values) Form 1 copies a given `~cycler.Cycler` object. @@ -772,11 +815,62 @@ def cycler(*args, **kwargs): return reduce(operator.add, (ccycler(k, v) for k, v in validated)) -class _DunderChecker(ast.NodeVisitor): - def visit_Attribute(self, node): - if node.attr.startswith("__") and node.attr.endswith("__"): - raise ValueError("cycler strings with dunders are forbidden") - self.generic_visit(node) +def _parse_cycler_string(s): + """ + Parse a string representation of a cycler into a Cycler object safely, + without using eval(). + + Accepts expressions like:: + + cycler('color', ['r', 'g', 'b']) + cycler('color', 'rgb') + cycler('linewidth', [1, 2, 3]) + cycler(c='rgb', lw=[1, 2, 3]) + cycler('c', 'rgb') * cycler('linestyle', ['-', '--']) + """ + try: + tree = ast.parse(s, mode='eval') + except SyntaxError as e: + raise ValueError(f"Could not parse {s!r}: {e}") from e + return _eval_cycler_expr(tree.body) + + +def _eval_cycler_expr(node): + """Recursively evaluate an AST node to build a Cycler object.""" + if isinstance(node, ast.BinOp): + left = _eval_cycler_expr(node.left) + right = _eval_cycler_expr(node.right) + if isinstance(node.op, ast.Add): + return left + right + if isinstance(node.op, ast.Mult): + return left * right + raise ValueError(f"Unsupported operator: {type(node.op).__name__}") + if isinstance(node, ast.Call): + if not (isinstance(node.func, ast.Name) + and node.func.id in ('cycler', 'concat')): + raise ValueError( + "only the 'cycler()' and 'concat()' functions are allowed") + func = cycler if node.func.id == 'cycler' else cconcat + args = [_eval_cycler_expr(a) for a in node.args] + kwargs = {kw.arg: _eval_cycler_expr(kw.value) for kw in node.keywords} + return func(*args, **kwargs) + if isinstance(node, ast.Subscript): + sl = node.slice + if not isinstance(sl, ast.Slice): + raise ValueError("only slicing is supported, not indexing") + s = slice( + ast.literal_eval(sl.lower) if sl.lower else None, + ast.literal_eval(sl.upper) if sl.upper else None, + ast.literal_eval(sl.step) if sl.step else None, + ) + value = _eval_cycler_expr(node.value) + return value[s] + # Allow literal values (int, strings, lists, tuples) as arguments + # to cycler() and concat(). + try: + return ast.literal_eval(node) + except (ValueError, TypeError): + raise ValueError( + f"Unsupported expression in cycler string: {ast.dump(node)}") # A validator dedicated to the named legend loc @@ -827,25 +921,11 @@ def _validate_legend_loc(loc): def validate_cycler(s): """Return a Cycler object from a string repr or the object itself.""" if isinstance(s, str): - # TODO: We might want to rethink this... - # While I think I have it quite locked down, it is execution of - # arbitrary code without sanitation. - # Combine this with the possibility that rcparams might come from the - # internet (future plans), this could be downright dangerous. - # I locked it down by only having the 'cycler()' function available. - # UPDATE: Partly plugging a security hole. - # I really should have read this: - # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html - # We should replace this eval with a combo of PyParsing and - # ast.literal_eval() try: - _DunderChecker().visit(ast.parse(s)) - s = eval(s, {'cycler': cycler, '__builtins__': {}}) - except BaseException as e: + s = _parse_cycler_string(s) + except Exception as e: raise ValueError(f"{s!r} is not a valid cycler construction: {e}" ) from e - # Should make sure what comes from the above eval() - # is a Cycler object. if isinstance(s, Cycler): cycler_inst = s else: @@ -963,7 +1043,7 @@ def _convert_validator_spec(key, conv): "patch.antialiased": validate_bool, # antialiased (no jaggies) ## hatch props - "hatch.color": validate_color, + "hatch.color": _validate_color_or_edge, "hatch.linewidth": validate_float, ## Histogram properties @@ -1015,6 +1095,7 @@ def _convert_validator_spec(key, conv): "boxplot.meanprops.linewidth": validate_float, ## font props + "font.enable_last_resort": validate_bool, "font.family": validate_stringlist, # used by text object "font.style": validate_string, "font.variant": validate_string, @@ -1030,13 +1111,15 @@ def _convert_validator_spec(key, conv): # text props "text.color": validate_color, "text.usetex": validate_bool, + "text.latex.engine": ["latex", "latex+dvipng"], "text.latex.preamble": validate_string, "text.hinting": ["default", "no_autohint", "force_autohint", "no_hinting", "auto", "native", "either", "none"], "text.hinting_factor": validate_int, - "text.kerning_factor": validate_int, + "text.kerning_factor": validate_int_or_None, "text.antialiased": validate_bool, "text.parse_math": validate_bool, + "text.language": validate_string_or_None, "mathtext.cal": validate_font_properties, "mathtext.rm": validate_font_properties, @@ -1048,12 +1131,12 @@ def _convert_validator_spec(key, conv): "mathtext.fontset": ["dejavusans", "dejavuserif", "cm", "stix", "stixsans", "custom"], "mathtext.default": ["rm", "cal", "bfit", "it", "tt", "sf", "bf", "default", - "bb", "frak", "scr", "regular"], + "bb", "frak", "scr", "regular", "normal"], "mathtext.fallback": _validate_mathtext_fallback, "image.aspect": validate_aspect, # equal, auto, a number "image.interpolation": validate_string, - "image.interpolation_stage": ["data", "rgba"], + "image.interpolation_stage": ["auto", "data", "rgba"], "image.cmap": _validate_cmap, # gray, jet, etc. "image.lut": validate_int, # lookup table "image.origin": ["upper", "lower"], @@ -1070,6 +1153,8 @@ def _convert_validator_spec(key, conv): # errorbar props "errorbar.capsize": validate_float, + "errorbar.capthick": validate_float_or_None, + "errorbar.elinewidth": validate_float_or_None, # axis props # alignment of x/y axis title @@ -1104,7 +1189,7 @@ def _convert_validator_spec(key, conv): "axes.labelcolor": validate_color, # color of axis label # use scientific notation if log10 of the axis range is smaller than the # first or larger than the second - "axes.formatter.limits": _listify_validator(validate_int, n=2), + "axes.formatter.limits": validate_intlist, # use current locale to format ticks "axes.formatter.use_locale": validate_bool, "axes.formatter.use_mathtext": validate_bool, @@ -1114,7 +1199,7 @@ def _convert_validator_spec(key, conv): "axes.formatter.offset_threshold": validate_int, "axes.unicode_minus": validate_bool, # This entry can be either a cycler object or a string repr of a - # cycler-object, which gets eval()'ed to create the object. + # cycler-object, which is parsed safely via AST. "axes.prop_cycle": validate_cycler, # If "data", axes limits are set close to the data. # If "round_numbers" axes limits are set to the nearest round numbers. @@ -1132,6 +1217,14 @@ def _convert_validator_spec(key, conv): "axes3d.yaxis.panecolor": validate_color, # 3d background pane "axes3d.zaxis.panecolor": validate_color, # 3d background pane + "axes3d.depthshade": validate_bool, # depth shade for 3D scatter plots + "axes3d.depthshade_minalpha": validate_float, # min alpha value for depth shading + + "axes3d.mouserotationstyle": ["azel", "trackball", "sphere", "arcball"], + "axes3d.trackballsize": validate_float, + "axes3d.trackballborder": validate_float, + "axes3d.snap_rotation": validate_float, + # scatter props "scatter.marker": _validate_marker, "scatter.edgecolors": validate_string, @@ -1169,6 +1262,8 @@ def _convert_validator_spec(key, conv): "legend.frameon": validate_bool, # alpha value of the legend frame "legend.framealpha": validate_float_or_None, + # linewidth of legend frame + "legend.linewidth": validate_float_or_None, ## the following dimensions are in fraction of the font size "legend.borderpad": validate_float, # units are fontsize @@ -1240,6 +1335,16 @@ def _convert_validator_spec(key, conv): "grid.linewidth": validate_float, # in points "grid.alpha": validate_float, + "grid.major.color": _validate_color_or_None, # grid color + "grid.major.linestyle": _validate_linestyle_or_None, # solid + "grid.major.linewidth": validate_float_or_None, # in points + "grid.major.alpha": validate_float_or_None, + + "grid.minor.color": _validate_color_or_None, # grid color + "grid.minor.linestyle": _validate_linestyle_or_None, # solid + "grid.minor.linewidth": validate_float_or_None, # in points + "grid.minor.alpha": validate_float_or_None, + ## figure props # figure title "figure.titlesize": validate_fontsize, @@ -1291,7 +1396,9 @@ def _convert_validator_spec(key, conv): "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg # Set the papersize/type - "ps.papersize": _validate_papersize, + "ps.papersize": _ignorecase( + ["figure", "letter", "legal", "ledger", + *[f"{ab}{i}" for ab in "ab" for i in range(11)]]), "ps.useafm": validate_bool, # use ghostscript or xpdf to distill ps output "ps.usedistiller": validate_ps_distiller, @@ -1311,6 +1418,7 @@ def _convert_validator_spec(key, conv): "svg.image_inline": validate_bool, "svg.fonttype": ["none", "path"], # save text as text ("none") or "paths" "svg.hashsalt": validate_string_or_None, + "svg.id": validate_string_or_None, # set this when you want to generate hardcopy docstring "docstring.hardcopy": validate_bool, @@ -1375,3 +1483,1949 @@ def _convert_validator_spec(key, conv): } _validators = {k: _convert_validator_spec(k, conv) for k, conv in _validators.items()} + + +@dataclass +class _Param: + name: str + default: Any + validator: Callable[[Any], Any] + description: str = None + + +@dataclass +class _Section: + title: str + description: str = None + + +@dataclass +class _Subsection: + title: str + description: str = None + + +# Definition of all rcParams. This is currently only used to generate the documentation. +# +# Actual runtime values do still come from the historic sources: +# - available parameters and defaults: lib/matplotlib/mpl-data/matplotlibrc +# - validators: _validators, see above +# +# The structure and format of this definition is not fixed and may change in the future. +# It's a work-in-progress state towards a consistent and more structured definition of +# rcParams that can be used both for documentation and runtime. The goal is to +# eventually eliminate the old sources of defaults and validators and have this be the +# single source of truth. +# +# In the transition phase, consistency is ensured via tests. +_DEFINITION = [ + _Section("Backends"), + _Param( + "webagg.port", + default=8988, + validator=validate_int, + description="The port to use for the web server in the WebAgg backend." + ), + _Param( + "webagg.address", + default="127.0.0.1", + validator=validate_string, + description="The address on which the WebAgg web server should be reachable." + ), + _Param( + "webagg.port_retries", + default=50, + validator=validate_int, + description="If webagg.port is unavailable, a number of other random ports " + "will be tried until one that is available is found." + ), + _Param( + "webagg.open_in_browser", + default=True, + validator=validate_bool, + description="When True, open the web browser to the plot that is shown" + ), + _Param( + "backend_fallback", + default=True, + validator=validate_bool, + description="If you are running pyplot inside a GUI and your backend choice " + "conflicts, we will automatically try to find a compatible one for " + "you if backend_fallback is True" + ), + _Param( + "interactive", + default=False, + validator=validate_bool + ), + _Param( + "figure.hooks", + default=[], + validator=validate_stringlist, + description="list of dotted.module.name:dotted.callable.name" + ), + _Param( + "toolbar", + default="toolbar2", + validator=_validate_toolbar, + description="{None, toolbar2, toolmanager}" + ), + _Param( + "timezone", + default="UTC", + validator=validate_string, + description="a pytz timezone string, e.g., US/Central or Europe/Paris" + ), + _Section( + "Lines", + description="Default properties for line objects, such as those returned by " + "plot()." + ), + _Param( + "lines.linewidth", + default=1.5, + validator=validate_float, + description="line width in points" + ), + _Param( + "lines.linestyle", + default="-", + validator=_validate_linestyle, + description="solid line" + ), + _Param( + "lines.color", + default="C0", + validator=validate_color, + description="has no affect on plot(); see axes.prop_cycle" + ), + _Param( + "lines.marker", + default="None", + validator=_validate_marker, + description="the default marker" + ), + _Param( + "lines.markerfacecolor", + default="auto", + validator=validate_color_or_auto, + description="the default marker face color" + ), + _Param( + "lines.markeredgecolor", + default="auto", + validator=validate_color_or_auto, + description="the default marker edge color" + ), + _Param( + "lines.markeredgewidth", + default=1.0, + validator=validate_float, + description="the line width around the marker symbol" + ), + _Param( + "lines.markersize", + default=6.0, + validator=validate_float, + description="marker size, in points" + ), + _Param( + "lines.dash_joinstyle", + default="round", + validator=JoinStyle, + description="{miter, round, bevel}" + ), + _Param( + "lines.dash_capstyle", + default="butt", + validator=CapStyle, + description="{butt, round, projecting}" + ), + _Param( + "lines.solid_joinstyle", + default="round", + validator=JoinStyle, + description="{miter, round, bevel}" + ), + _Param( + "lines.solid_capstyle", + default="projecting", + validator=CapStyle, + description="{butt, round, projecting}" + ), + _Param( + "lines.antialiased", + default=True, + validator=validate_bool, + description="render lines in antialiased (no jaggies)" + ), + _Param( + "lines.dashed_pattern", + default=[3.7, 1.6], + validator=validate_floatlist, + description="The dash pattern for linestyle 'dashed'" + ), + _Param( + "lines.dashdot_pattern", + default=[6.4, 1.6, 1.0, 1.6], + validator=validate_floatlist, + description="The dash pattern for linestyle 'dashdot'" + ), + _Param( + "lines.dotted_pattern", + default=[1.0, 1.65], + validator=validate_floatlist, + description="The dash pattern for linestyle 'dotted'" + ), + _Param( + "lines.scale_dashes", + default=True, + validator=validate_bool + ), + _Param( + "markers.fillstyle", + default="full", + validator=validate_fillstyle, + description="{full, left, right, bottom, top, none}" + ), + _Param( + "pcolor.shading", + default="auto", + validator=["auto", "flat", "nearest", "gouraud"] + ), + _Param( + "pcolormesh.snap", + default=True, + validator=validate_bool, + description="Whether to snap the mesh to pixel boundaries. This is provided " + "solely to allow old test images to remain unchanged. Set to False " + "to obtain the previous behavior." + ), + _Section("Patches"), + _Param( + "patch.linewidth", + default=1.0, + validator=validate_float, + description="edge width in points." + ), + _Param( + "patch.facecolor", + default="C0", + validator=validate_color + ), + _Param( + "patch.edgecolor", + default="black", + validator=validate_color, + description='By default, Patches and Collections do not draw edges. This value ' + 'is only used if facecolor is "none" (an Artist without facecolor ' + 'and edgecolor would be invisible) or if patch.force_edgecolor ' + 'is True.' + ), + _Param( + "patch.force_edgecolor", + default=False, + validator=validate_bool, + description="By default, Patches and Collections do not draw edges. Set this " + "to True to draw edges with patch.edgedcolor as the default " + "edgecolor. This is mainly relevant for styles." + ), + _Param( + "patch.antialiased", + default=True, + validator=validate_bool, + description="render patches in antialiased (no jaggies)" + ), + _Section("Hatches"), + _Param("hatch.color", "edge", _validate_color_or_edge), + _Param("hatch.linewidth", 1.0, validate_float), + _Section("Boxplot"), + _Param("boxplot.notch", False, validate_bool), + _Param("boxplot.vertical", True, validate_bool), + _Param("boxplot.whiskers", 1.5, validate_whiskers), + _Param("boxplot.bootstrap", None, validate_int_or_None), + _Param("boxplot.patchartist", False, validate_bool), + _Param("boxplot.showmeans", False, validate_bool), + _Param("boxplot.showcaps", True, validate_bool), + _Param("boxplot.showbox", True, validate_bool), + _Param("boxplot.showfliers", True, validate_bool), + _Param("boxplot.meanline", False, validate_bool), + _Param("boxplot.flierprops.color", "black", validate_color), + _Param("boxplot.flierprops.marker", "o", _validate_marker), + _Param("boxplot.flierprops.markerfacecolor", "none", validate_color_or_auto), + _Param("boxplot.flierprops.markeredgecolor", "black", validate_color), + _Param("boxplot.flierprops.markeredgewidth", 1.0, validate_float), + _Param("boxplot.flierprops.markersize", 6.0, validate_float), + _Param("boxplot.flierprops.linestyle", "none", _validate_linestyle), + _Param("boxplot.flierprops.linewidth", 1.0, validate_float), + _Param("boxplot.boxprops.color", "black", validate_color), + _Param("boxplot.boxprops.linewidth", 1.0, validate_float), + _Param("boxplot.boxprops.linestyle", "-", _validate_linestyle), + _Param("boxplot.whiskerprops.color", "black", validate_color), + _Param("boxplot.whiskerprops.linewidth", 1.0, validate_float), + _Param("boxplot.whiskerprops.linestyle", "-", _validate_linestyle), + _Param("boxplot.capprops.color", "black", validate_color), + _Param("boxplot.capprops.linewidth", 1.0, validate_float), + _Param("boxplot.capprops.linestyle", "-", _validate_linestyle), + _Param("boxplot.medianprops.color", "C1", validate_color), + _Param("boxplot.medianprops.linewidth", 1.0, validate_float), + _Param("boxplot.medianprops.linestyle", "-", _validate_linestyle), + _Param("boxplot.meanprops.color", "C2", validate_color), + _Param("boxplot.meanprops.marker", "^", _validate_marker), + _Param("boxplot.meanprops.markerfacecolor", "C2", validate_color), + _Param("boxplot.meanprops.markeredgecolor", "C2", validate_color), + _Param("boxplot.meanprops.markersize", 6.0, validate_float), + _Param("boxplot.meanprops.linestyle", "--", _validate_linestyle), + _Param("boxplot.meanprops.linewidth", 1.0, validate_float), + _Section( + "Font", + description="The font properties used by `.Text` " + "See https://matplotlib.org/stable/api/font_manager_api.html for " + "more information on font properties. The 6 font properties used " + "for font matching are given below with their default values." + ), + _Param("font.family", ["sans-serif"], validate_stringlist), + _Param("font.style", "normal", validate_string), + _Param("font.variant", "normal", validate_string), + _Param("font.weight", "normal", validate_fontweight), + _Param("font.stretch", "normal", validate_fontstretch), + _Param("font.size", 10.0, validate_float), + _Param( + "font.serif", + default=[ + "DejaVu Serif", "Bitstream Vera Serif", "Computer Modern Roman", + "New Century Schoolbook", "Century Schoolbook L", "Utopia", "ITC Bookman", + "Bookman", "Nimbus Roman No9 L", "Times New Roman", "Times", "Palatino", + "Charter", "serif", + ], + validator=validate_stringlist + ), + _Param( + "font.sans-serif", + default=[ + "DejaVu Sans", "Bitstream Vera Sans", "Computer Modern Sans Serif", + "Lucida Grande", "Verdana", "Geneva", "Lucid", "Arial", "Helvetica", + "Avant Garde", "sans-serif", + ], + validator=validate_stringlist + ), + _Param( + "font.cursive", + default=[ + "Apple Chancery", "Textile", "Zapf Chancery", "Sand", "Script MT", "Felipa", + "Comic Neue", "Comic Sans MS", "cursive", + ], + validator=validate_stringlist + ), + _Param( + "font.fantasy", + default=["Chicago", "Charcoal", "Impact", "Western", "xkcd script", "fantasy"], + validator=validate_stringlist + ), + _Param( + "font.monospace", + default=[ + "DejaVu Sans Mono", "Bitstream Vera Sans Mono", + "Computer Modern Typewriter", "Andale Mono", "Nimbus Mono L", "Courier New", + "Courier", "Fixed", "Terminal", "monospace", + ], + validator=validate_stringlist + ), + _Param( + "font.enable_last_resort", + default=True, + validator=validate_bool, + description="If True, then Unicode Consortium's Last Resort font will be " + "appended to all font selections. This ensures that there will " + "always be a glyph displayed." + ), + _Section("Text properties"), + _Param( + "text.color", + default="black", + validator=validate_color + ), + _Param( + "text.language", + default=None, + validator=validate_string_or_None, + description="The language of the text in a format accepted by libraqm, namely " + "`a BCP47 language code " + "`_. If " + "None, then no particular language will be implied, and default " + "font settings will be used." + ), + _Param( + "text.hinting", + default="default", + validator=[ + "default", "no_autohint", "force_autohint", "no_hinting", "auto", "native", + "either", "none", + ], + description="FreeType hinting flag (\"foo\" corresponds to FT_LOAD_FOO); may " + "be one of the following (Proprietary Matplotlib-specific synonyms " + "are given in parentheses, but their use is discouraged): " + "- default: Use the font's native hinter if possible, else " + " FreeType's auto-hinter. (\"either\" is a synonym)." + "- no_autohint: Use the font's native hinter if possible, else " + " don't hint. (\"native\" is a synonym.)" + "- force_autohint: Use FreeType's auto-hinter. (\"auto\" is a " + " synonym.)" + "- no_hinting: Disable hinting. (\"none\" is a synonym.)" + ), + _Param( + "text.hinting_factor", + default=1, + validator=validate_int, + description="Specifies the amount of softness for hinting in the horizontal " + "direction. A value of 1 will hint to full pixels. A value of 2 " + "will hint to half pixels etc." + ), + _Param( + "text.kerning_factor", + default=None, + validator=validate_int_or_None, + description="[DEPRECATED] Specifies the scaling factor for kerning values. " + "This is provided solely to allow old test images to remain " + "unchanged. Set to 6 to obtain previous behavior. Values other " + "than 0 or 6 have no defined meaning." + ), + _Param( + "text.antialiased", + default=True, + validator=validate_bool, + description="If True (default), the text will be antialiased. This only " + "affects raster outputs." + ), + _Param( + "text.parse_math", + default=True, + validator=validate_bool, + description="Use mathtext if there is an even number of unescaped dollar signs." + + ), + _Section("Mathtext and LaTeX"), + _Param( + "text.usetex", + default=False, + validator=validate_bool, + description="use latex for all text handling. The following fonts are " + "supported through the usual rc parameter settings: " + "new century schoolbook, bookman, times, palatino, zapf chancery, " + "charter, serif, sans-serif, helvetica, avant garde, courier, " + "monospace, computer modern roman, computer modern sans serif, " + "computer modern typewriter" + ), + _Param( + "text.latex.engine", + default="latex", + validator=["latex", "latex+dvipng"], + description=( + "The TeX engine/format to use. The following values are supported:\n" + "- 'latex': The classic TeX engine (the current default). All backends " + "render TeX's output by parsing the DVI output into glyphs and boxes and " + "emitting those one by one.\n" + "- 'latex+dvipng': The same as 'latex', with the exception that Agg-based " + "backends rely on dvipng to rasterize TeX's output. This value was the " + "default up to Matplotlib 3.10." + ) + ), + _Param( + "text.latex.preamble", + default="", + validator=validate_string, + description='IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES AND IS ' + 'THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP IF THIS FEATURE ' + 'DOES NOT DO WHAT YOU EXPECT IT TO. text.latex.preamble is a ' + 'single line of LaTeX code that will be passed on to the LaTeX ' + 'system. It may contain any code that is valid for the LaTeX ' + '"preamble", i.e. between the "\\documentclass" and ' + '"\\begin{document}" statements. Note that it has to be put on a ' + 'single line, which may become quite long. The following packages ' + 'are always loaded with usetex, so beware of package collisions: ' + ' color, fix-cm, geometry, graphicx, textcomp. PostScript ' + '(PSNFSS) font packages may also be loaded, depending on your font ' + 'settings.' + ), + _Param( + "mathtext.fontset", + default="dejavusans", + validator=["dejavusans", "dejavuserif", "cm", "stix", "stixsans", "custom"], + description="Should be 'dejavusans' (default), 'dejavuserif', " + "'cm' (Computer Modern), 'stix', 'stixsans' or 'custom'" + ), + _Param("mathtext.bf", "sans:bold", validate_font_properties), + _Param("mathtext.bfit", "sans:italic:bold", validate_font_properties), + _Param("mathtext.cal", "cursive", validate_font_properties), + _Param("mathtext.it", "sans:italic", validate_font_properties), + _Param("mathtext.rm", "sans", validate_font_properties), + _Param("mathtext.sf", "sans", validate_font_properties), + _Param("mathtext.tt", "monospace", validate_font_properties), + _Param( + "mathtext.fallback", + default="cm", + validator=_validate_mathtext_fallback, + description="Select fallback font from ['cm' (Computer Modern), 'stix', " + "'stixsans'] when a symbol cannot be found in one of the custom " + "math fonts. Select 'None' to not perform fallback and replace the " + "missing character by a dummy symbol." + ), + _Param("mathtext.default", "normal", + ["rm", "cal", "bfit", "it", "tt", "sf", "bf", "default", "bb", "frak", "scr", + "regular", "normal"], + description='The default font to use for math. Can be any of the LaTeX font ' + 'names, including the special name "regular" for the same font ' + 'used in regular text.', + ), + _Section("Axes"), + _Param( + "axes.facecolor", + default="white", + validator=validate_color, + description="axes background color" + ), + _Param( + "axes.edgecolor", + default="black", + validator=validate_color, + description="axes edge color" + ), + _Param( + "axes.linewidth", + default=0.8, + validator=validate_float, + description="edge line width" + ), + _Param( + "axes.grid", + default=False, + validator=validate_bool, + description="display grid or not" + ), + _Param( + "axes.grid.axis", + default="both", + validator=["x", "y", "both"], + description="which axis the grid should apply to" + ), + _Param( + "axes.grid.which", + default="major", + validator=["minor", "both", "major"], + description="grid lines at {major, minor, both} ticks" + ), + _Param( + "axes.titlelocation", + default="center", + validator=["left", "center", "right"], + description="alignment of the title: {left, right, center}" + ), + _Param( + "axes.titlesize", + default="large", + validator=validate_fontsize, + description="font size of the axes title" + ), + _Param( + "axes.titleweight", + default="normal", + validator=validate_fontweight, + description="font weight of title" + ), + _Param( + "axes.titlecolor", + default="auto", + validator=validate_color_or_auto, + description="color of the axes title, auto falls back to text.color as default " + "value" + ), + _Param( + "axes.titley", + default=None, + validator=validate_float_or_None, + description="position title (axes relative units). None implies auto" + ), + _Param( + "axes.titlepad", + default=6.0, + validator=validate_float, + description="pad between axes and title in points" + ), + _Param( + "axes.labelsize", + default="medium", + validator=validate_fontsize, + description="font size of the x and y labels" + ), + _Param( + "axes.labelpad", + default=4.0, + validator=validate_float, + description="space between label and axis" + ), + _Param( + "axes.labelweight", + default="normal", + validator=validate_fontweight, + description="weight of the x and y labels" + ), + _Param( + "axes.labelcolor", + default="black", + validator=validate_color + ), + _Param( + "axes.axisbelow", + default="line", + validator=validate_axisbelow, + description="draw axis gridlines and ticks: " + "- below patches (True) " + "- above patches but below lines ('line') " + "- above all (False)" + ), + _Param( + "axes.formatter.limits", + default=[-5, 6], + validator=validate_intlist, + description="use scientific notation if log10 of the axis range is smaller " + "than the first or larger than the second" + ), + _Param( + "axes.formatter.use_locale", + default=False, + validator=validate_bool, + description="When True, format tick labels according to the user's locale. " + "For example, use ',' as a decimal separator in the fr_FR locale." + ), + _Param( + "axes.formatter.use_mathtext", + default=False, + validator=validate_bool, + description="When True, use mathtext for scientific notation." + ), + _Param( + "axes.formatter.min_exponent", + default=0, + validator=validate_int, + description="minimum exponent to format in scientific notation" + ), + _Param( + "axes.formatter.useoffset", + default=True, + validator=validate_bool, + description="If True, the tick label formatter will default to labeling ticks " + "relative to an offset when the data range is small compared to " + "the minimum absolute value of the data." + ), + _Param( + "axes.formatter.offset_threshold", + default=4, + validator=validate_int, + description="When useoffset is True, the offset will be used when it can " + "remove at least this number of significant digits from tick " + "labels." + ), + _Param( + "axes.spines.left", + default=True, + validator=validate_bool, + description="display axis spines" + ), + _Param("axes.spines.bottom", True, validate_bool), + _Param("axes.spines.top", True, validate_bool), + _Param( + "axes.spines.right", + default=True, + validator=validate_bool + ), + _Param( + "axes.unicode_minus", + default=True, + validator=validate_bool, + description="use Unicode for the minus symbol rather than hyphen. See " + "https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes" + + ), + _Param("axes.prop_cycle", + default=cycler( + "color", + [(0.12156862745098039, 0.4666666666666667, 0.7058823529411765), + (1.0, 0.4980392156862745, 0.054901960784313725), + (0.17254901960784313, 0.6274509803921569, 0.17254901960784313), + (0.8392156862745098, 0.15294117647058825, 0.1568627450980392), + (0.5803921568627451, 0.403921568627451, 0.7411764705882353), + (0.5490196078431373, 0.33725490196078434, 0.29411764705882354), + (0.8901960784313725, 0.4666666666666667, 0.7607843137254902), + (0.4980392156862745, 0.4980392156862745, 0.4980392156862745), + (0.7372549019607844, 0.7411764705882353, 0.13333333333333333), + (0.09019607843137255, 0.7450980392156863, 0.8117647058823529), + ], + ), + validator=validate_cycler + ), + _Param( + "axes.xmargin", + default=0.05, + validator=_validate_greaterthan_minushalf, + description="x margin. See `~.axes.Axes.margins`" + ), + _Param( + "axes.ymargin", + default=0.05, + validator=_validate_greaterthan_minushalf, + description="y margin. See `~.axes.Axes.margins`" + ), + _Param( + "axes.zmargin", + default=0.05, + validator=_validate_greaterthan_minushalf, + description="z margin. See `~.axes.Axes.margins`" + ), + _Param( + "axes.autolimit_mode", + default="data", + validator=["data", "round_numbers"], + description='If "data", use axes.xmargin and axes.ymargin as is. If ' + '"round_numbers", after application of margins, axis limits are ' + 'further expanded to the nearest "round" number.', + ), + _Subsection("Polar Axes"), + _Param( + "polaraxes.grid", + default=True, + validator=validate_bool, + description="display grid on polar axes" + ), + _Subsection("3D Axes"), + _Param( + "axes3d.grid", + default=True, + validator=validate_bool, description="display grid on 3D axes" + ), + _Param( + "axes3d.automargin", + default=False, + validator=validate_bool, + description="automatically add margin when manually setting 3D axis limits" + ), + _Param( + "axes3d.xaxis.panecolor", + default=(0.95, 0.95, 0.95, 0.5), + validator=validate_color, + description="background pane on 3D axes" + ), + _Param( + "axes3d.yaxis.panecolor", + default=(0.9, 0.9, 0.9, 0.5), + validator=validate_color, + description="background pane on 3D axes" + ), + _Param( + "axes3d.zaxis.panecolor", + default=(0.925, 0.925, 0.925, 0.5), + validator=validate_color, + description="background pane on 3D axes" + ), + _Param( + "axes3d.depthshade", + default=True, + validator=validate_bool, + description="depth shade for 3D scatter plots" + ), + _Param( + "axes3d.depthshade_minalpha", + default=0.3, + validator=validate_float, + description="minimum alpha value for depth shading" + ), + _Param( + "axes3d.mouserotationstyle", + default="arcball", + validator=["azel", "trackball", "sphere", "arcball"], + description="{azel, trackball, sphere, arcball} See also " + "https://matplotlib.org/stable/api/toolkits/mplot3d/view_angles.html#rotation-with-mouse"), # noqa + _Param( + "axes3d.trackballsize", + default=0.667, + validator=validate_float, + description="trackball diameter, in units of the Axes bbox" + ), + _Param( + "axes3d.trackballborder", + default=0.2, + validator=validate_float, + description="trackball border width, in units of the Axes bbox (only for " + "'sphere' and 'arcball' style)" + ), + _Section("Axis"), + _Param( + "axes3d.snap_rotation", + default=5.0, + validator=validate_float, + description="Snap angle (in degrees) for 3D rotation when holding Control." + ), + _Param( + "xaxis.labellocation", + default="center", + validator=["left", "center", "right"], + description="alignment of the xaxis label: {left, right, center}" + ), + _Param( + "yaxis.labellocation", + default="center", + validator=["bottom", "center", "top"], + description="alignment of the yaxis label: {bottom, top, center}" + ), + _Section( + "Dates", + description="Default properties for date tick labels. These are used by the " + "`.AutoDateFormatter` when the appropriate time unit is detected." + "See " + "https://matplotlib.org/stable/api/dates_api.html#date-formatters " + "for more information." + ), + _Param("date.autoformatter.year", "%Y", validate_string), + _Param("date.autoformatter.month", "%Y-%m", validate_string), + _Param("date.autoformatter.day", "%Y-%m-%d", validate_string), + _Param("date.autoformatter.hour", "%m-%d %H", validate_string), + _Param("date.autoformatter.minute", "%d %H:%M", validate_string), + _Param("date.autoformatter.second", "%H:%M:%S", validate_string), + _Param("date.autoformatter.microsecond", "%M:%S.%f", validate_string), + _Param( + "date.epoch", + default="1970-01-01T00:00:00", + validator=_validate_date, + description="The reference date for Matplotlib's internal date representation. " + "See https://matplotlib.org/stable/gallery/ticks/date_precision_and_epochs.html"), #noqa + _Param( + "date.converter", + default="auto", + validator=["auto", "concise"], + description="'auto', 'concise'" + ), + _Param( + "date.interval_multiples", + default=True, + validator=validate_bool, + description="For auto converter whether to use interval_multiples" + ), + _Section("Ticks"), + _Param( + "xtick.top", + default=False, + validator=validate_bool, + description="draw ticks on the top side" + ), + _Param( + "xtick.bottom", + default=True, + validator=validate_bool, + description="draw ticks on the bottom side" + ), + _Param( + "xtick.labeltop", + default=False, + validator=validate_bool, + description="draw label on the top" + ), + _Param( + "xtick.labelbottom", + default=True, + validator=validate_bool, + description="draw label on the bottom" + ), + _Param( + "xtick.major.size", + default=3.5, + validator=validate_float, + description="major tick size in points" + ), + _Param( + "xtick.minor.size", + default=2.0, + validator=validate_float, + description="minor tick size in points" + ), + _Param( + "xtick.major.width", + default=0.8, + validator=validate_float, + description="major tick width in points" + ), + _Param( + "xtick.minor.width", + default=0.6, + validator=validate_float, + description="minor tick width in points" + ), + _Param( + "xtick.major.pad", + default=3.5, + validator=validate_float, + description="distance to major tick label in points" + ), + _Param( + "xtick.minor.pad", + default=3.4, + validator=validate_float, + description="distance to the minor tick label in points" + ), + _Param( + "xtick.color", + default="black", + validator=validate_color, + description="color of the ticks" + ), + _Param( + "xtick.labelcolor", + default="inherit", + validator=validate_color_or_inherit, + description="color of the tick labels or inherit from xtick.color" + ), + _Param( + "xtick.labelsize", + default="medium", + validator=validate_fontsize, + description="font size of the tick labels" + ), + _Param( + "xtick.direction", + default="out", + validator=["out", "in", "inout"], + description="direction: {in, out, inout}" + ), + _Param( + "xtick.minor.visible", + default=False, + validator=validate_bool, + description="visibility of minor ticks on x-axis" + ), + _Param( + "xtick.major.top", + default=True, + validator=validate_bool, + description="draw x axis top major ticks" + ), + _Param( + "xtick.major.bottom", + default=True, + validator=validate_bool, + description="draw x axis bottom major ticks" + ), + _Param( + "xtick.minor.top", + default=True, + validator=validate_bool, + description="draw x axis top minor ticks" + ), + _Param( + "xtick.minor.bottom", + default=True, + validator=validate_bool, + description="draw x axis bottom minor ticks" + ), + _Param( + "xtick.minor.ndivs", + default="auto", + validator=_validate_minor_tick_ndivs, + description="number of minor ticks between the major ticks on x-axis" + ), + _Param( + "xtick.alignment", + default="center", + validator=["center", "right", "left"], + description="alignment of xticks" + ), + _Param( + "ytick.left", + default=True, + validator=validate_bool, + description="draw ticks on the left side" + ), + _Param( + "ytick.right", + default=False, + validator=validate_bool, + description="draw ticks on the right side" + ), + _Param( + "ytick.labelleft", + default=True, + validator=validate_bool, + description="draw tick labels on the left side" + ), + _Param( + "ytick.labelright", + default=False, + validator=validate_bool, + description="draw tick labels on the right side" + ), + _Param( + "ytick.major.size", + default=3.5, + validator=validate_float, + description="major tick size in points" + ), + _Param( + "ytick.minor.size", + default=2.0, + validator=validate_float, + description="minor tick size in points" + ), + _Param( + "ytick.major.width", + default=0.8, + validator=validate_float, + description="major tick width in points" + ), + _Param( + "ytick.minor.width", + default=0.6, + validator=validate_float, + description="minor tick width in points" + ), + _Param( + "ytick.major.pad", + default=3.5, + validator=validate_float, + description="distance to major tick label in points" + ), + _Param( + "ytick.minor.pad", + default=3.4, + validator=validate_float, + description="distance to the minor tick label in points" + ), + _Param( + "ytick.color", + default="black", + validator=validate_color, + description="color of the ticks" + ), + _Param( + "ytick.labelcolor", + default="inherit", + validator=validate_color_or_inherit, + description="color of the tick labels or inherit from ytick.color" + ), + _Param( + "ytick.labelsize", + default="medium", + validator=validate_fontsize, + description="font size of the tick labels" + ), + _Param( + "ytick.direction", + default="out", + validator=["out", "in", "inout"], + description="direction: {in, out, inout}" + ), + _Param( + "ytick.minor.visible", + default=False, + validator=validate_bool, + description="visibility of minor ticks on y-axis" + ), + _Param( + "ytick.major.left", + default=True, + validator=validate_bool, + description="draw y axis left major ticks" + ), + _Param( + "ytick.major.right", + default=True, + validator=validate_bool, + description="draw y axis right major ticks" + ), + _Param( + "ytick.minor.left", + default=True, + validator=validate_bool, + description="draw y axis left minor ticks" + ), + _Param( + "ytick.minor.right", + default=True, + validator=validate_bool, + description="draw y axis right minor ticks" + ), + _Param( + "ytick.minor.ndivs", + default="auto", + validator=_validate_minor_tick_ndivs, + description="number of minor ticks between the major ticks on y-axis" + ), + _Param("ytick.alignment", "center_baseline", + ["center", "top", "bottom", "baseline", "center_baseline"], + description="alignment of yticks" + ), + _Section("Grid"), + _Param( + "grid.color", + default="#b0b0b0", + validator=validate_color, + description='b0b0b0" # grid color' + ), + _Param( + "grid.linestyle", + default="-", + validator=_validate_linestyle, + description="solid" + ), + _Param( + "grid.linewidth", + default=0.8, + validator=validate_float, + description="in points" + ), + _Param( + "grid.alpha", + default=1.0, + validator=validate_float, + description="transparency, between 0.0 and 1.0" + ), + _Param( + "grid.major.color", + default=None, + validator=_validate_color_or_None, + description="If None defaults to grid.color" + ), + _Param( + "grid.major.linestyle", + default=None, + validator=_validate_linestyle_or_None, + description="If None defaults to grid.linestyle" + ), + _Param( + "grid.major.linewidth", + default=None, + validator=validate_float_or_None, + description="If None defaults to grid.linewidth" + ), + _Param( + "grid.major.alpha", + default=None, + validator=validate_float_or_None, + description="If None defaults to grid.alpha" + ), + _Param( + "grid.minor.color", + default=None, + validator=_validate_color_or_None, + description="If None defaults to grid.color" + ), + _Param( + "grid.minor.linestyle", + default=None, + validator=_validate_linestyle_or_None, + description="If None defaults to grid.linestyle" + ), + _Param( + "grid.minor.linewidth", + default=None, + validator=validate_float_or_None, + description="If None defaults to grid.linewidth" + ), + _Param( + "grid.minor.alpha", + default=None, + validator=validate_float_or_None, + description="If None defaults to grid.alpha" + ), + _Section("Legend"), + _Param( + "legend.loc", + default="best", + validator=_validate_legend_loc + ), + _Param( + "legend.frameon", + default=True, + validator=validate_bool, + description="if True, draw the legend on a background patch" + ), + _Param( + "legend.framealpha", + default=0.8, + validator=validate_float_or_None, + description="legend patch transparency" + ), + _Param( + "legend.facecolor", + default="inherit", + validator=validate_color_or_inherit, + description="inherit from axes.facecolor; or color spec" + ), + _Param( + "legend.edgecolor", + default="0.8", + validator=validate_color_or_inherit, + description="background patch boundary color" + ), + _Param( + "legend.linewidth", + default=None, + validator=validate_float_or_None, + description="line width of the legend frame, None means inherit from " + "patch.linewidth" + ), + _Param( + "legend.fancybox", + default=True, + validator=validate_bool, + description="if True, use a rounded box for the legend background, else a " + "rectangle" + ), + _Param( + "legend.shadow", + default=False, + validator=validate_bool, + description="if True, give background a shadow effect" + ), + _Param( + "legend.numpoints", + default=1, + validator=validate_int, + description="the number of marker points in the legend line" + ), + _Param( + "legend.scatterpoints", + default=1, + validator=validate_int, + description="number of scatter points" + ), + _Param( + "legend.markerscale", + default=1.0, + validator=validate_float, + description="the relative size of legend markers vs. original" + ), + _Param( + "legend.fontsize", + default="medium", + validator=validate_fontsize + ), + _Param( + "legend.labelcolor", + default="None", + validator=_validate_color_or_linecolor + ), + _Param( + "legend.title_fontsize", + default=None, + validator=validate_fontsize_None, + description="None sets to the same as the default axes." + ), + _Param( + "legend.borderpad", + default=0.4, + validator=validate_float, + description="border whitespace" + ), + _Param( + "legend.labelspacing", + default=0.5, + validator=validate_float, + description="the vertical space between the legend entries" + ), + _Param( + "legend.handlelength", + default=2.0, + validator=validate_float, + description="the length of the legend lines" + ), + _Param( + "legend.handleheight", + default=0.7, + validator=validate_float, + description="the height of the legend handle" + ), + _Param( + "legend.handletextpad", + default=0.8, + validator=validate_float, + description="the space between the legend line and legend text" + ), + _Param( + "legend.borderaxespad", + default=0.5, + validator=validate_float, + description="the border between the axes and legend edge" + ), + _Param( + "legend.columnspacing", + default=2.0, + validator=validate_float, description="column separation" + ), + _Section("Figure"), + _Param( + "figure.titlesize", + default="large", + validator=validate_fontsize, + description="size of the figure title (``Figure.suptitle()``)" + ), + _Param( + "figure.titleweight", + default="normal", + validator=validate_fontweight, + description="weight of the figure title" + ), + _Param( + "figure.labelsize", + default="large", + validator=validate_fontsize, + description="size of the figure label (``Figure.sup[x|y]label()``)" + ), + _Param( + "figure.labelweight", + default="normal", + validator=validate_fontweight, + description="weight of the figure label" + ), + _Param( + "figure.figsize", + default=[6.4, 4.8], + validator=_listify_validator(validate_float, n=2), + description="figure size in inches" + ), + _Param( + "figure.dpi", + default=100.0, + validator=validate_float, description="figure dots per inch" + ), + _Param( + "figure.facecolor", + default="white", + validator=validate_color, description="figure face color" + ), + _Param( + "figure.edgecolor", + default="white", + validator=validate_color, description="figure edge color" + ), + _Param( + "figure.frameon", + default=True, + validator=validate_bool, description="enable figure frame" + ), + _Param( + "figure.max_open_warning", + default=20, + validator=validate_int, + description="The maximum number of figures to open through the pyplot " + "interface before emitting a warning. If less than one this " + "feature is disabled." + ), + _Param( + "figure.raise_window", + default=True, + validator=validate_bool, + description="Raise the GUI window to front when show() is called. If set to " + "False, we currently do not take any further actions and whether " + "the window appears on the front may depend on the GUI framework " + "and window manager." + ), + _Param( + "figure.subplot.left", + default=0.125, + validator=validate_float, + description="the left side of the subplots of the figure" + ), + _Param( + "figure.subplot.right", + default=0.9, + validator=validate_float, + description="the right side of the subplots of the figure" + ), + _Param( + "figure.subplot.bottom", + default=0.11, + validator=validate_float, + description="the bottom of the subplots of the figure" + ), + _Param( + "figure.subplot.top", + default=0.88, + validator=validate_float, + description="the top of the subplots of the figure" + ), + _Param( + "figure.subplot.wspace", + default=0.2, + validator=validate_float, + description="the amount of width reserved for space between subplots, " + "expressed as a fraction of the average axis width" + ), + _Param( + "figure.subplot.hspace", + default=0.2, + validator=validate_float, + description="the amount of height reserved for space between subplots, " + "expressed as a fraction of the average axis height" + ), + _Param( + "figure.autolayout", + default=False, + validator=validate_bool, + description="When True, automatically adjust subplot parameters to make the " + "plot fit the figure using `~.Figure.tight_layout`" + ), + _Param( + "figure.constrained_layout.use", + default=False, + validator=validate_bool, + description="When True, automatically make plot elements fit on the figure. " + '(Not compatible with "figure.autolayout", above).' + ), + _Param( + "figure.constrained_layout.h_pad", + default=0.04167, + validator=validate_float, + description="Padding (in inches) around axes; defaults to 3/72 inches, " + "i.e. 3 points" + ), + _Param( + "figure.constrained_layout.w_pad", + default=0.04167, + validator=validate_float, + description="Padding (in inches) around axes; defaults to 3/72 inches, " + "i.e. 3 points" + ), + _Param( + "figure.constrained_layout.hspace", + default=0.02, + validator=validate_float, + description="Spacing between subplots, relative to the subplot sizes. Much " + "smaller than for tight_layout (figure.subplot.hspace, " + "figure.subplot.wspace) as constrained_layout already takes " + "surrounding texts (titles, labels, # ticklabels) into account." + ), + _Param( + "figure.constrained_layout.wspace", + default=0.02, + validator=validate_float, + description="Spacing between subplots, relative to the subplot sizes. Much " + "smaller than for tight_layout (figure.subplot.hspace, " + "figure.subplot.wspace) as constrained_layout already takes " + "surrounding texts (titles, labels, # ticklabels) into account." + ), + _Section("Images"), + _Param( + "image.aspect", + default="equal", + validator=validate_aspect, + description="{equal, auto} or a number" + ), + _Param( + "image.interpolation", + default="auto", + validator=validate_string, + description="see help(imshow) for options" + ), + _Param( + "image.interpolation_stage", + default="auto", + validator=["auto", "data", "rgba"], + description="see help(imshow) for options" + ), + _Param( + "image.cmap", + default="viridis", + validator=_validate_cmap, + description="A colormap name (plasma, magma, etc.)" + ), + _Param( + "image.lut", + default=256, + validator=validate_int, + description="the size of the colormap lookup table" + ), + _Param( + "image.origin", + default="upper", + validator=["upper", "lower"], description="{lower, upper}" + ), + _Param( + "image.resample", + default=True, + validator=validate_bool + ), + _Param( + "image.composite_image", + default=True, + validator=validate_bool, + description="When True, all the images on a set of axes are combined into a " + "single composite image before saving a figure as a vector " + "graphics file, such as a PDF." + ), + _Section("Contour plots"), + _Param( + "contour.negative_linestyle", + default="dashed", + validator=_validate_linestyle, + description="string or on-off ink sequence" + ), + _Param( + "contour.corner_mask", + default=True, + validator=validate_bool, description="{True, False}" + ), + _Param( + "contour.linewidth", + default=None, + validator=validate_float_or_None, + description="{float, None} Size of the contour line widths. If set to None, it " + 'falls back to "line.linewidth".' + ), + _Param( + "contour.algorithm", + default="mpl2014", + validator=["mpl2005", "mpl2014", "serial", "threaded"], + description="{mpl2005, mpl2014, serial, threaded}" + ), + _Section("Errorbar plots"), + _Param( + "errorbar.capsize", + default=0.0, + validator=validate_float, + description="length of end cap on error bars in pixels" + ), + _Param( + "errorbar.capthick", + default=None, + validator=validate_float_or_None, + description="thickness of end cap on error bars in points."), + _Param( + "errorbar.elinewidth", + default=None, + validator=validate_float_or_None, + description="line width of the error bar lines in points." + ), + _Section("Histogram plots"), + _Param( + "hist.bins", + default=10, + validator=validate_hist_bins, + description="The default number of histogram bins or 'auto'." + ), + _Section("Scatter plots"), + _Param( + "scatter.marker", + default="o", + validator=_validate_marker, + description="The default marker type for scatter plots." + ), + _Param( + "scatter.edgecolors", + default="face", + validator=validate_string, + description="The default edge colors for scatter plots." + ), + _Section("AGG rendering"), + _Param( + "agg.path.chunksize", + default=0, + validator=validate_int, + description="0 to disable; values in the range 10000 to 100000 can improve " + "speed slightly and prevent an Agg rendering failure when plotting " + "very large data sets, especially if they are very gappy. It may " + "cause minor artifacts, though. A value of 20000 is probably a " + "good starting point." + ), + _Section("Paths"), + _Param( + "path.simplify", + default=True, + validator=validate_bool, + description='When True, simplify paths by removing "invisible" points to ' + 'reduce file size and increase rendering speed', + ), + _Param( + "path.simplify_threshold", + default=0.111111111111, + validator=_validate_greaterequal0_lessequal1, + description="The threshold of similarity below which vertices will be removed " + "in the simplification process." + ), + _Param( + "path.snap", + default=True, + validator=validate_bool, + description="When True, rectilinear axis-aligned paths will be snapped to the " + "nearest pixel when certain criteria are met. When False, paths " + "will never be snapped." + ), + _Param( + "path.sketch", + default=None, + validator=validate_sketch, + description="May be None, or a tuple of the form:" + "path.sketch: (scale, length, randomness)" + "- *scale* is the amplitude of the wiggle perpendicular to the line" + " (in pixels)." + "- *length* is the length of the wiggle along the line (in pixels)." + "- *randomness* is the factor by which the length is randomly " + " scaled." + ), + _Param( + "path.effects", + default=[], + validator=validate_anylist + ), + _Section("Saving figures"), + _Param( + "savefig.dpi", + default="figure", + validator=validate_dpi, + description="figure dots per inch or 'figure'" + ), + _Param( + "savefig.facecolor", + default="auto", + validator=validate_color_or_auto, + description="figure face color when saving" + ), + _Param( + "savefig.edgecolor", + default="auto", + validator=validate_color_or_auto, + description="figure edge color when saving" + ), + _Param( + "savefig.format", + default="png", + validator=validate_string, description="{png, ps, pdf, svg}" + ), + _Param( + "savefig.bbox", + default=None, + validator=validate_bbox, + description="{tight, standard} 'tight' is incompatible with generating frames " + "for animation" + ), + _Param( + "savefig.pad_inches", + default=0.1, + validator=validate_float, + description="padding to be used, when bbox is set to 'tight'" + ), + _Param( + "savefig.directory", + default="~", + validator=_validate_pathlike, + description="default directory in savefig dialog, gets updated after " + "interactive saves, unless set to the empty string (i.e. the " + "current directory); use '.' to start at the current directory but " + "update after interactive saves" + ), + _Param( + "savefig.transparent", + default=False, + validator=validate_bool, + description="whether figures are saved with a transparent background by default" + + ), + _Param( + "savefig.orientation", + default="portrait", + validator=["landscape", "portrait"], + description="orientation of saved figure, for PostScript output only" + ), + _Subsection("Mac OSX backend parameters"), + _Param( + "macosx.window_mode", + default="system", + validator=["system", "tab", "window"], + description="How to open new figures (system, tab, window) system uses " + "the MacOS system preferences" + ), + _Subsection("Tk backend parameters"), + _Param( + "tk.window_focus", + default=False, + validator=validate_bool, + description="Maintain shell focus for TkAgg" + ), + _Subsection("PS backend parameters"), + _Param( + "ps.papersize", + default="letter", + validator=_ignorecase( + ["figure", "letter", "legal", "ledger", + *[f"{ab}{i}" for ab in "ab" for i in range(11)], + ], + ), + description="{figure, letter, legal, ledger, A0-A10, B0-B10}" + ), + _Param( + "ps.useafm", + default=False, + validator=validate_bool, + description="use AFM fonts, results in small files" + ), + _Param( + "ps.usedistiller", + default=None, + validator=validate_ps_distiller, + description="{ghostscript, xpdf, None} Experimental: may produce smaller " + "files. xpdf intended for production of publication quality files, " + "but requires ghostscript, xpdf and ps2eps" + ), + _Param( + "ps.distiller.res", + default=6000, + validator=validate_int, description="dpi" + ), + _Param( + "ps.fonttype", + default=3, + validator=validate_fonttype, + description="Output Type 3 (Type3) or Type 42 (TrueType)" + ), + _Subsection("PDF backend parameters"), + _Param( + "pdf.compression", + default=6, + validator=validate_int, + description="integer from 0 to 9 0 disables compression (good for debugging)" + ), + _Param( + "pdf.fonttype", + default=3, + validator=validate_fonttype, + description="Output Type 3 (Type3) or Type 42 (TrueType)" + ), + _Param( + "pdf.use14corefonts", + default=False, + validator=validate_bool + ), + _Param( + "pdf.inheritcolor", + default=False, + validator=validate_bool + ), + _Subsection("SVG backend parameters"), + _Param( + "svg.image_inline", + default=True, + validator=validate_bool, + description="Write raster image data directly into the SVG file" + ), + _Param( + "svg.fonttype", + default="path", + validator=["none", "path"], + description="How to handle SVG fonts: " + "path: Embed characters as paths -- supported by most SVG " + " renderers" + "none: Assume fonts are installed on the machine where the SVG " + "will be viewed." + ), + _Param( + "svg.hashsalt", + default=None, + validator=validate_string_or_None, + description="If not None, use this string as hash salt instead of uuid4" + ), + _Param( + "svg.id", + default=None, + validator=validate_string_or_None, + description="If not None, use this string as the value for the `id` attribute " + "in the top tag" + ), + _Subsection("PGF parameters"), + _Param( + "pgf.rcfonts", + default=True, + validator=validate_bool + ), + _Param( + "pgf.preamble", + default="", + validator=validate_string, + description="See text.latex.preamble for documentation" + ), + _Param( + "pgf.texsystem", + default="xelatex", + validator=["xelatex", "lualatex", "pdflatex"] + ), + _Subsection("Docstring parameters"), + _Param( + "docstring.hardcopy", + default=False, + validator=validate_bool, + description="set this when you want to generate hardcopy docstring" + ), + _Section( + "Interactive keymaps", + description="Default key mappings for interactive navigation. See " + ":ref:`key-event-handling`." + ), + _Param( + "keymap.fullscreen", + default=["f", "ctrl+f"], + validator=validate_stringlist, + description="toggling" + ), + _Param( + "keymap.home", + default=["h", "r", "home"], + validator=validate_stringlist, + description="home or reset mnemonic" + ), + _Param( + "keymap.back", + default=["left", "c", "backspace", "MouseButton.BACK"], + validator=validate_stringlist, description="forward / backward keys" + ), + _Param( + "keymap.forward", + default=["right", "v", "MouseButton.FORWARD"], + validator=validate_stringlist, + description="for quick navigation" + ), + _Param( + "keymap.pan", + default=["p"], + validator=validate_stringlist, description="pan mnemonic" + ), + _Param( + "keymap.zoom", + default=["o"], + validator=validate_stringlist, description="zoom mnemonic" + ), + _Param( + "keymap.save", + default=["s", "ctrl+s"], + validator=validate_stringlist, + description="saving current figure" + ), + _Param( + "keymap.help", + default=["f1"], + validator=validate_stringlist, + description="display help about active tools" + ), + _Param( + "keymap.quit", + default=["ctrl+w", "cmd+w", "q"], + validator=validate_stringlist, + description="close the current figure" + ), + _Param( + "keymap.quit_all", + default=[], + validator=validate_stringlist, description="close all figures" + ), + _Param( + "keymap.grid", + default=["g"], + validator=validate_stringlist, + description="switching on/off major grids in current axes" + ), + _Param( + "keymap.grid_minor", + default=["G"], + validator=validate_stringlist, + description="switching on/off minor grids in current axes" + ), + _Param( + "keymap.yscale", + default=["l"], + validator=validate_stringlist, + description="toggle scaling of y-axes ('log'/'linear')" + ), + _Param( + "keymap.xscale", + default=["k", "L"], + validator=validate_stringlist, + description="toggle scaling of x-axes ('log'/'linear')" + ), + _Param( + "keymap.copy", + default=["ctrl+c", "cmd+c"], + validator=validate_stringlist, + description="copy figure to clipboard" + ), + _Section("Animation"), + _Param( + "animation.html", + default="none", + validator=["html5", "jshtml", "none"], + description="How to display the animation as HTML in the IPython notebook: " + "- 'html5' uses HTML5 video tag " + "- 'jshtml' creates a JavaScript animation" + ), + _Param( + "animation.writer", + default="ffmpeg", + validator=validate_string, + description="MovieWriter 'backend' to use" + ), + _Param( + "animation.codec", + default="h264", + validator=validate_string, + description="Codec to use for writing movie" + ), + _Param( + "animation.bitrate", + default=-1, + validator=validate_int, + description="Controls size/quality trade-off for movie. -1 implies let " + "utility auto-determine" + ), + _Param("animation.frame_format", "png", + ["png", "jpeg", "tiff", "raw", "rgba", "ppm", "sgi", "bmp", "pbm", "svg"], + description="Controls frame format used by temp files" + ), + _Param( + "animation.ffmpeg_path", + default="ffmpeg", + validator=_validate_pathlike, + description="Path to ffmpeg binary. Unqualified paths are resolved by " + "subprocess.Popen." + ), + _Param( + "animation.ffmpeg_args", + default=[], + validator=validate_stringlist, + description="Additional arguments to pass to ffmpeg" + ), + _Param( + "animation.convert_path", + default="convert", + validator=_validate_pathlike, + description="Path to ImageMagick's convert binary. Unqualified paths are " + "resolved by subprocess.Popen, except that on Windows, we look up " + "an install of ImageMagick in the registry (as convert is also the " + "name of a system tool)." + ), + _Param( + "animation.convert_args", + default=["-layers", "OptimizePlus"], + validator=validate_stringlist, + description="Additional arguments to pass to convert" + ), + _Param( + "animation.embed_limit", + default=20.0, + validator=validate_float, + description="Limit, in MB, of size of base64 encoded animation in HTML (i.e. " + "IPython notebook)" + ), + _Param( + "_internal.classic_mode", + default=False, + validator=validate_bool + ), + _Param("backend", None, validate_backend), +] + + +def _params_list(): + return [elem for elem in _DEFINITION if isinstance(elem, _Param)] diff --git a/lib/matplotlib/rcsetup.pyi b/lib/matplotlib/rcsetup.pyi index 1538dac7510e..120c0c93bec9 100644 --- a/lib/matplotlib/rcsetup.pyi +++ b/lib/matplotlib/rcsetup.pyi @@ -4,9 +4,6 @@ from collections.abc import Callable, Iterable from typing import Any, Literal, TypeVar from matplotlib.typing import ColorType, LineStyleType, MarkEveryType -interactive_bk: list[str] -non_interactive_bk: list[str] -all_backends: list[str] _T = TypeVar("_T") @@ -36,6 +33,7 @@ def validate_string_or_None(s: Any) -> str | None: ... def validate_stringlist(s: Any) -> list[str]: ... def validate_int(s: Any) -> int: ... def validate_int_or_None(s: Any) -> int | None: ... +def validate_intlist(s: Any) -> list[int]: ... def validate_float(s: Any) -> float: ... def validate_float_or_None(s: Any) -> float | None: ... def validate_floatlist(s: Any) -> list[float]: ... @@ -48,8 +46,10 @@ _auto_backend_sentinel: object def validate_backend(s: Any) -> str: ... def validate_color_or_inherit(s: Any) -> Literal["inherit"] | ColorType: ... def validate_color_or_auto(s: Any) -> ColorType | Literal["auto"]: ... +def _validate_color_or_edge(s: Any) -> ColorType | Literal["edge"]: ... def validate_color_for_prop_cycle(s: Any) -> ColorType: ... def validate_color(s: Any) -> ColorType: ... +def _validate_color_or_None(s: Any) -> ColorType | None: ... def validate_colorlist(s: Any) -> list[ColorType]: ... def _validate_color_or_linecolor( s: Any, @@ -139,6 +139,7 @@ def validate_fillstylelist( ) -> list[Literal["full", "left", "right", "bottom", "top", "none"]]: ... def validate_markevery(s: Any) -> MarkEveryType: ... def _validate_linestyle(s: Any) -> LineStyleType: ... +def _validate_linestyle_or_None(s: Any) -> LineStyleType | None: ... def validate_markeverylist(s: Any) -> list[MarkEveryType]: ... def validate_bbox(s: Any) -> Literal["tight", "standard"] | None: ... def validate_sketch(s: Any) -> None | tuple[float, float, float]: ... diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index 665b9d6deba2..637cfc849f9d 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -347,7 +347,7 @@ def _revert(self, path, first_action=Path.LINETO): # path[2] = path[2][::-1] # return path - @_docstring.dedent_interpd + @_docstring.interpd def add(self, patchlabel='', flows=None, orientations=None, labels='', trunklength=1.0, pathlengths=0.25, prior=None, connect=(0, 0), rotation=0, **kwargs): diff --git a/lib/matplotlib/sankey.pyi b/lib/matplotlib/sankey.pyi index 4a40c31e3c6a..083d590559ca 100644 --- a/lib/matplotlib/sankey.pyi +++ b/lib/matplotlib/sankey.pyi @@ -2,6 +2,7 @@ from matplotlib.axes import Axes from collections.abc import Callable, Iterable from typing import Any +from typing import Self import numpy as np @@ -56,6 +57,5 @@ class Sankey: connect: tuple[int, int] = ..., rotation: float = ..., **kwargs - # Replace return with Self when py3.9 is dropped - ) -> Sankey: ... + ) -> Self: ... def finish(self) -> list[Any]: ... diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 7f90362b574b..0793bb31e566 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -1,19 +1,38 @@ """ Scales define the distribution of data values on an axis, e.g. a log scaling. -They are defined as subclasses of `ScaleBase`. -See also `.axes.Axes.set_xscale` and the scales examples in the documentation. +The mapping is implemented through `.Transform` subclasses. -See :doc:`/gallery/scales/custom_scale` for a full example of defining a custom -scale. +The following scales are built-in: -Matplotlib also supports non-separable transformations that operate on both -`~.axis.Axis` at the same time. They are known as projections, and defined in -`matplotlib.projections`. -""" +.. _builtin_scales: + +============= ===================== ================================ ================================= +Name Class Transform Inverted transform +============= ===================== ================================ ================================= +"asinh" `AsinhScale` `AsinhTransform` `InvertedAsinhTransform` +"function" `FuncScale` `FuncTransform` `FuncTransform` +"functionlog" `FuncScaleLog` `FuncTransform` + `LogTransform` `InvertedLogTransform` + `FuncTransform` +"linear" `LinearScale` `.IdentityTransform` `.IdentityTransform` +"log" `LogScale` `LogTransform` `InvertedLogTransform` +"logit" `LogitScale` `LogitTransform` `LogisticTransform` +"symlog" `SymmetricalLogScale` `SymmetricalLogTransform` `InvertedSymmetricalLogTransform` +============= ===================== ================================ ================================= + +A user will often only use the scale name, e.g. when setting the scale through +`~.Axes.set_xscale`: ``ax.set_xscale("log")``. + +See also the :ref:`scales examples ` in the documentation. + +Custom scaling can be achieved through `FuncScale`, or by creating your own +`ScaleBase` subclass and corresponding transforms (see :doc:`/gallery/scales/custom_scale`). +Third parties can register their scales by name through `register_scale`. +""" # noqa: E501 import inspect +import math import textwrap +from functools import wraps import numpy as np @@ -34,7 +53,7 @@ class ScaleBase: Subclasses should override - :attr:`name` + :attr:`!name` The scale's name. :meth:`get_transform` A method returning a `.Transform`, which converts data coordinates to @@ -57,9 +76,20 @@ def __init__(self, axis): The following note is for scale implementers. For back-compatibility reasons, scales take an `~matplotlib.axis.Axis` - object as first argument. However, this argument should not - be used: a single scale object should be usable by multiple - `~matplotlib.axis.Axis`\es at the same time. + object as the first argument. + + .. deprecated:: 3.11 + + The *axis* parameter is now optional, i.e. matplotlib is compatible + with `.ScaleBase` subclasses that do not take an *axis* parameter. + + The *axis* parameter is pending-deprecated. It will be deprecated + in matplotlib 3.13, and removed in matplotlib 3.15. + + 3rd-party scales are recommended to remove the *axis* parameter now + if they can afford to restrict compatibility to matplotlib >= 3.11 + already. Otherwise, they may keep the *axis* parameter and remove it + in time for matplotlib 3.13. """ def get_transform(self): @@ -85,6 +115,69 @@ def limit_range_for_scale(self, vmin, vmax, minpos): """ return vmin, vmax + def val_in_range(self, val): + """ + Return whether the value(s) are within the valid range for this scale. + + This method is a generic implementation. Subclasses may implement more + efficient solutions for their domain. + """ + try: + if not math.isfinite(val): + return False + else: + vmin, vmax = self.limit_range_for_scale(val, val, minpos=1e-300) + return vmin == val and vmax == val + except (TypeError, ValueError): + return False + + +def _make_axis_parameter_optional(init_func): + """ + Decorator to allow leaving out the *axis* parameter in scale constructors. + + This decorator ensures backward compatibility for scale classes that + previously required an *axis* parameter. It allows constructors to be + callerd with or without the *axis* parameter. + + For simplicity, this does not handle the case when *axis* + is passed as a keyword. However, + scanning GitHub, there's no evidence that that is used anywhere. + + Parameters + ---------- + init_func : callable + The original __init__ method of a scale class. + + Returns + ------- + callable + A wrapped version of *init_func* that handles the optional *axis*. + + Notes + ----- + If the wrapped constructor defines *axis* as its first argument, the + parameter is preserved when present. Otherwise, the value `None` is injected + as the first argument. + + Examples + -------- + >>> from matplotlib.scale import ScaleBase + >>> class CustomScale(ScaleBase): + ... @_make_axis_parameter_optional + ... def __init__(self, axis, custom_param=1): + ... self.custom_param = custom_param + """ + @wraps(init_func) + def wrapper(self, *args, **kwargs): + if args and isinstance(args[0], mpl.axis.Axis): + return init_func(self, *args, **kwargs) + else: + # Remove 'axis' from kwargs to avoid double assignment + axis = kwargs.pop('axis', None) + return init_func(self, axis, *args, **kwargs) + return wrapper + class LinearScale(ScaleBase): """ @@ -93,6 +186,7 @@ class LinearScale(ScaleBase): name = 'linear' + @_make_axis_parameter_optional def __init__(self, axis): # This method is present only to prevent inheritance of the base class' # constructor docstring, which would otherwise end up interpolated into @@ -119,6 +213,14 @@ def get_transform(self): """ return IdentityTransform() + def val_in_range(self, val): + """ + Return whether the value is within the valid range for this scale. + + This is True for all values, except +-inf and NaN. + """ + return math.isfinite(val) + class FuncTransform(Transform): """ @@ -163,12 +265,19 @@ class FuncScale(ScaleBase): name = 'function' + @_make_axis_parameter_optional def __init__(self, axis, functions): """ Parameters ---------- axis : `~matplotlib.axis.Axis` The axis for the scale. + + .. note:: + This parameter is unused and will be removed in an imminent release. + It can already be left out because of special preprocessing, + so that ``FuncScale(functions)`` is valid. + functions : (callable, callable) two-tuple of the forward and inverse functions for the scale. The forward function must be monotonic. @@ -206,23 +315,22 @@ def __init__(self, base, nonpositive='clip'): if base <= 0 or base == 1: raise ValueError('The log base cannot be <= 0 or == 1') self.base = base - self._clip = _api.check_getitem( + self._clip = _api.getitem_checked( {"clip": True, "mask": False}, nonpositive=nonpositive) + self._log_funcs = {np.e: np.log, 2: np.log2, 10: np.log10} def __str__(self): return "{}(base={}, nonpositive={!r})".format( type(self).__name__, self.base, "clip" if self._clip else "mask") - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): # Ignore invalid values due to nans being passed to the transform. with np.errstate(divide="ignore", invalid="ignore"): - log = {np.e: np.log, 2: np.log2, 10: np.log10}.get(self.base) - if log: # If possible, do everything in a single call to NumPy. - out = log(values) + log_func = self._log_funcs.get(self.base) + if log_func: + out = log_func(values) else: - out = np.log(values) - out /= np.log(self.base) + out = np.log(values) / np.log(self.base) if self._clip: # SVG spec says that conforming viewers must support values up # to 3.4e38 (C float); however experiments suggest that @@ -246,13 +354,17 @@ class InvertedLogTransform(Transform): def __init__(self, base): super().__init__() self.base = base + self._exp_funcs = {np.e: np.exp, 2: np.exp2} def __str__(self): return f"{type(self).__name__}(base={self.base})" - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): - return np.power(self.base, values) + exp_func = self._exp_funcs.get(self.base) + if exp_func: + return exp_func(values) + else: + return np.exp(values * np.log(self.base)) def inverted(self): return LogTransform(self.base) @@ -264,12 +376,19 @@ class LogScale(ScaleBase): """ name = 'log' - def __init__(self, axis, *, base=10, subs=None, nonpositive="clip"): + @_make_axis_parameter_optional + def __init__(self, axis=None, *, base=10, subs=None, nonpositive="clip"): """ Parameters ---------- axis : `~matplotlib.axis.Axis` The axis for the scale. + + .. note:: + This parameter is unused and about to be removed in the future. + It can already now be left out because of special preprocessing, + so that ``LogScale(base=2)`` is valid. + base : float, default: 10 The base of the logarithm. nonpositive : {'clip', 'mask'}, default: 'clip' @@ -306,6 +425,14 @@ def limit_range_for_scale(self, vmin, vmax, minpos): return (minpos if vmin <= 0 else vmin, minpos if vmax <= 0 else vmax) + def val_in_range(self, val): + """ + Return whether the value is within the valid range for this scale. + + This is True for value(s) > 0 except +inf and NaN. + """ + return math.isfinite(val) and val > 0 + class FuncScaleLog(LogScale): """ @@ -315,6 +442,7 @@ class FuncScaleLog(LogScale): name = 'functionlog' + @_make_axis_parameter_optional def __init__(self, axis, functions, base=10): """ Parameters @@ -359,18 +487,20 @@ def __init__(self, base, linthresh, linscale): self.base = base self.linthresh = linthresh self.linscale = linscale - self._linscale_adj = (linscale / (1.0 - self.base ** -1)) - self._log_base = np.log(base) - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): + linscale_adj = self.linscale / (1.0 - 1.0 / self.base) + log_base = np.log(self.base) + abs_a = np.abs(values) + inside = abs_a <= self.linthresh + if np.all(inside): # Fast path: all values in linear region + return values * linscale_adj with np.errstate(divide="ignore", invalid="ignore"): out = np.sign(values) * self.linthresh * ( - self._linscale_adj + - np.log(abs_a / self.linthresh) / self._log_base) - inside = abs_a <= self.linthresh - out[inside] = values[inside] * self._linscale_adj + linscale_adj - np.log(self.linthresh) / log_base + + np.log(abs_a) / log_base) + out[inside] = values[inside] * linscale_adj return out def inverted(self): @@ -383,22 +513,35 @@ class InvertedSymmetricalLogTransform(Transform): def __init__(self, base, linthresh, linscale): super().__init__() - symlog = SymmetricalLogTransform(base, linthresh, linscale) + if base <= 1.0: + raise ValueError("'base' must be larger than 1") + if linthresh <= 0.0: + raise ValueError("'linthresh' must be positive") + if linscale <= 0.0: + raise ValueError("'linscale' must be positive") self.base = base self.linthresh = linthresh - self.invlinthresh = symlog.transform(linthresh) self.linscale = linscale - self._linscale_adj = (linscale / (1.0 - self.base ** -1)) - @_api.rename_parameter("3.8", "a", "values") + @_api.deprecated("3.11", name="invlinthresh", obj_type="attribute", + alternative=".inverted().transform(linthresh)") + @property + def invlinthresh(self): + invlinthresh = self.inverted().transform(self.linthresh) + return invlinthresh + def transform_non_affine(self, values): + linscale_adj = self.linscale / (1.0 - 1.0 / self.base) + invlinthresh = self.inverted().transform(self.linthresh) + abs_a = np.abs(values) + inside = abs_a <= invlinthresh + if np.all(inside): # Fast path: all values in linear region + return values / linscale_adj with np.errstate(divide="ignore", invalid="ignore"): - out = np.sign(values) * self.linthresh * ( - np.power(self.base, - abs_a / self.linthresh - self._linscale_adj)) - inside = abs_a <= self.invlinthresh - out[inside] = values[inside] / self._linscale_adj + out = np.sign(values) * self.linthresh * np.exp( + (abs_a / self.linthresh - linscale_adj) * np.log(self.base)) + out[inside] = values[inside] / linscale_adj return out def inverted(self): @@ -416,8 +559,18 @@ class SymmetricalLogScale(ScaleBase): *linthresh* allows the user to specify the size of this range (-*linthresh*, *linthresh*). + See :doc:`/gallery/scales/symlog_demo` for a detailed description. + Parameters ---------- + axis : `~matplotlib.axis.Axis` + The axis for the scale. + + .. note:: + This parameter is unused and about to be removed in the future. + It can already now be left out because of special preprocessing, + so that ``SymmetricalLocSacle(base=2)`` is valid. + base : float, default: 10 The base of the logarithm. @@ -440,7 +593,8 @@ class SymmetricalLogScale(ScaleBase): """ name = 'symlog' - def __init__(self, axis, *, base=10, linthresh=2, subs=None, linscale=1): + @_make_axis_parameter_optional + def __init__(self, axis=None, *, base=10, linthresh=2, subs=None, linscale=1): self._transform = SymmetricalLogTransform(base, linthresh, linscale) self.subs = subs @@ -460,6 +614,14 @@ def get_transform(self): """Return the `.SymmetricalLogTransform` associated with this scale.""" return self._transform + def val_in_range(self, val): + """ + Return whether the value is within the valid range for this scale. + + This is True for all values, except +-inf and NaN. + """ + return math.isfinite(val) + class AsinhTransform(Transform): """Inverse hyperbolic-sine transformation used by `.AsinhScale`""" @@ -472,7 +634,6 @@ def __init__(self, linear_width): "must be strictly positive") self.linear_width = linear_width - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): return self.linear_width * np.arcsinh(values / self.linear_width) @@ -488,7 +649,6 @@ def __init__(self, linear_width): super().__init__() self.linear_width = linear_width - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): return self.linear_width * np.sinh(values / self.linear_width) @@ -534,11 +694,20 @@ class AsinhScale(ScaleBase): 1024: (256, 512) } - def __init__(self, axis, *, linear_width=1.0, + @_make_axis_parameter_optional + def __init__(self, axis=None, *, linear_width=1.0, base=10, subs='auto', **kwargs): """ Parameters ---------- + axis : `~matplotlib.axis.Axis` + The axis for the scale. + + .. note:: + This parameter is unused and about to be removed in the future. + It can already now be left out because of special preprocessing, + so that ``AsinhScale()`` is valid. + linear_width : float, default: 1 The scale parameter (elsewhere referred to as :math:`a_0`) defining the extent of the quasi-linear region, @@ -579,6 +748,14 @@ def set_default_locators_and_formatters(self, axis): else: axis.set_major_formatter('{x:.3g}') + def val_in_range(self, val): + """ + Return whether the value is within the valid range for this scale. + + This is True for all values, except +-inf and NaN. + """ + return math.isfinite(val) + class LogitTransform(Transform): input_dims = output_dims = 1 @@ -589,7 +766,6 @@ def __init__(self, nonpositive='mask'): self._nonpositive = nonpositive self._clip = {"clip": True, "mask": False}[nonpositive] - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): """logit transform (base 10), masked or clipped""" with np.errstate(divide="ignore", invalid="ignore"): @@ -613,7 +789,6 @@ def __init__(self, nonpositive='mask'): super().__init__() self._nonpositive = nonpositive - @_api.rename_parameter("3.8", "a", "values") def transform_non_affine(self, values): """logistic transform (base 10)""" return 1.0 / (1 + 10**(-values)) @@ -634,13 +809,20 @@ class LogitScale(ScaleBase): """ name = 'logit' - def __init__(self, axis, nonpositive='mask', *, + @_make_axis_parameter_optional + def __init__(self, axis=None, nonpositive='mask', *, one_half=r"\frac{1}{2}", use_overline=False): r""" Parameters ---------- axis : `~matplotlib.axis.Axis` - Currently unused. + The axis for the scale. + + .. note:: + This parameter is unused and about to be removed in the future. + It can already now be left out because of special preprocessing, + so that ``LogitScale()`` is valid. + nonpositive : {'mask', 'clip'} Determines the behavior for values beyond the open interval ]0, 1[. They can either be masked as invalid, or clipped to a number very @@ -687,6 +869,14 @@ def limit_range_for_scale(self, vmin, vmax, minpos): return (minpos if vmin <= 0 else vmin, 1 - minpos if vmax >= 1 else vmax) + def val_in_range(self, val): + """ + Return whether the value is within the valid range for this scale. + + This is True for value(s) which are between 0 and 1 (excluded). + """ + return 0 < val < 1 + _scale_mapping = { 'linear': LinearScale, @@ -698,6 +888,20 @@ def limit_range_for_scale(self, vmin, vmax, minpos): 'functionlog': FuncScaleLog, } +# caching of signature info +# For backward compatibility, the built-in scales will keep the *axis* parameter +# in their constructors until matplotlib 3.15, i.e. as long as the *axis* parameter +# is still supported. +_scale_has_axis_parameter = { + 'linear': True, + 'log': True, + 'symlog': True, + 'asinh': True, + 'logit': True, + 'function': True, + 'functionlog': True, +} + def get_scale_names(): """Return the names of the available scales.""" @@ -713,8 +917,12 @@ def scale_factory(scale, axis, **kwargs): scale : {%(names)s} axis : `~matplotlib.axis.Axis` """ - scale_cls = _api.check_getitem(_scale_mapping, scale=scale) - return scale_cls(axis, **kwargs) + scale_cls = _api.getitem_checked(_scale_mapping, scale=scale) + + if _scale_has_axis_parameter[scale]: + return scale_cls(axis, **kwargs) + else: + return scale_cls(**kwargs) if scale_factory.__doc__: @@ -733,6 +941,20 @@ def register_scale(scale_class): """ _scale_mapping[scale_class.name] = scale_class + # migration code to handle the *axis* parameter + has_axis_parameter = "axis" in inspect.signature(scale_class).parameters + _scale_has_axis_parameter[scale_class.name] = has_axis_parameter + if has_axis_parameter: + _api.warn_deprecated( + "3.11", + message=f"The scale {scale_class.__qualname__!r} uses an 'axis' parameter " + "in the constructors. This parameter is pending-deprecated since " + "matplotlib 3.11. It will be fully deprecated in 3.13 and removed " + "in 3.15. Starting with 3.11, 'register_scale()' accepts scales " + "without the *axis* parameter.", + pending=True, + ) + def _get_scale_docs(): """ @@ -750,7 +972,7 @@ def _get_scale_docs(): return "\n".join(docs) -_docstring.interpd.update( +_docstring.interpd.register( scale_type='{%s}' % ', '.join([repr(x) for x in get_scale_names()]), scale_docs=_get_scale_docs().rstrip(), ) diff --git a/lib/matplotlib/scale.pyi b/lib/matplotlib/scale.pyi index 7fec8e68cc5a..866509ee020d 100644 --- a/lib/matplotlib/scale.pyi +++ b/lib/matplotlib/scale.pyi @@ -12,9 +12,14 @@ class ScaleBase: def limit_range_for_scale( self, vmin: float, vmax: float, minpos: float ) -> tuple[float, float]: ... + def val_in_range(self, val: float) -> bool: ... class LinearScale(ScaleBase): name: str + def __init__( + self, + axis: Axis | None, + ) -> None: ... class FuncTransform(Transform): input_dims: int @@ -57,7 +62,7 @@ class LogScale(ScaleBase): subs: Iterable[int] | None def __init__( self, - axis: Axis | None, + axis: Axis | None = ..., *, base: float = ..., subs: Iterable[int] | None = ..., @@ -94,8 +99,9 @@ class InvertedSymmetricalLogTransform(Transform): output_dims: int base: float linthresh: float - invlinthresh: float linscale: float + @property + def invlinthresh(self) -> float: ... def __init__(self, base: float, linthresh: float, linscale: float) -> None: ... def inverted(self) -> SymmetricalLogTransform: ... @@ -104,7 +110,7 @@ class SymmetricalLogScale(ScaleBase): subs: Iterable[int] | None def __init__( self, - axis: Axis | None, + axis: Axis | None = ..., *, base: float = ..., linthresh: float = ..., @@ -138,7 +144,7 @@ class AsinhScale(ScaleBase): auto_tick_multipliers: dict[int, tuple[int, ...]] def __init__( self, - axis: Axis | None, + axis: Axis | None = ..., *, linear_width: float = ..., base: float = ..., @@ -165,7 +171,7 @@ class LogitScale(ScaleBase): name: str def __init__( self, - axis: Axis | None, + axis: Axis | None = ..., nonpositive: Literal["mask", "clip"] = ..., *, one_half: str = ..., @@ -176,3 +182,4 @@ class LogitScale(ScaleBase): def get_scale_names() -> list[str]: ... def scale_factory(scale: str, axis: Axis, **kwargs) -> ScaleBase: ... def register_scale(scale_class: type[ScaleBase]) -> None: ... +def _make_axis_parameter_optional(init_func: Callable[..., None]) -> Callable[..., None]: ... diff --git a/lib/matplotlib/sphinxext/figmpl_directive.py b/lib/matplotlib/sphinxext/figmpl_directive.py index 5ef34f4dd0b1..7cb9c6c04e8a 100644 --- a/lib/matplotlib/sphinxext/figmpl_directive.py +++ b/lib/matplotlib/sphinxext/figmpl_directive.py @@ -12,16 +12,14 @@ See the *FigureMpl* documentation below. """ -from docutils import nodes - -from docutils.parsers.rst import directives -from docutils.parsers.rst.directives.images import Figure, Image - import os from os.path import relpath from pathlib import PurePath, Path import shutil +from docutils import nodes +from docutils.parsers.rst import directives +from docutils.parsers.rst.directives.images import Figure, Image from sphinx.errors import ExtensionError import matplotlib @@ -193,12 +191,13 @@ def visit_figmpl_html(self, node): # make uri also be relative... nm = PurePath(node['uri'][1:]).name uri = f'{imagerel}/{rel}{nm}' + img_attrs = {'src': uri, 'alt': node['alt']} # make srcset str. Need to change all the prefixes! maxsrc = uri - srcsetst = '' if srcset: maxmult = -1 + srcsetst = '' for mult, src in srcset.items(): nm = PurePath(src[1:]).name # ../../_images/plot_1_2_0x.png @@ -214,44 +213,43 @@ def visit_figmpl_html(self, node): maxsrc = path # trim trailing comma and space... - srcsetst = srcsetst[:-2] + img_attrs['srcset'] = srcsetst[:-2] - alt = node['alt'] if node['class'] is not None: - classst = ' '.join(node['class']) - classst = f'class="{classst}"' - - else: - classst = '' - - stylers = ['width', 'height', 'scale'] - stylest = '' - for style in stylers: + img_attrs['class'] = ' '.join(node['class']) + for style in ['width', 'height', 'scale']: if node[style]: - stylest += f'{style}: {node[style]};' - - figalign = node['align'] if node['align'] else 'center' - -#
-# -#
_images/index-1.2x.png -#
-#
-#

Figure caption is here.... -# #

-#
-# - img_block = (f'') - html_block = f'
\n' - html_block += f' \n' - html_block += f' {img_block}\n \n' + if 'style' not in img_attrs: + img_attrs['style'] = f'{style}: {node[style]};' + else: + img_attrs['style'] += f'{style}: {node[style]};' + + #
+ # + # _images/index-1.2x.png + # + #
+ #

Figure caption is here.... + # #

+ #
+ #
+ self.body.append( + self.starttag( + node, 'figure', + CLASS=f'align-{node["align"]}' if node['align'] else 'align-center')) + self.body.append( + self.starttag(node, 'a', CLASS='reference internal image-reference', + href=maxsrc) + + self.emptytag(node, 'img', **img_attrs) + + '\n') if node['caption']: - html_block += '
\n' - html_block += f'

{node["caption"]}

\n' - html_block += '
\n' - html_block += '
\n' - self.body.append(html_block) + self.body.append(self.starttag(node, 'figcaption')) + self.body.append(self.starttag(node, 'p')) + self.body.append(self.starttag(node, 'span', CLASS='caption-text')) + self.body.append(node['caption']) + self.body.append('

\n') + self.body.append('\n') def visit_figmpl_latex(self, node): diff --git a/lib/matplotlib/sphinxext/mathmpl.py b/lib/matplotlib/sphinxext/mathmpl.py index 3e0d562e2d15..30f024524258 100644 --- a/lib/matplotlib/sphinxext/mathmpl.py +++ b/lib/matplotlib/sphinxext/mathmpl.py @@ -146,7 +146,10 @@ def latex2html(node, source): fontset = node['fontset'] fontsize = node['fontsize'] name = 'math-{}'.format( - hashlib.md5(f'{latex}{fontset}{fontsize}'.encode()).hexdigest()[-10:]) + hashlib.sha256( + f'{latex}{fontset}{fontsize}'.encode(), + usedforsecurity=False, + ).hexdigest()[-10:]) destdir = Path(setup.app.builder.outdir, '_images', 'mathmpl') destdir.mkdir(parents=True, exist_ok=True) diff --git a/lib/matplotlib/sphinxext/meson.build b/lib/matplotlib/sphinxext/meson.build index 5dc7388384eb..35bb96fecbe1 100644 --- a/lib/matplotlib/sphinxext/meson.build +++ b/lib/matplotlib/sphinxext/meson.build @@ -3,6 +3,7 @@ python_sources = [ 'figmpl_directive.py', 'mathmpl.py', 'plot_directive.py', + 'roles.py', ] typing_sources = [ diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 65b25fb913a5..7b46b3145e2b 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -32,7 +32,7 @@ import matplotlib.pyplot as plt plt.plot([1, 2, 3], [4, 5, 6]) - plt.title("A plotting exammple") + plt.title("A plotting example") 3. Using **doctest** syntax:: @@ -47,6 +47,12 @@ The ``.. plot::`` directive supports the following options: +``:filename-prefix:`` : str + The base name (without the extension) of the outputted image and script + files. The default is to use the same name as the input script, or the + name of the RST document if no script is provided. The filename-prefix for + each plot directive must be unique. + ``:format:`` : {'python', 'doctest'} The format of the input. If unset, the format is auto-detected. @@ -78,6 +84,11 @@ figure. This overwrites the caption given in the content, when the plot is generated from a file. +``:code-caption:`` : str + If specified, the option's argument will be used as a caption for the + code block (when ``:include-source:`` is used). This is added as the + ``:caption:`` option to the ``.. code-block::`` directive. + Additionally, this directive supports all the options of the `image directive `_, except for ``:target:`` (since plot will add its own target). These include @@ -163,8 +174,10 @@ be customized by changing the *plot_template*. See the source of :doc:`/api/sphinxext_plot_directive_api` for the templates defined in *TEMPLATE* and *TEMPLATE_SRCSET*. + """ +from collections import defaultdict import contextlib import doctest from io import StringIO @@ -182,6 +195,7 @@ from docutils.parsers.rst.directives.images import Image import jinja2 # Sphinx dependency. +from sphinx.environment.collectors import EnvironmentCollector from sphinx.errors import ExtensionError import matplotlib @@ -265,12 +279,14 @@ class PlotDirective(Directive): 'scale': directives.nonnegative_int, 'align': Image.align, 'class': directives.class_option, + 'filename-prefix': directives.unchanged, 'include-source': _option_boolean, 'show-source-link': _option_boolean, 'format': _option_format, 'context': _option_context, 'nofigs': directives.flag, 'caption': directives.unchanged, + 'code-caption': directives.unchanged, } def run(self): @@ -312,9 +328,35 @@ def setup(app): app.connect('build-finished', _copy_css_file) metadata = {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': matplotlib.__version__} + app.connect('builder-inited', init_filename_registry) + app.add_env_collector(_FilenameCollector) return metadata +# ----------------------------------------------------------------------------- +# Handle Duplicate Filenames +# ----------------------------------------------------------------------------- + +def init_filename_registry(app): + env = app.builder.env + if not hasattr(env, 'mpl_plot_image_basenames'): + env.mpl_plot_image_basenames = defaultdict(set) + + +class _FilenameCollector(EnvironmentCollector): + def process_doc(self, app, doctree): + pass + + def clear_doc(self, app, env, docname): + if docname in env.mpl_plot_image_basenames: + del env.mpl_plot_image_basenames[docname] + + def merge_other(self, app, env, docnames, other): + for docname in other.mpl_plot_image_basenames: + env.mpl_plot_image_basenames[docname].update( + other.mpl_plot_image_basenames[docname]) + + # ----------------------------------------------------------------------------- # Doctest handling # ----------------------------------------------------------------------------- @@ -600,6 +642,25 @@ def _parse_srcset(entries): return srcset +def check_output_base_name(env, output_base): + docname = env.docname + + if '.' in output_base or '/' in output_base or '\\' in output_base: + raise PlotError( + f"The filename-prefix '{output_base}' is invalid. " + f"It must not contain dots or slashes.") + + for d in env.mpl_plot_image_basenames: + if output_base in env.mpl_plot_image_basenames[d]: + if d == docname: + raise PlotError( + f"The filename-prefix {output_base!r} is used multiple times.") + raise PlotError(f"The filename-prefix {output_base!r} is used multiple" + f"times (it is also used in {env.doc2path(d)}).") + + env.mpl_plot_image_basenames[docname].add(output_base) + + def render_figures(code, code_path, output_dir, output_base, context, function_name, config, context_reset=False, close_figs=False, @@ -722,7 +783,8 @@ def render_figures(code, code_path, output_dir, output_base, context, def run(arguments, content, options, state_machine, state, lineno): document = state_machine.document - config = document.settings.env.config + env = document.settings.env + config = env.config nofigs = 'nofigs' in options if config.plot_srcset and setup.app.builder.name == 'singlehtml': @@ -734,6 +796,7 @@ def run(arguments, content, options, state_machine, state, lineno): options.setdefault('include-source', config.plot_include_source) options.setdefault('show-source-link', config.plot_html_show_source_link) + options.setdefault('filename-prefix', None) if 'class' in options: # classes are parsed into a list of string, and output by simply @@ -775,14 +838,22 @@ def run(arguments, content, options, state_machine, state, lineno): function_name = None code = Path(source_file_name).read_text(encoding='utf-8') - output_base = os.path.basename(source_file_name) + if options['filename-prefix']: + output_base = options['filename-prefix'] + check_output_base_name(env, output_base) + else: + output_base = os.path.basename(source_file_name) else: source_file_name = rst_file code = textwrap.dedent("\n".join(map(str, content))) - counter = document.attributes.get('_plot_counter', 0) + 1 - document.attributes['_plot_counter'] = counter - base, ext = os.path.splitext(os.path.basename(source_file_name)) - output_base = '%s-%d.py' % (base, counter) + if options['filename-prefix']: + output_base = options['filename-prefix'] + check_output_base_name(env, output_base) + else: + base, ext = os.path.splitext(os.path.basename(source_file_name)) + counter = document.attributes.get('_plot_counter', 0) + 1 + document.attributes['_plot_counter'] = counter + output_base = '%s-%d.py' % (base, counter) function_name = None caption = options.get('caption', '') @@ -846,7 +917,7 @@ def run(arguments, content, options, state_machine, state, lineno): # save script (if necessary) if options['show-source-link']: - Path(build_dir, output_base + source_ext).write_text( + Path(build_dir, output_base + (source_ext or '.py')).write_text( doctest.script_from_examples(code) if source_file_name == rst_file and is_doctest else code, @@ -876,7 +947,7 @@ def run(arguments, content, options, state_machine, state, lineno): # Properly indent the caption if caption and config.plot_srcset: - caption = f':caption: {caption}' + caption = ':caption: ' + caption.replace('\n', ' ') elif caption: caption = '\n' + '\n'.join(' ' + line.strip() for line in caption.split('\n')) @@ -887,8 +958,11 @@ def run(arguments, content, options, state_machine, state, lineno): if is_doctest: lines = ['', *code_piece.splitlines()] else: - lines = ['.. code-block:: python', '', - *textwrap.indent(code_piece, ' ').splitlines()] + lines = ['.. code-block:: python'] + if 'code-caption' in options: + code_caption = options['code-caption'].replace('\n', ' ') + lines.append(f' :caption: {code_caption}') + lines.extend(['', *textwrap.indent(code_piece, ' ').splitlines()]) source_code = "\n".join(lines) else: source_code = "" @@ -896,6 +970,9 @@ def run(arguments, content, options, state_machine, state, lineno): if nofigs: images = [] + if 'alt' in options: + options['alt'] = options['alt'].replace('\n', ' ') + opts = [ f':{key}: {val}' for key, val in options.items() if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] @@ -903,7 +980,7 @@ def run(arguments, content, options, state_machine, state, lineno): # Not-None src_name signals the need for a source download in the # generated html if j == 0 and options['show-source-link']: - src_name = output_base + source_ext + src_name = output_base + (source_ext or '.py') else: src_name = None if config.plot_srcset: diff --git a/lib/matplotlib/sphinxext/roles.py b/lib/matplotlib/sphinxext/roles.py new file mode 100644 index 000000000000..0b696f830543 --- /dev/null +++ b/lib/matplotlib/sphinxext/roles.py @@ -0,0 +1,165 @@ +""" +Custom roles for the Matplotlib documentation. + +.. warning:: + + These roles are considered semi-public. They are only intended to be used in + the Matplotlib documentation. + +However, it can happen that downstream packages end up pulling these roles into +their documentation, which will result in documentation build errors. The following +describes the exact mechanism and how to fix the errors. + +There are two ways, Matplotlib docstrings can end up in downstream documentation. +You have to subclass a Matplotlib class and either use the ``:inherited-members:`` +option in your autodoc configuration, or you have to override a method without +specifying a new docstring; the new method will inherit the original docstring and +still render in your autodoc. If the docstring contains one of the custom sphinx +roles, you'll see one of the following error messages: + +.. code-block:: none + + Unknown interpreted text role "mpltype". + Unknown interpreted text role "rc". + +To fix this, you can add this module as extension to your sphinx :file:`conf.py`:: + + extensions = [ + 'matplotlib.sphinxext.roles', + # Other extensions. + ] + +.. warning:: + + Direct use of these roles in other packages is not officially supported. We + reserve the right to modify or remove these roles without prior notification. +""" + +from urllib.parse import urlsplit, urlunsplit + +from docutils import nodes + +import matplotlib +from matplotlib import rcParamsDefault + + +class _QueryReference(nodes.Inline, nodes.TextElement): + """ + Wraps a reference or pending reference to add a query string. + + The query string is generated from the attributes added to this node. + + Also equivalent to a `~docutils.nodes.literal` node. + """ + + def to_query_string(self): + """Generate query string from node attributes.""" + return '&'.join(f'{name}={value}' for name, value in self.attlist()) + + +def _visit_query_reference_node(self, node): + """ + Resolve *node* into query strings on its ``reference`` children. + + Then act as if this is a `~docutils.nodes.literal`. + """ + query = node.to_query_string() + for refnode in node.findall(nodes.reference): + uri = urlsplit(refnode['refuri'])._replace(query=query) + refnode['refuri'] = urlunsplit(uri) + + self.visit_literal(node) + + +def _depart_query_reference_node(self, node): + """ + Act as if this is a `~docutils.nodes.literal`. + """ + self.depart_literal(node) + + +# We sometimes want to use special notation in rcParam references, e.g. +# to use wildcards. This mapping maps such special notations to real rcParams, +# typically to the first relevant parameter in the group. +_RC_WILDCARD_LINK_MAPPING = { + "animation.[name-of-encoder]_args": "animation.ffmpeg_args", + "figure.subplot.*": "figure.subplot.left", + "figure.subplot.[name]": "figure.subplot.left", + "font.*": "font.family", + "lines.*": "lines.linewidth", + "patch.*": "patch.edgecolor", + "grid.major.*": "grid.major.color", + "grid.minor.*": "grid.minor.color", + "grid.*": "grid.color", +} + + +def _rcparam_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """ + Sphinx role ``:rc:`` to highlight and link ``rcParams`` entries. + + Usage: Give the desired ``rcParams`` key as parameter. + + :code:`:rc:`figure.dpi`` will render as: :rc:`figure.dpi` + """ + # Generate a pending cross-reference so that Sphinx will ensure this link + # isn't broken at some point in the future. + title = f'rcParams["{text}"]' + rc_param_name = _RC_WILDCARD_LINK_MAPPING.get(text, text) + target = f'rcparam_{rc_param_name.replace(".", "_")}' + ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>', + 'ref', lineno) + + qr = _QueryReference(rawtext, highlight=text) + qr += ref_nodes + node_list = [qr] + + # The default backend would be printed as "agg", but that's not correct (as + # the default is actually determined by fallback). + if text in rcParamsDefault and text != "backend": + node_list.extend([ + nodes.Text(' (default: '), + nodes.literal('', repr(rcParamsDefault[text])), + nodes.Text(')'), + ]) + + return node_list, messages + + +def _mpltype_role(name, rawtext, text, lineno, inliner, options=None, content=None): + """ + Sphinx role ``:mpltype:`` for custom matplotlib types. + + In Matplotlib, there are a number of type-like concepts that do not have a + direct type representation; example: color. This role allows to properly + highlight them in the docs and link to their definition. + + Currently supported values: + + - :code:`:mpltype:`color`` will render as: :mpltype:`color` + + """ + mpltype = text + type_to_link_target = { + 'color': 'colors_def', + 'hatch': 'hatch_def', + } + if mpltype not in type_to_link_target: + raise ValueError(f"Unknown mpltype: {mpltype!r}") + + node_list, messages = inliner.interpreted( + mpltype, f'{mpltype} <{type_to_link_target[mpltype]}>', 'ref', lineno) + return node_list, messages + + +def setup(app): + app.add_role("rc", _rcparam_role) + app.add_role("mpltype", _mpltype_role) + app.add_node( + _QueryReference, + html=(_visit_query_reference_node, _depart_query_reference_node), + latex=(_visit_query_reference_node, _depart_query_reference_node), + text=(_visit_query_reference_node, _depart_query_reference_node), + ) + return {"version": matplotlib.__version__, + "parallel_read_safe": True, "parallel_write_safe": True} diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 39cb99c53d72..741491b3dc58 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -32,7 +32,7 @@ class Spine(mpatches.Patch): def __str__(self): return "Spine" - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, axes, spine_type, path, **kwargs): """ Parameters @@ -53,7 +53,7 @@ def __init__(self, axes, spine_type, path, **kwargs): """ super().__init__(**kwargs) self.axes = axes - self.set_figure(self.axes.figure) + self.set_figure(self.axes.get_figure(root=False)) self.spine_type = spine_type self.set_facecolor('none') self.set_edgecolor(mpl.rcParams['axes.edgecolor']) @@ -174,8 +174,9 @@ def get_window_extent(self, renderer=None): else: padout = 0.5 padin = 0.5 - padout = padout * tickl / 72 * self.figure.dpi - padin = padin * tickl / 72 * self.figure.dpi + dpi = self.get_figure(root=True).dpi + padout = padout * tickl / 72 * dpi + padin = padin * tickl / 72 * dpi if tick.tick1line.get_visible(): if self.spine_type == 'left': @@ -231,12 +232,13 @@ def _clear(self): """ self._position = None # clear position - def _adjust_location(self): - """Automatically set spine bounds to the view interval.""" - - if self.spine_type == 'circle': - return + def _get_bounds_or_viewLim(self): + """ + Get the bounds of the spine. + If self._bounds is None, return self.axes.viewLim.intervalx + or self.axes.viewLim.intervaly based on self.spine_type + """ if self._bounds is not None: low, high = self._bounds elif self.spine_type in ('left', 'right'): @@ -244,7 +246,16 @@ def _adjust_location(self): elif self.spine_type in ('top', 'bottom'): low, high = self.axes.viewLim.intervalx else: - raise ValueError(f'unknown spine spine_type: {self.spine_type}') + raise ValueError(f'spine_type: {self.spine_type} not supported') + return low, high + + def _adjust_location(self): + """Automatically set spine bounds to the view interval.""" + + if self.spine_type == 'circle': + return + + low, high = self._get_bounds_or_viewLim() if self._patch_type == 'arc': if self.spine_type in ('bottom', 'top'): @@ -264,11 +275,17 @@ def _adjust_location(self): self._path = mpath.Path.arc(np.rad2deg(low), np.rad2deg(high)) if self.spine_type == 'bottom': - rmin, rmax = self.axes.viewLim.intervaly + if self.axis is None: + tr = mtransforms.IdentityTransform() + else: + tr = self.axis.get_transform() + rmin, rmax = tr.transform(self.axes.viewLim.intervaly) try: rorigin = self.axes.get_rorigin() except AttributeError: rorigin = rmin + else: + rorigin = tr.transform(rorigin) scaled_diameter = (rmin - rorigin) / (rmax - rorigin) self._height = scaled_diameter self._width = scaled_diameter @@ -368,7 +385,7 @@ def get_spine_transform(self): offset_dots = amount * np.array(offset_vec) / 72 return (base_transform + mtransforms.ScaledTranslation( - *offset_dots, self.figure.dpi_scale_trans)) + *offset_dots, self.get_figure(root=False).dpi_scale_trans)) elif position_type == 'axes': if self.spine_type in ['left', 'right']: # keep y unchanged, fix x at amount @@ -417,7 +434,7 @@ def set_bounds(self, low=None, high=None): 'set_bounds() method incompatible with circular spines') if high is None and np.iterable(low): low, high = low - old_low, old_high = self.get_bounds() or (None, None) + old_low, old_high = self._get_bounds_or_viewLim() if low is None: low = old_low if high is None: diff --git a/lib/matplotlib/spines.pyi b/lib/matplotlib/spines.pyi index 0f06a6d1ce2b..ff2a1a40bf94 100644 --- a/lib/matplotlib/spines.pyi +++ b/lib/matplotlib/spines.pyi @@ -1,5 +1,5 @@ from collections.abc import Callable, Iterator, MutableMapping -from typing import Any, Literal, TypeVar, overload +from typing import Literal, TypeVar, overload import matplotlib.patches as mpatches from matplotlib.axes import Axes diff --git a/lib/matplotlib/stackplot.py b/lib/matplotlib/stackplot.py index dd579bcd5877..25bb2f45a0c4 100644 --- a/lib/matplotlib/stackplot.py +++ b/lib/matplotlib/stackplot.py @@ -6,17 +6,16 @@ (https://stackoverflow.com/users/66549/doug) """ -import itertools import numpy as np -from matplotlib import _api +from matplotlib import cbook, collections, _api, _style_helpers __all__ = ['stackplot'] def stackplot(axes, x, *args, - labels=(), colors=None, hatch=None, baseline='zero', + labels=(), colors=None, baseline='zero', **kwargs): """ Draw a stacked area plot or a streamgraph. @@ -26,11 +25,11 @@ def stackplot(axes, x, *args, x : (N,) array-like y : (M, N) array-like - The data is assumed to be unstacked. Each of the following + The data can be either stacked or unstacked. Each of the following calls is legal:: - stackplot(x, y) # where y has shape (M, N) - stackplot(x, y1, y2, y3) # where y1, y2, y3, y4 have length N + stackplot(x, y) # where y has shape (M, N) e.g. y = [y1, y2, y3, y4] + stackplot(x, y1, y2, y3, y4) # where y1, y2, y3, y4 have length N baseline : {'zero', 'sym', 'wiggle', 'weighted_wiggle'} Method used to calculate the baseline: @@ -55,20 +54,26 @@ def stackplot(axes, x, *args, If not specified, the colors from the Axes property cycle will be used. - hatch : list of str, default: None - A sequence of hatching styles. See - :doc:`/gallery/shapes_and_collections/hatch_style_reference`. - The sequence will be cycled through for filling the - stacked areas from bottom to top. - It need not be exactly the same length as the number - of provided *y*, in which case the styles will repeat from the - beginning. - data : indexable object, optional DATA_PARAMETER_PLACEHOLDER **kwargs - All other keyword arguments are passed to `.Axes.fill_between`. + All other keyword arguments are passed to `.Axes.fill_between`. The + following parameters additionally accept a sequence of values + corresponding to the *y* datasets: + + - *hatch* + - *edgecolor* + - *facecolor* + - *linewidth* + - *linestyle* + + .. versionadded:: 3.9 + Allowing a sequence of strings for *hatch*. + + .. versionadded:: 3.11 + Allowing sequences of values in above listed `.Axes.fill_between` + parameters. Returns ------- @@ -80,15 +85,13 @@ def stackplot(axes, x, *args, y = np.vstack(args) labels = iter(labels) - if colors is not None: - colors = itertools.cycle(colors) - else: - colors = (axes._get_lines.get_next_color() for _ in y) + if colors is None: + colors = [axes._get_lines.get_next_color() for _ in y] + + kwargs = cbook.normalize_kwargs(kwargs, collections.PolyCollection) + kwargs.setdefault('facecolor', colors) - if hatch is None or isinstance(hatch, str): - hatch = itertools.cycle([hatch]) - else: - hatch = itertools.cycle(hatch) + kwargs, style_gen = _style_helpers.style_generator(kwargs) # Assume data passed has not been 'stacked', so stack it here. # We'll need a float buffer for the upcoming calculations. @@ -127,18 +130,14 @@ def stackplot(axes, x, *args, # Color between x = 0 and the first array. coll = axes.fill_between(x, first_line, stack[0, :], - facecolor=next(colors), - hatch=next(hatch), label=next(labels, None), - **kwargs) + **next(style_gen), **kwargs) coll.sticky_edges.y[:] = [0] r = [coll] # Color between array i-1 and array i for i in range(len(y) - 1): r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :], - facecolor=next(colors), - hatch=next(hatch), label=next(labels, None), - **kwargs)) + **next(style_gen), **kwargs)) return r diff --git a/lib/matplotlib/stackplot.pyi b/lib/matplotlib/stackplot.pyi index 2981d449b566..9509f858a4bf 100644 --- a/lib/matplotlib/stackplot.pyi +++ b/lib/matplotlib/stackplot.pyi @@ -16,3 +16,5 @@ def stackplot( baseline: Literal["zero", "sym", "wiggle", "weighted_wiggle"] = ..., **kwargs ) -> list[PolyCollection]: ... + +__all__ = ['stackplot'] diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py index 84f99732c709..725fff7b23fd 100644 --- a/lib/matplotlib/streamplot.py +++ b/lib/matplotlib/streamplot.py @@ -6,7 +6,7 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api, cm, patches +from matplotlib import _api, colorizer, patches import matplotlib.colors as mcolors import matplotlib.collections as mcollections import matplotlib.lines as mlines @@ -19,7 +19,8 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=None, start_points=None, maxlength=4.0, integration_direction='both', - broken_streamlines=True): + broken_streamlines=True, *, integration_max_step_scale=1.0, + integration_max_error_scale=1.0, num_arrows=1): """ Draw streamlines of a vector flow. @@ -73,6 +74,29 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, If False, forces streamlines to continue until they leave the plot domain. If True, they may be terminated if they come too close to another streamline. + integration_max_step_scale : float, default: 1.0 + Multiplier on the maximum allowable step in the streamline integration routine. + A value between zero and one results in a max integration step smaller than + the default max step, resulting in more accurate streamlines at the cost + of greater computation time; a value greater than one does the converse. Must be + greater than zero. + + .. versionadded:: 3.11 + + integration_max_error_scale : float, default: 1.0 + Multiplier on the maximum allowable error in the streamline integration routine. + A value between zero and one results in a tighter max integration error than + the default max error, resulting in more accurate streamlines at the cost + of greater computation time; a value greater than one does the converse. Must be + greater than zero. + + .. versionadded:: 3.11 + + num_arrows : int + Number of arrows per streamline. The arrows are spaced equally along the steps + each streamline takes. Note that this can be different to being spaced equally + along the distance of the streamline. + Returns ------- @@ -92,6 +116,21 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, mask = StreamMask(density) dmap = DomainMap(grid, mask) + if integration_max_step_scale <= 0.0: + raise ValueError( + "The value of integration_max_step_scale must be > 0, " + + f"got {integration_max_step_scale}" + ) + + if integration_max_error_scale <= 0.0: + raise ValueError( + "The value of integration_max_error_scale must be > 0, " + + f"got {integration_max_error_scale}" + ) + + if num_arrows < 0: + raise ValueError(f"The value of num_arrows must be >= 0, got {num_arrows=}") + if zorder is None: zorder = mlines.Line2D.zorder @@ -102,8 +141,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, if color is None: color = axes._get_lines.get_next_color() - if linewidth is None: - linewidth = mpl.rcParams['lines.linewidth'] + linewidth = mpl._val_or_rc(linewidth, 'lines.linewidth') line_kw = {} arrow_kw = dict(arrowstyle=arrowstyle, mutation_scale=10 * arrowsize) @@ -152,7 +190,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, for xm, ym in _gen_starting_points(mask.shape): if mask[ym, xm] == 0: xg, yg = dmap.mask2grid(xm, ym) - t = integrate(xg, yg, broken_streamlines) + t = integrate(xg, yg, broken_streamlines, + integration_max_step_scale, + integration_max_error_scale) if t is not None: trajectories.append(t) else: @@ -180,14 +220,15 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, xg = np.clip(xg, 0, grid.nx - 1) yg = np.clip(yg, 0, grid.ny - 1) - t = integrate(xg, yg, broken_streamlines) + t = integrate(xg, yg, broken_streamlines, integration_max_step_scale, + integration_max_error_scale) if t is not None: trajectories.append(t) if use_multicolor_lines: if norm is None: norm = mcolors.Normalize(color.min(), color.max()) - cmap = cm._ensure_cmap(cmap) + cmap = colorizer._ensure_cmap(cmap) streamlines = [] arrows = [] @@ -206,25 +247,31 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, points = np.transpose([tx, ty]) streamlines.append(points) - # Add arrows halfway along each trajectory. + # Distance along streamline s = np.cumsum(np.hypot(np.diff(tx), np.diff(ty))) - n = np.searchsorted(s, s[-1] / 2.) - arrow_tail = (tx[n], ty[n]) - arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2])) - if isinstance(linewidth, np.ndarray): line_widths = interpgrid(linewidth, tgx, tgy)[:-1] line_kw['linewidth'].extend(line_widths) - arrow_kw['linewidth'] = line_widths[n] - if use_multicolor_lines: color_values = interpgrid(color, tgx, tgy)[:-1] line_colors.append(color_values) - arrow_kw['color'] = cmap(norm(color_values[n])) - p = patches.FancyArrowPatch( - arrow_tail, arrow_head, transform=transform, **arrow_kw) - arrows.append(p) + # Add arrows along each trajectory. + for x in range(1, num_arrows+1): + # Get index of distance along streamline to place arrow + idx = np.searchsorted(s, s[-1] * (x/(num_arrows+1))) + arrow_tail = (tx[idx], ty[idx]) + arrow_head = (np.mean(tx[idx:idx + 2]), np.mean(ty[idx:idx + 2])) + + if isinstance(linewidth, np.ndarray): + arrow_kw['linewidth'] = line_widths[idx] + + if use_multicolor_lines: + arrow_kw['color'] = cmap(norm(color_values[idx])) + + p = patches.FancyArrowPatch( + arrow_tail, arrow_head, transform=transform, **arrow_kw) + arrows.append(p) lc = mcollections.LineCollection( streamlines, transform=transform, **line_kw) @@ -467,7 +514,8 @@ def backward_time(xi, yi): dxi, dyi = forward_time(xi, yi) return -dxi, -dyi - def integrate(x0, y0, broken_streamlines=True): + def integrate(x0, y0, broken_streamlines=True, integration_max_step_scale=1.0, + integration_max_error_scale=1.0): """ Return x, y grid-coordinates of trajectory based on starting point. @@ -487,14 +535,18 @@ def integrate(x0, y0, broken_streamlines=True): return None if integration_direction in ['both', 'backward']: s, xyt = _integrate_rk12(x0, y0, dmap, backward_time, maxlength, - broken_streamlines) + broken_streamlines, + integration_max_step_scale, + integration_max_error_scale) stotal += s xy_traj += xyt[::-1] if integration_direction in ['both', 'forward']: dmap.reset_start_point(x0, y0) s, xyt = _integrate_rk12(x0, y0, dmap, forward_time, maxlength, - broken_streamlines) + broken_streamlines, + integration_max_step_scale, + integration_max_error_scale) stotal += s xy_traj += xyt[1:] @@ -511,7 +563,9 @@ class OutOfBounds(IndexError): pass -def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True): +def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True, + integration_max_step_scale=1.0, + integration_max_error_scale=1.0): """ 2nd-order Runge-Kutta algorithm with adaptive step size. @@ -537,7 +591,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True): # This error is below that needed to match the RK4 integrator. It # is set for visual reasons -- too low and corners start # appearing ugly and jagged. Can be tuned. - maxerror = 0.003 + maxerror = 0.003 * integration_max_error_scale # This limit is important (for all integrators) to avoid the # trajectory skipping some mask cells. We could relax this @@ -546,6 +600,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True): # nature of the interpolation, this doesn't boost speed by much # for quite a bit of complexity. maxds = min(1. / dmap.mask.nx, 1. / dmap.mask.ny, 0.1) + maxds *= integration_max_step_scale ds = maxds stotal = 0 diff --git a/lib/matplotlib/streamplot.pyi b/lib/matplotlib/streamplot.pyi index 9da83096e5a8..ca3553edc2fd 100644 --- a/lib/matplotlib/streamplot.pyi +++ b/lib/matplotlib/streamplot.pyi @@ -28,6 +28,10 @@ def streamplot( maxlength: float = ..., integration_direction: Literal["forward", "backward", "both"] = ..., broken_streamlines: bool = ..., + *, + integration_max_step_scale: float = ..., + integration_max_error_scale: float = ..., + num_arrows: int = ..., ) -> StreamplotSet: ... class StreamplotSet: @@ -80,3 +84,5 @@ class StreamMask: class InvalidIndexError(Exception): ... class TerminateTrajectory(Exception): ... class OutOfBounds(IndexError): ... + +__all__ = ['streamplot'] diff --git a/lib/matplotlib/style/__init__.py b/lib/matplotlib/style/__init__.py index 488c6d6ae1ec..80c6de00a18d 100644 --- a/lib/matplotlib/style/__init__.py +++ b/lib/matplotlib/style/__init__.py @@ -1,4 +1,253 @@ -from .core import available, context, library, reload_library, use +""" +Core functions and attributes for the matplotlib style library: +``use`` + Select style sheet to override the current matplotlib settings. +``context`` + Context manager to use a style sheet temporarily. +``available`` + List available style sheets. Underscore-prefixed names are considered private and + not listed, though may still be accessed directly from ``library``. +``library`` + A dictionary of style names and matplotlib settings. +""" -__all__ = ["available", "context", "library", "reload_library", "use"] +import contextlib +import importlib.resources +import logging +import os +from pathlib import Path +import warnings + +import matplotlib as mpl +from matplotlib import _api, _docstring, rc_params_from_file, rcParamsDefault + +_log = logging.getLogger(__name__) + +__all__ = ['use', 'context', 'available', 'library', 'reload_library'] + + +_BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib') +# Users may want multiple library paths, so store a list of paths. +USER_LIBRARY_PATHS = [os.path.join(mpl.get_configdir(), 'stylelib')] +_STYLE_EXTENSION = 'mplstyle' +# A list of rcParams that should not be applied from styles +_STYLE_BLACKLIST = { + 'interactive', 'backend', 'webagg.port', 'webagg.address', + 'webagg.port_retries', 'webagg.open_in_browser', 'backend_fallback', + 'toolbar', 'timezone', 'figure.max_open_warning', + 'figure.raise_window', 'savefig.directory', 'tk.window_focus', + 'docstring.hardcopy', 'date.epoch'} + + +@_docstring.Substitution( + "\n".join(map("- {}".format, sorted(_STYLE_BLACKLIST, key=str.lower))) +) +def use(style): + """ + Use Matplotlib style settings from a style specification. + + The style name of 'default' is reserved for reverting back to + the default style settings. + + .. note:: + + This updates the `.rcParams` with the settings from the style. + `.rcParams` not defined in the style are kept. + + Parameters + ---------- + style : str, dict, Path or list + + A style specification. Valid options are: + + str + - One of the style names in `.style.available` (a builtin style or + a style installed in the user library path). + + - A dotted name of the form "package.style_name"; in that case, + "package" should be an importable Python package name, e.g. at + ``/path/to/package/__init__.py``; the loaded style file is + ``/path/to/package/style_name.mplstyle``. (Style files in + subpackages are likewise supported.) + + - The path or URL to a style file, which gets loaded by + `.rc_params_from_file`. + + dict + A mapping of key/value pairs for `matplotlib.rcParams`. + + Path + The path to a style file, which gets loaded by + `.rc_params_from_file`. + + list + A list of style specifiers (str, Path or dict), which are applied + from first to last in the list. + + Notes + ----- + The following `.rcParams` are not related to style and will be ignored if + found in a style specification: + + %s + """ + if isinstance(style, (str, Path)) or hasattr(style, 'keys'): + # If name is a single str, Path or dict, make it a single element list. + styles = [style] + else: + styles = style + + style_alias = {'mpl20': 'default', 'mpl15': 'classic'} + + for style in styles: + if isinstance(style, str): + style = style_alias.get(style, style) + if style == "default": + # Deprecation warnings were already handled when creating + # rcParamsDefault, no need to reemit them here. + with _api.suppress_matplotlib_deprecation_warning(): + # don't trigger RcParams.__getitem__('backend') + style = {k: rcParamsDefault[k] for k in rcParamsDefault + if k not in _STYLE_BLACKLIST} + elif style in library: + style = library[style] + elif "." in style: + pkg, _, name = style.rpartition(".") + try: + path = importlib.resources.files(pkg) / f"{name}.{_STYLE_EXTENSION}" + style = rc_params_from_file(path, use_default_template=False) + except (ModuleNotFoundError, OSError, TypeError) as exc: + # There is an ambiguity whether a dotted name refers to a + # package.style_name or to a dotted file path. Currently, + # we silently try the first form and then the second one; + # in the future, we may consider forcing file paths to + # either use Path objects or be prepended with "./" and use + # the slash as marker for file paths. + pass + if isinstance(style, (str, Path)): + try: + style = rc_params_from_file(style, use_default_template=False) + except OSError as err: + raise OSError( + f"{style!r} is not a valid package style, path of style " + f"file, URL of style file, or library style name (library " + f"styles are listed in `style.available`)") from err + filtered = {} + for k in style: # don't trigger RcParams.__getitem__('backend') + if k in _STYLE_BLACKLIST: + _api.warn_external( + f"Style includes a parameter, {k!r}, that is not " + f"related to style. Ignoring this parameter.") + else: + filtered[k] = style[k] + mpl.rcParams.update(filtered) + + +@contextlib.contextmanager +def context(style, after_reset=False): + """ + Context manager for using style settings temporarily. + + Parameters + ---------- + style : str, dict, Path or list + A style specification. Valid options are: + + str + - One of the style names in `.style.available` (a builtin style or + a style installed in the user library path). + + - A dotted name of the form "package.style_name"; in that case, + "package" should be an importable Python package name, e.g. at + ``/path/to/package/__init__.py``; the loaded style file is + ``/path/to/package/style_name.mplstyle``. (Style files in + subpackages are likewise supported.) + + - The path or URL to a style file, which gets loaded by + `.rc_params_from_file`. + dict + A mapping of key/value pairs for `matplotlib.rcParams`. + + Path + The path to a style file, which gets loaded by + `.rc_params_from_file`. + + list + A list of style specifiers (str, Path or dict), which are applied + from first to last in the list. + + after_reset : bool + If True, apply style after resetting settings to their defaults; + otherwise, apply style on top of the current settings. + """ + with mpl.rc_context(): + if after_reset: + mpl.rcdefaults() + use(style) + yield + + +def _update_user_library(library): + """Update style library with user-defined rc files.""" + for stylelib_path in map(os.path.expanduser, USER_LIBRARY_PATHS): + styles = _read_style_directory(stylelib_path) + _update_nested_dict(library, styles) + return library + + +@_api.deprecated("3.11") +def update_user_library(library): + return _update_user_library(library) + + +def _read_style_directory(style_dir): + """Return dictionary of styles defined in *style_dir*.""" + styles = dict() + for path in Path(style_dir).glob(f"*.{_STYLE_EXTENSION}"): + with warnings.catch_warnings(record=True) as warns: + styles[path.stem] = rc_params_from_file(path, use_default_template=False) + for w in warns: + _log.warning('In %s: %s', path, w.message) + return styles + + +@_api.deprecated("3.11") +def read_style_directory(style_dir): + return _read_style_directory(style_dir) + + +def _update_nested_dict(main_dict, new_dict): + """ + Update nested dict (only level of nesting) with new values. + + Unlike `dict.update`, this assumes that the values of the parent dict are + dicts (or dict-like), so you shouldn't replace the nested dict if it + already exists. Instead you should update the sub-dict. + """ + # update named styles specified by user + for name, rc_dict in new_dict.items(): + main_dict.setdefault(name, {}).update(rc_dict) + return main_dict + + +@_api.deprecated("3.11") +def update_nested_dict(main_dict, new_dict): + return _update_nested_dict(main_dict, new_dict) + + +# Load style library +# ================== +_base_library = _read_style_directory(_BASE_LIBRARY_PATH) +library = {} +available = [] + + +def reload_library(): + """Reload the style library.""" + library.clear() + library.update(_update_user_library(_base_library.copy())) + available[:] = sorted(name for name in library if not name.startswith('_')) + + +reload_library() diff --git a/lib/matplotlib/style/__init__.pyi b/lib/matplotlib/style/__init__.pyi new file mode 100644 index 000000000000..c93b504fe6bd --- /dev/null +++ b/lib/matplotlib/style/__init__.pyi @@ -0,0 +1,20 @@ +from collections.abc import Generator +import contextlib + +from matplotlib import RcParams +from matplotlib.typing import RcStyleType + +USER_LIBRARY_PATHS: list[str] = ... + +def use(style: RcStyleType) -> None: ... +@contextlib.contextmanager +def context( + style: RcStyleType, after_reset: bool = ... +) -> Generator[None, None, None]: ... + +library: dict[str, RcParams] +available: list[str] + +def reload_library() -> None: ... + +__all__ = ['use', 'context', 'available', 'library', 'reload_library'] diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 7e9008c56165..c377bc64077a 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -11,235 +11,17 @@ A dictionary of style names and matplotlib settings. """ -import contextlib -import logging -import os -from pathlib import Path -import sys -import warnings - -if sys.version_info >= (3, 10): - import importlib.resources as importlib_resources -else: - # Even though Py3.9 has importlib.resources, it doesn't properly handle - # modules added in sys.path. - import importlib_resources - -import matplotlib as mpl -from matplotlib import _api, _docstring, _rc_params_in_file, rcParamsDefault - -_log = logging.getLogger(__name__) - -__all__ = ['use', 'context', 'available', 'library', 'reload_library'] - - -BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib') -# Users may want multiple library paths, so store a list of paths. -USER_LIBRARY_PATHS = [os.path.join(mpl.get_configdir(), 'stylelib')] -STYLE_EXTENSION = 'mplstyle' -# A list of rcParams that should not be applied from styles -STYLE_BLACKLIST = { - 'interactive', 'backend', 'webagg.port', 'webagg.address', - 'webagg.port_retries', 'webagg.open_in_browser', 'backend_fallback', - 'toolbar', 'timezone', 'figure.max_open_warning', - 'figure.raise_window', 'savefig.directory', 'tk.window_focus', - 'docstring.hardcopy', 'date.epoch'} - - -@_docstring.Substitution( - "\n".join(map("- {}".format, sorted(STYLE_BLACKLIST, key=str.lower))) +from .. import _api +from . import ( + use, context, available, library, reload_library, USER_LIBRARY_PATHS, + _BASE_LIBRARY_PATH as BASE_LIBRARY_PATH, + _STYLE_EXTENSION as STYLE_EXTENSION, + _STYLE_BLACKLIST as STYLE_BLACKLIST, ) -def use(style): - """ - Use Matplotlib style settings from a style specification. - - The style name of 'default' is reserved for reverting back to - the default style settings. - - .. note:: - - This updates the `.rcParams` with the settings from the style. - `.rcParams` not defined in the style are kept. - - Parameters - ---------- - style : str, dict, Path or list - - A style specification. Valid options are: - - str - - One of the style names in `.style.available` (a builtin style or - a style installed in the user library path). - - - A dotted name of the form "package.style_name"; in that case, - "package" should be an importable Python package name, e.g. at - ``/path/to/package/__init__.py``; the loaded style file is - ``/path/to/package/style_name.mplstyle``. (Style files in - subpackages are likewise supported.) - - - The path or URL to a style file, which gets loaded by - `.rc_params_from_file`. - - dict - A mapping of key/value pairs for `matplotlib.rcParams`. - - Path - The path to a style file, which gets loaded by - `.rc_params_from_file`. - - list - A list of style specifiers (str, Path or dict), which are applied - from first to last in the list. - - Notes - ----- - The following `.rcParams` are not related to style and will be ignored if - found in a style specification: - - %s - """ - if isinstance(style, (str, Path)) or hasattr(style, 'keys'): - # If name is a single str, Path or dict, make it a single element list. - styles = [style] - else: - styles = style - - style_alias = {'mpl20': 'default', 'mpl15': 'classic'} - - for style in styles: - if isinstance(style, str): - style = style_alias.get(style, style) - if style == "default": - # Deprecation warnings were already handled when creating - # rcParamsDefault, no need to reemit them here. - with _api.suppress_matplotlib_deprecation_warning(): - # don't trigger RcParams.__getitem__('backend') - style = {k: rcParamsDefault[k] for k in rcParamsDefault - if k not in STYLE_BLACKLIST} - elif style in library: - style = library[style] - elif "." in style: - pkg, _, name = style.rpartition(".") - try: - path = (importlib_resources.files(pkg) - / f"{name}.{STYLE_EXTENSION}") - style = _rc_params_in_file(path) - except (ModuleNotFoundError, OSError, TypeError) as exc: - # There is an ambiguity whether a dotted name refers to a - # package.style_name or to a dotted file path. Currently, - # we silently try the first form and then the second one; - # in the future, we may consider forcing file paths to - # either use Path objects or be prepended with "./" and use - # the slash as marker for file paths. - pass - if isinstance(style, (str, Path)): - try: - style = _rc_params_in_file(style) - except OSError as err: - raise OSError( - f"{style!r} is not a valid package style, path of style " - f"file, URL of style file, or library style name (library " - f"styles are listed in `style.available`)") from err - filtered = {} - for k in style: # don't trigger RcParams.__getitem__('backend') - if k in STYLE_BLACKLIST: - _api.warn_external( - f"Style includes a parameter, {k!r}, that is not " - f"related to style. Ignoring this parameter.") - else: - filtered[k] = style[k] - mpl.rcParams.update(filtered) - - -@contextlib.contextmanager -def context(style, after_reset=False): - """ - Context manager for using style settings temporarily. - - Parameters - ---------- - style : str, dict, Path or list - A style specification. Valid options are: - - str - - One of the style names in `.style.available` (a builtin style or - a style installed in the user library path). - - - A dotted name of the form "package.style_name"; in that case, - "package" should be an importable Python package name, e.g. at - ``/path/to/package/__init__.py``; the loaded style file is - ``/path/to/package/style_name.mplstyle``. (Style files in - subpackages are likewise supported.) - - - The path or URL to a style file, which gets loaded by - `.rc_params_from_file`. - dict - A mapping of key/value pairs for `matplotlib.rcParams`. - - Path - The path to a style file, which gets loaded by - `.rc_params_from_file`. - - list - A list of style specifiers (str, Path or dict), which are applied - from first to last in the list. - - after_reset : bool - If True, apply style after resetting settings to their defaults; - otherwise, apply style on top of the current settings. - """ - with mpl.rc_context(): - if after_reset: - mpl.rcdefaults() - use(style) - yield - - -def update_user_library(library): - """Update style library with user-defined rc files.""" - for stylelib_path in map(os.path.expanduser, USER_LIBRARY_PATHS): - styles = read_style_directory(stylelib_path) - update_nested_dict(library, styles) - return library - - -def read_style_directory(style_dir): - """Return dictionary of styles defined in *style_dir*.""" - styles = dict() - for path in Path(style_dir).glob(f"*.{STYLE_EXTENSION}"): - with warnings.catch_warnings(record=True) as warns: - styles[path.stem] = _rc_params_in_file(path) - for w in warns: - _log.warning('In %s: %s', path, w.message) - return styles - - -def update_nested_dict(main_dict, new_dict): - """ - Update nested dict (only level of nesting) with new values. - - Unlike `dict.update`, this assumes that the values of the parent dict are - dicts (or dict-like), so you shouldn't replace the nested dict if it - already exists. Instead you should update the sub-dict. - """ - # update named styles specified by user - for name, rc_dict in new_dict.items(): - main_dict.setdefault(name, {}).update(rc_dict) - return main_dict - - -# Load style library -# ================== -_base_library = read_style_directory(BASE_LIBRARY_PATH) -library = {} -available = [] - - -def reload_library(): - """Reload the style library.""" - library.clear() - library.update(update_user_library(_base_library)) - available[:] = sorted(library.keys()) +__all__ = [ + "use", "context", "available", "library", "reload_library", + "USER_LIBRARY_PATHS", "BASE_LIBRARY_PATH", "STYLE_EXTENSION", "STYLE_BLACKLIST", +] -reload_library() +_api.warn_deprecated("3.11", name=__name__, obj_type="module") diff --git a/lib/matplotlib/style/core.pyi b/lib/matplotlib/style/core.pyi index 73400492143c..ee21d2f41ef5 100644 --- a/lib/matplotlib/style/core.pyi +++ b/lib/matplotlib/style/core.pyi @@ -5,7 +5,9 @@ from matplotlib import RcParams from matplotlib.typing import RcStyleType USER_LIBRARY_PATHS: list[str] = ... +BASE_LIBRARY_PATH: str = ... STYLE_EXTENSION: str = ... +STYLE_BLACKLIST: set[str] = ... def use(style: RcStyleType) -> None: ... @contextlib.contextmanager @@ -17,3 +19,8 @@ library: dict[str, RcParams] available: list[str] def reload_library() -> None: ... + +__all__ = [ + "use", "context", "available", "library", "reload_library", + "USER_LIBRARY_PATHS", "BASE_LIBRARY_PATH", "STYLE_EXTENSION", "STYLE_BLACKLIST", +] diff --git a/lib/matplotlib/style/meson.build b/lib/matplotlib/style/meson.build index 03e7972132bb..e7a183c8581c 100644 --- a/lib/matplotlib/style/meson.build +++ b/lib/matplotlib/style/meson.build @@ -4,6 +4,7 @@ python_sources = [ ] typing_sources = [ + '__init__.pyi', 'core.pyi', ] diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 7d8c8ec4c3f4..91dddba6c31f 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -33,6 +33,8 @@ from .transforms import Bbox from .path import Path +from .cbook import _is_pandas_dataframe + class Cell(Rectangle): """ @@ -103,7 +105,6 @@ def __init__(self, xy, width, height, *, text=text, fontproperties=fontproperties, horizontalalignment=loc, verticalalignment='center') - @_api.rename_parameter("3.8", "trans", "t") def set_transform(self, t): super().set_transform(t) # the text does not get the transform! @@ -176,7 +177,7 @@ def get_required_width(self, renderer): l, b, w, h = self.get_text_bounds(renderer) return w * (1.0 + (2.0 * self.PAD)) - @_docstring.dedent_interpd + @_docstring.interpd def set_text_props(self, **kwargs): """ Update the text properties. @@ -239,6 +240,13 @@ class Table(Artist): """ A table of cells. + .. note:: + + ``table()`` has some fundamental design limitations and will not be + developed further. If you need more functionality, consider + `blume `__. + + The table consists of a grid of cells, which are indexed by (row, column). For a simple table, you'll have a full grid of cells with indices from @@ -303,7 +311,7 @@ def __init__(self, ax, loc=None, bbox=None, **kwargs): "Unrecognized location {!r}. Valid locations are\n\t{}" .format(loc, '\n\t'.join(self.codes))) loc = self.codes[loc] - self.set_figure(ax.figure) + self.set_figure(ax.get_figure(root=False)) self._axes = ax self._loc = loc self._bbox = bbox @@ -354,7 +362,7 @@ def __setitem__(self, position, cell): except Exception as err: raise KeyError('Only tuples length 2 are accepted as ' 'coordinates') from err - cell.set_figure(self.figure) + cell.set_figure(self.get_figure(root=False)) cell.set_transform(self.get_transform()) cell.set_clip_on(False) self._cells[row, col] = cell @@ -389,7 +397,7 @@ def edges(self, value): self.stale = True def _approx_text_height(self): - return (self.FONTSIZE / 72.0 * self.figure.dpi / + return (self.FONTSIZE / 72.0 * self.get_figure(root=True).dpi / self._axes.bbox.height * 1.2) @allow_rasterization @@ -399,7 +407,7 @@ def draw(self, renderer): # Need a renderer to do hit tests on mouseevent; assume the last one # will do if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() if renderer is None: raise RuntimeError('No renderer defined') @@ -432,7 +440,7 @@ def contains(self, mouseevent): return False, {} # TODO: Return index of the cell containing the cursor so that the user # doesn't have to bind to each one individually. - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() if renderer is not None: boxes = [cell.get_window_extent(renderer) for (row, col), cell in self._cells.items() @@ -449,7 +457,7 @@ def get_children(self): def get_window_extent(self, renderer=None): # docstring inherited if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() self._update_positions(renderer) boxes = [cell.get_window_extent(renderer) for cell in self._cells.values()] @@ -497,11 +505,7 @@ def auto_set_column_width(self, col): """ col1d = np.atleast_1d(col) if not np.issubdtype(col1d.dtype, np.integer): - _api.warn_deprecated("3.8", name="col", - message="%(name)r must be an int or sequence of ints. " - "Passing other types is deprecated since %(since)s " - "and will be removed %(removal)s.") - return + raise TypeError("col must be an int or sequence of ints.") for cell in col1d: self._autoColumns.append(cell) @@ -650,7 +654,7 @@ def get_celld(self): return self._cells -@_docstring.dedent_interpd +@_docstring.interpd def table(ax, cellText=None, cellColours=None, cellLoc='right', colWidths=None, @@ -661,6 +665,12 @@ def table(ax, """ Add a table to an `~.axes.Axes`. + .. note:: + + ``table()`` has some fundamental design limitations and will not be + developed further. If you need more functionality, consider + `blume `__. + At least one of *cellText* or *cellColours* must be specified. These parameters must be 2D lists, in which the outer lists define the rows and the inner list define the column values per row. Each row must have the @@ -675,7 +685,7 @@ def table(ax, Parameters ---------- - cellText : 2D list of str, optional + cellText : 2D list of str or pandas.DataFrame, optional The texts to place into the table cells. *Note*: Line breaks in the strings are currently not accounted for and @@ -745,6 +755,21 @@ def table(ax, cols = len(cellColours[0]) cellText = [[''] * cols] * rows + # Check if we have a Pandas DataFrame + if _is_pandas_dataframe(cellText): + # if rowLabels/colLabels are empty, use DataFrame entries. + # Otherwise, throw an error. + if rowLabels is None: + rowLabels = cellText.index + else: + raise ValueError("rowLabels cannot be used alongside Pandas DataFrame") + if colLabels is None: + colLabels = cellText.columns + else: + raise ValueError("colLabels cannot be used alongside Pandas DataFrame") + # Update cellText with only values + cellText = cellText.values + rows = len(cellText) cols = len(cellText[0]) for row in cellText: @@ -826,5 +851,9 @@ def table(ax, if rowLabelWidth == 0: table.auto_set_column_width(-1) + # set_fontsize is only effective after cells are added + if "fontsize" in kwargs: + table.set_fontsize(kwargs["fontsize"]) + ax.add_table(table) return table diff --git a/lib/matplotlib/table.pyi b/lib/matplotlib/table.pyi index 0108ecd99f89..167d98d3c4cb 100644 --- a/lib/matplotlib/table.pyi +++ b/lib/matplotlib/table.pyi @@ -10,6 +10,8 @@ from .typing import ColorType from collections.abc import Sequence from typing import Any, Literal +from pandas import DataFrame + class Cell(Rectangle): PAD: float def __init__( @@ -68,7 +70,7 @@ class Table(Artist): def table( ax: Axes, - cellText: Sequence[Sequence[str]] | None = ..., + cellText: Sequence[Sequence[str]] | DataFrame | None = ..., cellColours: Sequence[Sequence[ColorType]] | None = ..., cellLoc: Literal["left", "center", "right"] = ..., colWidths: Sequence[float] | None = ..., diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 8e60267ed608..9fcdb6aeee03 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -1,13 +1,15 @@ """ Helper functions for testing. """ -from pathlib import Path -from tempfile import TemporaryDirectory +import itertools import locale import logging import os +from pathlib import Path +import string import subprocess import sys +from tempfile import TemporaryDirectory import matplotlib as mpl from matplotlib import _api @@ -17,8 +19,15 @@ def set_font_settings_for_testing(): mpl.rcParams['font.family'] = 'DejaVu Sans' - mpl.rcParams['text.hinting'] = 'none' - mpl.rcParams['text.hinting_factor'] = 8 + # We've changed the default for ourselves here, but for backwards-compatibility, use + # the old setting if not called in our own tests (which would set + # `_called_from_pytest` from our `conftest.py`). + if getattr(mpl, '_called_from_pytest', False): + mpl.rcParams['text.hinting'] = 'default' + mpl.rcParams['text.hinting_factor'] = 1 + else: + mpl.rcParams['text.hinting'] = 'none' + mpl.rcParams['text.hinting_factor'] = 8 def set_reproducibility_for_testing(): @@ -52,7 +61,7 @@ def setup(): def subprocess_run_for_testing(command, env=None, timeout=60, stdout=None, stderr=None, check=False, text=True, - capture_output=False): + capture_output=False, **kwargs): """ Create and run a subprocess. @@ -85,17 +94,29 @@ def subprocess_run_for_testing(command, env=None, timeout=60, stdout=None, Raises ------ + pytest.skip + If running on emscripten, which does not support subprocesses. pytest.xfail If platform is Cygwin and subprocess reports a fork() failure. """ + if sys.platform == 'emscripten': + import pytest + pytest.skip('emscripten does not support subprocesses') if capture_output: stdout = stderr = subprocess.PIPE + # Add CREATE_NO_WINDOW flag on Windows to prevent console window overhead + # This is added in an attempt to fix flaky timeouts of subprocesses on Windows + if sys.platform == 'win32': + if 'creationflags' not in kwargs: + kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW + else: + kwargs['creationflags'] |= subprocess.CREATE_NO_WINDOW try: proc = subprocess.run( command, env=env, timeout=timeout, check=check, stdout=stdout, stderr=stderr, - text=text + text=text, **kwargs ) except BlockingIOError: if sys.platform == "cygwin": @@ -103,6 +124,16 @@ def subprocess_run_for_testing(command, env=None, timeout=60, stdout=None, import pytest pytest.xfail("Fork failure") raise + except subprocess.CalledProcessError as e: + if e.stdout: + _log.error(f"Subprocess output:\n{e.stdout}") + if e.stderr: + _log.error(f"Subprocess error:\n{e.stderr}") + raise e + if proc.stdout: + _log.debug(f"Subprocess output:\n{proc.stdout}") + if proc.stderr: + _log.debug(f"Subprocess error:\n{proc.stderr}") return proc @@ -132,13 +163,26 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None): f"_module = importlib.util.module_from_spec(_spec);" f"_spec.loader.exec_module(_module);" f"_module.{target}()", - *args + *args, ], - env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, - timeout=timeout, check=True, + env={ + **os.environ, + "SOURCE_DATE_EPOCH": "0", + # subprocess_run_helper sets SOURCE_DATE_EPOCH=0 above, so for a dirty tree, + # the version will have the date 19700101 which breaks pickle tests with a + # warning if the working tree is dirty. + # + # This will also avoid at least one additional subprocess call for + # setuptools-scm query git, so we tell the subprocess what version + # to report as the test process. + "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MATPLOTLIB": mpl.__version__, + **(extra_env or {}), + }, + timeout=timeout, + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - text=True + text=True, ) return proc @@ -164,7 +208,8 @@ def _check_for_pgf(texsystem): """, encoding="utf-8") try: subprocess.check_call( - [texsystem, "-halt-on-error", str(tex_path)], cwd=tmpdir, + [texsystem, "-halt-on-error", "-no-shell-escape", + str(tex_path)], cwd=tmpdir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except (OSError, subprocess.CalledProcessError): return False @@ -175,7 +220,7 @@ def _has_tex_package(package): try: mpl.dviread.find_tex_file(f"{package}.sty") return True - except FileNotFoundError: + except (FileNotFoundError, OSError): return False @@ -211,3 +256,67 @@ def ipython_in_subprocess(requested_backend_or_gui_framework, all_expected_backe ) assert proc.stdout.strip().endswith(f"'{expected_backend}'") + + +def is_ci_environment(): + # Common CI variables + ci_environment_variables = [ + 'CI', # Generic CI environment variable + 'CONTINUOUS_INTEGRATION', # Generic CI environment variable + 'TRAVIS', # Travis CI + 'CIRCLECI', # CircleCI + 'JENKINS', # Jenkins + 'GITLAB_CI', # GitLab CI + 'GITHUB_ACTIONS', # GitHub Actions + 'TEAMCITY_VERSION' # TeamCity + # Add other CI environment variables as needed + ] + + for env_var in ci_environment_variables: + if os.getenv(env_var): + return True + + return False + + +def _gen_multi_font_text(): + """ + Generate text intended for use with multiple fonts to exercise font fallbacks. + + Returns + ------- + fonts : list of str + The names of the fonts used to render the test string, sorted by intended + priority. This should be set as the font family for the Figure or Text artist. + text : str + The test string. + """ + # These fonts are serif and sans-serif, and would not normally be combined, but that + # should make it easier to see which glyph is from which font. + fonts = ['cmr10', 'DejaVu Sans'] + # cmr10 does not contain accented characters, so they should fall back to DejaVu + # Sans. However, some accented capital A versions *are* in cmr10 with non-standard + # glyph shapes, so don't test those (otherwise this Latin1 supplement group would + # start at 0xA0.) + start = 0xC5 + latin1_supplement = [chr(x) for x in range(start, 0xFF+1)] + latin_extended_A = [chr(x) for x in range(0x100, 0x17F+1)] + latin_extended_B = [chr(x) for x in range(0x180, 0x24F+1)] + non_basic_multilingual_plane = [chr(x) for x in range(0x1F600, 0x1F610)] + count = itertools.count(start - 0xA0) + non_basic_characters = '\n'.join( + ''.join(line) + for _, line in itertools.groupby( # Replace with itertools.batched for Py3.12+. + [*latin1_supplement, *latin_extended_A, *latin_extended_B, + *non_basic_multilingual_plane], + key=lambda x: next(count) // 32) # 32 characters per line. + ) + test_str = f"""There are basic characters +{string.ascii_uppercase} {string.ascii_lowercase} +{string.digits} {string.punctuation} +and accented characters +{non_basic_characters} +in between!""" + # The resulting string contains 491 unique characters. Some file formats use 8-bit + # tables, which the large number of characters exercises twice over. + return fonts, test_str diff --git a/lib/matplotlib/testing/__init__.pyi b/lib/matplotlib/testing/__init__.pyi index 1f52a8ccb8ee..accf973615fa 100644 --- a/lib/matplotlib/testing/__init__.pyi +++ b/lib/matplotlib/testing/__init__.pyi @@ -16,6 +16,7 @@ def subprocess_run_for_testing( *, text: Literal[True], capture_output: bool = ..., + **kwargs, ) -> subprocess.CompletedProcess[str]: ... @overload def subprocess_run_for_testing( @@ -27,6 +28,7 @@ def subprocess_run_for_testing( check: bool = ..., text: Literal[False] = ..., capture_output: bool = ..., + **kwargs, ) -> subprocess.CompletedProcess[bytes]: ... @overload def subprocess_run_for_testing( @@ -38,6 +40,7 @@ def subprocess_run_for_testing( check: bool = ..., text: bool = ..., capture_output: bool = ..., + **kwargs, ) -> subprocess.CompletedProcess[bytes] | subprocess.CompletedProcess[str]: ... def subprocess_run_helper( func: Callable[[], None], @@ -51,3 +54,5 @@ def ipython_in_subprocess( requested_backend_or_gui_framework: str, all_expected_backends: dict[tuple[int, int], str], ) -> None: ... +def is_ci_environment() -> bool: ... +def _gen_multi_font_text() -> tuple[list[str], str]: ... diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index ee93061480e7..2b94847a72b6 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -13,12 +13,13 @@ import sys from tempfile import TemporaryDirectory, TemporaryFile import weakref +import re import numpy as np from PIL import Image import matplotlib as mpl -from matplotlib import cbook +from matplotlib import cbook, _image from matplotlib.testing.exceptions import ImageComparisonFailure _log = logging.getLogger(__name__) @@ -45,22 +46,20 @@ def get_cache_dir(): def get_file_hash(path, block_size=2 ** 20): - md5 = hashlib.md5() + sha256 = hashlib.sha256(usedforsecurity=False) with open(path, 'rb') as fd: while True: data = fd.read(block_size) if not data: break - md5.update(data) + sha256.update(data) if Path(path).suffix == '.pdf': - md5.update(str(mpl._get_executable_info("gs").version) - .encode('utf-8')) + sha256.update(str(mpl._get_executable_info("gs").version).encode('utf-8')) elif Path(path).suffix == '.svg': - md5.update(str(mpl._get_executable_info("inkscape").version) - .encode('utf-8')) + sha256.update(str(mpl._get_executable_info("inkscape").version).encode('utf-8')) - return md5.hexdigest() + return sha256.hexdigest() class _ConverterError(Exception): @@ -98,6 +97,16 @@ def _read_until(self, terminator): return bytes(buf) +class _MagickConverter: + def __call__(self, orig, dest): + try: + subprocess.run( + [mpl._get_executable_info("magick").executable, orig, dest], + check=True) + except subprocess.CalledProcessError as e: + raise _ConverterError() from e + + class _GSConverter(_Converter): def __call__(self, orig, dest): if not self._proc: @@ -215,7 +224,7 @@ def __del__(self): class _SVGWithMatplotlibFontsConverter(_SVGConverter): """ - A SVG converter which explicitly adds the fonts shipped by Matplotlib to + An SVG converter which explicitly adds the fonts shipped by Matplotlib to Inkspace's font search path, to better support `svg.fonttype = "none"` (which is in particular used by certain mathtext tests). """ @@ -229,6 +238,12 @@ def __call__(self, orig, dest): def _update_converter(): + try: + mpl._get_executable_info("magick") + except mpl.ExecutableNotFoundError: + pass + else: + converter['gif'] = _MagickConverter() try: mpl._get_executable_info("gs") except mpl.ExecutableNotFoundError: @@ -299,13 +314,21 @@ def convert(filename, cache): _log.debug("For %s: converting to png.", filename) convert = converter[path.suffix[1:]] if path.suffix == ".svg": - contents = path.read_text() + contents = path.read_text(encoding="utf-8") # NOTE: This check should be kept in sync with font styling in # `lib/matplotlib/backends/backend_svg.py`. If it changes, then be sure to # re-generate any SVG test files using this mode, or else such tests will # fail to use the converter for the expected images (but will for the # results), and the tests will fail strangely. - if 'style="font:' in contents: + if re.search( + # searches for attributes : + # style=[font|font-size|font-weight| + # font-family|font-variant|font-style] + # taking care of the possibility of multiple style attributes + # before the font styling (i.e. opacity) + r'style="[^"]*font(|-size|-weight|-family|-variant|-style):', + contents # raw contents of the svg file + ): # for svg.fonttype = none, we explicitly patch the font search # path so that fonts shipped by Matplotlib are found. convert = _svg_with_matplotlib_fonts_converter @@ -388,8 +411,8 @@ def compare_images(expected, actual, tol, in_decorator=False): Compare two "image" files checking differences within a tolerance. The two given filenames may point to files which are convertible to - PNG via the `.converter` dictionary. The underlying RMS is calculated - with the `.calculate_rms` function. + PNG via the `!converter` dictionary. The underlying RMS is calculated + in a similar way to the `.calculate_rms` function. Parameters ---------- @@ -460,17 +483,12 @@ def compare_images(expected, actual, tol, in_decorator=False): if np.array_equal(expected_image, actual_image): return None - # convert to signed integers, so that the images can be subtracted without - # overflow - expected_image = expected_image.astype(np.int16) - actual_image = actual_image.astype(np.int16) - - rms = calculate_rms(expected_image, actual_image) + rms, abs_diff = _image.calculate_rms_and_diff(expected_image, actual_image) if rms <= tol: return None - save_diff_image(expected, actual, diff_image) + Image.fromarray(abs_diff).save(diff_image, format="png") results = dict(rms=rms, expected=str(expected), actual=str(actual), diff=str(diff_image), tol=tol) diff --git a/lib/matplotlib/testing/conftest.py b/lib/matplotlib/testing/conftest.py index c285c247e7b4..c60a38254aad 100644 --- a/lib/matplotlib/testing/conftest.py +++ b/lib/matplotlib/testing/conftest.py @@ -1,5 +1,8 @@ -import pytest +import os import sys + +import pytest + import matplotlib from matplotlib import _api @@ -80,9 +83,29 @@ def mpl_test_settings(request): matplotlib.use(prev_backend) +@pytest.fixture +def high_memory(pytestconfig): + from matplotlib.testing import is_ci_environment + if not (os.environ.get('MPL_TEST_EXPENSIVE') or is_ci_environment()): + pytest.skip('Test uses too much memory') + + @pytest.fixture def pd(): - """Fixture to import and configure pandas.""" + """ + Fixture to import and configure pandas. Using this fixture, the test is skipped when + pandas is not installed. Use this fixture instead of importing pandas in test files. + + Examples + -------- + Request the pandas fixture by passing in ``pd`` as an argument to the test :: + + def test_matshow_pandas(pd): + + df = pd.DataFrame({'x':[1,2,3], 'y':[4,5,6]}) + im = plt.figure().subplots().matshow(df) + np.testing.assert_array_equal(im.get_array(), df) + """ pd = pytest.importorskip('pandas') try: from pandas.plotting import ( @@ -95,6 +118,82 @@ def pd(): @pytest.fixture def xr(): - """Fixture to import xarray.""" + """ + Fixture to import xarray so that the test is skipped when xarray is not installed. + Use this fixture instead of importing xrray in test files. + + Examples + -------- + Request the xarray fixture by passing in ``xr`` as an argument to the test :: + + def test_imshow_xarray(xr): + + ds = xr.DataArray(np.random.randn(2, 3)) + im = plt.figure().subplots().imshow(ds) + np.testing.assert_array_equal(im.get_array(), ds) + """ + xr = pytest.importorskip('xarray') return xr + + +@pytest.fixture +def text_placeholders(monkeypatch): + """ + Replace texts with placeholder rectangles. + + The rectangle size only depends on the font size and the number of characters. It is + thus insensitive to font properties and rendering details. This should be used for + tests that depend on text geometries but not the actual text rendering, e.g. layout + tests. + """ + from matplotlib.patches import Rectangle + + def patched_get_sfnt_table(font, name): + """ + Replace ``FT2Font.get_sfnt_table`` with empty results. + + This forces ``Text._get_layout`` to fall back to + ``get_text_width_height_descent``, which produces results from the patch below. + """ + return None + + def patched_get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi): + """ + Replace ``_get_text_metrics_with_cache`` with fixed results. + + The usual ``renderer.get_text_width_height_descent`` would depend on font + metrics; instead the fixed results are based on font size and the length of the + string only. + """ + # While get_window_extent returns pixels and font size is in points, font size + # includes ascenders and descenders. Leaving out this factor and setting + # descent=0 ends up with a box that is relatively close to DejaVu Sans. + height = fontprop.get_size() + width = len(text) * height / 1.618 # Golden ratio for character size. + descent = 0 + return width, height, descent + + def patched_text_draw(self, renderer): + """ + Replace ``Text.draw`` with a fixed bounding box Rectangle. + + The bounding box corresponds to ``Text.get_window_extent``, which ultimately + depends on the above patched ``_get_text_metrics_with_cache``. + """ + if renderer is not None: + self._renderer = renderer + if not self.get_visible(): + return + if self.get_text() == '': + return + bbox = self.get_window_extent() + rect = Rectangle(bbox.p0, bbox.width, bbox.height, + facecolor=self.get_color(), edgecolor='none') + rect.draw(renderer) + + monkeypatch.setattr('matplotlib.ft2font.FT2Font.get_sfnt_table', + patched_get_sfnt_table) + monkeypatch.setattr('matplotlib.text._get_text_metrics_with_cache', + patched_get_text_metrics_with_cache) + monkeypatch.setattr('matplotlib.text.Text.draw', patched_text_draw) diff --git a/lib/matplotlib/testing/conftest.pyi b/lib/matplotlib/testing/conftest.pyi index 2af0eb93cc8a..df9f3bdeb36f 100644 --- a/lib/matplotlib/testing/conftest.pyi +++ b/lib/matplotlib/testing/conftest.pyi @@ -7,6 +7,10 @@ def pytest_unconfigure(config: pytest.Config) -> None: ... @pytest.fixture def mpl_test_settings(request: pytest.FixtureRequest) -> None: ... @pytest.fixture +def high_memory(pytestconfig: pytest.Config) -> None: ... +@pytest.fixture def pd() -> ModuleType: ... @pytest.fixture def xr() -> ModuleType: ... +@pytest.fixture +def text_placeholders(monkeypatch: pytest.MonkeyPatch) -> None: ... diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 49ac4a1506f8..f404d7ae84ee 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -15,6 +15,7 @@ import matplotlib.units import matplotlib.testing from matplotlib import _pylab_helpers, cbook, ft2font, pyplot as plt, ticker +from matplotlib.figure import Figure from .compare import comparable_formats, compare_images, make_test_filename from .exceptions import ImageComparisonFailure @@ -138,6 +139,8 @@ def copy_baseline(self, baseline, extension): try: if 'microsoft' in uname().release.lower(): raise OSError # On WSL, symlink breaks silently + if sys.platform == 'emscripten': + raise OSError os.symlink(orig_expected_path, expected_fname) except OSError: # On Windows, symlink *may* be unavailable. shutil.copyfile(orig_expected_path, expected_fname) @@ -204,6 +207,7 @@ def wrapper(*args, extension, request, **kwargs): if extension not in comparable_formats(): reason = { + 'gif': 'because ImageMagick is not installed', 'pdf': 'because Ghostscript is not installed', 'eps': 'because Ghostscript is not installed', 'svg': 'because Inkscape is not installed', @@ -263,7 +267,7 @@ def image_comparison(baseline_images, extensions=None, tol=0, style=("classic", "_classic_test_patch")): """ Compare images generated by the test with those specified in - *baseline_images*, which must correspond, else an `ImageComparisonFailure` + *baseline_images*, which must correspond, else an `.ImageComparisonFailure` exception will be raised. Parameters @@ -279,7 +283,7 @@ def image_comparison(baseline_images, extensions=None, tol=0, extensions : None or list of str The list of extensions to test, e.g. ``['png', 'pdf']``. - If *None*, defaults to all supported extensions: png, pdf, and svg. + If *None*, defaults to: png, pdf, and svg. When testing a single extension, it can be directly included in the names passed to *baseline_images*. In that case, *extensions* must not @@ -346,7 +350,7 @@ def image_comparison(baseline_images, extensions=None, tol=0, savefig_kwargs=savefig_kwarg, style=style) -def check_figures_equal(*, extensions=("png", "pdf", "svg"), tol=0): +def check_figures_equal(*, extensions=("png", ), tol=0): """ Decorator for test cases that generate and compare two figures. @@ -359,8 +363,13 @@ def check_figures_equal(*, extensions=("png", "pdf", "svg"), tol=0): Parameters ---------- - extensions : list, default: ["png", "pdf", "svg"] - The extensions to test. + extensions : list, default: ["png"] + The extensions to test. Supported extensions are "png", "pdf", "svg". + + Testing with the one default extension is sufficient if the output is not + format dependent, e.g. if you test that a ``bar()`` plot yields the same + result as some manually placed Rectangles. You should use all extensions + if a renderer property is involved, e.g. correct alpha blending. tol : float The RMS threshold above which the test is considered failed. @@ -404,27 +413,21 @@ def wrapper(*args, ext, request, **kwargs): file_name = "".join(c for c in request.node.name if c in ALLOWED_CHARS) - try: - fig_test = plt.figure("test") - fig_ref = plt.figure("reference") - with _collect_new_figures() as figs: - func(*args, fig_test=fig_test, fig_ref=fig_ref, **kwargs) - if figs: - raise RuntimeError('Number of open figures changed during ' - 'test. Make sure you are plotting to ' - 'fig_test or fig_ref, or if this is ' - 'deliberate explicitly close the ' - 'new figure(s) inside the test.') - test_image_path = result_dir / (file_name + "." + ext) - ref_image_path = result_dir / (file_name + "-expected." + ext) - fig_test.savefig(test_image_path) - fig_ref.savefig(ref_image_path) - _raise_on_image_difference( - ref_image_path, test_image_path, tol=tol - ) - finally: - plt.close(fig_test) - plt.close(fig_ref) + fig_test = Figure() + fig_ref = Figure() + func(*args, fig_test=fig_test, fig_ref=fig_ref, **kwargs) + if len(fig_test.get_children()) == 1 and len(fig_ref.get_children()) == 1: + # no artists have been added. The only child is fig.patch. + raise RuntimeError("Both figures are empty. Make sure you are " + "plotting to fig_test or fig_ref.") + + test_image_path = result_dir / (file_name + "." + ext) + ref_image_path = result_dir / (file_name + "-expected." + ext) + fig_test.savefig(test_image_path) + fig_ref.savefig(ref_image_path) + _raise_on_image_difference( + ref_image_path, test_image_path, tol=tol + ) parameters = [ param diff --git a/lib/matplotlib/testing/jpl_units/UnitDbl.py b/lib/matplotlib/testing/jpl_units/UnitDbl.py index 5226c06ad54b..95869740ace2 100644 --- a/lib/matplotlib/testing/jpl_units/UnitDbl.py +++ b/lib/matplotlib/testing/jpl_units/UnitDbl.py @@ -49,7 +49,7 @@ def __init__(self, value, units): - value The numeric value of the UnitDbl. - units The string name of the units the value is in. """ - data = _api.check_getitem(self.allowed, units=units) + data = _api.getitem_checked(self.allowed, units=units) self._value = float(value * data[0]) self._units = data[1] @@ -69,7 +69,7 @@ def convert(self, units): """ if self._units == units: return self._value - data = _api.check_getitem(self.allowed, units=units) + data = _api.getitem_checked(self.allowed, units=units) if self._units != data[1]: raise ValueError(f"Error trying to convert to different units.\n" f" Invalid conversion requested.\n" diff --git a/lib/matplotlib/testing/widgets.py b/lib/matplotlib/testing/widgets.py index 748cdaccc7e9..c528ffb2537c 100644 --- a/lib/matplotlib/testing/widgets.py +++ b/lib/matplotlib/testing/widgets.py @@ -8,6 +8,8 @@ from unittest import mock +from matplotlib import _api +from matplotlib.backend_bases import MouseEvent, KeyEvent import matplotlib.pyplot as plt @@ -16,7 +18,7 @@ def get_ax(): fig, ax = plt.subplots(1, 1) ax.plot([0, 200], [0, 200]) ax.set_aspect(1.0) - ax.figure.canvas.draw() + fig.canvas.draw() return ax @@ -24,6 +26,7 @@ def noop(*args, **kwargs): pass +@_api.deprecated("3.11", alternative="MouseEvent or KeyEvent") def mock_event(ax, button=1, xdata=0, ydata=0, key=None, step=1): r""" Create a mock event that can stand in for `.Event` and its subclasses. @@ -57,7 +60,7 @@ def mock_event(ax, button=1, xdata=0, ydata=0, key=None, step=1): (xdata, ydata)])[0] event.xdata, event.ydata = xdata, ydata event.inaxes = ax - event.canvas = ax.figure.canvas + event.canvas = ax.get_figure(root=True).canvas event.key = key event.step = step event.guiEvent = None @@ -65,6 +68,7 @@ def mock_event(ax, button=1, xdata=0, ydata=0, key=None, step=1): return event +@_api.deprecated("3.11", alternative="callbacks.process(event)") def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1): """ Trigger an event on the given tool. @@ -105,15 +109,12 @@ def click_and_drag(tool, start, end, key=None): An optional key that is pressed during the whole operation (see also `.KeyEvent`). """ - if key is not None: - # Press key - do_event(tool, 'on_key_press', xdata=start[0], ydata=start[1], - button=1, key=key) + ax = tool.ax + if key is not None: # Press key + KeyEvent._from_ax_coords("key_press_event", ax, start, key)._process() # Click, move, and release mouse - do_event(tool, 'press', xdata=start[0], ydata=start[1], button=1) - do_event(tool, 'onmove', xdata=end[0], ydata=end[1], button=1) - do_event(tool, 'release', xdata=end[0], ydata=end[1], button=1) - if key is not None: - # Release key - do_event(tool, 'on_key_release', xdata=end[0], ydata=end[1], - button=1, key=key) + MouseEvent._from_ax_coords("button_press_event", ax, start, 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, end, 1)._process() + MouseEvent._from_ax_coords("button_release_event", ax, end, 1)._process() + if key is not None: # Release key + KeyEvent._from_ax_coords("key_release_event", ax, end, key)._process() diff --git a/lib/matplotlib/tests/baseline_images/dviread/lualatex.json b/lib/matplotlib/tests/baseline_images/dviread/lualatex.json new file mode 100644 index 000000000000..8f2d95017ec7 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/dviread/lualatex.json @@ -0,0 +1 @@ +[{"text": [[5046272, 4128768, "A", "lmroman10-regular.otf", 9.96, {}], [5756027, 4128768, "L", "lmroman10-regular.otf", 9.96, {}], [5929697, 4012179, "A", "lmroman7-regular.otf", 6.97, {}], [6218125, 4128768, "T", "lmroman10-regular.otf", 9.96, {}], [6582045, 4269998, "E", "lmroman10-regular.otf", 9.96, {}], [6946425, 4128768, "X", "lmroman10-regular.otf", 9.96, {}], [7656180, 4128768, "d", "DejaVuSans.ttf", 9.96, {"extend": 1.25, "slant": 0.25, "embolden": 0.25}], [8072180, 4128768, "o", "DejaVuSans.ttf", 9.96, {"extend": 1.25, "slant": 0.25, "embolden": 0.25}], [8473140, 4128768, "c", "DejaVuSans.ttf", 9.96, {"extend": 1.25, "slant": 0.25, "embolden": 0.25}], [8833460, 4128768, ".", "DejaVuSans.ttf", 9.96, {"extend": 1.25, "slant": 0.25, "embolden": 0.25}]], "boxes": []}, {"text": [[13686374, 5056284, "\u03c0", "cmmi5.pfb", 4.98, {}], [13716923, 5390308, "2", "cmr5.pfb", 4.98, {}], [13355110, 5463127, "integraldisplay", "cmex10.pfb", 9.96, {}], [13406537, 7324364, "0", "cmr7.pfb", 6.97, {}], [14010471, 5627696, "parenleftBig", "cmex10.pfb", 9.96, {}], [14937513, 5911796, "x", "cmmi10.pfb", 9.96, {}], [14480510, 6804696, "s", "lmroman10-regular.otf", 9.96, {}], [14738721, 6804696, "i", "lmroman10-regular.otf", 9.96, {}], [14920911, 6804696, "n", "lmroman10-regular.otf", 9.96, {}], [15394516, 6804696, "x", "cmmi10.pfb", 9.96, {}], [15847715, 5627696, "parenrightBig", "cmex10.pfb", 9.96, {}], [16239111, 5763501, "2", "cmr7.pfb", 6.97, {}], [16642338, 6355152, "d", "lmroman10-regular.otf", 9.96, {}], [17006718, 6355152, "x", "cmmi10.pfb", 9.96, {}]], "boxes": [[13686374, 5130818, 26213, 284106], [14480510, 6204418, 26213, 1288562]]}] diff --git a/lib/matplotlib/tests/baseline_images/dviread/pdflatex.json b/lib/matplotlib/tests/baseline_images/dviread/pdflatex.json new file mode 100644 index 000000000000..4754b722aa58 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/dviread/pdflatex.json @@ -0,0 +1 @@ +[{"text": [[5046272, 4128768, "A", "cmr10.pfb", 9.96, {}], [5756246, 4128768, "L", "cmr10.pfb", 9.96, {}], [5929917, 3994421, "A", "cmr7.pfb", 6.97, {}], [6218464, 4128768, "T", "cmr10.pfb", 9.96, {}], [6582530, 4269852, "E", "cmr10.pfb", 9.96, {}], [6946620, 4128768, "X", "cmr10.pfb", 9.96, {}], [7656594, 4128768, "d", "cmr10.pfb", 9.96, {}], [8020684, 4128768, "o", "cmr10.pfb", 9.96, {}], [8366570, 4128768, "c", "cmr10.pfb", 9.96, {}], [8657841, 4128768, ".", "cmr10.pfb", 9.96, {}]], "boxes": []}, {"text": [[13686591, 5056284, "\u03c0", "cmmi5.pfb", 4.98, {}], [13717140, 5390308, "2", "cmr5.pfb", 4.98, {}], [13355327, 5463127, "integraldisplay", "cmex10.pfb", 9.96, {}], [13406754, 7324364, "0", "cmr7.pfb", 6.97, {}], [14010688, 5627696, "parenleftBig", "cmex10.pfb", 9.96, {}], [14937658, 5911796, "x", "cmmi10.pfb", 9.96, {}], [14480727, 6804696, "s", "cmr10.pfb", 9.96, {}], [14739230, 6804696, "i", "cmr10.pfb", 9.96, {}], [14921275, 6804696, "n", "cmr10.pfb", 9.96, {}], [15394589, 6804696, "x", "cmmi10.pfb", 9.96, {}], [15847788, 5627696, "parenrightBig", "cmex10.pfb", 9.96, {}], [16239184, 5763501, "2", "cmr7.pfb", 6.97, {}], [16642411, 6355152, "d", "cmr10.pfb", 9.96, {}], [17006501, 6355152, "x", "cmmi10.pfb", 9.96, {}]], "boxes": [[13686591, 5130818, 26213, 284106], [14480727, 6204418, 26213, 1288418]]}] diff --git a/lib/matplotlib/tests/baseline_images/dviread/test.dvi b/lib/matplotlib/tests/baseline_images/dviread/test.dvi deleted file mode 100644 index 93751ffdcba0..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/dviread/test.dvi and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/dviread/test.json b/lib/matplotlib/tests/baseline_images/dviread/test.json deleted file mode 100644 index 0809cb9531f1..000000000000 --- a/lib/matplotlib/tests/baseline_images/dviread/test.json +++ /dev/null @@ -1,94 +0,0 @@ -[ - { - "text": [ - [5046272, 4128768, "T", "cmr10", 9.96], - [5519588, 4128768, "h", "cmr10", 9.96], - [5883678, 4128768, "i", "cmr10", 9.96], - [6065723, 4128768, "s", "cmr10", 9.96], - [6542679, 4128768, "i", "cmr10", 9.96], - [6724724, 4128768, "s", "cmr10", 9.96], - [7201680, 4128768, "a", "cmr10", 9.96], - [7747814, 4128768, "L", "cmr10", 9.96], - [7921485, 3994421, "A", "cmr7", 6.97], - [8210032, 4128768, "T", "cmr10", 9.96], - [8574098, 4269852, "E", "cmr10", 9.96], - [8938188, 4128768, "X", "cmr10", 9.96], - [9648162, 4128768, "t", "cmr10", 9.96], - [9903025, 4128768, "e", "cmr10", 9.96], - [10194296, 4128768, "s", "cmr10", 9.96], - [10452799, 4128768, "t", "cmr10", 9.96], - [10926115, 4128768, "d", "cmr10", 9.96], - [11290205, 4128768, "o", "cmr10", 9.96], - [11636091, 4128768, "c", "cmr10", 9.96], - [11927362, 4128768, "u", "cmr10", 9.96], - [12291452, 4128768, "m", "cmr10", 9.96], - [12837587, 4128768, "e", "cmr10", 9.96], - [13128858, 4128768, "n", "cmr10", 9.96], - [13474743, 4128768, "t", "cmr10", 9.96], - [4063232, 4915200, "f", "cmr10", 9.96], - [4263482, 4915200, "o", "cmr10", 9.96], - [4591163, 4915200, "r", "cmr10", 9.96], - [5066299, 4915200, "t", "cmr10", 9.96], - [5321162, 4915200, "e", "cmr10", 9.96], - [5612433, 4915200, "s", "cmr10", 9.96], - [5870936, 4915200, "t", "cmr10", 9.96], - [6125799, 4915200, "i", "cmr10", 9.96], - [6307844, 4915200, "n", "cmr10", 9.96], - [6671934, 4915200, "g", "cmr10", 9.96], - [7218068, 4915200, "m", "cmr10", 9.96], - [7764203, 4915200, "a", "cmr10", 9.96], - [8091884, 4915200, "t", "cmr10", 9.96], - [8346747, 4915200, "p", "cmr10", 9.96], - [8710837, 4915200, "l", "cmr10", 9.96], - [8892882, 4915200, "o", "cmr10", 9.96], - [9220563, 4915200, "t", "cmr10", 9.96], - [9475426, 4915200, "l", "cmr10", 9.96], - [9657471, 4915200, "i", "cmr10", 9.96], - [9839516, 4915200, "b", "cmr10", 9.96], - [10203606, 4915200, "'", "cmr10", 9.96], - [10385651, 4915200, "s", "cmr10", 9.96], - [10862607, 4915200, "d", "cmr10", 9.96], - [11226697, 4915200, "v", "cmr10", 9.96], - [11572583, 4915200, "i", "cmr10", 9.96], - [11754628, 4915200, "r", "cmr10", 9.96], - [12011311, 4915200, "e", "cmr10", 9.96], - [12302582, 4915200, "a", "cmr10", 9.96], - [12630263, 4915200, "d", "cmr10", 9.96], - [13686591, 6629148, "\u0019", "cmmi5", 4.98], - [13717140, 6963172, "2", "cmr5", 4.98], - [13355327, 7035991, "Z", "cmex10", 9.96], - [13406754, 8897228, "0", "cmr7", 6.97], - [14010688, 7200560, "\u0010", "cmex10", 9.96], - [14937658, 7484660, "x", "cmmi10", 9.96], - [14480727, 8377560, "s", "cmr10", 9.96], - [14739230, 8377560, "i", "cmr10", 9.96], - [14921275, 8377560, "n", "cmr10", 9.96], - [15394589, 8377560, "x", "cmmi10", 9.96], - [15847788, 7200560, "\u0011", "cmex10", 9.96], - [16239184, 7336365, "2", "cmr7", 6.97], - [16642411, 7928016, "d", "cmr10", 9.96], - [17006501, 7928016, "x", "cmmi10", 9.96] - ], - "boxes": [ - [4063232, 5701632, 65536, 22609920], - [13686591, 6703682, 26213, 284106], - [14480727, 7777282, 26213, 1288418] - ] - }, - { - "text": [ - [5046272, 4128768, "a", "cmr10", 9.96], - [5373953, 4128768, "n", "cmr10", 9.96], - [5738043, 4128768, "o", "cmr10", 9.96], - [6065724, 4128768, "t", "cmr10", 9.96], - [6320587, 4128768, "h", "cmr10", 9.96], - [6684677, 4128768, "e", "cmr10", 9.96], - [6975948, 4128768, "r", "cmr10", 9.96], - [7451084, 4128768, "p", "cmr10", 9.96], - [7815174, 4128768, "a", "cmr10", 9.96], - [8142855, 4128768, "g", "cmr10", 9.96], - [8470536, 4128768, "e", "cmr10", 9.96] - ], - "boxes": [] - } -] diff --git a/lib/matplotlib/tests/baseline_images/dviread/test.tex b/lib/matplotlib/tests/baseline_images/dviread/test.tex index 33220fedae3e..4a2d4720c065 100644 --- a/lib/matplotlib/tests/baseline_images/dviread/test.tex +++ b/lib/matplotlib/tests/baseline_images/dviread/test.tex @@ -1,17 +1,19 @@ -% source file for test.dvi \documentclass{article} +\usepackage{iftex} +\iftutex\usepackage{fontspec}\fi % xetex or luatex \pagestyle{empty} + \begin{document} -This is a \LaTeX\ test document\\ -for testing matplotlib's dviread +A \LaTeX { + \iftutex\fontspec{DejaVuSans.ttf}[ + FakeSlant=0.25, FakeStretch=1.25, FakeBold=2.5, Color=0000FF]\fi + doc. +} -\noindent\rule{\textwidth}{1pt} +\newpage \[ \int\limits_0^{\frac{\pi}{2}} \Bigl(\frac{x}{\sin x}\Bigr)^2\,\mathrm{d}x \] \special{Special!} -\newpage -another page - \end{document} diff --git a/lib/matplotlib/tests/baseline_images/dviread/xelatex.json b/lib/matplotlib/tests/baseline_images/dviread/xelatex.json new file mode 100644 index 000000000000..8fb81ddf0c7e --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/dviread/xelatex.json @@ -0,0 +1 @@ +[{"text": [[5046272, 4128768, "A", "lmroman10-regular.otf", 9.96, {}], [5756027, 4128768, "L", "lmroman10-regular.otf", 9.96, {}], [5929697, 4012179, "A", "lmroman7-regular.otf", 6.97, {}], [6218125, 4128768, "T", "lmroman10-regular.otf", 9.96, {}], [6582045, 4269998, "E", "lmroman10-regular.otf", 9.96, {}], [6946425, 4128768, "X", "lmroman10-regular.otf", 9.96, {}], [7656180, 4128768, "d", "DejaVuSans.ttf", 9.96, {"rgba": [0, 0, 255, 255], "extend": 1.25, "slant": 0.25, "embolden": 0.25}], [8176180, 4128768, "o", "DejaVuSans.ttf", 9.96, {"rgba": [0, 0, 255, 255], "extend": 1.25, "slant": 0.25, "embolden": 0.25}], [8677380, 4128768, "c", "DejaVuSans.ttf", 9.96, {"rgba": [0, 0, 255, 255], "extend": 1.25, "slant": 0.25, "embolden": 0.25}], [9127780, 4128768, ".", "DejaVuSans.ttf", 9.96, {"rgba": [0, 0, 255, 255], "extend": 1.25, "slant": 0.25, "embolden": 0.25}]], "boxes": []}, {"text": [[13686374, 5056284, "\u03c0", "cmmi5.pfb", 4.98, {}], [13716923, 5390308, "2", "cmr5.pfb", 4.98, {}], [13355110, 5463127, "integraldisplay", "cmex10.pfb", 9.96, {}], [13406537, 7324364, "0", "cmr7.pfb", 6.97, {}], [14010471, 5627696, "parenleftBig", "cmex10.pfb", 9.96, {}], [14937513, 5911796, "x", "cmmi10.pfb", 9.96, {}], [14480510, 6804696, "s", "lmroman10-regular.otf", 9.96, {}], [14738722, 6804696, "i", "lmroman10-regular.otf", 9.96, {}], [14920912, 6804696, "n", "lmroman10-regular.otf", 9.96, {}], [15394516, 6804696, "x", "cmmi10.pfb", 9.96, {}], [15847715, 5627696, "parenrightBig", "cmex10.pfb", 9.96, {}], [16239111, 5763501, "2", "cmr7.pfb", 6.97, {}], [16642338, 6355152, "d", "lmroman10-regular.otf", 9.96, {}], [17006718, 6355152, "x", "cmmi10.pfb", 9.96, {}]], "boxes": [[13686374, 5130818, 26213, 284106], [14480510, 6204418, 26213, 1288562]]}] diff --git a/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.gif b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.gif new file mode 100644 index 000000000000..a99e5c7456a3 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.gif differ diff --git a/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.pdf b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.pdf index 3d9d77f1a8ec..997900512110 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.pdf and b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.png b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.png index dce047707da8..5221b493e160 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.png and b/lib/matplotlib/tests/baseline_images/test_agg_filter/agg_filter_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_adjustment_test_image.png b/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_adjustment_test_image.png new file mode 100644 index 000000000000..976c830edcbd Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_adjustment_test_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png b/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png index 11ad0b89b4db..f9362197ad8f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png and b/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.pdf b/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.pdf deleted file mode 100644 index 124014fc4869..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.svg b/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.svg deleted file mode 100644 index 6fb9d9d64991..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_arrow_patches/fancyarrow_test_image.svg +++ /dev/null @@ -1,2891 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.pdf b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.pdf index 054fe8d8264f..cac3b8f7751e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.pdf and b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.png b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.png index cf2ebc38391d..1846832dc3f3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.png and b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.svg b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.svg index e6743bd2a79b..eb2fc6501453 100644 --- a/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.svg +++ b/lib/matplotlib/tests/baseline_images/test_artist/clip_path_clipping.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-05-18T15:59:59.749730 + image/svg+xml + + + Matplotlib v3.11.0.dev842+g991ee94077, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 576 432 L 576 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,10 +35,10 @@ L 274.909091 388.8 L 274.909091 43.2 L 72 43.2 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" clip-path="url(#p4234805953)" style="fill: url(#h8da01be9d9); fill-opacity: 0.7; stroke: #0000ff; stroke-opacity: 0.7; stroke-width: 5"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -162,94 +173,94 @@ L 0 4 - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -262,10 +273,10 @@ L 518.4 388.8 L 518.4 43.2 L 315.490909 43.2 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" clip-path="url(#p1824667f16)" style="fill: url(#h8da01be9d9); opacity: 0.7; stroke: #0000ff; stroke-width: 5; stroke-linejoin: miter"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -390,84 +401,84 @@ L 518.4 43.2 - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -475,7 +486,7 @@ L 518.4 43.2 - + - + - + + +z +" style="fill: #0000ff; stroke: #0000ff; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 0.7"/> diff --git a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png index 5c771fb23216..1db4f18ea98d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png and b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_artist/hatching.pdf b/lib/matplotlib/tests/baseline_images/test_artist/hatching.pdf index c812f811812a..df8dcbeed8e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/hatching.pdf and b/lib/matplotlib/tests/baseline_images/test_artist/hatching.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.pdf b/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.pdf deleted file mode 100644 index 42652378a5d0..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.svg b/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.svg deleted file mode 100644 index e87ecb06a6bd..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/autoscale_tiny_range.svg +++ /dev/null @@ -1,763 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.pdf b/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.pdf deleted file mode 100644 index d034617daa24..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.png b/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.png index 65e032b20dca..25203d28051c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.png and b/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.svg b/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.svg deleted file mode 100644 index 7b05c11742e5..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/axhspan_epoch.svg +++ /dev/null @@ -1,723 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.pdf b/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.pdf deleted file mode 100644 index 2d9006680410..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.png b/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.png index 833a8d77be1d..a43db5bff5cb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.png and b/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.svg b/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.svg deleted file mode 100644 index 54ce8e9308f5..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/axvspan_epoch.svg +++ /dev/null @@ -1,719 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple.png b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple.png index 4f8e5815ba73..542bc145ccf6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple.png and b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple_old_label_alignment.png b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple_old_label_alignment.png index 11523f308363..2dffb4a35b55 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple_old_label_alignment.png and b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_multiple_old_label_alignment.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_single.png b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_single.png index a89e8697bdc7..1f07545ad922 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_single.png and b/lib/matplotlib/tests/baseline_images/test_axes/bar_tick_label_single.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/barh_tick_label.png b/lib/matplotlib/tests/baseline_images/test_axes/barh_tick_label.png index 7e63910badd0..c77021d6dfb5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/barh_tick_label.png and b/lib/matplotlib/tests/baseline_images/test_axes/barh_tick_label.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.pdf b/lib/matplotlib/tests/baseline_images/test_axes/boxplot.pdf deleted file mode 100644 index c34dddea28c9..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.png b/lib/matplotlib/tests/baseline_images/test_axes/boxplot.png index adcf44ec780c..3860b73ca22f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.png and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.svg b/lib/matplotlib/tests/baseline_images/test_axes/boxplot.svg deleted file mode 100644 index 6774f852dc9e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/boxplot.svg +++ /dev/null @@ -1,397 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_false_whiskers.png b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_false_whiskers.png index 14675de05163..3675f801cc34 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_false_whiskers.png and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_false_whiskers.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_true_whiskers.png b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_true_whiskers.png index c6f11f0411ae..b10c0d22cb38 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_true_whiskers.png and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_autorange_true_whiskers.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_custom_capwidths.png b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_custom_capwidths.png index 6282584ca548..784c0269d758 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_custom_capwidths.png and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_custom_capwidths.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf deleted file mode 100644 index c424bc5e982f..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg deleted file mode 100644 index c3a3cda7a9a0..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - - - 2024-04-17T16:38:51.018485 - image/svg+xml - - - Matplotlib v3.9.0.dev1517+g1fa7dd164e.d20240417, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_baseline.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_baseline.png index 44de05620d19..22d7f5430f4c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_baseline.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_baseline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidth.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidth.png index 2e7c530beecf..513586b42250 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidth.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidth.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidths.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidths.png index 1d8e44ccbecd..9fedf7f41d80 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidths.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_custom_capwidths.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_percentilewhis.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_percentilewhis.png index 29853ae8d778..1ab340a27017 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_percentilewhis.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_percentilewhis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_rangewhis.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_rangewhis.png index d9dc4379c6ae..30f857f5439a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_rangewhis.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_rangewhis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_xlabels.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_xlabels.png index f864ac1c4267..677c6430c607 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_xlabels.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_xlabels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_ylabels.png b/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_ylabels.png index af0fb83ddd22..beb9e916d821 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_ylabels.png and b/lib/matplotlib/tests/baseline_images/test_axes/bxp_with_ylabels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf b/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf index 617efead8028..a8d3252c5ab9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.png b/lib/matplotlib/tests/baseline_images/test_axes/canonical.png index cde84119c3cc..509d8f92b0a0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/canonical.png and b/lib/matplotlib/tests/baseline_images/test_axes/canonical.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg b/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg index 75819df16c66..26e76e8338cd 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg @@ -1,412 +1,518 @@ - - + + + + + + 2026-04-02T23:33:31.612880 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+g6b5bbf332, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - + + + + + + + + + + + + + + + + - + - - - - - + + + + - - - - - +" transform="scale(0.015625)"/> + + + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + - + - - - - + + + + - - - - - +" transform="scale(0.015625)"/> + + + + + - + - + + + + + + + + + + + + - + - - - - - - - - - + + + + + + + - + - + + + + + + + + + + + + - + - - - - - - + + + + + + + - + + + + + + - + + + + + + + + + + + + - + - - - - - - - - - + + + + + + + - - - + - - - - + + + + + + + + + + + + - - - - + - - - - - - + + + + + + + - + - + + + + + + + + + + + + - + - - - - - - + + + + + + + - + - - - - - - + - - - - - - + + + + + + + - - - - - - - + + - + - - - - - - + + + + + + + - - - - - - - + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.pdf b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.pdf index d38d94962848..334e7eb8e3ed 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.png b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.png index 8fd8e5c018d6..2ffece597ca7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.png and b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.svg b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.svg index bf0b1f15812d..f79b5632f71c 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/contour_colorbar.svg @@ -6,11 +6,11 @@ - 2023-05-08T08:38:16.254819 + 2026-03-12T19:45:49.237181 image/svg+xml - Matplotlib v3.8.0.dev1017+g22694d6944.d20230508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -309,7 +309,7 @@ L 264.681664 68.942565 L 266.599087 70.000101 L 267.778802 70.543059 z -" clip-path="url(#p20b471bf05)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #67001f"/> +" clip-path="url(#p80c72a205c)" style="fill: #a51429"/> +" clip-path="url(#p80c72a205c)" style="fill: #e48066"/> +" clip-path="url(#p80c72a205c)" style="fill: #fcdfcf"/> +" clip-path="url(#p80c72a205c)" style="fill: #d7e8f1"/> +" clip-path="url(#p80c72a205c)" style="fill: #6bacd1"/> +" clip-path="url(#p80c72a205c)" style="fill: #1c5c9f"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> +" clip-path="url(#p80c72a205c)" style="fill: #053061"/> - - + - + - - - - + + - + - + - - - + + - + - + - - - + + - + - + - - + - + - - + + - + - - + + - + - - + + - + - + - - + - + - + - - + @@ -14611,132 +14611,132 @@ z - - + - - - + + + - + - - - + + + - + - - - + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + + +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> - +" clip-path="url(#p80c72a205c)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> + +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #67001f"/> +" clip-path="url(#p17027362ae)" style="fill: #a51429"/> +" clip-path="url(#p17027362ae)" style="fill: #e48066"/> +" clip-path="url(#p17027362ae)" style="fill: #fcdfcf"/> +" clip-path="url(#p17027362ae)" style="fill: #d7e8f1"/> +" clip-path="url(#p17027362ae)" style="fill: #6bacd1"/> +" clip-path="url(#p17027362ae)" style="fill: #1c5c9f"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> +" clip-path="url(#p17027362ae)" style="fill: #053061"/> - - + - + - - - - - - + + + + - + - - - - - + + + + + - + - + - - - - - + + + + - + - - - - - + + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + @@ -18533,57 +18533,57 @@ z +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p3360d9719c)" style="fill: none; stroke: #bfbf00; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> +" clip-path="url(#p6116f3d6ae)" style="fill: none; stroke: #00bfbf; stroke-width: 2"/> + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.pdf b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.pdf deleted file mode 100644 index 503bdf11dabc..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.png b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.png index c52285cae2ca..fbf4e99f85e9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.png and b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.svg b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.svg deleted file mode 100644 index 8ea6f573d94b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_limits.svg +++ /dev/null @@ -1,1673 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.pdf b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.pdf deleted file mode 100644 index c145cbbeb74d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.png b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.png index 9f3ff4488a99..0e8ee85a28c7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.png and b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.svg b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.svg deleted file mode 100644 index b4d97d7d0e9f..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_mixed.svg +++ /dev/null @@ -1,2391 +0,0 @@ - - - - - - - - 2022-01-07T01:42:44.033823 - image/svg+xml - - - Matplotlib v3.6.0.dev1138+gd48fca95df.d20220107, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.pdf b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.pdf deleted file mode 100644 index 10773bfe9083..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.png b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.png index e21e2b9e119e..6f6179ec6360 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.png and b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.svg b/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.svg deleted file mode 100644 index 5e25759468d1..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/errorbar_zorder.svg +++ /dev/null @@ -1,1015 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/eventplot.pdf b/lib/matplotlib/tests/baseline_images/test_axes/eventplot.pdf deleted file mode 100644 index e98c3f6d6b34..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/eventplot.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/eventplot.svg b/lib/matplotlib/tests/baseline_images/test_axes/eventplot.svg deleted file mode 100644 index dcc145527b7c..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/eventplot.svg +++ /dev/null @@ -1,2628 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/extent_units.png b/lib/matplotlib/tests/baseline_images/test_axes/extent_units.png index 28bde8bf76ec..a831390647e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/extent_units.png and b/lib/matplotlib/tests/baseline_images/test_axes/extent_units.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.pdf b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.pdf deleted file mode 100644 index 6a624dea46c8..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.svg b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.svg deleted file mode 100644 index 2e77acfd7601..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate.svg +++ /dev/null @@ -1,1267 +0,0 @@ - - - - - - - - 2021-02-17T21:57:55.184111 - image/svg+xml - - - Matplotlib v3.3.4.post2378.dev0+g01d3149b6, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.pdf b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.pdf deleted file mode 100644 index 4042ec1c9fba..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.svg b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.svg deleted file mode 100644 index 857e8c92f833..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_decreasing.svg +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.pdf b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.pdf deleted file mode 100644 index dffa63bd7972..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.svg b/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.svg deleted file mode 100644 index a200927ef2fe..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/fill_between_interpolate_nan.svg +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - - 2021-02-17T21:51:47.989640 - image/svg+xml - - - Matplotlib v3.3.4.post2378.dev0+g01d3149b6, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/fill_units.png b/lib/matplotlib/tests/baseline_images/test_axes/fill_units.png index 497154993f93..b69837954299 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/fill_units.png and b/lib/matplotlib/tests/baseline_images/test_axes/fill_units.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.pdf b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.pdf deleted file mode 100644 index 43a2c30a0368..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.png b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.png index aabd4ac94536..d300993681b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.png and b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.svg b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.svg deleted file mode 100644 index 9b2b29fd4e98..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_001.svg +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.pdf b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.pdf deleted file mode 100644 index 3465f1bd42d0..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.png b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.png index f078fe0132b6..a5d532a6cc5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.png and b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.svg b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.svg deleted file mode 100644 index 03adfb5247d3..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_002.svg +++ /dev/null @@ -1,905 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.pdf b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.pdf deleted file mode 100644 index f071d453dc7b..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.png b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.png index 28ac41050b6f..ad0e570afd68 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.png and b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.svg b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.svg deleted file mode 100644 index 9aa79e5a566f..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_003.svg +++ /dev/null @@ -1,905 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.pdf b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.pdf deleted file mode 100644 index ad0fa75eafae..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.png b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.png index af03f2706e62..3cec5e5de795 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.png and b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.svg b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.svg deleted file mode 100644 index d4c6c2b72e4c..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_004.svg +++ /dev/null @@ -1,796 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.pdf b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.pdf deleted file mode 100644 index 21f9705474e8..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.png b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.png index 11306b83ce6c..1a302ad56950 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.png and b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.svg b/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.svg deleted file mode 100644 index d1eea00c3208..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/formatter_ticker_005.svg +++ /dev/null @@ -1,798 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/grouped_bar.png b/lib/matplotlib/tests/baseline_images/test_axes/grouped_bar.png new file mode 100644 index 000000000000..5f294ec64f93 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/grouped_bar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png b/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png index 466519461aac..7dc68e25fae6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png and b/lib/matplotlib/tests/baseline_images/test_axes/hexbin_log.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist2d.pdf deleted file mode 100644 index 8343cc2efd97..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg deleted file mode 100644 index 025441b800db..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.pdf deleted file mode 100644 index d7a58f772a40..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg deleted file mode 100644 index 8111cb56486a..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_density.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_density.png index d75fc1fd8849..68273e23a2c9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_density.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_density.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf deleted file mode 100644 index 9c6e73351b0d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg deleted file mode 100644 index d0825b425ba3..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_log.svg +++ /dev/null @@ -1,468 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.pdf deleted file mode 100644 index 45cc03fb511f..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.png index eb0f25ceb27f..4aaf8551aa9b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.svg deleted file mode 100644 index e3865b8cf8bf..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_offset.svg +++ /dev/null @@ -1,646 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.pdf deleted file mode 100644 index 8c1e835c2729..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.png index 62648f7316a4..e524ccaf7739 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.svg deleted file mode 100644 index c5bcbc473301..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_bar.svg +++ /dev/null @@ -1,1485 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.pdf deleted file mode 100644 index c63109d24640..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.png index 8c8ed086421f..a9bf4abf95f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.svg deleted file mode 100644 index a48c7fdd2eda..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_normed.svg +++ /dev/null @@ -1,657 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.pdf deleted file mode 100644 index a9e7412b235b..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.png index f9cc06935326..e5236c4b41c9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.svg deleted file mode 100644 index 19e4ea8a8969..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_step.svg +++ /dev/null @@ -1,553 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.pdf deleted file mode 100644 index 4c55a0fa010d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.png index b4501e742c59..e673c066cb9b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.svg deleted file mode 100644 index 5be44c0cf6b7..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled.svg +++ /dev/null @@ -1,591 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.pdf deleted file mode 100644 index 182020177eda..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.png index 3ae2167b0220..7350f908a21b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.svg deleted file mode 100644 index 7d595a98a5ef..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_stepfilled_alpha.svg +++ /dev/null @@ -1,591 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.pdf deleted file mode 100644 index f1c383b62486..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.png index 88fc91a155fb..0b84f702fd53 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.svg deleted file mode 100644 index 452839806945..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked_weights.svg +++ /dev/null @@ -1,610 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_step_horiz.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_step_horiz.png index c1be634320d5..c451da4dce6f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hist_step_horiz.png and b/lib/matplotlib/tests/baseline_images/test_axes/hist_step_horiz.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hlines_basic.png b/lib/matplotlib/tests/baseline_images/test_axes/hlines_basic.png index 6d73964e0d54..224247f1611f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hlines_basic.png and b/lib/matplotlib/tests/baseline_images/test_axes/hlines_basic.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hlines_masked.png b/lib/matplotlib/tests/baseline_images/test_axes/hlines_masked.png index 30500a08d538..c7619a45e09e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hlines_masked.png and b/lib/matplotlib/tests/baseline_images/test_axes/hlines_masked.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hlines_with_nan.png b/lib/matplotlib/tests/baseline_images/test_axes/hlines_with_nan.png index 4ab611a2ad6a..9f14dddfd61a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/hlines_with_nan.png and b/lib/matplotlib/tests/baseline_images/test_axes/hlines_with_nan.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow.pdf b/lib/matplotlib/tests/baseline_images/test_axes/imshow.pdf index 183b072fc312..f50aff2b4190 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/imshow.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/imshow.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow.png b/lib/matplotlib/tests/baseline_images/test_axes/imshow.png index c19c4e069b15..d6f4bb78250e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/imshow.png and b/lib/matplotlib/tests/baseline_images/test_axes/imshow.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow.svg b/lib/matplotlib/tests/baseline_images/test_axes/imshow.svg index 3931a1fce23f..ba0ebee53d2c 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/imshow.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/imshow.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-01-30T01:51:17.751114 + image/svg+xml + + + Matplotlib v3.11.0.dev1729+g1f7cad29d, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,50 +35,50 @@ L 369.216 307.584 L 369.216 41.472 L 103.104 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + @@ -76,40 +87,40 @@ L 0 3.5 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + @@ -117,28 +128,28 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.pdf b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.pdf index f4bbc73544a5..f80bd4b76868 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.png b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.png index cde64b03c7f6..d7505003ba78 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.png and b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.svg b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.svg index d1169e860808..c1f98c3c72c1 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/imshow_clip.svg @@ -1,23 +1,23 @@ - + - + - 2021-03-02T20:09:49.859581 + 2026-03-12T19:45:26.730441 image/svg+xml - Matplotlib v3.3.4.post2495+g8432e3164, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -35,29 +35,29 @@ L 369.216 307.584 L 369.216 41.472 L 103.104 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - +" transform="scale(0.015625)"/> - + - + - + - +" transform="scale(0.015625)"/> - - + + - + - + - +" transform="scale(0.015625)"/> - - + + - + - + - +" transform="scale(0.015625)"/> - - + + - + - + - +" transform="scale(0.015625)"/> - - + + @@ -264,80 +264,80 @@ z - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - + + - + - - - + + + - + - - - + + + - + - - - + + + - + - - - + + + - - + +" clip-path="url(#pb72bf0dac8)" style="fill: none; stroke: #440154; stroke-width: 1.5"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery.pdf deleted file mode 100644 index 8a2a4887d2d7..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery.png b/lib/matplotlib/tests/baseline_images/test_axes/markevery.png index 6363e0a89c9d..69e08632f555 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery.png and b/lib/matplotlib/tests/baseline_images/test_axes/markevery.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery.svg deleted file mode 100644 index 8ae4cdac28a1..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery.svg +++ /dev/null @@ -1,987 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.pdf deleted file mode 100644 index 9bff9fcd374d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.png b/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.png index 91cbb0c2ec0c..ffb59042caae 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.png and b/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.svg deleted file mode 100644 index db71bd78fa57..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_line.svg +++ /dev/null @@ -1,1407 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.pdf deleted file mode 100644 index e0f266c1670c..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.svg deleted file mode 100644 index 760903302ecd..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales.svg +++ /dev/null @@ -1,3177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.pdf deleted file mode 100644 index bd4feafd6aa0..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.svg deleted file mode 100644 index c52aaf9de094..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_nans.svg +++ /dev/null @@ -1,3581 +0,0 @@ - - - - - - - - 2022-03-28T18:57:12.789026 - image/svg+xml - - - Matplotlib v3.6.0.dev1706+g252085fd25, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.pdf deleted file mode 100644 index 103c8c292503..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.svg deleted file mode 100644 index 746e66f65dfb..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_linear_scales_zoomed.svg +++ /dev/null @@ -1,3420 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.pdf deleted file mode 100644 index d2c84bf64c66..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.svg deleted file mode 100644 index 1c1ee14a1282..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_log_scales.svg +++ /dev/null @@ -1,6491 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf deleted file mode 100644 index b69860d339e9..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg deleted file mode 100644 index 19730045f101..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg +++ /dev/null @@ -1,4181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.pdf b/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.pdf deleted file mode 100644 index c9306e718556..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.svg b/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.svg deleted file mode 100644 index 64359dcf6376..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/mollweide_grid.svg +++ /dev/null @@ -1,1851 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.pdf b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.pdf index 4b448293dac6..37a3858c07f4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.png b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.png index 15b812a1104e..aaf3c34a90f8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.png and b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.svg b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.svg index c1a886b6ff5f..1c3495b6738f 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/nonfinite_limits.svg @@ -1,563 +1,468 @@ - - + + + + + + 2026-04-02T23:43:35.136617 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+g0081dee59, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - - - - - - - - - + - - - + + - - - - - +M 2034 4750 +Q 2819 4750 3233 4129 +Q 3647 3509 3647 2328 +Q 3647 1150 3233 529 +Q 2819 -91 2034 -91 +Q 1250 -91 836 529 +Q 422 1150 422 2328 +Q 422 3509 836 4129 +Q 1250 4750 2034 4750 +z +" transform="scale(0.015625)"/> + + + + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - + - + - - + + + - - - - +M 2253 4666 +L 3047 4666 +L 3047 1625 +L 3713 1625 +L 3713 1100 +L 3047 1100 +L 3047 0 +L 2419 0 +L 2419 1100 +L 313 1100 +L 313 1709 +L 2253 4666 +z +" transform="scale(0.015625)"/> + + + - - - - - - - + + - + - + - - - + + + + + + - - - - - - - + + - + - + - - - + + + - - - - - - - + + - + - + - - - + + + - - - - - - - + + - + - + - - + + - - - - - - - + + - + - + - - + + + + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/o_marker_path_snap.png b/lib/matplotlib/tests/baseline_images/test_axes/o_marker_path_snap.png index 7615b9e3ca96..d51ec5ee80f0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/o_marker_path_snap.png and b/lib/matplotlib/tests/baseline_images/test_axes/o_marker_path_snap.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.pdf b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.pdf index 83893144f509..c88b5938a999 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.png b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.png index df2fee46e5e6..eec055f76f19 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.png and b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.svg b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.svg index 85d75654c318..7e085f62207b 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/offset_points.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/offset_points.svg @@ -1,652 +1,551 @@ - - + + + + + + 2026-04-02T23:33:30.019529 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+g6b5bbf332, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - - - - - - - - - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - + - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + + + + + + + + + + + + + + + + - - + + + + + - - - + + - - - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pcolor_datetime_axis.png b/lib/matplotlib/tests/baseline_images/test_axes/pcolor_datetime_axis.png index e91562470215..bd103b297ecd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pcolor_datetime_axis.png and b/lib/matplotlib/tests/baseline_images/test_axes/pcolor_datetime_axis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pcolormesh_datetime_axis.png b/lib/matplotlib/tests/baseline_images/test_axes/pcolormesh_datetime_axis.png index e91562470215..865b63185782 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pcolormesh_datetime_axis.png and b/lib/matplotlib/tests/baseline_images/test_axes/pcolormesh_datetime_axis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_ccw_true.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_ccw_true.png index c5236a34b9e1..711ab8d327a9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_ccw_true.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_ccw_true.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_center_radius.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_center_radius.png index 64b2244711f9..15111ae27089 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_center_radius.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_center_radius.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_default.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_default.png index f3935a9e159a..bbd8e6c58eea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_default.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_default.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_frame_grid.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_frame_grid.png index 4e4edbeed0ed..9e120c623c40 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_frame_grid.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_frame_grid.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_0.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_0.png index e814e061205a..be3ef834cdad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_0.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_0.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_2.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_2.png index e12d743fbc45..46ed069ac4dd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_2.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_linewidth_2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_no_label.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_no_label.png index c6fd5262acce..d697478cb58c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_no_label.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_no_label.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/pie_rotatelabels_true.png b/lib/matplotlib/tests/baseline_images/test_axes/pie_rotatelabels_true.png index d5875752c3cd..a945e3f1c657 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/pie_rotatelabels_true.png and b/lib/matplotlib/tests/baseline_images/test_axes/pie_rotatelabels_true.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png b/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png index 0b60b60f4849..e6beb32ed024 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png and b/lib/matplotlib/tests/baseline_images/test_axes/preset_clip_paths.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png b/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png index 4b927a9ecbff..f5b824f1a57f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png and b/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/rc_markerfill.png b/lib/matplotlib/tests/baseline_images/test_axes/rc_markerfill.png index 456963223ae4..08988e9190e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/rc_markerfill.png and b/lib/matplotlib/tests/baseline_images/test_axes/rc_markerfill.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png b/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png index de8078b3d406..c68d6e48852e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png and b/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/retain_tick_visibility.png b/lib/matplotlib/tests/baseline_images/test_axes/retain_tick_visibility.png index 7ac4cb6f6fbd..a61987893008 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/retain_tick_visibility.png and b/lib/matplotlib/tests/baseline_images/test_axes/retain_tick_visibility.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png b/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png index 8398034d1891..4e62975a0079 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png and b/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/set_get_ticklabels.png b/lib/matplotlib/tests/baseline_images/test_axes/set_get_ticklabels.png index 5962db72d117..0d83bc7daad8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/set_get_ticklabels.png and b/lib/matplotlib/tests/baseline_images/test_axes/set_get_ticklabels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/single_date.png b/lib/matplotlib/tests/baseline_images/test_axes/single_date.png deleted file mode 100644 index 9df3334340c2..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/single_date.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/single_point.pdf b/lib/matplotlib/tests/baseline_images/test_axes/single_point.pdf index acfb0e5f2367..3f1b4c37af42 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/single_point.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/single_point.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/single_point.png b/lib/matplotlib/tests/baseline_images/test_axes/single_point.png index 6a836c4289d8..71fad09fb7ae 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/single_point.png and b/lib/matplotlib/tests/baseline_images/test_axes/single_point.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/single_point.svg b/lib/matplotlib/tests/baseline_images/test_axes/single_point.svg index 5f940bb5a83c..b22331d7ba2c 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/single_point.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/single_point.svg @@ -1,1187 +1,766 @@ - - + + + + + + 2026-04-02T23:43:32.692531 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+g0081dee59, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - + + - + - + - + - - - + + - - + - - - - - - - - +M 2034 4750 +Q 2819 4750 3233 4129 +Q 3647 3509 3647 2328 +Q 3647 1150 3233 529 +Q 2819 -91 2034 -91 +Q 1250 -91 836 529 +Q 422 1150 422 2328 +Q 422 3509 836 4129 +Q 1250 4750 2034 4750 +z +" transform="scale(0.015625)"/> + + + + + + + + - - - - - - - - - - - + + - + - + - - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + - - - - - - - - - - - + + - + - + - - - - - - - - - - + + + + + + - - - - - - - - - - - + + - + - + - - - - - - + + + + + + - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - - + + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - + + - - - - +" style="stroke: #1f77b4"/> + + + - - + + - - + + - - + + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + - + - + - + - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_freqs.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_freqs.png index eb1a29a7a83c..2c458150af68 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_freqs.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_freqs.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_noise.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_noise.png index a9e3c4d20791..465a029c8e98 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_noise.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_angle_noise.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs.png index 6e3a21bcace3..a766ce6f0e42 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs_linear.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs_linear.png index 6e3a21bcace3..ffbaea3bfdfa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs_linear.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_freqs_linear.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs.png index ce598a5fd2ab..3406a1baf496 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs_linear.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs_linear.png index ce598a5fd2ab..70e503305782 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs_linear.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_freqs_linear.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise.png index 4d25d92b4010..c3b484da7a24 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise_linear.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise_linear.png index 4d25d92b4010..0f54ea20b947 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise_linear.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_magnitude_noise_linear.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise.png index 584cf973d51d..7c253e92f9f4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise_linear.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise_linear.png index 584cf973d51d..0d40e285c5c7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise_linear.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_noise_linear.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_freqs.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_freqs.png index 2deeb4e12ac4..f11ccdc3fdce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_freqs.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_freqs.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_noise.png b/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_noise.png index 5980e91135f5..c7ac679392e1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_noise.png and b/lib/matplotlib/tests/baseline_images/test_axes/specgram_phase_noise.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.pdf b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.pdf deleted file mode 100644 index 531c9c5e9b3f..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.svg b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.svg deleted file mode 100644 index ec64d7cdbf4e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_baseline.svg +++ /dev/null @@ -1,3359 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.pdf b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.pdf deleted file mode 100644 index 8f08637012e4..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.png b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.png index cb50323170cb..e787dea7ad41 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.png and b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.svg b/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.svg deleted file mode 100644 index 421dc4448593..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/stackplot_test_image.svg +++ /dev/null @@ -1,651 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stem.png b/lib/matplotlib/tests/baseline_images/test_axes/stem.png index 4c6b3af3205b..2e6968b6183a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/stem.png and b/lib/matplotlib/tests/baseline_images/test_axes/stem.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png new file mode 100644 index 000000000000..a3fb13d0716a Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png new file mode 100644 index 000000000000..a2e185c2769d Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/sticky_tolerance_cf.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf index d3a109773d24..a7f9fe438ffc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/test_centered_bar_label_nonlinear.svg b/lib/matplotlib/tests/baseline_images/test_axes/test_centered_bar_label_nonlinear.svg index cea1050932a6..4b1797ddd4d3 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/test_centered_bar_label_nonlinear.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/test_centered_bar_label_nonlinear.svg @@ -1,16 +1,16 @@ - + - 2022-09-10T15:01:10.033044 + 2026-04-02T23:33:42.122859 image/svg+xml - Matplotlib v3.5.0.dev5765+gcb3beb2f91.d20220910, https://matplotlib.org/ + Matplotlib v3.11.0.dev2221+g6b5bbf332, https://matplotlib.org/ @@ -21,43 +21,43 @@ - - +" clip-path="url(#p8a44455d6c)" style="fill: #1f77b4"/> - +" clip-path="url(#p8a44455d6c)" style="fill: #1f77b4"/> - +" clip-path="url(#p8a44455d6c)" style="fill: #1f77b4"/> - + - - - - - - + + + + - + - - - - - + + + + - + - - - - - + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_datetime.png b/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_datetime.png index fa499047b0f8..473d8ed624e3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_datetime.png and b/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_datetime.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_options.png b/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_options.png index 3367067f3605..32fdd5ac4ec2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_options.png and b/lib/matplotlib/tests/baseline_images/test_axes/test_stairs_options.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.pdf b/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.pdf deleted file mode 100644 index f189df616a33..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.png b/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.png index 32e8d0cec937..7874e0ccaf1a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.png and b/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.svg b/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.svg deleted file mode 100644 index 37a6b88f3e73..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/twin_axis_locators_formatters.svg +++ /dev/null @@ -1,1243 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/use_colorizer_keyword.png b/lib/matplotlib/tests/baseline_images/test_axes/use_colorizer_keyword.png new file mode 100644 index 000000000000..24fb2e78b0ce Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/use_colorizer_keyword.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_baseline.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_baseline.png index a57a3c166f2b..428f30f461c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_baseline.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_baseline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_10.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_10.png index 41ab9d6bd63b..c0e9285c43c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_10.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_200.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_200.png index 997697406c9e..9f8eb400519f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_200.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_custompoints_200.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showall.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showall.png index b3e807c153d9..acf0933d605d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showall.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showall.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showextrema.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showextrema.png index ebec57328603..0576b1be910b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showextrema.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showextrema.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmeans.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmeans.png index da9b58ac8cbb..005e745e47f2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmeans.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmeans.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmedians.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmedians.png index 5eb2d131e0b5..0139e077e425 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmedians.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_horiz_showmedians.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_baseline.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_baseline.png index 28414f431d52..b9163c54d9e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_baseline.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_baseline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_10.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_10.png index bace4a3b3646..e058cbe788e9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_10.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_200.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_200.png index 0822e8ae3dcf..1735ab6f804d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_200.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_custompoints_200.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showall.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showall.png index 49383f98bc3e..820c8ecde925 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showall.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showall.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showextrema.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showextrema.png index 2dfb490793d2..3c04ce8e3ef5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showextrema.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showextrema.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmeans.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmeans.png index fb726bef2daf..370f372a6bb3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmeans.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmeans.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmedians.png b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmedians.png index 50faa0e80b65..c8b50dca6185 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmedians.png and b/lib/matplotlib/tests/baseline_images/test_axes/violinplot_vert_showmedians.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.pdf b/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.pdf deleted file mode 100644 index e6974a409dca..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.png b/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.png index 9efe1aa2f615..ac690c76edf8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.png and b/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.svg b/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.svg deleted file mode 100644 index a2eaa040cbb3..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/vline_hline_zorder.svg +++ /dev/null @@ -1,1011 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vlines_basic.png b/lib/matplotlib/tests/baseline_images/test_axes/vlines_basic.png index 9bca1aebcb46..10d2f774379e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vlines_basic.png and b/lib/matplotlib/tests/baseline_images/test_axes/vlines_basic.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vlines_hlines_blended_transform.png b/lib/matplotlib/tests/baseline_images/test_axes/vlines_hlines_blended_transform.png index bcaee389dffe..ac1d09cc95bf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vlines_hlines_blended_transform.png and b/lib/matplotlib/tests/baseline_images/test_axes/vlines_hlines_blended_transform.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vlines_masked.png b/lib/matplotlib/tests/baseline_images/test_axes/vlines_masked.png index b328c720be72..27a884256f80 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vlines_masked.png and b/lib/matplotlib/tests/baseline_images/test_axes/vlines_masked.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vlines_with_nan.png b/lib/matplotlib/tests/baseline_images/test_axes/vlines_with_nan.png index b4335041bf46..bbb899c4ea1b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/vlines_with_nan.png and b/lib/matplotlib/tests/baseline_images/test_axes/vlines_with_nan.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-bitstream-charter.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-bitstream-charter.pdf new file mode 100644 index 000000000000..c8f9411fb3d9 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-bitstream-charter.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-dejavusans.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-dejavusans.pdf new file mode 100644 index 000000000000..fd907dee6687 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-dejavusans.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-heuristica.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-heuristica.pdf new file mode 100644 index 000000000000..ca9b38d09b89 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/font-heuristica.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/hatching_legend.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/hatching_legend.pdf index 146d4dd92d4d..57fc311ee81b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pdf/hatching_legend.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/hatching_legend.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/kerning.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/kerning.pdf index 90bf2a5c9845..5235e67edefd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pdf/kerning.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/kerning.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf index a148a7d571b1..a675c1d59818 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf index e33f8d803b12..6798177ebb90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf index 5cdc2e34e25d..b7dbb9adec70 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/truetype-conversion.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/truetype-conversion.pdf new file mode 100644 index 000000000000..876edfde0d05 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/truetype-conversion.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type3.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type3.pdf new file mode 100644 index 000000000000..abba76f2960a Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type3.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type42.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type42.pdf new file mode 100644 index 000000000000..e62a73a1fe13 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/ttc_type42.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf index 24e16a2873c6..5f5c14f76dc2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_document_font_size.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_document_font_size.pdf index 9f060419a2a7..5476885da6a7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_document_font_size.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_document_font_size.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf index fd7cf7a5c0d1..d29201638b7b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_pdflatex.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_pdflatex.pdf index c93b5de52674..a0d5872c9af4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_pdflatex.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_pdflatex.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate1.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate1.pdf index fbf9f7271e49..349826ce9340 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate1.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate1.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate2.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate2.pdf index e5f9cd6e8e94..9d05f9e3e559 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate2.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_rcupdate2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_xelatex.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_xelatex.pdf index aff1d4d6dd28..ae51c55ca505 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_xelatex.pdf and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_xelatex.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/ttc_pgf.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/ttc_pgf.pdf new file mode 100644 index 000000000000..5d695e734577 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pgf/ttc_pgf.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/colorbar_shift.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/colorbar_shift.eps index b88e23a33c42..7aa442326ec7 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_ps/colorbar_shift.eps +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/colorbar_shift.eps @@ -1,13 +1,14 @@ %!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 %%Title: colorbar_shift.eps -%%Creator: Matplotlib v3.7.0.dev1597+g613b343238.d20230210, https://matplotlib.org/ -%%CreationDate: Fri Feb 10 16:16:04 2023 +%%Creator: Matplotlib v3.11.0.dev2075+g5a13dc378e, https://matplotlib.org/ +%%CreationDate: Fri Mar 13 23:13:37 2026 %%Orientation: portrait -%%BoundingBox: 110 245 502 547 -%%HiResBoundingBox: 110.097762 245.509625 501.902238 546.490375 +%%BoundingBox: 0 0 392 302 +%%HiResBoundingBox: 0.000000 0.000000 391.773225 301.310828 %%EndComments %%BeginProlog -/mpldict 11 dict def +/mpldict 10 dict def mpldict begin /_d { bind def } bind def /m { moveto } _d @@ -16,38 +17,18 @@ mpldict begin /c { curveto } _d /cl { closepath } _d /ce { closepath eofill } _d -/box { - m - 1 index 0 r - 0 exch r - neg 0 r - cl - } _d -/clipbox { - box - clip - newpath - } _d /sc { setcachedevice } _d %!PS-Adobe-3.0 Resource-Font %%Creator: Converted from TrueType to Type 3 by Matplotlib. 10 dict begin -/FontName /DejaVuSans def +/FontName /DejaVuSans-0 def /PaintType 0 def /FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def /FontBBox [-2090 -948 3673 2524] def /FontType 3 def -/Encoding [/period /zero /one /two /minus /four /five /six /eight /nine] def -/CharStrings 11 dict dup begin +/Encoding [/zero /period /two /four /six /eight /one /nine /five] def +/CharStrings 10 dict dup begin /.notdef 0 def -/period{651 0 219 0 430 254 sc -219 254 m -430 254 l -430 0 l -219 0 l -219 254 l - -ce} _d /zero{1303 0 135 -29 1167 1520 sc 651 1360 m 547 1360 469 1309 416 1206 c @@ -70,19 +51,12 @@ ce} _d 356 1454 484 1520 651 1520 c ce} _d -/one{1303 0 225 0 1114 1493 sc -254 170 m -584 170 l -584 1309 l -225 1237 l -225 1421 l -582 1493 l -784 1493 l -784 170 l -1114 170 l -1114 0 l -254 0 l -254 170 l +/period{651 0 219 0 430 254 sc +219 254 m +430 254 l +430 0 l +219 0 l +219 254 l ce} _d /two{1303 0 150 0 1098 1520 sc @@ -109,14 +83,6 @@ ce} _d 913 706 860 650 771 557 c 682 465 556 336 393 170 c -ce} _d -/minus{1716 0 217 557 1499 727 sc -217 727 m -1499 727 l -1499 557 l -217 557 l -217 727 l - ce} _d /four{1303 0 100 0 1188 1493 sc 774 1317 m @@ -137,32 +103,6 @@ ce} _d 100 547 l 721 1493 l -ce} _d -/five{1303 0 158 -29 1124 1493 sc -221 1493 m -1014 1493 l -1014 1323 l -406 1323 l -406 957 l -435 967 465 974 494 979 c -523 984 553 987 582 987 c -749 987 881 941 978 850 c -1075 759 1124 635 1124 479 c -1124 318 1074 193 974 104 c -874 15 733 -29 551 -29 c -488 -29 424 -24 359 -13 c -294 -2 227 14 158 35 c -158 238 l -218 205 280 181 344 165 c -408 149 476 141 547 141 c -662 141 754 171 821 232 c -888 293 922 375 922 479 c -922 583 888 665 821 726 c -754 787 662 817 547 817 c -493 817 439 811 385 799 c -332 787 277 768 221 743 c -221 1493 l - ce} _d /six{1303 0 143 -29 1174 1520 sc 676 827 m @@ -234,6 +174,21 @@ ce} _d 564 1360 496 1338 447 1295 c 399 1252 375 1191 375 1114 c +ce} _d +/one{1303 0 225 0 1114 1493 sc +254 170 m +584 170 l +584 1309 l +225 1237 l +225 1421 l +582 1493 l +784 1493 l +784 170 l +1114 170 l +1114 0 l +254 0 l +254 170 l + ce} _d /nine{1303 0 129 -29 1159 1520 sc 225 31 m @@ -265,6 +220,67 @@ ce} _d 332 904 358 819 411 757 c 464 695 536 664 627 664 c +ce} _d +/five{1303 0 158 -29 1124 1493 sc +221 1493 m +1014 1493 l +1014 1323 l +406 1323 l +406 957 l +435 967 465 974 494 979 c +523 984 553 987 582 987 c +749 987 881 941 978 850 c +1075 759 1124 635 1124 479 c +1124 318 1074 193 974 104 c +874 15 733 -29 551 -29 c +488 -29 424 -24 359 -13 c +294 -2 227 14 158 35 c +158 238 l +218 205 280 181 344 165 c +408 149 476 141 547 141 c +662 141 754 171 821 232 c +888 293 922 375 922 479 c +922 583 888 665 821 726 c +754 787 662 817 547 817 c +493 817 439 811 385 799 c +332 787 277 768 221 743 c +221 1493 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/minus] def +/CharStrings 2 dict dup begin +/.notdef 0 def +/minus{1716 0 217 557 1499 727 sc +217 727 m +1499 727 l +1499 557 l +217 557 l +217 727 l + ce} _d end readonly def @@ -285,24 +301,24 @@ FontName currentdict end definefont pop end %%EndProlog mpldict begin -110.098 245.51 translate -391.804 300.981 0 0 clipbox +0 0 translate +0 0 391.773 301.311 rectclip gsave 0 0 m -391.804475 0 l -391.804475 300.98075 l -0 300.98075 l +391.773225 0 l +391.773225 301.310828 l +0 301.310828 l cl -1.000 setgray +1 setgray fill grestore gsave -36.465625 23.871875 m -322.161625 23.871875 l -322.161625 289.983875 l -36.465625 289.983875 l +36.45 24.2 m +322.146 24.2 l +322.146 290.312 l +36.45 290.312 l cl -1.000 setgray +1 setgray fill grestore /p0_0 { @@ -320,30 +336,30 @@ translate cl } bind def -1.000 setlinewidth +1 setlinewidth 1 setlinejoin 0 setlinecap [] 0 setdash -0.000 0.500 0.000 setrgbcolor +0 0.5 0 setrgbcolor gsave -285.696 266.112 36.466 23.872 clipbox -49.4518 156.928 p0_0 +36.45 24.2 285.696 266.112 rectclip +49.4362 157.256 p0_0 gsave fill grestore stroke grestore -0.000 0.000 1.000 setrgbcolor +0 0 1 setrgbcolor gsave -285.696 266.112 36.466 23.872 clipbox -309.175 156.928 p0_0 +36.45 24.2 285.696 266.112 rectclip +309.16 157.256 p0_0 gsave fill grestore stroke grestore -0.800 setlinewidth -0.000 setgray +0.8 setlinewidth +0 setgray gsave /o { gsave @@ -358,22 +374,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -49.4518 23.8719 o +49.4362 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -41.4987 9.27812 translate +41.4909 9.60234 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow grestore gsave /o { @@ -389,22 +405,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -101.397 23.8719 o +101.381 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -93.4434 9.27812 translate +93.4356 9.60234 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /two glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /two glyphshow grestore gsave /o { @@ -420,22 +436,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -153.341 23.8719 o +153.326 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -145.388 9.27812 translate +145.38 9.60234 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /four glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /four glyphshow grestore gsave /o { @@ -451,22 +467,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -205.286 23.8719 o +205.27 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -197.333 9.27812 translate +197.325 9.60234 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /six glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /six glyphshow grestore gsave /o { @@ -482,22 +498,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -257.231 23.8719 o +257.215 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -249.278 9.27812 translate +249.27 9.60234 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /eight glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /eight glyphshow grestore gsave /o { @@ -513,22 +529,22 @@ translate 0 -3.5 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -309.175 23.8719 o +309.16 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -301.222 9.27812 translate +301.215 9.60234 translate 0 rotate 0 0 m /one glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow grestore gsave /o { @@ -544,23 +560,23 @@ translate -3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -36.4656 60.1599 o +36.45 60.488 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -7.2 56.363 translate +7.2 56.6892 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /nine glyphshow -15.9033 0 m /six glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /nine glyphshow +15.8906 0 m /six glyphshow grestore gsave /o { @@ -576,23 +592,23 @@ translate -3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -36.4656 108.544 o +36.45 108.872 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -7.2 104.747 translate +7.2 105.073 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /nine glyphshow -15.9033 0 m /eight glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /nine glyphshow +15.8906 0 m /eight glyphshow grestore gsave /o { @@ -608,23 +624,23 @@ translate -3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -36.4656 156.928 o +36.45 157.256 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -7.2 153.131 translate +7.2 153.457 translate 0 rotate 0 0 m /one glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow -15.9033 0 m /zero glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow +15.8906 0 m /zero glyphshow grestore gsave /o { @@ -640,23 +656,23 @@ translate -3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -36.4656 205.312 o +36.45 205.64 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -7.2 201.515 translate +7.2 201.841 translate 0 rotate 0 0 m /one glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow -15.9033 0 m /two glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow +15.8906 0 m /two glyphshow grestore gsave /o { @@ -672,83 +688,83 @@ translate -3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -36.4656 253.696 o +36.45 254.024 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -7.2 249.899 translate +7.2 250.225 translate 0 rotate 0 0 m /one glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow -15.9033 0 m /four glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow +15.8906 0 m /four glyphshow grestore 0 setlinejoin 2 setlinecap gsave -36.465625 23.871875 m -36.465625 289.983875 l +36.45 24.2 m +36.45 290.312 l stroke grestore gsave -322.161625 23.871875 m -322.161625 289.983875 l +322.146 24.2 m +322.146 290.312 l stroke grestore gsave -36.465625 23.871875 m -322.161625 23.871875 l +36.45 24.2 m +322.146 24.2 l stroke grestore gsave -36.465625 289.983875 m -322.161625 289.983875 l +36.45 290.312 m +322.146 290.312 l stroke grestore gsave -340.017625 23.871875 m -353.323225 23.871875 l -353.323225 289.983875 l -340.017625 289.983875 l +340.002 24.2 m +353.3076 24.2 l +353.3076 290.312 l +340.002 290.312 l cl -1.000 setgray +1 setgray fill grestore gsave -13.306 266.112 340.018 23.872 clipbox -340.017625 23.871875 m -353.323225 23.871875 l -353.323225 112.575875 l -340.017625 112.575875 l -340.017625 23.871875 l -1.000 0.000 0.000 setrgbcolor +340.002 24.2 13.306 266.112 rectclip +340.002 24.2 m +353.3076 24.2 l +353.3076 112.904 l +340.002 112.904 l +340.002 24.2 l +1 0 0 setrgbcolor fill grestore gsave -13.306 266.112 340.018 23.872 clipbox -340.017625 112.575875 m -353.323225 112.575875 l -353.323225 201.279875 l -340.017625 201.279875 l -340.017625 112.575875 l -0.000 0.500 0.000 setrgbcolor +340.002 24.2 13.306 266.112 rectclip +340.002 112.904 m +353.3076 112.904 l +353.3076 201.608 l +340.002 201.608 l +340.002 112.904 l +0 0.5 0 setrgbcolor fill grestore gsave -13.306 266.112 340.018 23.872 clipbox -340.017625 201.279875 m -353.323225 201.279875 l -353.323225 289.983875 l -340.017625 289.983875 l -340.017625 201.279875 l -0.000 0.000 1.000 setrgbcolor +340.002 24.2 13.306 266.112 rectclip +340.002 201.608 m +353.3076 201.608 l +353.3076 290.312 l +340.002 290.312 l +340.002 201.608 l +0 0 1 setrgbcolor fill grestore 1 setlinejoin @@ -767,23 +783,29 @@ translate 3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -353.323 23.8719 o +353.308 24.2 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-1 10.000 selectfont gsave -360.323 20.075 translate +360.308 20.4012 translate 0 rotate 0 0 m /minus glyphshow -8.37891 0 m /one glyphshow -14.7412 0 m /period glyphshow -17.9199 0 m /zero glyphshow +grestore +/DejaVuSans-0 10.000 selectfont +gsave + +360.308 20.4012 translate +0 rotate +8.375 0 m /one glyphshow +14.7344 0 m /period glyphshow +17.9062 0 m /zero glyphshow grestore gsave /o { @@ -799,23 +821,29 @@ translate 3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -353.323 112.576 o +353.308 112.904 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-1 10.000 selectfont gsave -360.323 108.779 translate +360.308 109.105 translate 0 rotate 0 0 m /minus glyphshow -8.37891 0 m /zero glyphshow -14.7412 0 m /period glyphshow -17.9199 0 m /five glyphshow +grestore +/DejaVuSans-0 10.000 selectfont +gsave + +360.308 109.105 translate +0 rotate +8.375 0 m /zero glyphshow +14.7344 0 m /period glyphshow +17.9062 0 m /five glyphshow grestore gsave /o { @@ -831,22 +859,22 @@ translate 3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -353.323 201.28 o +353.308 201.608 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -360.323 197.483 translate +360.308 197.809 translate 0 rotate 0 0 m /zero glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /five glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /five glyphshow grestore gsave /o { @@ -862,33 +890,33 @@ translate 3.5 0 l gsave -0.000 setgray +0 setgray fill grestore stroke grestore } bind def -353.323 289.984 o +353.308 290.312 o grestore -/DejaVuSans 10.000 selectfont +/DejaVuSans-0 10.000 selectfont gsave -360.323 286.187 translate +360.308 286.513 translate 0 rotate 0 0 m /one glyphshow -6.3623 0 m /period glyphshow -9.54102 0 m /zero glyphshow +6.35938 0 m /period glyphshow +9.53125 0 m /zero glyphshow grestore 0 setlinejoin 2 setlinecap gsave -340.017625 23.871875 m -346.670425 23.871875 l -353.323225 23.871875 l -353.323225 289.983875 l -346.670425 289.983875 l -340.017625 289.983875 l -340.017625 23.871875 l +340.002 24.2 m +346.6548 24.2 l +353.3076 24.2 l +353.3076 290.312 l +346.6548 290.312 l +340.002 290.312 l +340.002 24.2 l cl stroke grestore diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps index efc8fc9416a7..72133caba911 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps @@ -1,10 +1,11 @@ %!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 %%Title: multi_font_type3.eps -%%Creator: Matplotlib v3.6.0.dev2856+g4848cedd6d.d20220807, https://matplotlib.org/ -%%CreationDate: Sun Aug 7 16:45:01 2022 +%%Creator: Matplotlib v3.11.0.dev2222+g4230aa142, https://matplotlib.org/ +%%CreationDate: Fri Apr 3 00:28:30 2026 %%Orientation: portrait -%%BoundingBox: 18 180 594 612 -%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%BoundingBox: 0 0 576 432 +%%HiResBoundingBox: 0.000000 0.000000 576.000000 432.000000 %%EndComments %%BeginProlog /mpldict 12 dict def @@ -16,76 +17,14146 @@ mpldict begin /c { curveto } _d /cl { closepath } _d /ce { closepath eofill } _d -/box { - m - 1 index 0 r - 0 exch r - neg 0 r - cl - } _d -/clipbox { - box - clip - newpath - } _d /sc { setcachedevice } _d %!PS-Adobe-3.0 Resource-Font %%Creator: Converted from TrueType to Type 3 by Matplotlib. 10 dict begin -/FontName /DejaVuSans def +/FontName /Cmr10-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-90 -512 2066 1536] def +/FontType 3 def +/Encoding [/T /h /e /r /space /a /b /s /i /c /t /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /U /V /W /X /Y /Z /d /f /g /j /k /l /m /n /o /p /q /u /v /w /x /y /z /zero /one /two /three /four /five /six /seven /eight /nine /exclam /quotedblright /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /colon /semicolon /exclamdown /equal /questiondown /question /at /bracketleft /quotedblleft /bracketright /circumflex /dotaccent /quoteleft /emdash /endash /hungarumlaut /tilde] def +/CharStrings 96 dict dup begin +/.notdef 0 def +/T{1479 0 74 0 1403 1399 sc +346 0 m +346 72 l +415 72 481 76 546 83 c +611 91 643 109 643 137 c +643 1262 l +643 1291 635 1309 618 1316 c +602 1323 576 1327 539 1327 c +453 1327 l +340 1327 260 1302 213 1253 c +188 1228 171 1189 160 1134 c +149 1080 140 1012 133 930 c +74 930 l +113 1399 l +1364 1399 l +1403 930 l +1343 930 l +1335 1018 1326 1088 1316 1139 c +1307 1191 1289 1229 1264 1253 c +1216 1302 1136 1327 1024 1327 c +938 1327 l +913 1327 894 1326 880 1324 c +867 1322 856 1316 847 1306 c +838 1297 834 1282 834 1262 c +834 137 l +834 109 866 91 931 83 c +996 76 1062 72 1130 72 c +1130 0 l +346 0 l + +ce} _d +/h{1137 0 61 0 1100 1421 sc +61 0 m +61 72 l +108 72 146 76 176 83 c +206 90 221 108 221 137 c +221 1212 l +221 1249 215 1275 204 1291 c +193 1308 178 1318 157 1321 c +136 1325 104 1327 61 1327 c +61 1399 l +365 1421 l +365 717 l +394 774 434 819 485 853 c +536 888 593 905 655 905 c +750 905 821 882 868 837 c +916 792 940 722 940 629 c +940 137 l +940 108 955 90 985 83 c +1015 76 1053 72 1100 72 c +1100 0 l +631 0 l +631 72 l +678 72 716 76 746 83 c +776 90 791 108 791 137 c +791 623 l +791 690 781 744 762 787 c +743 830 703 852 643 852 c +564 852 498 820 447 757 c +396 694 371 622 371 541 c +371 137 l +371 108 386 90 416 83 c +446 76 484 72 530 72 c +530 0 l +61 0 l + +ce} _d +/e{909 0 57 -23 850 918 sc +510 -23 m +427 -23 350 -1 280 42 c +211 86 156 144 116 217 c +77 290 57 368 57 449 c +57 529 75 605 111 677 c +148 749 198 807 263 851 c +328 896 401 918 481 918 c +544 918 598 907 644 886 c +691 865 729 836 759 799 c +789 762 812 718 827 667 c +842 616 850 561 850 500 c +850 482 843 473 829 473 c +236 473 l +236 451 l +236 338 259 240 304 159 c +350 78 425 37 528 37 c +570 37 609 46 644 65 c +680 84 711 110 737 143 c +764 176 782 212 791 250 c +792 255 795 259 798 262 c +802 266 806 268 811 268 c +829 268 l +843 268 850 259 850 242 c +831 165 789 101 725 51 c +661 2 589 -23 510 -23 c + +238 524 m +705 524 l +705 575 698 627 683 680 c +669 733 645 776 612 811 c +579 846 535 864 481 864 c +404 864 344 828 301 755 c +259 683 238 606 238 524 c + +ce} _d +/r{801 0 53 0 745 905 sc +53 0 m +53 72 l +100 72 138 76 168 83 c +198 90 213 108 213 137 c +213 696 l +213 733 207 759 196 775 c +185 792 170 802 149 805 c +128 809 96 811 53 811 c +53 883 l +346 905 l +346 705 l +368 764 399 812 440 849 c +481 886 530 905 588 905 c +629 905 665 893 697 869 c +729 845 745 813 745 774 c +745 749 736 728 718 709 c +701 691 679 682 653 682 c +628 682 606 691 588 709 c +570 727 561 749 561 774 c +561 811 574 837 600 852 c +588 852 l +533 852 487 832 452 792 c +417 752 393 702 378 643 c +363 584 356 527 356 473 c +356 137 l +356 94 422 72 555 72 c +555 0 l +53 0 l + +ce} _d +/space{682 0 0 0 0 0 sc +ce} _d +/a{1024 0 82 -23 1010 918 sc +82 201 m +82 282 114 348 178 399 c +242 450 319 486 408 507 c +498 528 583 539 664 539 c +664 623 l +664 662 655 700 638 737 c +621 774 596 805 563 828 c +530 852 494 864 455 864 c +364 864 295 844 248 803 c +274 803 295 793 312 773 c +329 754 338 731 338 705 c +338 678 328 654 309 635 c +290 616 267 606 240 606 c +213 606 189 616 170 635 c +151 654 141 678 141 705 c +141 777 174 830 239 865 c +304 900 376 918 455 918 c +510 918 566 906 622 882 c +678 859 724 825 759 781 c +795 737 813 686 813 627 c +813 166 l +813 139 819 115 830 92 c +841 70 859 59 883 59 c +906 59 922 70 933 93 c +944 116 950 140 950 166 c +950 297 l +1010 297 l +1010 166 l +1010 135 1002 106 986 78 c +970 51 948 29 921 12 c +894 -4 865 -12 834 -12 c +794 -12 759 3 730 34 c +701 65 685 102 682 145 c +657 94 619 53 570 22 c +521 -8 468 -23 412 -23 c +360 -23 309 -15 258 0 c +208 15 166 39 132 72 c +99 105 82 148 82 201 c + +248 201 m +248 153 266 113 301 80 c +336 47 378 31 426 31 c +470 31 510 42 546 64 c +582 86 611 116 632 154 c +653 192 664 232 664 274 c +664 487 l +602 487 538 477 473 456 c +408 436 355 404 312 361 c +269 318 248 264 248 201 c + +ce} _d +/b{1137 0 53 -23 1069 1421 sc +213 0 m +213 1212 l +213 1249 207 1275 196 1291 c +185 1308 170 1318 149 1321 c +128 1325 96 1327 53 1327 c +53 1399 l +356 1421 l +356 780 l +379 805 405 827 435 846 c +466 865 498 880 533 890 c +568 900 603 905 639 905 c +700 905 757 893 809 868 c +862 843 907 809 946 766 c +985 723 1015 673 1036 616 c +1058 560 1069 502 1069 442 c +1069 359 1049 282 1008 211 c +968 140 913 83 843 40 c +774 -2 697 -23 614 -23 c +562 -23 512 -10 463 17 c +414 44 374 79 342 123 c +272 0 l +213 0 l + +362 201 m +385 151 417 110 460 78 c +503 47 550 31 602 31 c +673 31 730 51 773 92 c +817 133 848 184 865 246 c +882 308 891 373 891 442 c +891 557 876 645 846 705 c +833 732 814 756 791 779 c +768 802 743 819 714 832 c +686 845 656 852 625 852 c +570 852 520 837 473 808 c +426 779 389 741 362 692 c +362 201 l + +ce} _d +/s{807 0 68 -23 737 918 sc +68 -6 m +68 328 l +68 339 74 344 86 344 c +111 344 l +119 344 124 339 127 328 c +165 130 257 31 403 31 c +468 31 522 46 565 75 c +609 104 631 150 631 211 c +631 255 614 292 580 323 c +546 354 506 376 459 387 c +322 414 l +276 424 234 439 196 460 c +159 481 128 508 104 542 c +80 577 68 617 68 662 c +68 722 84 771 115 809 c +147 848 188 875 239 892 c +290 909 344 918 403 918 c +473 918 534 899 586 862 c +645 913 l +645 916 648 918 655 918 c +670 918 l +674 918 678 916 681 912 c +684 909 686 905 686 901 c +686 633 l +686 620 681 614 670 614 c +645 614 l +633 614 627 620 627 633 c +627 704 607 762 567 805 c +528 848 472 870 401 870 c +340 870 286 859 241 836 c +196 813 174 774 174 719 c +174 681 190 650 222 625 c +255 601 293 584 336 573 c +475 547 l +522 536 565 518 605 493 c +646 468 678 436 701 397 c +725 358 737 315 737 266 c +737 217 728 174 711 137 c +694 101 671 71 640 47 c +610 23 574 5 533 -6 c +492 -17 448 -23 403 -23 c +318 -23 245 6 184 63 c +109 -18 l +109 -21 105 -23 98 -23 c +86 -23 l +74 -23 68 -17 68 -6 c + +ce} _d +/i{567 0 63 0 510 1370 sc +63 0 m +63 72 l +110 72 148 76 178 83 c +208 90 223 108 223 137 c +223 696 l +223 749 213 781 192 793 c +172 805 132 811 72 811 c +72 883 l +367 905 l +367 137 l +367 108 380 90 406 83 c +432 76 467 72 510 72 c +510 0 l +63 0 l + +150 1257 m +150 1287 161 1313 184 1336 c +207 1359 233 1370 262 1370 c +281 1370 300 1365 318 1355 c +336 1345 350 1331 360 1313 c +370 1295 375 1276 375 1257 c +375 1228 364 1202 341 1179 c +318 1156 292 1145 262 1145 c +233 1145 207 1156 184 1179 c +161 1202 150 1228 150 1257 c + +ce} _d +/c{909 0 68 -23 850 918 sc +510 -23 m +427 -23 352 -2 285 41 c +218 84 165 142 126 213 c +87 285 68 361 68 442 c +68 523 87 600 125 674 c +164 748 217 807 284 851 c +352 896 427 918 510 918 c +590 918 663 902 728 871 c +794 840 827 788 827 717 c +827 690 817 667 798 647 c +779 628 756 618 729 618 c +702 618 678 628 659 647 c +640 667 631 690 631 717 c +631 741 639 762 654 779 c +669 797 688 808 711 813 c +664 843 597 858 512 858 c +447 858 394 836 354 793 c +314 750 286 696 270 632 c +254 568 246 505 246 442 c +246 376 256 312 275 250 c +295 189 327 138 370 97 c +414 57 469 37 535 37 c +600 37 655 57 700 96 c +745 136 776 188 793 252 c +793 260 798 264 809 264 c +834 264 l +838 264 842 262 845 258 c +848 255 850 251 850 246 c +850 240 l +829 159 788 95 727 48 c +666 1 593 -23 510 -23 c + +ce} _d +/t{795 0 39 -23 680 1260 sc +209 246 m +209 811 l +39 811 l +39 864 l +128 864 194 906 236 989 c +278 1072 299 1163 299 1260 c +358 1260 l +358 883 l +647 883 l +647 811 l +358 811 l +358 250 l +358 193 367 144 386 101 c +405 58 440 37 489 37 c +536 37 569 59 590 104 c +611 149 621 198 621 250 c +621 371 l +680 371 l +680 246 l +680 203 672 161 656 119 c +641 78 618 44 587 17 c +556 -10 519 -23 475 -23 c +393 -23 328 1 280 50 c +233 99 209 165 209 246 c + +ce} _d +/A{1536 0 66 0 1468 1466 sc +66 0 m +66 72 l +188 72 263 112 291 193 c +721 1444 l +725 1459 736 1466 754 1466 c +780 1466 l +798 1466 809 1459 813 1444 c +1262 137 l +1275 108 1299 90 1334 83 c +1370 76 1415 72 1468 72 c +1468 0 l +897 0 l +897 72 l +1010 72 1067 90 1067 127 c +1067 137 l +956 457 l +459 457 l +367 193 l +366 188 365 181 365 172 c +365 138 381 113 413 96 c +446 80 481 72 518 72 c +518 0 l +66 0 l + +483 528 m +932 528 l +707 1182 l +483 528 l + +ce} _d +/B{1450 0 70 0 1333 1399 sc +70 0 m +70 72 l +211 72 281 94 281 137 c +281 1262 l +281 1305 211 1327 70 1327 c +70 1399 l +823 1399 l +892 1399 962 1385 1033 1358 c +1104 1331 1162 1291 1208 1238 c +1255 1185 1278 1123 1278 1051 c +1278 968 1244 898 1175 841 c +1107 785 1027 748 936 731 c +995 731 1056 715 1119 682 c +1182 649 1233 606 1273 551 c +1313 496 1333 438 1333 377 c +1333 302 1311 236 1266 179 c +1222 122 1165 77 1094 46 c +1023 15 952 0 881 0 c +70 0 l + +459 137 m +459 108 467 89 484 82 c +501 75 527 72 563 72 c +823 72 l +876 72 926 86 971 114 c +1017 142 1053 179 1080 226 c +1107 273 1120 324 1120 377 c +1120 429 1109 480 1087 529 c +1065 579 1033 620 992 652 c +951 684 905 700 852 700 c +459 700 l +459 137 l + +459 754 m +766 754 l +807 754 846 761 882 776 c +919 791 951 813 980 841 c +1009 870 1032 902 1047 937 c +1063 973 1071 1011 1071 1051 c +1071 1086 1065 1120 1053 1153 c +1042 1186 1025 1215 1002 1242 c +979 1269 953 1289 922 1304 c +891 1319 858 1327 823 1327 c +563 1327 l +538 1327 519 1326 505 1324 c +492 1322 481 1316 472 1306 c +463 1297 459 1282 459 1262 c +459 754 l + +ce} _d +/C{1479 0 115 -45 1362 1444 sc +467 219 m +514 160 572 113 642 78 c +712 44 785 27 860 27 c +923 27 982 39 1036 64 c +1090 89 1137 124 1177 169 c +1218 214 1249 264 1270 321 c +1292 378 1303 437 1303 500 c +1303 511 1309 516 1321 516 c +1346 516 l +1357 516 1362 509 1362 496 c +1362 425 1348 356 1321 289 c +1294 223 1255 165 1205 114 c +1155 64 1097 25 1032 -3 c +967 -31 900 -45 829 -45 c +731 -45 638 -25 550 14 c +463 54 386 108 321 177 c +256 246 206 326 169 417 c +133 508 115 602 115 700 c +115 798 133 892 169 983 c +206 1074 256 1154 321 1223 c +386 1292 463 1346 550 1385 c +638 1424 731 1444 829 1444 c +899 1444 966 1429 1030 1398 c +1094 1368 1150 1325 1198 1270 c +1317 1438 l +1325 1442 1330 1444 1331 1444 c +1346 1444 l +1350 1444 1354 1442 1357 1439 c +1360 1436 1362 1432 1362 1427 c +1362 874 l +1362 862 1357 856 1346 856 c +1309 856 l +1296 856 1290 862 1290 874 c +1290 913 1282 957 1266 1006 c +1251 1055 1231 1101 1206 1144 c +1182 1188 1154 1227 1122 1260 c +1045 1335 957 1372 858 1372 c +783 1372 710 1355 641 1321 c +572 1287 514 1240 467 1180 c +417 1116 382 1043 363 961 c +344 880 334 793 334 700 c +334 607 344 520 363 438 c +382 356 417 283 467 219 c + +ce} _d +/D{1563 0 68 0 1448 1399 sc +68 0 m +68 72 l +209 72 279 94 279 137 c +279 1262 l +279 1305 209 1327 68 1327 c +68 1399 l +823 1399 l +916 1399 1002 1379 1079 1338 c +1156 1298 1222 1244 1277 1177 c +1332 1110 1374 1033 1403 948 c +1433 863 1448 775 1448 686 c +1448 599 1433 515 1403 433 c +1373 352 1330 278 1273 212 c +1217 147 1150 95 1073 57 c +996 19 913 0 823 0 c +68 0 l + +463 137 m +463 108 471 89 488 82 c +505 75 531 72 567 72 c +770 72 l +840 72 906 87 969 117 c +1032 148 1085 190 1126 244 c +1169 301 1198 365 1213 436 c +1228 507 1235 591 1235 686 c +1235 785 1228 872 1213 947 c +1198 1022 1169 1088 1126 1147 c +1085 1205 1034 1249 971 1280 c +908 1311 841 1327 770 1327 c +567 1327 l +542 1327 523 1326 509 1324 c +496 1322 485 1316 476 1306 c +467 1297 463 1282 463 1262 c +463 137 l + +ce} _d +/E{1393 0 63 0 1335 1399 sc +63 0 m +63 72 l +204 72 274 94 274 137 c +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +1221 1399 l +1278 930 l +1219 930 l +1204 1048 1184 1134 1157 1187 c +1131 1240 1089 1277 1031 1297 c +973 1317 886 1327 770 1327 c +569 1327 l +544 1327 525 1326 511 1324 c +498 1322 487 1316 478 1306 c +469 1297 465 1282 465 1262 c +465 762 l +616 762 l +685 762 737 768 771 779 c +806 791 829 813 842 846 c +855 879 862 931 862 1001 c +922 1001 l +922 451 l +862 451 l +862 520 855 572 842 605 c +829 638 806 661 771 672 c +737 684 685 690 616 690 c +465 690 l +465 137 l +465 108 473 89 490 82 c +507 75 533 72 569 72 c +786 72 l +881 72 957 79 1015 94 c +1074 109 1119 134 1151 169 c +1184 204 1209 250 1226 305 c +1244 361 1261 438 1276 537 c +1335 537 l +1249 0 l +63 0 l + +ce} _d +/F{1335 0 63 0 1249 1399 sc +63 0 m +63 72 l +204 72 274 94 274 137 c +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +1192 1399 l +1249 930 l +1190 930 l +1181 1016 1168 1084 1152 1133 c +1137 1183 1114 1222 1084 1250 c +1054 1279 1014 1299 963 1310 c +913 1321 844 1327 756 1327 c +569 1327 l +544 1327 525 1326 511 1324 c +498 1322 487 1316 478 1306 c +469 1297 465 1282 465 1262 c +465 735 l +610 735 l +680 735 731 741 764 753 c +797 766 819 788 831 821 c +844 854 850 905 850 975 c +909 975 l +909 424 l +850 424 l +850 494 844 545 831 578 c +819 611 797 633 764 645 c +731 658 680 664 610 664 c +465 664 l +465 137 l +465 94 552 72 727 72 c +727 0 l +63 0 l + +ce} _d +/G{1606 0 115 -45 1505 1444 sc +471 219 m +520 159 580 112 651 78 c +722 44 797 27 874 27 c +950 27 1019 46 1081 85 c +1143 124 1174 179 1174 250 c +1174 422 l +1174 465 1089 487 918 487 c +918 559 l +1505 559 l +1505 487 l +1462 487 1427 483 1402 476 c +1377 469 1364 451 1364 422 c +1364 18 l +1364 13 1362 9 1358 5 c +1355 2 1351 0 1346 0 c +1333 0 1312 15 1282 45 c +1253 75 1229 102 1212 125 c +1179 68 1126 25 1055 -3 c +984 -31 909 -45 831 -45 c +698 -45 577 -11 468 57 c +359 126 273 217 210 331 c +147 446 115 569 115 700 c +115 797 133 891 170 982 c +207 1073 258 1154 323 1223 c +389 1292 466 1346 553 1385 c +640 1424 733 1444 831 1444 c +902 1444 968 1429 1031 1398 c +1094 1368 1151 1325 1200 1270 c +1319 1438 l +1327 1442 1332 1444 1333 1444 c +1348 1444 l +1352 1444 1356 1442 1359 1439 c +1362 1436 1364 1432 1364 1427 c +1364 874 l +1364 862 1359 856 1348 856 c +1311 856 l +1298 856 1292 862 1292 874 c +1292 913 1284 957 1268 1006 c +1253 1055 1233 1101 1208 1144 c +1184 1188 1156 1227 1124 1260 c +1047 1335 959 1372 860 1372 c +784 1372 711 1355 642 1321 c +573 1287 514 1240 467 1180 c +417 1116 382 1043 363 961 c +344 880 334 793 334 700 c +334 491 380 330 471 219 c + +ce} _d +/H{1536 0 63 0 1470 1399 sc +63 0 m +63 72 l +204 72 274 94 274 137 c +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +676 1399 l +676 1327 l +535 1327 465 1305 465 1262 c +465 764 l +1069 764 l +1069 1262 l +1069 1305 999 1327 858 1327 c +858 1399 l +1470 1399 l +1470 1327 l +1330 1327 1260 1305 1260 1262 c +1260 137 l +1260 94 1330 72 1470 72 c +1470 0 l +858 0 l +858 72 l +999 72 1069 94 1069 137 c +1069 692 l +465 692 l +465 137 l +465 94 535 72 676 72 c +676 0 l +63 0 l + +ce} _d +/I{739 0 53 0 686 1399 sc +53 0 m +53 72 l +200 72 274 94 274 137 c +274 1262 l +274 1305 200 1327 53 1327 c +53 1399 l +686 1399 l +686 1327 l +539 1327 465 1305 465 1262 c +465 137 l +465 94 539 72 686 72 c +686 0 l +53 0 l + +ce} _d +/J{1051 0 76 -45 952 1399 sc +186 115 m +211 80 243 54 282 35 c +321 17 363 8 408 8 c +452 8 489 23 519 53 c +550 84 572 121 587 166 c +602 211 610 255 610 297 c +610 1262 l +610 1305 519 1327 336 1327 c +336 1399 l +952 1399 l +952 1327 l +906 1327 868 1323 839 1316 c +810 1309 795 1291 795 1262 c +795 289 l +795 224 776 166 738 115 c +701 64 652 25 592 -3 c +533 -31 471 -45 408 -45 c +353 -45 300 -34 249 -11 c +198 11 157 43 124 85 c +92 128 76 177 76 233 c +76 267 87 295 110 318 c +133 341 161 352 195 352 c +217 352 237 347 255 336 c +273 326 287 312 297 293 c +308 274 313 254 313 233 c +313 200 302 172 279 149 c +256 126 228 115 195 115 c +186 115 l + +ce} _d +/K{1591 0 63 0 1507 1399 sc +63 0 m +63 72 l +204 72 274 94 274 137 c +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +676 1399 l +676 1327 l +535 1327 465 1305 465 1262 c +465 598 l +1098 1206 l +1115 1225 1124 1243 1124 1262 c +1124 1283 1115 1299 1096 1310 c +1078 1321 1057 1327 1034 1327 c +1034 1399 l +1479 1399 l +1479 1327 l +1367 1327 1269 1287 1184 1206 c +821 858 l +1270 193 l +1307 139 1339 105 1366 92 c +1394 79 1441 72 1507 72 c +1507 0 l +971 0 l +971 72 l +1004 72 1031 75 1053 82 c +1076 89 1087 104 1087 129 c +1087 146 1078 167 1061 193 c +694 737 l +465 516 l +465 137 l +465 94 535 72 676 72 c +676 0 l +63 0 l + +ce} _d +/L{1280 0 63 0 1192 1399 sc +63 0 m +63 72 l +204 72 274 94 274 137 c +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +727 1399 l +727 1327 l +552 1327 465 1305 465 1262 c +465 137 l +465 108 473 89 490 82 c +507 75 533 72 569 72 c +727 72 l +829 72 908 91 963 128 c +1018 165 1057 216 1080 280 c +1103 345 1121 430 1133 537 c +1192 537 l +1135 0 l +63 0 l + +ce} _d +/M{1876 0 72 0 1804 1399 sc +72 0 m +72 72 l +213 72 283 112 283 193 c +283 1262 l +283 1305 213 1327 72 1327 c +72 1399 l +459 1399 l +476 1399 487 1391 492 1376 c +938 217 l +1384 1376 l +1389 1391 1400 1399 1417 1399 c +1804 1399 l +1804 1327 l +1663 1327 1593 1305 1593 1262 c +1593 137 l +1593 94 1663 72 1804 72 c +1804 0 l +1208 0 l +1208 72 l +1349 72 1419 94 1419 137 c +1419 1329 l +915 23 l +909 8 898 0 881 0 c +864 0 852 8 846 23 c +348 1313 l +348 193 l +348 112 418 72 559 72 c +559 0 l +72 0 l + +ce} _d +/N{1536 0 63 0 1470 1399 sc +63 0 m +63 72 l +204 72 274 112 274 193 c +274 1315 l +229 1323 159 1327 63 1327 c +63 1399 l +455 1399 l +462 1399 466 1396 469 1391 c +1194 324 l +1194 1206 l +1194 1287 1124 1327 983 1327 c +983 1399 l +1470 1399 l +1470 1327 l +1330 1327 1260 1287 1260 1206 c +1260 18 l +1260 14 1257 10 1252 6 c +1247 2 1242 0 1239 0 c +1214 0 l +1207 0 1203 3 1200 8 c +340 1274 l +340 193 l +340 112 410 72 551 72 c +551 0 l +63 0 l + +ce} _d +/O{1591 0 115 -45 1477 1444 sc +797 -45 m +667 -45 550 -11 446 58 c +343 127 262 219 203 333 c +144 448 115 568 115 694 c +115 787 132 880 165 971 c +199 1062 246 1143 306 1214 c +367 1285 439 1341 524 1382 c +609 1423 700 1444 797 1444 c +894 1444 984 1423 1069 1381 c +1154 1340 1227 1283 1288 1212 c +1349 1141 1395 1061 1428 972 c +1461 883 1477 791 1477 694 c +1477 568 1448 448 1389 333 c +1330 219 1249 127 1145 58 c +1041 -11 925 -45 797 -45 c + +457 221 m +497 158 546 108 605 71 c +664 34 728 16 797 16 c +842 16 885 25 928 43 c +971 61 1010 86 1045 117 c +1080 148 1110 183 1135 221 c +1216 344 1257 512 1257 727 c +1257 924 1216 1079 1135 1194 c +1095 1251 1045 1297 985 1332 c +926 1367 863 1384 797 1384 c +730 1384 667 1367 607 1332 c +547 1297 497 1251 457 1194 c +425 1149 400 1102 382 1051 c +365 1001 352 949 345 895 c +338 841 334 785 334 727 c +334 665 337 604 344 545 c +351 486 364 429 383 372 c +402 315 427 265 457 221 c + +ce} _d +/P{1393 0 68 0 1278 1399 sc +68 0 m +68 72 l +209 72 279 94 279 137 c +279 1262 l +279 1305 209 1327 68 1327 c +68 1399 l +797 1399 l +872 1399 946 1384 1021 1353 c +1096 1322 1157 1278 1205 1219 c +1254 1160 1278 1092 1278 1014 c +1278 938 1254 871 1205 814 c +1156 757 1095 713 1021 683 c +947 654 872 639 797 639 c +469 639 l +469 137 l +469 94 539 72 680 72 c +680 0 l +68 0 l + +463 700 m +741 700 l +816 700 877 711 924 732 c +971 754 1005 788 1026 833 c +1048 879 1059 939 1059 1014 c +1059 1125 1034 1205 985 1254 c +936 1303 855 1327 741 1327 c +567 1327 l +542 1327 523 1326 509 1324 c +496 1322 485 1316 476 1306 c +467 1297 463 1282 463 1262 c +463 700 l + +ce} _d +/Q{1591 0 115 -397 1489 1444 sc +797 -45 m +667 -45 550 -11 446 58 c +343 127 262 219 203 333 c +144 448 115 568 115 694 c +115 787 132 880 165 971 c +199 1062 246 1143 306 1214 c +367 1285 439 1341 524 1382 c +609 1423 700 1444 797 1444 c +894 1444 984 1423 1069 1381 c +1154 1340 1227 1283 1288 1212 c +1349 1141 1395 1061 1428 972 c +1461 883 1477 791 1477 694 c +1477 600 1460 508 1427 419 c +1394 330 1346 250 1283 179 c +1220 108 1147 53 1063 14 c +1089 -47 1116 -94 1143 -127 c +1170 -161 1208 -178 1257 -178 c +1308 -178 1352 -160 1389 -125 c +1426 -90 1444 -47 1444 4 c +1444 9 1446 13 1451 17 c +1456 21 1461 23 1466 23 c +1481 23 1489 14 1489 -4 c +1489 -102 1470 -192 1431 -274 c +1393 -356 1330 -397 1243 -397 c +1195 -397 1155 -385 1124 -361 c +1093 -338 1069 -308 1052 -271 c +1036 -235 1023 -193 1014 -145 c +1005 -97 996 -53 989 -14 c +929 -35 865 -45 797 -45 c + +797 14 m +858 14 918 30 975 61 c +962 120 943 166 916 201 c +890 236 850 254 797 254 c +764 254 737 242 714 217 c +691 193 680 165 680 133 c +680 98 691 70 712 47 c +734 25 762 14 797 14 c + +627 133 m +627 163 634 191 649 218 c +664 245 684 266 710 282 c +737 299 766 307 797 307 c +856 307 903 288 938 249 c +973 211 1003 159 1030 94 c +1075 128 1113 168 1144 214 c +1175 261 1198 309 1215 360 c +1232 411 1245 465 1252 522 c +1260 579 1264 637 1264 694 c +1264 789 1254 878 1235 961 c +1216 1044 1184 1119 1139 1186 c +1100 1245 1050 1293 989 1329 c +928 1366 864 1384 797 1384 c +728 1384 664 1366 603 1330 c +543 1295 493 1247 453 1186 c +406 1118 374 1042 355 959 c +337 876 328 787 328 694 c +328 549 353 416 402 297 c +451 178 534 94 649 45 c +634 73 627 102 627 133 c + +ce} _d +/R{1507 0 68 -45 1499 1399 sc +68 0 m +68 72 l +209 72 279 94 279 137 c +279 1262 l +279 1305 209 1327 68 1327 c +68 1399 l +711 1399 l +788 1399 868 1385 952 1357 c +1037 1330 1107 1288 1164 1231 c +1221 1174 1249 1107 1249 1028 c +1249 971 1232 919 1197 874 c +1163 829 1119 792 1065 761 c +1012 731 957 709 901 696 c +962 675 1016 641 1062 594 c +1108 547 1136 494 1145 434 c +1174 252 l +1187 170 1201 109 1216 68 c +1231 28 1263 8 1313 8 c +1356 8 1387 28 1408 67 c +1429 107 1440 150 1440 197 c +1440 202 1442 206 1446 209 c +1451 213 1455 215 1460 215 c +1479 215 l +1492 215 1499 206 1499 188 c +1499 151 1492 114 1477 78 c +1462 43 1441 13 1413 -10 c +1386 -33 1353 -45 1315 -45 c +1216 -45 1131 -21 1060 28 c +989 77 954 149 954 244 c +954 426 l +954 494 930 552 883 601 c +836 650 778 674 709 674 c +463 674 l +463 137 l +463 94 533 72 674 72 c +674 0 l +68 0 l + +463 727 m +682 727 l +795 727 882 750 941 795 c +1000 841 1030 919 1030 1028 c +1030 1137 1001 1214 942 1259 c +883 1304 797 1327 682 1327 c +567 1327 l +542 1327 523 1326 509 1324 c +496 1322 485 1316 476 1306 c +467 1297 463 1282 463 1262 c +463 727 l + +ce} _d +/S{1137 0 115 -45 1022 1444 sc +115 -29 m +115 449 l +115 460 121 465 133 465 c +158 465 l +162 465 166 463 169 460 c +172 457 174 453 174 449 c +174 315 215 211 298 137 c +381 64 490 27 625 27 c +672 27 716 41 756 68 c +796 95 827 131 849 175 c +872 220 883 266 883 313 c +883 354 875 394 858 433 c +841 472 817 506 786 535 c +755 564 719 583 680 592 c +414 657 l +326 680 254 728 198 800 c +143 873 115 954 115 1044 c +115 1115 133 1181 169 1243 c +205 1305 253 1354 314 1390 c +375 1426 441 1444 512 1444 c +649 1444 757 1398 838 1305 c +922 1438 l +926 1442 931 1444 936 1444 c +950 1444 l +954 1444 958 1442 961 1439 c +965 1436 967 1432 967 1427 c +967 952 l +967 940 961 934 950 934 c +926 934 l +913 934 907 940 907 952 c +907 987 901 1025 889 1066 c +878 1107 862 1147 841 1185 c +820 1223 798 1254 774 1278 c +708 1345 621 1378 512 1378 c +465 1378 422 1366 383 1342 c +344 1319 312 1287 289 1246 c +266 1205 254 1163 254 1118 c +254 1059 272 1006 308 958 c +345 911 392 879 451 864 c +717 799 l +761 788 802 769 841 741 c +880 714 913 682 939 645 c +965 608 985 567 1000 522 c +1015 477 1022 431 1022 383 c +1022 309 1005 239 971 173 c +937 108 889 55 827 15 c +766 -25 698 -45 625 -45 c +580 -45 533 -40 486 -30 c +439 -20 395 -5 355 16 c +315 37 279 63 246 96 c +160 -39 l +156 -43 151 -45 145 -45 c +133 -45 l +121 -45 115 -40 115 -29 c + +ce} _d +/U{1536 0 63 -45 1470 1399 sc +274 463 m +274 1262 l +274 1305 204 1327 63 1327 c +63 1399 l +676 1399 l +676 1327 l +535 1327 465 1305 465 1262 c +465 471 l +465 395 475 323 496 255 c +517 188 553 133 602 90 c +652 48 717 27 797 27 c +875 27 944 47 1003 88 c +1062 129 1108 184 1140 252 c +1172 320 1188 393 1188 471 c +1188 1206 l +1188 1287 1118 1327 977 1327 c +977 1399 l +1470 1399 l +1470 1327 l +1330 1327 1260 1287 1260 1206 c +1260 463 l +1260 400 1249 338 1226 277 c +1204 216 1172 161 1129 112 c +1087 63 1037 25 980 -3 c +923 -31 862 -45 797 -45 c +706 -45 620 -22 539 23 c +458 68 394 130 346 208 c +298 286 274 371 274 463 c + +ce} _d +/V{1536 0 39 -45 1495 1399 sc +721 -23 m +238 1262 l +226 1291 203 1309 169 1316 c +135 1323 92 1327 39 1327 c +39 1399 l +602 1399 l +602 1327 l +489 1327 432 1309 432 1274 c +433 1272 433 1270 433 1268 c +434 1267 434 1265 434 1262 c +827 217 l +1198 1206 l +1201 1211 1202 1220 1202 1231 c +1202 1264 1187 1289 1156 1304 c +1125 1319 1091 1327 1053 1327 c +1053 1399 l +1495 1399 l +1495 1327 l +1444 1327 1398 1317 1359 1298 c +1320 1279 1293 1249 1276 1206 c +813 -23 l +809 -38 798 -45 780 -45 c +754 -45 l +736 -45 725 -38 721 -23 c + +ce} _d +/W{2103 0 37 -45 2066 1399 sc +637 -23 m +219 1262 l +208 1291 188 1309 157 1316 c +126 1323 86 1327 37 1327 c +37 1399 l +586 1399 l +586 1327 l +471 1327 414 1309 414 1272 c +414 1262 l +741 254 l +1020 1116 l +973 1262 l +962 1291 942 1309 911 1316 c +880 1323 840 1327 791 1327 c +791 1399 l +1339 1399 l +1339 1327 l +1223 1327 1165 1309 1165 1272 c +1165 1270 1166 1267 1167 1263 c +1168 1259 1169 1256 1169 1255 c +1495 254 l +1802 1206 l +1803 1210 1804 1216 1804 1225 c +1804 1261 1785 1287 1747 1303 c +1709 1319 1669 1327 1628 1327 c +1628 1399 l +2066 1399 l +2066 1327 l +1958 1327 1891 1287 1866 1206 c +1466 -23 l +1461 -38 1451 -45 1436 -45 c +1421 -45 l +1406 -45 1396 -38 1391 -23 c +1053 1020 l +713 -23 l +708 -38 698 -45 682 -45 c +668 -45 l +652 -45 642 -38 637 -23 c + +ce} _d +/X{1536 0 47 0 1487 1399 sc +47 0 m +47 72 l +186 72 283 111 338 188 c +678 694 l +301 1268 l +280 1293 251 1309 214 1316 c +178 1323 132 1327 76 1327 c +76 1399 l +649 1399 l +649 1327 l +624 1327 596 1322 564 1312 c +532 1303 516 1289 516 1272 c +516 1269 517 1267 518 1264 c +786 856 l +1022 1208 l +1027 1220 1030 1230 1030 1237 c +1030 1265 1016 1287 988 1303 c +961 1319 932 1327 901 1327 c +901 1399 l +1401 1399 l +1401 1327 l +1262 1327 1165 1288 1110 1210 c +829 791 l +1262 131 l +1285 105 1315 89 1350 82 c +1386 75 1432 72 1487 72 c +1487 0 l +913 0 l +913 72 l +935 72 963 77 996 86 c +1030 96 1047 110 1047 127 c +1047 131 1046 134 1044 135 c +721 629 l +426 190 l +422 182 420 173 420 162 c +420 134 434 112 461 96 c +488 80 517 72 547 72 c +547 0 l +47 0 l + +ce} _d +/Y{1536 0 23 0 1511 1399 sc +463 0 m +463 72 l +604 72 674 94 674 137 c +674 559 l +244 1266 l +224 1293 196 1310 160 1317 c +124 1324 78 1327 23 1327 c +23 1399 l +600 1399 l +600 1327 l +505 1327 457 1311 457 1280 c +457 1277 458 1272 461 1264 c +831 653 l +1169 1208 l +1178 1221 1182 1234 1182 1249 c +1182 1276 1170 1296 1146 1308 c +1123 1321 1096 1327 1067 1327 c +1067 1399 l +1511 1399 l +1511 1327 l +1458 1327 1408 1317 1362 1298 c +1317 1279 1281 1250 1255 1210 c +858 559 l +858 137 l +858 94 928 72 1069 72 c +1069 0 l +463 0 l + +ce} _d +/Z{1251 0 115 0 1147 1399 sc +137 0 m +122 0 115 8 115 23 c +115 51 l +115 56 116 60 119 63 c +915 1327 l +627 1327 l +531 1327 452 1314 389 1289 c +327 1264 280 1222 248 1164 c +217 1106 201 1028 201 930 c +141 930 l +164 1399 l +1112 1399 l +1127 1399 1135 1391 1135 1376 c +1135 1352 l +1135 1346 1134 1342 1133 1339 c +336 78 l +637 78 l +710 78 775 85 833 98 c +891 111 940 137 979 176 c +1006 203 1028 238 1043 279 c +1058 320 1068 360 1073 399 c +1078 438 1082 490 1087 555 c +1147 555 l +1112 0 l +137 0 l + +ce} _d +/d{1137 0 68 -23 1083 1421 sc +500 -23 m +419 -23 346 -1 279 42 c +212 86 160 144 123 215 c +86 286 68 362 68 442 c +68 525 88 601 128 672 c +169 743 224 800 293 842 c +362 884 439 905 522 905 c +572 905 619 894 664 873 c +709 852 747 823 780 786 c +780 1212 l +780 1249 774 1275 763 1291 c +752 1308 737 1318 716 1321 c +696 1325 664 1327 621 1327 c +621 1399 l +924 1421 l +924 186 l +924 150 929 124 940 107 c +951 91 967 81 987 77 c +1008 74 1040 72 1083 72 c +1083 0 l +774 -23 l +774 106 l +739 65 697 34 648 11 c +599 -12 550 -23 500 -23 c + +291 178 m +314 133 345 98 384 71 c +423 44 466 31 512 31 c +569 31 621 47 668 80 c +715 113 751 155 774 207 c +774 698 l +758 728 738 755 713 778 c +689 802 662 820 631 833 c +601 846 569 852 535 852 c +464 852 406 832 363 791 c +320 751 289 700 272 637 c +255 574 246 509 246 440 c +246 385 249 338 254 297 c +260 256 272 217 291 178 c + +ce} _d +/f{625 0 66 0 739 1444 sc +66 0 m +66 72 l +112 72 150 76 180 83 c +210 90 225 108 225 137 c +225 811 l +68 811 l +68 883 l +225 883 l +225 1128 l +225 1172 234 1213 252 1251 c +271 1290 295 1323 326 1352 c +357 1381 393 1403 433 1419 c +474 1436 516 1444 559 1444 c +605 1444 646 1430 683 1403 c +720 1376 739 1339 739 1294 c +739 1268 730 1246 712 1228 c +695 1211 673 1202 647 1202 c +621 1202 599 1211 580 1228 c +562 1246 553 1268 553 1294 c +553 1337 571 1365 608 1380 c +586 1387 566 1391 549 1391 c +509 1391 475 1377 446 1348 c +418 1320 397 1286 383 1245 c +369 1204 362 1164 362 1124 c +362 883 l +598 883 l +598 811 l +369 811 l +369 137 l +369 109 389 91 429 83 c +469 76 515 72 567 72 c +567 0 l +66 0 l + +ce} _d +/g{1024 0 57 -422 993 928 sc +57 -160 m +57 -111 75 -69 110 -32 c +145 4 187 30 236 45 c +209 66 188 92 173 123 c +159 154 152 188 152 223 c +152 287 172 344 213 393 c +150 454 119 525 119 604 c +119 647 128 687 146 724 c +165 761 190 794 223 821 c +256 848 292 869 332 883 c +372 898 413 905 455 905 c +536 905 609 881 674 834 c +702 864 735 887 773 903 c +812 920 852 928 893 928 c +922 928 946 917 965 896 c +984 875 993 850 993 821 c +993 804 987 790 974 777 c +961 764 947 758 930 758 c +913 758 898 764 885 777 c +872 790 866 804 866 821 c +866 846 874 864 891 874 c +820 874 760 850 709 801 c +734 776 753 746 768 710 c +783 675 791 639 791 604 c +791 546 775 494 743 447 c +711 401 669 365 616 339 c +564 314 510 301 455 301 c +380 301 312 321 250 362 c +231 335 221 305 221 272 c +221 236 233 204 256 177 c +280 150 310 137 346 137 c +514 137 l +595 137 669 130 734 115 c +799 100 854 71 898 27 c +943 -17 965 -79 965 -160 c +965 -220 940 -270 889 -309 c +838 -349 778 -378 707 -395 c +637 -413 572 -422 512 -422 c +451 -422 386 -413 315 -395 c +244 -378 184 -349 133 -309 c +82 -270 57 -220 57 -160 c + +172 -160 m +172 -206 191 -244 228 -275 c +265 -306 310 -329 363 -344 c +416 -359 465 -367 512 -367 c +558 -367 607 -359 660 -344 c +713 -329 757 -306 794 -275 c +831 -244 850 -206 850 -160 c +850 -89 817 -42 752 -21 c +687 -0 607 10 514 10 c +346 10 l +315 10 286 3 259 -12 c +233 -27 212 -48 196 -75 c +180 -102 172 -131 172 -160 c + +455 356 m +571 356 629 439 629 604 c +629 675 617 734 592 780 c +567 827 522 850 455 850 c +388 850 343 827 318 780 c +293 734 281 675 281 604 c +281 559 286 518 295 481 c +304 444 322 414 347 391 c +372 368 408 356 455 356 c + +ce} _d +/j{625 0 -90 -420 434 1370 sc +41 -344 m +72 -359 108 -367 147 -367 c +201 -367 238 -339 259 -284 c +280 -229 291 -170 291 -106 c +291 696 l +291 733 284 759 271 775 c +258 792 239 802 216 805 c +193 809 160 811 115 811 c +115 883 l +434 905 l +434 -115 l +434 -168 421 -217 395 -264 c +369 -311 334 -349 289 -377 c +244 -406 196 -420 143 -420 c +84 -420 30 -405 -18 -376 c +-66 -347 -90 -305 -90 -252 c +-90 -225 -80 -202 -61 -183 c +-42 -164 -19 -154 8 -154 c +35 -154 58 -164 77 -183 c +96 -202 106 -225 106 -252 c +106 -273 100 -292 88 -309 c +77 -326 61 -338 41 -344 c + +209 1257 m +209 1287 220 1313 243 1336 c +266 1359 292 1370 322 1370 c +341 1370 359 1365 377 1355 c +395 1345 409 1331 419 1313 c +429 1295 434 1276 434 1257 c +434 1228 423 1202 401 1179 c +379 1156 353 1145 322 1145 c +292 1145 266 1156 243 1179 c +220 1202 209 1228 209 1257 c + +ce} _d +/k{1079 0 53 0 1047 1421 sc +53 0 m +53 72 l +100 72 138 76 168 83 c +198 90 213 108 213 137 c +213 1212 l +213 1249 207 1275 196 1291 c +185 1308 170 1318 149 1321 c +128 1325 96 1327 53 1327 c +53 1399 l +356 1421 l +356 449 l +631 690 l +658 716 672 740 672 762 c +672 778 666 790 655 798 c +644 807 630 811 614 811 c +614 883 l +999 883 l +999 811 l +906 811 814 771 721 690 c +575 563 l +836 193 l +872 142 902 109 926 94 c +951 79 991 72 1047 72 c +1047 0 l +639 0 l +639 72 l +686 72 709 86 709 115 c +709 136 697 162 672 193 c +475 473 l +350 365 l +350 137 l +350 108 365 90 395 83 c +426 76 464 72 510 72 c +510 0 l +53 0 l + +ce} _d +/l{567 0 63 0 526 1421 sc +63 0 m +63 72 l +110 72 148 76 178 83 c +208 90 223 108 223 137 c +223 1212 l +223 1249 217 1275 206 1291 c +195 1308 180 1318 159 1321 c +138 1325 106 1327 63 1327 c +63 1399 l +367 1421 l +367 137 l +367 108 382 90 412 83 c +442 76 480 72 526 72 c +526 0 l +63 0 l + +ce} _d +/m{1706 0 61 0 1669 905 sc +61 0 m +61 72 l +108 72 146 76 176 83 c +206 90 221 108 221 137 c +221 696 l +221 733 215 759 204 775 c +193 792 178 802 157 805 c +136 809 104 811 61 811 c +61 883 l +358 905 l +358 705 l +385 764 426 812 479 849 c +533 886 592 905 655 905 c +812 905 905 841 932 713 c +959 770 999 817 1052 852 c +1105 887 1162 905 1225 905 c +1287 905 1339 895 1381 875 c +1424 855 1456 824 1477 783 c +1498 742 1509 691 1509 629 c +1509 137 l +1509 108 1524 90 1554 83 c +1585 76 1623 72 1669 72 c +1669 0 l +1200 0 l +1200 72 l +1247 72 1285 76 1315 83 c +1345 90 1360 108 1360 137 c +1360 623 l +1360 692 1350 747 1331 789 c +1312 831 1272 852 1212 852 c +1133 852 1068 820 1017 757 c +966 694 940 622 940 541 c +940 137 l +940 108 955 90 985 83 c +1015 76 1053 72 1100 72 c +1100 0 l +631 0 l +631 72 l +678 72 716 76 746 83 c +776 90 791 108 791 137 c +791 623 l +791 690 781 744 762 787 c +743 830 703 852 643 852 c +564 852 498 820 447 757 c +396 694 371 622 371 541 c +371 137 l +371 108 386 90 416 83 c +446 76 484 72 530 72 c +530 0 l +61 0 l + +ce} _d +/n{1137 0 61 0 1100 905 sc +61 0 m +61 72 l +108 72 146 76 176 83 c +206 90 221 108 221 137 c +221 696 l +221 733 215 759 204 775 c +193 792 178 802 157 805 c +136 809 104 811 61 811 c +61 883 l +358 905 l +358 705 l +385 764 426 812 479 849 c +533 886 592 905 655 905 c +750 905 821 882 868 837 c +916 792 940 722 940 629 c +940 137 l +940 108 955 90 985 83 c +1015 76 1053 72 1100 72 c +1100 0 l +631 0 l +631 72 l +678 72 716 76 746 83 c +776 90 791 108 791 137 c +791 623 l +791 690 781 744 762 787 c +743 830 703 852 643 852 c +564 852 498 820 447 757 c +396 694 371 622 371 541 c +371 137 l +371 108 386 90 416 83 c +446 76 484 72 530 72 c +530 0 l +61 0 l + +ce} _d +/o{1024 0 57 -23 965 918 sc +512 -23 m +430 -23 354 -2 284 39 c +214 81 159 137 118 207 c +77 277 57 353 57 436 c +57 499 68 559 90 617 c +113 675 145 727 186 772 c +228 818 277 854 332 879 c +387 905 447 918 512 918 c +596 918 672 896 741 851 c +810 807 865 748 905 673 c +945 599 965 520 965 436 c +965 354 945 278 904 207 c +863 137 808 81 738 39 c +669 -2 593 -23 512 -23 c + +512 37 m +621 37 694 77 731 156 c +768 235 786 336 786 459 c +786 528 782 584 775 629 c +768 674 752 715 727 752 c +712 775 692 794 668 811 c +645 828 620 841 593 850 c +567 859 540 864 512 864 c +469 864 429 854 390 835 c +352 816 320 788 295 752 c +270 713 253 671 246 624 c +239 578 236 523 236 459 c +236 382 243 313 256 252 c +269 191 296 140 336 99 c +377 58 435 37 512 37 c + +ce} _d +/p{1137 0 53 -397 1069 905 sc +53 -397 m +53 -326 l +100 -326 138 -322 168 -314 c +198 -306 213 -288 213 -260 c +213 727 l +213 764 200 788 173 797 c +146 806 106 811 53 811 c +53 883 l +356 905 l +356 778 l +393 819 437 851 486 872 c +536 894 589 905 645 905 c +726 905 798 883 863 839 c +928 796 978 738 1014 667 c +1051 596 1069 521 1069 442 c +1069 359 1049 282 1008 211 c +968 140 913 83 843 40 c +774 -2 697 -23 614 -23 c +515 -23 431 17 362 98 c +362 -260 l +362 -288 377 -306 407 -314 c +438 -322 476 -326 522 -326 c +522 -397 l +53 -397 l + +362 199 m +386 150 419 110 462 78 c +505 47 551 31 602 31 c +649 31 691 44 727 69 c +764 94 794 128 819 171 c +844 214 862 258 873 305 c +885 352 891 398 891 442 c +891 497 881 556 861 619 c +842 683 812 737 771 780 c +731 824 682 846 625 846 c +570 846 519 832 472 803 c +426 775 389 737 362 688 c +362 199 l + +ce} _d +/q{1079 0 68 -397 1083 905 sc +614 -397 m +614 -326 l +661 -326 699 -322 729 -314 c +759 -306 774 -288 774 -260 c +774 119 l +742 76 702 42 653 16 c +604 -10 553 -23 500 -23 c +439 -23 382 -10 329 15 c +276 40 230 75 191 118 c +152 161 122 211 100 268 c +79 325 68 383 68 442 c +68 523 88 600 128 671 c +168 743 223 800 292 842 c +362 884 437 905 518 905 c +576 905 630 889 679 856 c +728 823 768 780 797 725 c +870 905 l +924 905 l +924 -260 l +924 -288 939 -306 969 -314 c +999 -322 1037 -326 1083 -326 c +1083 -397 l +614 -397 l + +512 31 m +573 31 627 51 674 91 c +722 132 757 183 780 244 c +780 604 l +766 669 737 726 693 774 c +649 822 596 846 535 846 c +488 846 447 834 410 809 c +373 784 343 751 318 710 c +294 669 276 624 264 576 c +252 528 246 483 246 440 c +246 385 256 326 275 261 c +294 197 324 143 364 98 c +404 53 453 31 512 31 c + +ce} _d +/u{1137 0 61 -23 1100 905 sc +221 244 m +221 696 l +221 733 215 759 204 775 c +193 792 178 802 157 805 c +136 809 104 811 61 811 c +61 883 l +371 905 l +371 244 l +371 191 375 149 382 119 c +390 90 406 68 431 53 c +456 38 496 31 551 31 c +624 31 683 62 726 123 c +769 184 791 254 791 332 c +791 696 l +791 733 785 759 774 775 c +763 792 747 802 726 805 c +706 809 674 811 631 811 c +631 883 l +940 905 l +940 186 l +940 150 945 124 956 107 c +967 91 983 81 1004 77 c +1025 74 1057 72 1100 72 c +1100 0 l +797 -23 l +797 150 l +772 99 736 57 691 25 c +646 -7 596 -23 541 -23 c +443 -23 365 -2 307 39 c +250 81 221 149 221 244 c + +ce} _d +/v{1079 0 39 -23 1040 883 sc +500 0 m +201 752 l +188 777 169 793 142 800 c +116 807 82 811 39 811 c +39 883 l +469 883 l +469 811 l +392 811 354 795 354 762 c +354 757 355 753 356 750 c +586 172 l +793 694 l +797 705 799 716 799 727 c +799 753 789 773 769 788 c +750 803 727 811 700 811 c +700 883 l +1040 883 l +1040 811 l +998 811 961 801 929 782 c +898 763 873 734 856 696 c +580 0 l +575 -15 564 -23 547 -23 c +532 -23 l +515 -23 505 -15 500 0 c + +ce} _d +/w{1479 0 37 -23 1440 883 sc +453 0 m +188 745 l +176 775 159 793 136 800 c +113 807 80 811 37 811 c +37 883 l +459 883 l +459 811 l +378 811 338 794 338 760 c +339 758 339 756 339 754 c +340 752 340 749 340 745 c +537 193 l +707 674 l +680 745 l +669 775 652 793 629 800 c +606 807 573 811 530 811 c +530 883 l +934 883 l +934 811 l +853 811 813 794 813 760 c +813 755 814 750 815 745 c +1020 168 l +1206 690 l +1209 701 1210 710 1210 719 c +1210 748 1198 770 1173 786 c +1149 803 1122 811 1092 811 c +1092 883 l +1440 883 l +1440 811 l +1399 811 1363 800 1333 778 c +1304 757 1282 727 1268 690 c +1024 0 l +1019 -15 1009 -23 993 -23 c +977 -23 l +961 -23 951 -15 946 0 c +739 584 l +532 0 l +525 -15 515 -23 500 -23 c +485 -23 l +468 -23 458 -15 453 0 c + +ce} _d +/x{1079 0 25 0 1057 883 sc +25 0 m +25 72 l +77 72 126 82 172 102 c +218 123 257 153 289 193 c +475 430 l +233 745 l +209 775 183 793 154 800 c +126 807 86 811 35 811 c +35 883 l +461 883 l +461 811 l +443 811 426 807 410 799 c +395 791 387 779 387 764 c +387 759 389 752 393 745 c +557 532 l +680 690 l +697 710 705 730 705 750 c +705 767 699 781 688 793 c +677 805 663 811 645 811 c +645 883 l +1022 883 l +1022 811 l +969 811 920 801 873 780 c +827 760 788 730 756 690 c +594 483 l +856 137 l +882 107 909 89 937 82 c +965 75 1005 72 1057 72 c +1057 0 l +631 0 l +631 72 l +648 72 664 76 679 84 c +694 92 702 104 702 119 c +702 125 700 131 696 137 c +512 381 l +365 193 l +350 176 342 156 342 133 c +342 116 348 102 359 90 c +370 78 384 72 399 72 c +399 0 l +25 0 l + +ce} _d +/y{1079 0 39 -420 1040 883 sc +141 -336 m +167 -357 196 -367 227 -367 c +313 -367 383 -302 438 -172 c +508 0 l +201 752 l +188 777 169 793 143 800 c +117 807 82 811 39 811 c +39 883 l +469 883 l +469 811 l +394 811 356 795 356 762 c +356 757 357 753 358 750 c +586 190 l +791 694 l +795 705 797 716 797 729 c +797 746 792 760 783 772 c +774 785 763 794 748 801 c +734 808 718 811 700 811 c +700 883 l +1040 883 l +1040 811 l +998 811 961 801 929 782 c +898 763 873 734 856 696 c +502 -172 l +483 -216 461 -256 436 -293 c +411 -330 381 -361 345 -384 c +309 -408 270 -420 227 -420 c +177 -420 133 -403 95 -370 c +58 -337 39 -297 39 -248 c +39 -223 48 -201 65 -184 c +82 -167 104 -158 129 -158 c +146 -158 162 -162 175 -169 c +189 -177 200 -188 207 -201 c +215 -214 219 -230 219 -248 c +219 -270 212 -290 197 -307 c +182 -324 164 -334 141 -336 c + +ce} _d +/z{909 0 57 0 821 883 sc +80 0 m +65 0 57 8 57 23 c +57 39 l +57 44 59 49 63 53 c +635 829 l +451 829 l +393 829 345 825 307 818 c +270 811 239 797 214 776 c +190 756 172 728 161 692 c +150 657 145 608 145 545 c +86 545 l +109 883 l +795 883 l +801 883 806 881 810 876 c +815 872 817 867 817 860 c +817 848 l +817 844 816 839 813 834 c +240 59 l +436 59 l +495 59 545 63 584 70 c +624 77 658 95 686 123 c +712 149 730 184 739 228 c +749 272 757 326 762 391 c +821 391 l +786 0 l +80 0 l + +ce} _d +/zero{1024 0 80 -45 942 1364 sc +512 -45 m +345 -45 231 24 170 161 c +110 299 80 463 80 653 c +80 772 91 883 112 988 c +134 1093 177 1181 241 1254 c +306 1327 396 1364 512 1364 c +602 1364 676 1342 733 1298 c +790 1254 834 1197 864 1127 c +894 1058 914 983 925 903 c +936 824 942 740 942 653 c +942 536 931 426 909 323 c +888 221 845 134 782 62 c +719 -9 629 -45 512 -45 c + +512 8 m +588 8 645 47 682 125 c +719 203 742 289 751 384 c +760 479 764 579 764 686 c +764 789 760 883 751 970 c +742 1057 719 1135 682 1205 c +645 1276 589 1311 512 1311 c +435 1311 377 1276 340 1205 c +303 1134 280 1056 271 969 c +262 883 258 789 258 686 c +258 610 260 538 263 471 c +267 404 277 334 293 262 c +309 191 335 130 370 81 c +406 32 453 8 512 8 c + +ce} _d +/one{1024 0 178 0 862 1364 sc +190 0 m +190 72 l +361 72 446 94 446 137 c +446 1212 l +375 1178 286 1161 178 1161 c +178 1233 l +345 1233 472 1277 557 1364 c +586 1364 l +591 1364 595 1362 599 1358 c +604 1355 606 1351 606 1346 c +606 137 l +606 94 691 72 862 72 c +862 0 l +190 0 l + +ce} _d +/two{1024 0 102 0 920 1364 sc +102 0 m +102 55 l +102 58 103 62 106 66 c +424 418 l +472 470 511 514 541 549 c +571 584 601 625 630 671 c +659 717 682 764 699 811 c +716 859 725 910 725 963 c +725 1019 715 1072 694 1123 c +673 1174 642 1215 601 1246 c +560 1277 511 1292 453 1292 c +394 1292 340 1274 293 1238 c +246 1203 212 1157 193 1100 c +198 1101 206 1102 215 1102 c +246 1102 272 1092 293 1071 c +315 1050 326 1024 326 991 c +326 960 315 933 293 911 c +272 890 246 879 215 879 c +183 879 156 890 134 912 c +113 935 102 961 102 991 c +102 1042 112 1090 131 1135 c +150 1180 178 1220 214 1255 c +251 1290 292 1317 337 1336 c +383 1355 432 1364 483 1364 c +561 1364 634 1347 701 1314 c +768 1281 822 1235 861 1174 c +900 1114 920 1044 920 963 c +920 904 907 847 881 794 c +855 741 822 692 781 648 c +740 605 688 555 625 500 c +562 445 520 408 500 389 c +268 166 l +465 166 l +562 166 642 167 707 168 c +772 170 807 173 811 176 c +827 193 843 256 860 365 c +920 365 l +862 0 l +102 0 l + +ce} _d +/three{1024 0 86 -45 936 1364 sc +195 158 m +227 111 270 77 324 54 c +378 31 436 20 498 20 c +577 20 634 54 667 121 c +700 189 717 266 717 352 c +717 391 713 429 706 468 c +699 507 688 543 671 576 c +654 609 631 636 602 656 c +573 676 538 686 496 686 c +360 686 l +348 686 342 692 342 705 c +342 723 l +342 734 348 739 360 739 c +473 748 l +521 748 561 766 592 802 c +624 838 647 882 662 933 c +677 985 684 1034 684 1081 c +684 1146 669 1200 638 1242 c +607 1284 561 1305 498 1305 c +446 1305 396 1295 349 1275 c +302 1256 264 1226 236 1186 c +239 1187 241 1187 243 1187 c +245 1188 247 1188 250 1188 c +281 1188 306 1177 327 1156 c +348 1135 358 1109 358 1079 c +358 1050 348 1024 327 1003 c +306 982 281 971 250 971 c +220 971 194 982 173 1003 c +152 1024 141 1050 141 1079 c +141 1138 159 1189 194 1232 c +229 1275 275 1308 330 1330 c +386 1353 442 1364 498 1364 c +539 1364 583 1358 629 1345 c +675 1333 717 1315 754 1292 c +791 1269 822 1240 845 1204 c +869 1168 881 1127 881 1081 c +881 1024 868 971 842 922 c +817 873 782 831 737 796 c +692 761 643 734 590 717 c +649 706 706 683 759 650 c +812 617 855 574 887 522 c +920 470 936 414 936 354 c +936 279 915 210 874 149 c +833 88 778 41 711 6 c +644 -28 573 -45 498 -45 c +434 -45 370 -33 305 -8 c +241 16 188 52 147 101 c +106 150 86 208 86 276 c +86 310 97 338 120 361 c +143 384 171 395 205 395 c +227 395 247 390 265 379 c +284 369 298 355 308 336 c +319 317 324 297 324 276 c +324 243 312 215 289 192 c +266 169 238 158 205 158 c +195 158 l + +ce} _d +/four{1024 0 57 0 965 1364 sc +57 338 m +57 410 l +690 1354 l +695 1361 702 1364 711 1364 c +741 1364 l +756 1364 764 1356 764 1341 c +764 410 l +965 410 l +965 338 l +764 338 l +764 137 l +764 109 784 91 824 83 c +864 76 910 72 963 72 c +963 0 l +399 0 l +399 72 l +452 72 498 76 538 83 c +578 91 598 109 598 137 c +598 338 l +57 338 l + +125 410 m +610 410 l +610 1135 l +125 410 l + +ce} _d +/five{1024 0 102 -45 920 1364 sc +178 233 m +192 193 213 157 242 124 c +271 91 306 66 345 47 c +385 29 426 20 469 20 c +568 20 636 58 673 135 c +710 212 729 305 729 414 c +729 461 728 501 726 533 c +725 566 720 597 713 627 c +700 675 678 717 646 753 c +615 789 576 807 530 807 c +484 807 444 800 411 786 c +378 772 352 756 331 737 c +310 718 292 699 276 678 c +260 657 250 646 246 645 c +223 645 l +220 645 215 647 210 651 c +205 656 203 660 203 664 c +203 1348 l +203 1351 205 1355 209 1358 c +214 1362 218 1364 223 1364 c +229 1364 l +321 1320 419 1298 522 1298 c +623 1298 721 1320 815 1364 c +821 1364 l +826 1364 830 1362 834 1359 c +838 1356 840 1352 840 1348 c +840 1329 l +840 1322 839 1319 836 1319 c +789 1257 731 1209 660 1174 c +590 1139 517 1122 442 1122 c +387 1122 331 1130 274 1145 c +274 758 l +319 795 360 821 395 836 c +431 852 477 860 532 860 c +607 860 675 838 734 795 c +794 752 840 695 872 625 c +904 556 920 485 920 412 c +920 330 900 254 859 184 c +819 114 764 58 695 17 c +626 -24 550 -45 469 -45 c +402 -45 340 -28 283 7 c +227 42 183 88 150 147 c +118 206 102 268 102 334 c +102 365 112 390 132 409 c +152 428 177 438 207 438 c +237 438 262 428 282 408 c +303 389 313 364 313 334 c +313 305 303 280 282 259 c +262 239 237 229 207 229 c +202 229 197 229 191 230 c +185 231 181 232 178 233 c + +ce} _d +/six{1024 0 86 -45 936 1364 sc +512 -45 m +427 -45 357 -23 300 22 c +243 67 199 126 168 197 c +137 269 116 344 104 423 c +92 502 86 581 86 662 c +86 770 107 878 149 987 c +191 1096 253 1186 334 1257 c +416 1328 513 1364 625 1364 c +672 1364 715 1355 755 1337 c +796 1320 827 1294 850 1259 c +873 1225 885 1184 885 1135 c +885 1107 875 1083 856 1064 c +837 1045 814 1036 786 1036 c +759 1036 736 1046 717 1065 c +698 1084 688 1108 688 1135 c +688 1162 698 1185 717 1204 c +736 1223 759 1233 786 1233 c +797 1233 l +780 1258 755 1276 723 1287 c +692 1299 659 1305 625 1305 c +584 1305 545 1296 510 1278 c +475 1260 444 1236 416 1205 c +388 1174 365 1140 346 1103 c +327 1066 313 1024 302 977 c +292 930 286 885 283 844 c +280 803 279 751 279 688 c +303 744 337 790 381 825 c +425 861 475 879 530 879 c +591 879 646 867 696 842 c +746 817 789 783 825 739 c +861 696 888 646 907 590 c +926 534 936 477 936 420 c +936 340 918 264 882 191 c +847 119 797 62 732 19 c +667 -24 594 -45 512 -45 c + +512 20 m +565 20 607 32 639 56 c +671 80 694 112 709 151 c +724 191 734 231 737 271 c +741 312 743 361 743 420 c +743 497 739 563 732 618 c +725 673 705 721 672 762 c +639 804 589 825 522 825 c +467 825 421 806 385 769 c +350 732 324 684 307 627 c +291 570 283 516 283 463 c +283 445 284 431 285 422 c +285 420 285 418 284 417 c +284 416 284 414 283 412 c +283 353 289 294 301 234 c +313 174 336 123 370 82 c +404 41 451 20 512 20 c + +ce} _d +/seven{1024 0 115 -45 993 1384 sc +356 53 m +356 129 363 203 376 276 c +389 349 409 420 434 491 c +460 562 491 632 527 700 c +564 769 604 833 647 893 c +834 1153 l +600 1153 l +357 1153 232 1150 225 1143 c +207 1121 190 1058 174 954 c +115 954 l +182 1384 l +242 1384 l +242 1378 l +242 1353 284 1337 367 1330 c +450 1323 532 1319 612 1319 c +993 1319 l +993 1266 l +993 1265 993 1264 992 1263 c +992 1262 992 1261 991 1260 c +709 864 l +640 761 596 647 579 522 c +562 397 553 240 553 53 c +553 26 543 3 524 -16 c +505 -35 482 -45 455 -45 c +428 -45 404 -35 385 -16 c +366 3 356 26 356 53 c + +ce} _d +/eight{1024 0 86 -45 936 1364 sc +86 311 m +86 393 113 465 167 528 c +221 591 290 644 375 686 c +299 735 l +252 766 214 806 185 857 c +156 908 141 962 141 1018 c +141 1083 158 1142 192 1195 c +227 1248 272 1289 329 1319 c +386 1349 447 1364 512 1364 c +573 1364 631 1352 687 1327 c +744 1302 790 1267 826 1221 c +863 1175 881 1120 881 1057 c +881 1011 870 968 848 929 c +827 890 797 854 759 823 c +722 792 682 765 639 743 c +756 668 l +810 633 853 586 886 529 c +919 472 936 411 936 348 c +936 274 916 207 876 146 c +837 85 784 38 719 5 c +654 -28 585 -45 512 -45 c +441 -45 373 -31 307 -2 c +242 27 188 68 147 122 c +106 177 86 240 86 311 c + +197 311 m +197 257 212 208 241 163 c +271 118 310 83 359 58 c +408 33 459 20 512 20 c +591 20 663 43 728 89 c +793 136 825 197 825 274 c +825 300 820 326 809 351 c +799 377 785 400 766 421 c +748 442 728 460 705 473 c +430 651 l +387 628 348 600 312 565 c +277 530 249 491 228 448 c +207 405 197 359 197 311 c + +338 936 m +586 776 l +643 809 690 850 727 897 c +764 944 782 998 782 1057 c +782 1103 769 1145 743 1183 c +718 1222 684 1252 643 1273 c +602 1294 557 1305 510 1305 c +469 1305 427 1297 385 1281 c +343 1265 308 1241 281 1209 c +254 1178 240 1141 240 1098 c +240 1034 273 980 338 936 c + +ce} _d +/nine{1024 0 86 -45 936 1364 sc +231 86 m +268 42 333 20 426 20 c +478 20 526 38 571 73 c +616 108 651 152 676 203 c +705 261 723 323 731 388 c +739 454 743 536 743 633 c +720 578 686 532 642 497 c +599 462 549 444 492 444 c +413 444 342 465 279 508 c +217 551 169 608 136 678 c +103 749 86 824 86 903 c +86 985 105 1061 142 1132 c +179 1203 231 1260 297 1301 c +363 1343 438 1364 522 1364 c +605 1364 674 1341 729 1296 c +785 1251 828 1193 857 1122 c +886 1051 907 976 918 897 c +930 818 936 739 936 662 c +936 557 917 449 878 339 c +839 230 781 138 704 65 c +627 -8 535 -45 426 -45 c +345 -45 277 -26 221 12 c +165 50 137 107 137 184 c +137 212 146 235 165 254 c +184 273 208 283 236 283 c +263 283 286 273 305 254 c +324 235 334 212 334 184 c +334 157 324 134 305 115 c +286 96 263 86 236 86 c +231 86 l + +500 498 m +556 498 602 517 637 554 c +673 592 699 639 715 695 c +731 751 739 807 739 862 c +739 901 l +739 909 l +739 1012 724 1103 694 1184 c +664 1265 607 1305 522 1305 c +468 1305 424 1293 390 1269 c +357 1246 332 1214 316 1175 c +300 1136 290 1094 285 1049 c +281 1004 279 956 279 903 c +279 826 283 760 290 705 c +297 650 317 602 350 560 c +383 519 433 498 500 498 c + +ce} _d +/exclam{567 0 172 0 397 1466 sc +172 113 m +172 144 183 170 206 192 c +229 214 255 225 285 225 c +304 225 322 220 340 210 c +358 200 372 186 382 168 c +392 150 397 132 397 113 c +397 83 386 57 364 34 c +342 11 316 0 285 0 c +255 0 229 11 206 34 c +183 57 172 83 172 113 c + +256 408 m +172 1352 l +172 1364 l +172 1393 183 1417 206 1436 c +229 1456 256 1466 285 1466 c +315 1466 341 1456 363 1436 c +386 1417 397 1393 397 1364 c +397 1352 l +315 408 l +315 403 313 399 309 395 c +305 391 301 389 297 389 c +272 389 l +269 389 265 391 261 395 c +258 400 256 404 256 408 c + +ce} _d +/quotedblright{1024 0 68 799 719 1421 sc +98 827 m +98 833 101 839 106 844 c +157 888 196 942 225 1006 c +254 1070 268 1136 268 1204 c +268 1218 267 1228 266 1235 c +247 1209 218 1196 180 1196 c +149 1196 123 1207 101 1229 c +79 1251 68 1278 68 1309 c +68 1341 79 1368 101 1389 c +123 1410 149 1421 180 1421 c +214 1421 242 1410 263 1387 c +284 1364 299 1336 308 1302 c +317 1268 322 1235 322 1204 c +322 1129 306 1055 273 984 c +241 913 197 852 141 803 c +136 800 132 799 129 799 c +122 799 115 802 108 808 c +101 814 98 820 98 827 c + +496 827 m +496 833 499 839 504 844 c +556 889 596 943 624 1006 c +652 1069 666 1135 666 1204 c +666 1218 665 1228 664 1235 c +644 1209 615 1196 578 1196 c +547 1196 520 1207 498 1229 c +476 1251 465 1278 465 1309 c +465 1341 476 1368 498 1389 c +520 1410 547 1421 578 1421 c +611 1421 639 1410 660 1387 c +681 1364 696 1336 705 1302 c +714 1268 719 1235 719 1204 c +719 1129 703 1055 670 984 c +638 913 594 853 539 803 c +534 800 529 799 526 799 c +519 799 513 802 506 808 c +499 814 496 820 496 827 c + +ce} _d +/numbersign{1706 0 115 -397 1589 1421 sc +342 -356 m +342 -353 343 -351 344 -348 c +510 272 l +154 272 l +143 272 133 276 126 285 c +119 294 115 303 115 313 c +115 324 119 334 126 342 c +133 350 143 354 154 354 c +535 354 l +616 670 l +154 670 l +143 670 133 674 126 682 c +119 690 115 700 115 711 c +115 721 119 730 126 739 c +133 748 143 752 154 752 c +641 752 l +813 1391 l +815 1400 819 1407 826 1412 c +833 1418 842 1421 852 1421 c +863 1421 873 1417 881 1409 c +889 1401 893 1391 893 1380 c +893 1372 l +725 752 l +1110 752 l +1282 1391 l +1284 1400 1288 1407 1295 1412 c +1302 1418 1311 1421 1321 1421 c +1332 1421 1342 1417 1350 1409 c +1358 1401 1362 1391 1362 1380 c +1362 1372 l +1194 752 l +1552 752 l +1563 752 1571 748 1578 739 c +1585 730 1589 721 1589 711 c +1589 700 1585 690 1578 682 c +1571 674 1563 670 1552 670 c +1169 670 l +1087 354 l +1552 354 l +1563 354 1571 350 1578 342 c +1585 334 1589 324 1589 313 c +1589 303 1585 294 1578 285 c +1571 276 1563 272 1552 272 c +1063 272 l +893 -367 l +886 -387 872 -397 852 -397 c +841 -397 831 -393 823 -385 c +815 -377 811 -367 811 -356 c +811 -353 812 -351 813 -348 c +979 272 l +594 272 l +424 -367 l +417 -387 403 -397 383 -397 c +372 -397 362 -393 354 -385 c +346 -377 342 -367 342 -356 c + +618 354 m +1004 354 l +1085 670 l +700 670 l +618 354 l + +ce} _d +/dollar{1024 0 115 -115 907 1536 sc +475 -115 m +475 -20 l +402 -20 339 -2 284 34 c +230 70 188 118 159 179 c +130 240 115 306 115 377 c +115 404 125 427 144 446 c +163 465 186 475 213 475 c +240 475 263 465 282 446 c +301 427 311 404 311 377 c +311 350 301 327 282 308 c +263 289 240 279 213 279 c +211 279 l +202 279 195 280 190 281 c +189 282 187 282 186 282 c +185 283 185 283 184 283 c +193 240 212 200 241 164 c +270 128 306 100 347 80 c +388 61 431 51 475 51 c +475 649 l +435 660 406 669 389 674 c +372 679 355 686 336 695 c +318 704 300 714 283 725 c +266 736 248 751 229 770 c +153 847 115 940 115 1047 c +115 1049 l +115 1051 l +115 1101 125 1150 145 1197 c +165 1245 193 1288 229 1327 c +258 1356 296 1382 343 1406 c +390 1430 434 1442 475 1442 c +475 1536 l +547 1536 l +547 1444 l +615 1444 677 1428 732 1395 c +787 1363 830 1319 861 1262 c +892 1206 907 1143 907 1073 c +907 1046 897 1023 878 1004 c +859 985 836 975 809 975 c +782 975 759 985 740 1004 c +721 1023 711 1046 711 1073 c +711 1100 721 1123 740 1142 c +759 1161 782 1171 809 1171 c +811 1171 l +820 1171 827 1170 831 1169 c +836 1169 l +824 1210 803 1245 774 1275 c +745 1305 710 1328 669 1345 c +629 1362 588 1370 547 1370 c +547 827 l +603 814 649 800 684 783 c +719 767 753 743 784 711 c +824 671 854 624 875 570 c +896 517 907 461 907 403 c +907 399 l +907 344 897 291 877 239 c +858 187 830 141 793 102 c +760 69 720 40 674 16 c +629 -8 586 -20 547 -20 c +547 -115 l +475 -115 l + +547 51 m +593 57 635 74 673 102 c +712 131 742 166 763 209 c +784 252 795 295 795 340 c +795 393 785 438 764 477 c +743 516 714 549 677 575 c +640 601 597 620 547 633 c +547 51 l + +475 846 m +475 1370 l +433 1365 392 1351 353 1326 c +314 1302 284 1271 261 1233 c +238 1196 227 1155 227 1110 c +227 977 310 889 475 846 c + +ce} _d +/percent{1706 0 115 -115 1589 1536 sc +285 -74 m +285 -66 287 -59 291 -53 c +1219 1329 l +1137 1283 1047 1260 948 1260 c +841 1260 739 1288 641 1343 c +668 1278 682 1205 682 1124 c +682 1080 677 1034 666 987 c +656 940 640 896 619 854 c +598 813 570 778 536 751 c +503 724 463 711 418 711 c +354 711 299 732 253 775 c +207 818 172 871 149 935 c +126 999 115 1062 115 1124 c +115 1185 126 1247 149 1311 c +172 1376 207 1429 253 1472 c +299 1515 354 1536 418 1536 c +469 1536 515 1516 557 1475 c +667 1367 797 1313 948 1313 c +1029 1313 1104 1331 1174 1366 c +1244 1402 1301 1453 1346 1520 c +1353 1531 1363 1536 1378 1536 c +1390 1536 1400 1532 1407 1525 c +1415 1518 1419 1508 1419 1495 c +1419 1488 1417 1481 1413 1475 c +356 -102 l +350 -111 340 -115 326 -115 c +315 -115 305 -111 297 -102 c +289 -93 285 -84 285 -74 c + +418 764 m +485 764 536 804 571 884 c +606 965 623 1045 623 1124 c +623 1169 616 1219 601 1276 c +587 1333 565 1382 534 1422 c +503 1463 465 1483 418 1483 c +350 1483 305 1445 283 1369 c +261 1294 250 1211 250 1122 c +250 1036 261 955 284 878 c +307 802 351 764 418 764 c + +1325 -115 m +1261 -115 1206 -94 1160 -51 c +1114 -8 1079 45 1056 109 c +1033 174 1022 237 1022 299 c +1022 360 1033 422 1056 486 c +1079 551 1114 604 1160 647 c +1206 690 1261 711 1325 711 c +1384 711 1434 688 1474 643 c +1514 598 1543 544 1561 481 c +1580 418 1589 357 1589 299 c +1589 255 1584 210 1573 163 c +1563 116 1547 72 1526 29 c +1505 -13 1478 -47 1444 -74 c +1411 -101 1371 -115 1325 -115 c + +1325 -61 m +1371 -61 1410 -41 1441 0 c +1472 41 1495 90 1509 147 c +1523 204 1530 254 1530 299 c +1530 378 1513 457 1478 537 c +1443 617 1392 657 1325 657 c +1257 657 1212 619 1190 543 c +1168 468 1157 386 1157 297 c +1157 210 1168 129 1191 53 c +1214 -23 1258 -61 1325 -61 c + +ce} _d +/ampersand{1591 0 86 -45 1489 1466 sc +86 266 m +86 343 113 408 168 463 c +412 717 l +386 785 366 855 351 926 c +337 998 330 1069 330 1139 c +330 1193 342 1245 365 1295 c +389 1346 423 1387 466 1418 c +510 1450 561 1466 618 1466 c +681 1466 726 1437 755 1380 c +784 1323 799 1259 799 1190 c +799 1129 776 1067 729 1002 c +683 937 622 864 547 782 c +566 735 587 690 609 648 c +631 606 656 563 684 518 c +713 473 744 428 777 382 c +811 336 845 291 879 248 c +920 296 955 343 985 390 c +1016 437 1059 507 1114 600 c +1165 690 l +1172 698 1176 710 1176 725 c +1176 757 1161 779 1132 792 c +1103 805 1069 811 1032 811 c +1032 883 l +1489 883 l +1489 811 l +1366 811 1280 771 1233 690 c +1167 578 l +1122 499 1080 431 1042 372 c +1005 314 963 258 918 205 c +963 154 1007 111 1052 77 c +1097 44 1145 27 1194 27 c +1233 27 1270 37 1304 57 c +1339 77 1366 104 1386 137 c +1407 171 1417 208 1417 248 c +1477 248 l +1477 197 1464 149 1439 104 c +1414 59 1379 23 1336 -4 c +1293 -31 1245 -45 1194 -45 c +1062 -45 940 7 827 111 c +713 7 589 -45 455 -45 c +395 -45 336 -32 279 -7 c +222 18 175 55 139 102 c +104 150 86 205 86 266 c + +473 27 m +585 27 688 69 782 152 c +715 222 650 303 587 395 c +524 487 473 577 434 664 c +352 580 l +293 519 264 435 264 330 c +264 284 271 238 286 191 c +301 145 324 106 355 74 c +387 43 426 27 473 27 c + +526 838 m +588 907 639 970 679 1027 c +719 1084 739 1139 739 1192 c +739 1225 736 1257 729 1290 c +722 1323 710 1351 691 1376 c +673 1401 649 1413 618 1413 c +583 1413 554 1401 531 1377 c +508 1354 492 1325 481 1290 c +470 1256 465 1223 465 1190 c +465 1072 485 955 526 838 c + +ce} _d +/quoteright{567 0 172 799 426 1421 sc +203 827 m +203 833 206 839 211 844 c +263 889 303 943 331 1006 c +359 1069 373 1135 373 1204 c +373 1218 372 1228 371 1235 c +351 1209 322 1196 285 1196 c +254 1196 227 1207 205 1229 c +183 1251 172 1278 172 1309 c +172 1341 183 1368 205 1389 c +227 1410 254 1421 285 1421 c +318 1421 346 1410 367 1387 c +388 1364 403 1336 412 1302 c +421 1268 426 1235 426 1204 c +426 1129 410 1055 377 984 c +345 913 301 853 246 803 c +241 800 236 799 233 799 c +226 799 220 802 213 808 c +206 814 203 820 203 827 c + +ce} _d +/parenleft{795 0 199 -512 680 1536 sc +635 -508 m +559 -448 493 -379 438 -301 c +383 -224 338 -141 303 -53 c +268 35 242 127 225 223 c +208 319 199 415 199 512 c +199 610 208 707 225 803 c +242 899 269 991 304 1080 c +340 1169 386 1252 441 1329 c +496 1406 561 1474 635 1532 c +635 1535 638 1536 645 1536 c +664 1536 l +668 1536 672 1534 675 1530 c +678 1527 680 1523 680 1518 c +680 1512 679 1508 676 1505 c +609 1440 554 1370 509 1295 c +465 1220 429 1141 402 1056 c +375 972 356 885 344 794 c +332 704 326 610 326 512 c +326 78 442 -252 674 -477 c +678 -481 680 -487 680 -494 c +680 -497 678 -501 674 -505 c +671 -510 667 -512 664 -512 c +645 -512 l +638 -512 635 -511 635 -508 c + +ce} _d +/parenright{795 0 115 -512 596 1536 sc +133 -512 m +121 -512 115 -506 115 -494 c +115 -488 116 -484 119 -481 c +352 -253 469 78 469 512 c +469 946 354 1276 123 1501 c +118 1504 115 1510 115 1518 c +115 1523 117 1527 120 1530 c +124 1534 128 1536 133 1536 c +152 1536 l +156 1536 159 1535 162 1532 c +260 1455 342 1361 407 1250 c +472 1139 520 1021 550 896 c +581 771 596 643 596 512 c +596 415 588 320 571 226 c +555 133 529 41 493 -50 c +458 -141 413 -225 358 -302 c +303 -379 238 -448 162 -508 c +159 -511 156 -512 152 -512 c +133 -512 l + +ce} _d +/asterisk{1024 0 133 653 889 1536 sc +193 844 m +178 844 164 850 151 863 c +139 876 133 891 133 907 c +133 930 143 946 162 956 c +457 1096 l +162 1233 l +143 1243 133 1259 133 1282 c +133 1299 139 1314 151 1327 c +163 1340 177 1346 193 1346 c +204 1346 214 1342 223 1335 c +483 1145 l +453 1477 l +451 1481 l +451 1496 457 1509 469 1520 c +482 1531 496 1536 512 1536 c +527 1536 540 1531 552 1521 c +565 1511 571 1498 571 1481 c +571 1477 l +539 1145 l +799 1335 l +808 1342 818 1346 829 1346 c +846 1346 860 1340 871 1327 c +883 1314 889 1299 889 1282 c +889 1259 879 1243 860 1233 c +565 1096 l +860 956 l +879 946 889 930 889 907 c +889 891 883 876 871 863 c +860 850 846 844 829 844 c +818 844 808 847 799 854 c +539 1044 l +571 713 l +571 709 l +571 693 565 680 552 669 c +540 658 527 653 512 653 c +496 653 482 658 469 669 c +457 680 451 694 451 709 c +453 713 l +483 1044 l +223 854 l +215 847 205 844 193 844 c + +ce} _d +/plus{1591 0 115 -170 1477 1194 sc +154 471 m +143 471 133 475 126 484 c +119 493 115 502 115 512 c +115 522 119 531 126 540 c +133 549 143 553 154 553 c +756 553 l +756 1157 l +756 1168 760 1176 768 1183 c +776 1190 786 1194 797 1194 c +807 1194 816 1190 825 1183 c +834 1176 838 1168 838 1157 c +838 553 l +1440 553 l +1450 553 1459 549 1466 540 c +1473 531 1477 522 1477 512 c +1477 502 1473 493 1466 484 c +1459 475 1450 471 1440 471 c +838 471 l +838 -133 l +838 -144 834 -152 825 -159 c +816 -166 807 -170 797 -170 c +786 -170 776 -166 768 -159 c +760 -152 756 -144 756 -133 c +756 471 l +154 471 l + +ce} _d +/comma{567 0 172 -397 420 225 sc +203 -369 m +203 -363 206 -357 211 -352 c +260 -305 299 -250 326 -188 c +353 -126 367 -61 367 8 c +367 33 l +345 11 318 0 285 0 c +254 0 227 11 205 33 c +183 55 172 82 172 113 c +172 145 183 172 205 193 c +227 214 254 225 285 225 c +334 225 368 202 389 157 c +410 112 420 63 420 8 c +420 -68 405 -140 374 -208 c +344 -277 301 -338 246 -393 c +241 -396 236 -397 233 -397 c +226 -397 220 -394 213 -388 c +206 -382 203 -376 203 -369 c + +ce} _d +/hyphen{682 0 23 379 565 506 sc +23 379 m +23 506 l +565 506 l +565 379 l +23 379 l + +ce} _d +/period{567 0 172 0 397 225 sc +172 113 m +172 144 183 170 206 192 c +229 214 255 225 285 225 c +304 225 322 220 340 210 c +358 200 372 186 382 168 c +392 150 397 132 397 113 c +397 83 386 57 364 34 c +342 11 316 0 285 0 c +255 0 229 11 206 34 c +183 57 172 83 172 113 c + +ce} _d +/slash{1024 0 115 -512 907 1536 sc +115 -471 m +115 -467 116 -464 117 -463 c +829 1511 l +832 1519 836 1525 843 1529 c +850 1534 857 1536 866 1536 c +878 1536 888 1532 895 1525 c +903 1518 907 1508 907 1495 c +907 1487 l +195 -487 l +187 -504 174 -512 156 -512 c +145 -512 135 -508 127 -500 c +119 -492 115 -482 115 -471 c + +ce} _d +/colon{567 0 172 0 397 883 sc +172 113 m +172 144 183 170 206 192 c +229 214 255 225 285 225 c +304 225 322 220 340 210 c +358 200 372 186 382 168 c +392 150 397 132 397 113 c +397 83 386 57 364 34 c +342 11 316 0 285 0 c +255 0 229 11 206 34 c +183 57 172 83 172 113 c + +172 770 m +172 789 177 808 187 825 c +197 842 211 856 228 867 c +246 878 265 883 285 883 c +304 883 323 878 340 867 c +358 856 372 842 382 825 c +392 808 397 789 397 770 c +397 739 386 713 364 690 c +343 668 316 657 285 657 c +254 657 228 668 205 690 c +183 713 172 739 172 770 c + +ce} _d +/semicolon{567 0 172 -397 403 883 sc +203 -369 m +203 -363 204 -359 207 -356 c +302 -253 350 -131 350 8 c +350 18 l +330 6 308 0 285 0 c +254 0 227 11 205 33 c +183 55 172 82 172 113 c +172 145 183 172 205 193 c +227 214 254 225 285 225 c +332 225 364 203 379 159 c +395 116 403 65 403 8 c +403 -40 397 -87 385 -134 c +374 -181 356 -226 332 -271 c +309 -316 281 -356 248 -391 c +244 -395 239 -397 233 -397 c +226 -397 220 -394 213 -388 c +206 -382 203 -376 203 -369 c + +172 770 m +172 789 177 808 187 825 c +197 842 211 856 228 867 c +246 878 265 883 285 883 c +304 883 323 878 340 867 c +358 856 372 842 382 825 c +392 808 397 789 397 770 c +397 739 386 713 364 690 c +343 668 316 657 285 657 c +254 657 228 668 205 690 c +183 713 172 739 172 770 c + +ce} _d +/exclamdown{567 0 172 -442 397 1024 sc +172 -340 m +172 -328 l +256 616 l +256 620 257 624 260 628 c +263 633 267 635 272 635 c +297 635 l +302 635 306 633 309 629 c +313 625 315 621 315 616 c +397 -328 l +397 -340 l +397 -369 386 -393 363 -412 c +341 -432 315 -442 285 -442 c +256 -442 229 -432 206 -412 c +183 -393 172 -369 172 -340 c + +172 911 m +172 941 183 967 206 990 c +229 1013 255 1024 285 1024 c +304 1024 322 1019 340 1009 c +358 999 372 985 382 967 c +392 949 397 930 397 911 c +397 882 386 856 364 833 c +342 810 316 799 285 799 c +255 799 229 810 206 833 c +183 856 172 882 172 911 c + +ce} _d +/equal{1591 0 115 272 1477 752 sc +154 272 m +143 272 133 276 126 285 c +119 294 115 303 115 313 c +115 324 119 334 126 342 c +133 350 143 354 154 354 c +1440 354 l +1450 354 1459 350 1466 342 c +1473 334 1477 324 1477 313 c +1477 303 1473 294 1466 285 c +1459 276 1450 272 1440 272 c +154 272 l + +154 670 m +143 670 133 674 126 682 c +119 690 115 700 115 711 c +115 721 119 730 126 739 c +133 748 143 752 154 752 c +1440 752 l +1450 752 1459 748 1466 739 c +1473 730 1477 721 1477 711 c +1477 700 1473 690 1466 682 c +1459 674 1450 670 1440 670 c +154 670 l + +ce} _d +/questiondown{967 0 115 -420 850 1024 sc +115 -141 m +115 -102 123 -63 138 -26 c +153 11 176 41 207 66 c +253 103 292 146 324 193 c +357 240 382 291 399 346 c +417 401 426 457 426 516 c +426 616 l +426 620 427 624 430 628 c +433 633 437 635 442 635 c +467 635 l +472 635 476 633 479 629 c +483 625 485 621 485 616 c +485 512 l +485 425 472 340 447 255 c +422 170 385 94 336 27 c +307 -12 293 -68 293 -143 c +293 -193 296 -233 302 -264 c +308 -295 323 -320 346 -339 c +370 -358 406 -367 455 -367 c +516 -367 575 -357 631 -337 c +688 -317 731 -285 762 -240 c +752 -240 l +725 -240 701 -230 682 -211 c +663 -192 653 -168 653 -141 c +653 -114 663 -91 682 -72 c +701 -53 725 -43 752 -43 c +779 -43 802 -53 821 -72 c +840 -91 850 -114 850 -141 c +850 -204 830 -256 789 -298 c +748 -341 697 -372 636 -391 c +575 -410 514 -420 455 -420 c +358 -420 277 -397 212 -351 c +147 -305 115 -235 115 -141 c + +342 911 m +342 941 353 967 376 990 c +399 1013 425 1024 455 1024 c +474 1024 492 1019 510 1009 c +528 999 542 985 552 967 c +562 949 567 930 567 911 c +567 882 556 856 534 833 c +512 810 486 799 455 799 c +425 799 399 810 376 833 c +353 856 342 882 342 911 c + +ce} _d +/question{967 0 115 0 850 1444 sc +342 113 m +342 144 353 170 376 192 c +399 214 425 225 455 225 c +474 225 492 220 510 210 c +528 200 542 186 552 168 c +562 150 567 132 567 113 c +567 83 556 57 534 34 c +512 11 486 0 455 0 c +425 0 399 11 376 34 c +353 57 342 83 342 113 c + +426 408 m +426 512 l +426 601 442 689 475 775 c +508 861 554 935 614 997 c +634 1018 649 1044 658 1073 c +667 1103 672 1134 672 1167 c +672 1223 666 1267 653 1299 c +640 1331 618 1354 587 1369 c +556 1384 512 1391 455 1391 c +402 1391 353 1380 307 1359 c +262 1338 226 1306 201 1264 c +213 1264 l +240 1264 263 1254 282 1235 c +301 1216 311 1193 311 1165 c +311 1138 301 1115 282 1096 c +263 1077 240 1067 213 1067 c +186 1067 163 1077 144 1096 c +125 1115 115 1138 115 1165 c +115 1221 131 1270 164 1312 c +197 1355 240 1387 293 1410 c +346 1433 400 1444 455 1444 c +520 1444 582 1435 642 1418 c +703 1401 752 1372 791 1330 c +830 1288 850 1233 850 1165 c +850 1125 841 1086 822 1049 c +803 1012 777 982 743 958 c +665 903 602 837 555 758 c +508 679 485 596 485 508 c +485 408 l +485 403 483 399 479 395 c +475 391 471 389 467 389 c +442 389 l +439 389 435 391 431 395 c +428 400 426 404 426 408 c + +ce} _d +/at{1591 0 115 -23 1477 1444 sc +797 -23 m +700 -23 609 -3 526 36 c +443 76 371 131 309 200 c +247 270 199 349 165 437 c +132 526 115 617 115 711 c +115 805 132 896 165 984 c +199 1073 247 1152 309 1221 c +371 1291 443 1346 526 1385 c +609 1424 700 1444 797 1444 c +894 1444 984 1424 1067 1385 c +1150 1346 1223 1291 1284 1221 c +1346 1152 1394 1073 1427 984 c +1460 896 1477 805 1477 711 c +1477 586 1464 479 1439 391 c +1414 304 1355 260 1264 260 c +1216 260 1172 272 1132 296 c +1092 320 1067 354 1057 397 c +1025 356 986 322 940 297 c +895 272 847 260 797 260 c +718 260 648 281 586 323 c +524 366 475 422 440 492 c +405 562 387 635 387 711 c +387 786 405 858 440 928 c +475 999 524 1055 586 1097 c +648 1140 718 1161 797 1161 c +855 1161 910 1145 961 1112 c +1012 1079 1054 1037 1085 985 c +1188 985 l +1192 985 1196 983 1199 979 c +1202 976 1204 972 1204 967 c +1204 430 l +1204 402 1209 375 1219 350 c +1229 325 1246 313 1270 313 c +1333 313 1373 353 1390 434 c +1408 515 1417 606 1417 709 c +1417 794 1402 879 1371 962 c +1340 1046 1298 1120 1243 1183 c +1189 1247 1123 1298 1045 1335 c +968 1372 885 1391 797 1391 c +709 1391 626 1372 548 1334 c +471 1297 404 1246 349 1183 c +294 1120 251 1048 220 965 c +189 882 174 798 174 711 c +174 624 189 540 219 459 c +250 378 293 305 349 240 c +405 175 472 124 550 87 c +628 50 711 31 799 31 c +864 31 928 36 993 46 c +1058 57 1122 72 1185 91 c +1248 110 1309 135 1368 164 c +1460 164 l +1464 164 1468 162 1471 158 c +1475 154 1477 150 1477 145 c +1477 136 1473 130 1464 127 c +1251 27 1028 -23 797 -23 c + +797 313 m +852 313 902 331 948 366 c +994 402 1030 447 1055 502 c +1055 920 l +1038 955 1017 986 991 1014 c +966 1043 936 1065 901 1082 c +867 1099 832 1108 797 1108 c +753 1108 714 1095 681 1068 c +648 1042 621 1009 600 969 c +579 929 564 886 553 839 c +542 793 537 750 537 711 c +537 655 546 596 565 534 c +584 472 613 420 652 377 c +691 334 739 313 797 313 c + +ce} _d +/bracketleft{567 0 242 -512 522 1536 sc +242 -512 m +242 1536 l +522 1536 l +522 1454 l +324 1454 l +324 -430 l +522 -430 l +522 -512 l +242 -512 l + +ce} _d +/quotedblleft{1024 0 303 799 954 1421 sc +444 799 m +395 799 360 821 337 866 c +314 911 303 961 303 1016 c +303 1091 319 1164 350 1235 c +382 1306 426 1366 483 1417 c +488 1420 493 1421 496 1421 c +503 1421 510 1418 516 1411 c +523 1405 526 1399 526 1393 c +526 1387 523 1381 518 1376 c +485 1347 456 1313 431 1273 c +406 1233 387 1191 374 1147 c +362 1104 356 1060 356 1016 c +356 1002 357 992 358 985 c +378 1011 407 1024 444 1024 c +465 1024 484 1019 501 1009 c +518 999 532 986 542 969 c +552 952 557 933 557 911 c +557 880 546 853 524 831 c +503 810 476 799 444 799 c + +842 799 m +793 799 757 821 734 866 c +711 911 700 961 700 1016 c +700 1068 707 1118 722 1166 c +737 1215 758 1261 785 1304 c +813 1348 845 1386 881 1417 c +886 1420 890 1421 893 1421 c +900 1421 906 1418 913 1411 c +920 1405 924 1399 924 1393 c +924 1386 921 1381 915 1376 c +882 1347 854 1313 829 1274 c +804 1235 786 1194 773 1149 c +760 1104 754 1060 754 1016 c +754 1002 755 992 756 985 c +775 1011 804 1024 842 1024 c +875 1024 901 1013 922 991 c +943 970 954 943 954 911 c +954 880 943 854 922 832 c +901 810 874 799 842 799 c + +ce} _d +/bracketright{567 0 45 -512 326 1536 sc +45 -512 m +45 -430 l +244 -430 l +244 1454 l +45 1454 l +45 1536 l +326 1536 l +326 -512 l +45 -512 l + +ce} _d +/circumflex{1024 0 236 1102 786 1421 sc +276 1102 m +236 1145 l +512 1421 l +786 1145 l +745 1102 l +512 1307 l +276 1102 l + +ce} _d +/dotaccent{567 0 172 1145 397 1370 sc +172 1257 m +172 1287 183 1313 206 1336 c +229 1359 255 1370 285 1370 c +304 1370 322 1365 340 1355 c +358 1345 372 1331 382 1313 c +392 1295 397 1276 397 1257 c +397 1228 386 1202 364 1179 c +342 1156 316 1145 285 1145 c +255 1145 229 1156 206 1179 c +183 1202 172 1228 172 1257 c + +ce} _d +/quoteleft{567 0 143 799 397 1421 sc +285 799 m +236 799 200 821 177 866 c +154 911 143 961 143 1016 c +143 1068 150 1118 165 1166 c +180 1215 201 1261 228 1304 c +256 1348 288 1386 324 1417 c +329 1420 333 1421 336 1421 c +343 1421 349 1418 356 1411 c +363 1405 367 1399 367 1393 c +367 1388 364 1382 358 1376 c +325 1347 297 1313 272 1274 c +247 1235 229 1194 216 1149 c +203 1104 197 1060 197 1016 c +197 1002 198 992 199 985 c +218 1011 247 1024 285 1024 c +318 1024 344 1013 365 991 c +386 970 397 943 397 911 c +397 880 386 854 365 832 c +344 810 317 799 285 799 c + +ce} _d +/emdash{1024 0 0 518 1022 571 sc +0 518 m +0 571 l +1022 571 l +1022 518 l +0 518 l + +ce} _d +/endash{2048 0 0 518 2046 571 sc +0 518 m +0 571 l +2046 571 l +2046 518 l +0 518 l + +ce} _d +/hungarumlaut{1024 0 258 1049 860 1434 sc +258 1075 m +369 1382 l +381 1417 403 1434 436 1434 c +449 1434 462 1431 475 1424 c +488 1417 499 1408 506 1395 c +514 1382 518 1370 518 1358 c +518 1344 513 1328 502 1311 c +311 1049 l +258 1075 l + +600 1075 m +711 1382 l +723 1417 745 1434 778 1434 c +791 1434 804 1431 817 1424 c +830 1417 841 1408 848 1395 c +856 1382 860 1370 860 1358 c +860 1344 855 1328 844 1311 c +653 1049 l +600 1075 l + +ce} _d +/tilde{1024 0 170 1171 852 1368 sc +170 1206 m +229 1276 l +282 1337 336 1368 389 1368 c +415 1368 443 1360 474 1345 c +505 1330 536 1314 567 1299 c +598 1284 627 1276 653 1276 c +708 1276 760 1307 811 1368 c +852 1333 l +793 1264 l +739 1202 686 1171 633 1171 c +607 1171 578 1179 547 1194 c +516 1210 485 1226 454 1241 c +423 1256 395 1264 369 1264 c +314 1264 262 1233 211 1171 c +170 1206 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def +/CharStrings 60 dict dup begin +/.notdef 0 def +/Aring{1401 0 16 0 1384 1901 sc +852 1626 m +852 1668 837 1704 807 1733 c +778 1763 742 1778 700 1778 c +657 1778 621 1763 592 1734 c +563 1705 549 1669 549 1626 c +549 1584 564 1548 593 1519 c +622 1490 658 1475 700 1475 c +742 1475 778 1490 807 1519 c +837 1548 852 1584 852 1626 c + +700 1294 m +428 551 l +973 551 l +700 1294 l + +549 1397 m +508 1424 478 1457 457 1495 c +436 1534 426 1577 426 1626 c +426 1703 452 1768 505 1821 c +558 1874 623 1901 700 1901 c +776 1901 841 1874 894 1820 c +948 1767 975 1702 975 1626 c +975 1579 964 1536 943 1497 c +922 1458 892 1424 852 1397 c +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +549 1397 l + +ce} _d +/AE{1995 0 8 0 1864 1493 sc +1845 1493 m +1845 1323 l +1104 1323 l +1104 881 l +1815 881 l +1815 711 l +1104 711 l +1104 170 l +1864 170 l +1864 0 l +901 0 l +901 383 l +373 383 l +213 0 l +8 0 l +633 1493 l +1845 1493 l + +772 1335 m +442 551 l +901 551 l +901 1335 l +772 1335 l + +ce} _d +/Ccedilla{1430 0 115 -395 1319 1520 sc +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1175 1461 1250 1425 1319 1378 c + +897 0 m +934 -41 961 -79 979 -114 c +997 -149 1006 -183 1006 -215 c +1006 -274 986 -319 946 -349 c +906 -380 847 -395 768 -395 c +737 -395 707 -393 678 -389 c +649 -385 621 -379 592 -371 c +592 -240 l +615 -251 638 -259 663 -264 c +688 -269 716 -272 747 -272 c +786 -272 816 -264 836 -248 c +856 -232 866 -209 866 -178 c +866 -158 859 -133 844 -104 c +830 -75 808 -41 778 0 c +897 0 l + +ce} _d +/Egrave{1294 0 201 0 1163 1899 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +613 1899 m +809 1635 l +656 1635 l +426 1899 l +613 1899 l + +ce} _d +/Eacute{1294 0 201 0 1163 1899 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +725 1899 m +910 1899 l +682 1635 l +529 1635 l +725 1899 l + +ce} _d +/Ecircumflex{1294 0 201 0 1163 1901 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +576 1901 m +764 1901 l +975 1635 l +836 1635 l +670 1813 l +504 1635 l +365 1635 l +576 1901 l + +ce} _d +/Edieresis{1294 0 201 0 1163 1870 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +764 1870 m +967 1870 l +967 1667 l +764 1667 l +764 1870 l + +373 1870 m +576 1870 l +576 1667 l +373 1667 l +373 1870 l + +ce} _d +/Igrave{604 0 59 0 442 1899 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +246 1899 m +442 1635 l +289 1635 l +59 1899 l +246 1899 l + +ce} _d +/Iacute{604 0 162 0 543 1899 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +358 1899 m +543 1899 l +315 1635 l +162 1635 l +358 1899 l + +ce} _d +/Icircumflex{604 0 -2 0 608 1901 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +209 1901 m +397 1901 l +608 1635 l +469 1635 l +303 1813 l +137 1635 l +-2 1635 l +209 1901 l + +ce} _d +/Idieresis{604 0 6 0 600 1870 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +397 1870 m +600 1870 l +600 1667 l +397 1667 l +397 1870 l + +6 1870 m +209 1870 l +209 1667 l +6 1667 l +6 1870 l + +ce} _d +/Eth{1587 0 10 0 1466 1493 sc +211 1493 m +627 1493 l +916 1493 1128 1433 1263 1312 c +1398 1192 1466 1004 1466 748 c +1466 491 1398 302 1262 181 c +1127 60 915 0 627 0 c +211 0 l +211 700 l +10 700 l +10 844 l +211 844 l +211 1493 l + +414 1327 m +414 844 l +750 844 l +750 700 l +414 700 l +414 166 l +657 166 l +863 166 1014 213 1109 306 c +1205 399 1253 547 1253 748 c +1253 948 1205 1094 1109 1187 c +1014 1280 863 1327 657 1327 c +414 1327 l + +ce} _d +/Ntilde{1532 0 201 0 1331 1886 sc +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +762 1710 m +705 1743 l +688 1752 675 1759 664 1762 c +654 1766 645 1768 637 1768 c +613 1768 594 1760 581 1743 c +568 1726 561 1703 561 1673 c +561 1667 l +436 1667 l +436 1734 453 1788 487 1827 c +522 1866 568 1886 625 1886 c +649 1886 671 1883 691 1878 c +712 1873 738 1861 770 1843 c +827 1813 l +842 1804 856 1798 867 1794 c +878 1790 889 1788 899 1788 c +920 1788 938 1796 951 1813 c +964 1830 971 1853 971 1880 c +971 1886 l +1096 1886 l +1095 1819 1077 1766 1042 1726 c +1008 1687 963 1667 907 1667 c +884 1667 863 1670 843 1675 c +824 1680 797 1692 762 1710 c + +ce} _d +/Ograve{1612 0 115 -29 1497 1899 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +750 1899 m +946 1635 l +793 1635 l +563 1899 l +750 1899 l + +ce} _d +/Oacute{1612 0 115 -29 1497 1899 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +862 1899 m +1047 1899 l +819 1635 l +666 1635 l +862 1899 l + +ce} _d +/Ocircumflex{1612 0 115 -29 1497 1901 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +713 1901 m +901 1901 l +1112 1635 l +973 1635 l +807 1813 l +641 1635 l +502 1635 l +713 1901 l + +ce} _d +/Otilde{1612 0 115 -29 1497 1886 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +803 1710 m +746 1743 l +729 1752 716 1759 705 1762 c +695 1766 686 1768 678 1768 c +654 1768 635 1760 622 1743 c +609 1726 602 1703 602 1673 c +602 1667 l +477 1667 l +477 1734 494 1788 528 1827 c +563 1866 609 1886 666 1886 c +690 1886 712 1883 732 1878 c +753 1873 779 1861 811 1843 c +868 1813 l +883 1804 897 1798 908 1794 c +919 1790 930 1788 940 1788 c +961 1788 979 1796 992 1813 c +1005 1830 1012 1853 1012 1880 c +1012 1886 l +1137 1886 l +1136 1819 1118 1766 1083 1726 c +1049 1687 1004 1667 948 1667 c +925 1667 904 1670 884 1675 c +865 1680 838 1692 803 1710 c + +ce} _d +/Odieresis{1612 0 115 -29 1497 1870 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +901 1870 m +1104 1870 l +1104 1667 l +901 1667 l +901 1870 l + +510 1870 m +713 1870 l +713 1667 l +510 1667 l +510 1870 l + +ce} _d +/multiply{1716 0 281 63 1436 1221 sc +1436 1100 m +979 641 l +1436 184 l +1317 63 l +858 522 l +399 63 l +281 184 l +737 641 l +281 1100 l +399 1221 l +858 762 l +1317 1221 l +1436 1100 l + +ce} _d +/Oslash{1612 0 102 -70 1509 1559 sc +1206 1112 m +489 266 l +530 223 578 191 631 168 c +685 146 744 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 820 1277 888 1264 949 c +1251 1010 1232 1065 1206 1112 c + +1124 1225 m +1083 1268 1036 1300 982 1322 c +929 1345 870 1356 807 1356 c +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 670 334 602 347 539 c +360 476 380 422 406 377 c +1124 1225 l + +272 219 m +220 287 181 365 154 453 c +128 541 115 638 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c +894 1520 974 1507 1047 1481 c +1121 1456 1187 1418 1245 1368 c +1407 1559 l +1509 1470 l +1339 1272 l +1391 1203 1430 1125 1457 1036 c +1484 947 1497 850 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +722 -29 642 -17 568 8 c +495 33 428 71 367 121 c +205 -70 l +102 18 l +272 219 l + +ce} _d +/Ugrave{1499 0 178 -29 1321 1899 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +693 1899 m +889 1635 l +736 1635 l +506 1899 l +693 1899 l + +ce} _d +/Uacute{1499 0 178 -29 1321 1899 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +805 1899 m +990 1899 l +762 1635 l +609 1635 l +805 1899 l + +ce} _d +/Ucircumflex{1499 0 178 -29 1321 1901 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +656 1901 m +844 1901 l +1055 1635 l +916 1635 l +750 1813 l +584 1635 l +445 1635 l +656 1901 l + +ce} _d +/Udieresis{1499 0 178 -29 1321 1870 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +844 1870 m +1047 1870 l +1047 1667 l +844 1667 l +844 1870 l + +453 1870 m +656 1870 l +656 1667 l +453 1667 l +453 1870 l + +ce} _d +/Yacute{1251 0 -4 0 1255 1899 sc +-4 1493 m +213 1493 l +627 879 l +1038 1493 l +1255 1493 l +727 711 l +727 0 l +524 0 l +524 711 l +-4 1493 l + +682 1899 m +867 1899 l +639 1635 l +486 1635 l +682 1899 l + +ce} _d +/Thorn{1239 0 201 0 1165 1493 sc +201 1493 m +403 1493 l +403 1229 l +657 1229 l +824 1229 951 1191 1036 1116 c +1122 1041 1165 931 1165 784 c +1165 637 1122 526 1036 451 c +951 376 824 338 657 338 c +403 338 l +403 0 l +201 0 l +201 1493 l + +403 1063 m +403 504 l +657 504 l +751 504 824 528 875 577 c +926 626 952 695 952 784 c +952 873 926 942 875 990 c +824 1039 752 1063 657 1063 c +403 1063 l + +ce} _d +/germandbls{1290 0 186 -29 1196 1556 sc +186 1137 m +186 1270 226 1373 305 1446 c +385 1519 498 1556 643 1556 c +782 1556 887 1517 960 1440 c +1033 1363 1071 1249 1073 1100 c +972 1095 894 1073 838 1034 c +782 996 754 945 754 881 c +754 850 764 820 783 793 c +802 766 834 739 877 711 c +934 674 l +1045 603 1116 544 1148 497 c +1180 450 1196 393 1196 326 c +1196 211 1158 123 1083 62 c +1008 1 901 -29 760 -29 c +717 -29 673 -25 628 -16 c +583 -8 536 4 487 20 c +487 184 l +540 164 590 149 637 139 c +684 130 729 125 772 125 c +849 125 908 141 948 172 c +988 204 1008 250 1008 311 c +1008 353 998 388 978 416 c +959 444 915 479 848 520 c +756 575 l +692 614 645 656 616 701 c +587 746 573 799 573 860 c +573 945 601 1016 656 1073 c +712 1130 790 1169 891 1188 c +886 1257 861 1310 817 1347 c +774 1384 714 1403 639 1403 c +552 1403 486 1380 441 1333 c +396 1287 373 1220 373 1133 c +373 0 l +186 0 l +186 1137 l + +ce} _d +/agrave{1255 0 123 -29 1069 1638 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +449 1638 m +731 1264 l +578 1264 l +252 1638 l +449 1638 l + +ce} _d +/aacute{1255 0 123 -29 1069 1638 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +733 1638 m +932 1638 l +606 1262 l +453 1262 l +733 1638 l + +ce} _d +/acircumflex{1255 0 123 -29 1069 1638 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +520 1638 m +668 1638 l +913 1262 l +774 1262 l +594 1507 l +414 1262 l +275 1262 l +520 1638 l + +ce} _d +/atilde{1255 0 123 -29 1069 1591 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +590 1370 m +533 1425 l +518 1438 505 1448 494 1454 c +483 1461 474 1464 465 1464 c +440 1464 421 1452 409 1427 c +397 1403 390 1364 389 1309 c +264 1309 l +265 1399 283 1468 317 1517 c +351 1566 398 1591 459 1591 c +484 1591 508 1586 529 1577 c +550 1568 573 1552 598 1530 c +655 1475 l +670 1462 682 1452 693 1445 c +704 1439 714 1436 723 1436 c +748 1436 767 1448 779 1472 c +791 1497 798 1536 799 1591 c +924 1591 l +923 1501 905 1431 871 1382 c +837 1333 790 1309 729 1309 c +704 1309 680 1314 659 1323 c +638 1332 615 1348 590 1370 c + +ce} _d +/adieresis{1255 0 123 -29 1069 1552 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +688 1552 m +891 1552 l +891 1350 l +688 1350 l +688 1552 l + +297 1552 m +500 1552 l +500 1350 l +297 1350 l +297 1552 l + +ce} _d +/aring{1255 0 123 -29 1069 1798 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +746 1524 m +746 1566 731 1602 702 1631 c +673 1660 637 1675 594 1675 c +551 1675 514 1660 485 1631 c +456 1602 442 1567 442 1524 c +442 1481 456 1444 485 1415 c +514 1386 551 1372 594 1372 c +637 1372 673 1387 702 1416 c +731 1445 746 1481 746 1524 c + +868 1524 m +868 1447 841 1382 788 1329 c +735 1276 671 1249 594 1249 c +517 1249 452 1276 399 1329 c +346 1382 320 1447 320 1524 c +320 1601 346 1665 399 1718 c +452 1771 517 1798 594 1798 c +671 1798 735 1771 788 1718 c +841 1665 868 1601 868 1524 c + +ce} _d +/ae{2011 0 123 -29 1903 1147 sc +1718 660 m +1717 761 1689 841 1634 901 c +1579 961 1506 991 1415 991 c +1313 991 1231 962 1169 904 c +1108 846 1072 764 1063 659 c +1718 660 l + +995 963 m +1044 1023 1104 1069 1175 1100 c +1246 1131 1325 1147 1413 1147 c +1564 1147 1683 1098 1771 1001 c +1859 904 1903 773 1903 606 c +1903 516 l +1057 516 l +1065 389 1103 292 1171 225 c +1239 158 1334 125 1456 125 c +1525 125 1593 134 1660 151 c +1727 169 1793 196 1860 231 c +1860 57 l +1793 29 1725 8 1656 -7 c +1587 -22 1517 -29 1446 -29 c +1335 -29 1238 -9 1155 31 c +1072 72 1005 132 954 211 c +905 131 845 71 773 31 c +701 -9 617 -29 522 -29 c +396 -29 298 2 228 64 c +158 127 123 214 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +681 1147 763 1131 834 1099 c +905 1067 959 1022 995 963 c + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +ce} _d +/ccedilla{1126 0 113 -395 999 1147 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +739 0 m +776 -41 803 -79 821 -114 c +839 -149 848 -183 848 -215 c +848 -274 828 -319 788 -349 c +748 -380 689 -395 610 -395 c +579 -395 549 -393 520 -389 c +491 -385 463 -379 434 -371 c +434 -240 l +457 -251 480 -259 505 -264 c +530 -269 558 -272 589 -272 c +628 -272 658 -264 678 -248 c +698 -232 708 -209 708 -178 c +708 -158 701 -133 686 -104 c +672 -75 650 -41 620 0 c +739 0 l + +ce} _d +/egrave{1260 0 113 -29 1151 1638 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +506 1638 m +788 1264 l +635 1264 l +309 1638 l +506 1638 l + +ce} _d +/eacute{1260 0 113 -29 1151 1638 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +790 1638 m +989 1638 l +663 1262 l +510 1262 l +790 1638 l + +ce} _d +/ecircumflex{1260 0 113 -29 1151 1638 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +577 1638 m +725 1638 l +970 1262 l +831 1262 l +651 1507 l +471 1262 l +332 1262 l +577 1638 l + +ce} _d +/edieresis{1260 0 113 -29 1151 1552 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +745 1552 m +948 1552 l +948 1350 l +745 1350 l +745 1552 l + +354 1552 m +557 1552 l +557 1350 l +354 1350 l +354 1552 l + +ce} _d +/igrave{569 0 -57 0 422 1638 sc +140 1638 m +422 1264 l +269 1264 l +-57 1638 l +140 1638 l + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/iacute{569 0 144 0 623 1638 sc +424 1638 m +623 1638 l +297 1262 l +144 1262 l +424 1638 l + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/icircumflex{569 0 -34 0 604 1638 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +211 1638 m +359 1638 l +604 1262 l +465 1262 l +285 1507 l +105 1262 l +-34 1262 l +211 1638 l + +ce} _d +/idieresis{569 0 -12 0 582 1552 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +379 1552 m +582 1552 l +582 1350 l +379 1350 l +379 1552 l + +-12 1552 m +191 1552 l +191 1350 l +-12 1350 l +-12 1552 l + +ce} _d +/eth{1253 0 113 -29 1141 1556 sc +838 915 m +805 926 773 935 744 940 c +715 945 686 948 659 948 c +548 948 461 912 399 840 c +338 768 307 667 307 537 c +307 412 336 312 394 238 c +452 164 530 127 627 127 c +724 127 801 164 859 238 c +917 312 946 412 946 537 c +946 618 937 690 919 753 c +901 816 874 870 838 915 c + +901 1141 m +985 1046 1046 950 1084 854 c +1122 758 1141 652 1141 537 c +1141 367 1094 230 999 126 c +904 23 780 -29 627 -29 c +473 -29 349 23 254 126 c +160 230 113 367 113 537 c +113 704 159 839 251 942 c +343 1046 463 1098 610 1098 c +622 1098 637 1097 654 1095 c +671 1094 694 1091 722 1088 c +563 1268 l +244 1161 l +211 1260 l +492 1352 l +311 1556 l +539 1556 l +666 1411 l +999 1522 l +1032 1425 l +737 1327 l +901 1141 l + +ce} _d +/ntilde{1298 0 186 0 1124 1591 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +660 1370 m +603 1425 l +588 1438 575 1448 564 1454 c +553 1461 544 1464 535 1464 c +510 1464 491 1452 479 1427 c +467 1403 460 1364 459 1309 c +334 1309 l +335 1399 353 1468 387 1517 c +421 1566 468 1591 529 1591 c +554 1591 578 1586 599 1577 c +620 1568 643 1552 668 1530 c +725 1475 l +740 1462 752 1452 763 1445 c +774 1439 784 1436 793 1436 c +818 1436 837 1448 849 1472 c +861 1497 868 1536 869 1591 c +994 1591 l +993 1501 975 1431 941 1382 c +907 1333 860 1309 799 1309 c +774 1309 750 1314 729 1323 c +708 1332 685 1348 660 1370 c + +ce} _d +/ograve{1253 0 113 -29 1141 1638 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +482 1638 m +764 1264 l +611 1264 l +285 1638 l +482 1638 l + +ce} _d +/oacute{1253 0 113 -29 1141 1638 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +766 1638 m +965 1638 l +639 1262 l +486 1262 l +766 1638 l + +ce} _d +/ocircumflex{1253 0 113 -29 1141 1638 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +553 1638 m +701 1638 l +946 1262 l +807 1262 l +627 1507 l +447 1262 l +308 1262 l +553 1638 l + +ce} _d +/otilde{1253 0 113 -29 1141 1591 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +623 1370 m +566 1425 l +551 1438 538 1448 527 1454 c +516 1461 507 1464 498 1464 c +473 1464 454 1452 442 1427 c +430 1403 423 1364 422 1309 c +297 1309 l +298 1399 316 1468 350 1517 c +384 1566 431 1591 492 1591 c +517 1591 541 1586 562 1577 c +583 1568 606 1552 631 1530 c +688 1475 l +703 1462 715 1452 726 1445 c +737 1439 747 1436 756 1436 c +781 1436 800 1448 812 1472 c +824 1497 831 1536 832 1591 c +957 1591 l +956 1501 938 1431 904 1382 c +870 1333 823 1309 762 1309 c +737 1309 713 1314 692 1323 c +671 1332 648 1348 623 1370 c + +ce} _d +/odieresis{1253 0 113 -29 1141 1552 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +721 1552 m +924 1552 l +924 1350 l +721 1350 l +721 1552 l + +330 1552 m +533 1552 l +533 1350 l +330 1350 l +330 1552 l + +ce} _d +/divide{1716 0 217 150 1499 1135 sc +735 1135 m +981 1135 l +981 889 l +735 889 l +735 1135 l + +735 395 m +981 395 l +981 150 l +735 150 l +735 395 l + +217 727 m +1499 727 l +1499 557 l +217 557 l +217 727 l + +ce} _d +/oslash{1253 0 72 -94 1180 1212 sc +905 801 m +418 209 l +445 181 476 160 510 147 c +545 134 584 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 612 943 657 936 696 c +929 735 919 770 905 801 c + +834 909 m +806 936 775 957 740 970 c +706 984 668 991 627 991 c +526 991 448 952 391 873 c +335 795 307 686 307 545 c +307 497 310 455 316 418 c +323 381 333 348 346 317 c +834 909 l + +221 166 m +185 217 158 276 140 341 c +122 407 113 480 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c +689 1147 746 1138 799 1121 c +852 1104 901 1079 946 1044 c +1085 1212 l +1180 1133 l +1034 954 l +1069 903 1096 844 1114 778 c +1132 712 1141 639 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +563 -29 504 -20 450 -3 c +397 14 349 40 307 74 c +168 -94 l +72 -16 l +221 166 l + +ce} _d +/ugrave{1298 0 174 -29 1112 1638 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +490 1638 m +772 1264 l +619 1264 l +293 1638 l +490 1638 l + +ce} _d +/uacute{1298 0 174 -29 1112 1638 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +774 1638 m +973 1638 l +647 1262 l +494 1262 l +774 1638 l + +ce} _d +/ucircumflex{1298 0 174 -29 1112 1638 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +561 1638 m +709 1638 l +954 1262 l +815 1262 l +635 1507 l +455 1262 l +316 1262 l +561 1638 l + +ce} _d +/udieresis{1298 0 174 -29 1112 1552 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +729 1552 m +932 1552 l +932 1350 l +729 1350 l +729 1552 l + +338 1552 m +541 1552 l +541 1350 l +338 1350 l +338 1552 l + +ce} _d +/yacute{1212 0 61 -426 1151 1638 sc +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +956 1120 l +1151 1120 l +659 -104 l + +745 1638 m +944 1638 l +618 1262 l +465 1262 l +745 1638 l + +ce} _d +/thorn{1300 0 186 -426 1188 1556 sc +371 168 m +371 -426 l +186 -426 l +186 1556 l +371 1556 l +371 950 l +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c + +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +ce} _d +/ydieresis{1212 0 61 -426 1151 1552 sc +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +956 1120 l +1151 1120 l +659 -104 l + +700 1552 m +903 1552 l +903 1350 l +700 1350 l +700 1552 l + +309 1552 m +512 1552 l +512 1350 l +309 1350 l +309 1552 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/Amacron /amacron /Abreve /abreve /Aogonek /aogonek /Cacute /cacute /Ccircumflex /ccircumflex /Cdotaccent /cdotaccent /Ccaron /ccaron /Dcaron /dcaron /Dcroat /dcroat /Emacron /emacron /Ebreve /ebreve /Edotaccent /edotaccent /Eogonek /eogonek /Ecaron /ecaron /Gcircumflex /gcircumflex /Gbreve /gbreve /Gdotaccent /gdotaccent /Gcommaaccent /gcommaaccent /Hcircumflex /hcircumflex /Hbar /hbar /Itilde /itilde /Imacron /imacron /Ibreve /ibreve /Iogonek /iogonek /Idotaccent /dotlessi /IJ /ij /Jcircumflex /jcircumflex /Kcommaaccent /kcommaaccent /kgreenlandic /Lacute /lacute /Lcommaaccent /lcommaaccent /Lcaron /lcaron /Ldot /ldot /Lslash /lslash /Nacute /nacute /Ncommaaccent /ncommaaccent /Ncaron /ncaron /napostrophe /Eng /eng /Omacron /omacron /Obreve /obreve /Ohungarumlaut /ohungarumlaut /OE /oe /Racute /racute /Rcommaaccent /rcommaaccent /Rcaron /rcaron /Sacute /sacute /Scircumflex /scircumflex /Scedilla /scedilla /Scaron /scaron /Tcommaaccent /tcommaaccent /Tcaron /tcaron /Tbar /tbar /Utilde /utilde /Umacron /umacron /Ubreve /ubreve /Uring /uring /Uhungarumlaut /uhungarumlaut /Uogonek /uogonek /Wcircumflex /wcircumflex /Ycircumflex /ycircumflex /Ydieresis /Zacute /zacute /Zdotaccent /zdotaccent /Zcaron /zcaron /longs /uni0180 /uni0181 /uni0182 /uni0183 /uni0184 /uni0185 /uni0186 /uni0187 /uni0188 /uni0189 /uni018A /uni018B /uni018C /uni018D /uni018E /uni018F /uni0190 /uni0191 /florin /uni0193 /uni0194 /uni0195 /uni0196 /uni0197 /uni0198 /uni0199 /uni019A /uni019B /uni019C /uni019D /uni019E /uni019F /Ohorn /ohorn /uni01A2 /uni01A3 /uni01A4 /uni01A5 /uni01A6 /uni01A7 /uni01A8 /uni01A9 /uni01AA /uni01AB /uni01AC /uni01AD /uni01AE /Uhorn /uhorn /uni01B1 /uni01B2 /uni01B3 /uni01B4 /uni01B5 /uni01B6 /uni01B7 /uni01B8 /uni01B9 /uni01BA /uni01BB /uni01BC /uni01BD /uni01BE /uni01BF /uni01C0 /uni01C1 /uni01C2 /uni01C3 /uni01C4 /uni01C5 /uni01C6 /uni01C7 /uni01C8 /uni01C9 /uni01CA /uni01CB /uni01CC /uni01CD /uni01CE /uni01CF /uni01D0 /uni01D1 /uni01D2 /uni01D3 /uni01D4 /uni01D5 /uni01D6 /uni01D7 /uni01D8 /uni01D9 /uni01DA /uni01DB /uni01DC /uni01DD /uni01DE /uni01DF /uni01E0 /uni01E1 /uni01E2 /uni01E3 /uni01E4 /uni01E5 /Gcaron /gcaron /uni01E8 /uni01E9 /uni01EA /uni01EB /uni01EC /uni01ED /uni01EE /uni01EF /uni01F0 /uni01F1 /uni01F2 /uni01F3 /uni01F4 /uni01F5 /uni01F6 /uni01F7 /uni01F8 /uni01F9 /Aringacute /aringacute /AEacute /aeacute /Oslashacute /oslashacute] def +/CharStrings 257 dict dup begin +/.notdef 0 def +/Amacron{1401 0 16 0 1384 1841 sc +401 1841 m +999 1841 l +999 1693 l +401 1693 l +401 1841 l + +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +ce} _d +/amacron{1255 0 123 -29 1069 1526 sc +287 1526 m +885 1526 l +885 1378 l +287 1378 l +287 1526 l + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/Abreve{1401 0 16 0 1384 1938 sc +405 1938 m +523 1938 l +530 1888 550 1850 582 1825 c +615 1800 660 1788 718 1788 c +775 1788 820 1800 852 1825 c +884 1850 904 1887 913 1938 c +1031 1938 l +1024 1843 995 1771 942 1723 c +889 1675 815 1651 718 1651 c +621 1651 547 1675 494 1723 c +441 1771 412 1843 405 1938 c + +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +ce} _d +/abreve{1255 0 123 -29 1069 1567 sc +278 1567 m +396 1567 l +403 1517 423 1479 455 1454 c +488 1429 533 1417 591 1417 c +648 1417 693 1429 725 1454 c +757 1479 777 1516 786 1567 c +904 1567 l +897 1472 868 1400 815 1352 c +762 1304 688 1280 591 1280 c +494 1280 420 1304 367 1352 c +314 1400 285 1472 278 1567 c + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/Aogonek{1401 0 16 -395 1445 1493 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +1180 0 m +1299 0 l +1269 -41 1247 -76 1232 -105 c +1218 -134 1211 -159 1211 -180 c +1211 -211 1220 -234 1238 -249 c +1257 -264 1284 -272 1320 -272 c +1341 -272 1362 -269 1383 -264 c +1404 -259 1424 -252 1445 -242 c +1445 -375 l +1420 -382 1396 -387 1373 -390 c +1350 -393 1329 -395 1309 -395 c +1228 -395 1168 -380 1129 -351 c +1091 -322 1072 -277 1072 -215 c +1072 -183 1081 -149 1098 -114 c +1116 -79 1143 -41 1180 0 c + +ce} _d +/aogonek{1255 0 123 -395 1152 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +887 0 m +1006 0 l +976 -41 954 -76 939 -105 c +925 -134 918 -159 918 -180 c +918 -211 927 -234 945 -249 c +964 -264 991 -272 1027 -272 c +1048 -272 1069 -269 1090 -264 c +1111 -259 1131 -252 1152 -242 c +1152 -375 l +1127 -382 1103 -387 1080 -390 c +1057 -393 1036 -395 1016 -395 c +935 -395 875 -380 836 -351 c +798 -322 779 -277 779 -215 c +779 -183 788 -149 805 -114 c +823 -79 850 -41 887 0 c + +ce} _d +/Cacute{1430 0 115 -29 1319 1899 sc +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1175 1461 1250 1425 1319 1378 c + +868 1899 m +1053 1899 l +825 1635 l +672 1635 l +868 1899 l + +ce} _d +/cacute{1126 0 113 -29 999 1638 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +788 1638 m +987 1638 l +661 1262 l +508 1262 l +788 1638 l + +ce} _d +/Ccircumflex{1430 0 115 -29 1319 1901 sc +750 1901 m +938 1901 l +1149 1635 l +1010 1635 l +844 1813 l +678 1635 l +539 1635 l +750 1901 l + +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1175 1461 1250 1425 1319 1378 c + +ce} _d +/ccircumflex{1126 0 113 -29 999 1638 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +602 1638 m +750 1638 l +995 1262 l +856 1262 l +676 1507 l +496 1262 l +357 1262 l +602 1638 l + +ce} _d +/Cdotaccent{1430 0 115 -29 1319 1872 sc +742 1872 m +946 1872 l +946 1667 l +742 1667 l +742 1872 l + +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1175 1461 1250 1425 1319 1378 c + +ce} _d +/cdotaccent{1126 0 113 -29 999 1556 sc +582 1556 m +766 1556 l +766 1323 l +582 1323 l +582 1556 l + +676 1147 m +676 1147 l + +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +ce} _d +/Ccaron{1430 0 115 -29 1319 1901 sc +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1175 1461 1250 1425 1319 1378 c + +719 1635 m +508 1901 l +647 1901 l +813 1723 l +979 1901 l +1118 1901 l +907 1635 l +719 1635 l + +ce} _d +/ccaron{1126 0 113 -29 999 1638 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +575 1262 m +330 1638 l +469 1638 l +649 1393 l +829 1638 l +968 1638 l +723 1262 l +575 1262 l + +ce} _d +/Dcaron{1577 0 201 0 1456 1901 sc +654 1635 m +443 1901 l +582 1901 l +748 1723 l +914 1901 l +1053 1901 l +842 1635 l +654 1635 l + +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/dcaron{1300 0 113 -29 1499 1556 sc +930 950 m +930 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +1300 1554 m +1499 1554 l +1382 1178 l +1229 1178 l +1300 1554 l + +ce} _d +/Dcroat{1587 0 10 0 1466 1493 sc +211 1493 m +627 1493 l +916 1493 1128 1433 1263 1312 c +1398 1192 1466 1004 1466 748 c +1466 491 1398 302 1262 181 c +1127 60 915 0 627 0 c +211 0 l +211 700 l +10 700 l +10 844 l +211 844 l +211 1493 l + +414 1327 m +414 844 l +750 844 l +750 700 l +414 700 l +414 166 l +657 166 l +863 166 1014 213 1109 306 c +1205 399 1253 547 1253 748 c +1253 948 1205 1094 1109 1187 c +1014 1280 863 1327 657 1327 c +414 1327 l + +ce} _d +/dcroat{1300 0 113 -29 1268 1556 sc +930 950 m +930 1284 l +604 1284 l +604 1409 l +930 1409 l +930 1556 l +1114 1556 l +1114 1409 l +1268 1409 l +1268 1284 l +1114 1284 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/Emacron{1294 0 201 0 1163 1843 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +374 1843 m +972 1843 l +972 1695 l +374 1695 l +374 1843 l + +ce} _d +/emacron{1260 0 113 -29 1151 1526 sc +363 1526 m +961 1526 l +961 1378 l +363 1378 l +363 1526 l + +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/Ebreve{1294 0 201 0 1163 1901 sc +360 1901 m +478 1901 l +487 1865 507 1837 540 1818 c +573 1799 618 1790 673 1790 c +728 1790 771 1799 803 1817 c +836 1836 857 1864 868 1901 c +986 1901 l +979 1822 949 1761 896 1720 c +843 1679 768 1659 673 1659 c +577 1659 502 1679 449 1720 c +396 1761 367 1821 360 1901 c + +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +ce} _d +/ebreve{1260 0 113 -29 1151 1608 sc +349 1608 m +467 1608 l +474 1558 494 1520 526 1495 c +559 1470 604 1458 662 1458 c +719 1458 764 1470 796 1495 c +828 1520 848 1557 857 1608 c +975 1608 l +968 1513 939 1441 886 1393 c +833 1345 759 1321 662 1321 c +565 1321 491 1345 438 1393 c +385 1441 356 1513 349 1608 c + +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/Edotaccent{1294 0 201 0 1163 1872 sc +568 1872 m +772 1872 l +772 1667 l +568 1667 l +568 1872 l + +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +ce} _d +/edotaccent{1260 0 113 -29 1151 1556 sc +568 1556 m +752 1556 l +752 1323 l +568 1323 l +568 1556 l + +662 1147 m +662 1147 l + +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/Eogonek{1294 0 201 -395 1165 1493 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +900 0 m +1019 0 l +989 -41 967 -76 952 -105 c +938 -134 931 -159 931 -180 c +931 -211 940 -234 958 -249 c +977 -264 1004 -272 1040 -272 c +1061 -272 1082 -269 1103 -264 c +1124 -259 1144 -252 1165 -242 c +1165 -375 l +1140 -382 1116 -387 1093 -390 c +1070 -393 1049 -395 1029 -395 c +948 -395 888 -380 849 -351 c +811 -322 792 -277 792 -215 c +792 -183 801 -149 818 -114 c +836 -79 863 -41 900 0 c + +ce} _d +/eogonek{1260 0 113 -395 1151 1147 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +816 0 m +935 0 l +905 -41 883 -76 868 -105 c +854 -134 847 -159 847 -180 c +847 -211 856 -234 874 -249 c +893 -264 920 -272 956 -272 c +977 -272 998 -269 1019 -264 c +1040 -259 1060 -252 1081 -242 c +1081 -375 l +1056 -382 1032 -387 1009 -390 c +986 -393 965 -395 945 -395 c +864 -395 804 -380 765 -351 c +727 -322 708 -277 708 -215 c +708 -183 717 -149 734 -114 c +752 -79 779 -41 816 0 c + +ce} _d +/Ecaron{1294 0 201 0 1163 1895 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +584 1629 m +373 1895 l +512 1895 l +678 1717 l +844 1895 l +983 1895 l +772 1629 l +584 1629 l + +ce} _d +/ecaron{1260 0 113 -29 1151 1633 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +586 1257 m +341 1633 l +480 1633 l +660 1388 l +840 1633 l +979 1633 l +734 1257 l +586 1257 l + +ce} _d +/Gcircumflex{1587 0 115 -29 1419 1901 sc +766 1901 m +954 1901 l +1165 1635 l +1026 1635 l +860 1813 l +694 1635 l +555 1635 l +766 1901 l + +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +ce} _d +/gcircumflex{1300 0 113 -426 1114 1638 sc +542 1638 m +690 1638 l +935 1262 l +796 1262 l +616 1507 l +436 1262 l +297 1262 l +542 1638 l + +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/Gbreve{1587 0 115 -29 1419 1901 sc +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +482 1901 m +600 1901 l +609 1865 629 1837 662 1818 c +695 1799 740 1790 795 1790 c +850 1790 893 1799 925 1817 c +958 1836 979 1864 990 1901 c +1108 1901 l +1101 1822 1071 1761 1018 1720 c +965 1679 890 1659 795 1659 c +699 1659 624 1679 571 1720 c +518 1761 489 1821 482 1901 c + +ce} _d +/gbreve{1300 0 113 -426 1114 1608 sc +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +338 1608 m +456 1608 l +463 1558 483 1520 515 1495 c +548 1470 593 1458 651 1458 c +708 1458 753 1470 785 1495 c +817 1520 837 1557 846 1608 c +964 1608 l +957 1513 928 1441 875 1393 c +822 1345 748 1321 651 1321 c +554 1321 480 1345 427 1393 c +374 1441 345 1513 338 1608 c + +ce} _d +/Gdotaccent{1587 0 115 -29 1419 1872 sc +758 1872 m +962 1872 l +962 1667 l +758 1667 l +758 1872 l + +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +ce} _d +/gdotaccent{1300 0 113 -426 1114 1556 sc +524 1556 m +708 1556 l +708 1323 l +524 1323 l +524 1556 l + +618 1147 m +618 1147 l + +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/Gcommaaccent{1587 0 115 -511 1419 1520 sc +794 -191 m +1005 -191 l +841 -511 l +712 -511 l +794 -191 l + +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +ce} _d +/gcommaaccent{1300 0 113 -426 1114 1588 sc +722 1269 m +511 1269 l +675 1588 l +804 1588 l +722 1269 l + +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/Hcircumflex{1540 0 201 0 1339 1901 sc +676 1901 m +864 1901 l +1075 1635 l +936 1635 l +770 1813 l +604 1635 l +465 1635 l +676 1901 l + +201 1493 m +403 1493 l +403 881 l +1137 881 l +1137 1493 l +1339 1493 l +1339 0 l +1137 0 l +1137 711 l +403 711 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/hcircumflex{1298 0 -27 0 1124 1901 sc +184 1901 m +372 1901 l +583 1635 l +444 1635 l +278 1813 l +112 1635 l +-27 1635 l +184 1901 l + +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/Hbar{1876 0 201 0 1675 1493 sc +369 1493 m +571 1493 l +571 1269 l +1305 1269 l +1305 1493 l +1507 1493 l +1507 1269 l +1675 1269 l +1675 1105 l +1507 1105 l +1507 0 l +1305 0 l +1305 711 l +571 711 l +571 0 l +369 0 l +369 1105 l +201 1105 l +201 1269 l +369 1269 l +369 1493 l + +571 1105 m +571 881 l +1305 881 l +1305 1105 l +571 1105 l + +ce} _d +/hbar{1423 0 120 0 1183 1556 sc +1183 676 m +1183 0 l +999 0 l +999 670 l +999 776 978 855 937 908 c +896 961 834 987 751 987 c +652 987 573 955 516 892 c +459 829 430 742 430 633 c +430 0 l +245 0 l +245 1270 l +120 1270 l +120 1434 l +245 1434 l +245 1556 l +430 1556 l +430 1434 l +782 1434 l +782 1270 l +430 1270 l +430 946 l +474 1013 526 1064 585 1097 c +645 1130 714 1147 792 1147 c +921 1147 1018 1107 1084 1027 c +1150 948 1183 831 1183 676 c + +ce} _d +/Itilde{604 0 -28 0 632 1886 sc +298 1710 m +241 1743 l +224 1752 211 1759 200 1762 c +190 1766 181 1768 173 1768 c +149 1768 130 1760 117 1743 c +104 1726 97 1703 97 1673 c +97 1667 l +-28 1667 l +-28 1734 -11 1788 23 1827 c +58 1866 104 1886 161 1886 c +185 1886 207 1883 227 1878 c +248 1873 274 1861 306 1843 c +363 1813 l +378 1804 392 1798 403 1794 c +414 1790 425 1788 435 1788 c +456 1788 474 1796 487 1813 c +500 1830 507 1853 507 1880 c +507 1886 l +632 1886 l +631 1819 613 1766 578 1726 c +544 1687 499 1667 443 1667 c +420 1667 399 1670 379 1675 c +360 1680 333 1692 298 1710 c + +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/itilde{569 0 -45 0 615 1591 sc +281 1370 m +224 1425 l +209 1438 196 1448 185 1454 c +174 1461 165 1464 156 1464 c +131 1464 112 1452 100 1427 c +88 1403 81 1364 80 1309 c +-45 1309 l +-44 1399 -26 1468 8 1517 c +42 1566 89 1591 150 1591 c +175 1591 199 1586 220 1577 c +241 1568 264 1552 289 1530 c +346 1475 l +361 1462 373 1452 384 1445 c +395 1439 405 1436 414 1436 c +439 1436 458 1448 470 1472 c +482 1497 489 1536 490 1591 c +615 1591 l +614 1501 596 1431 562 1382 c +528 1333 481 1309 420 1309 c +395 1309 371 1314 350 1323 c +329 1332 306 1348 281 1370 c + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/Imacron{604 0 3 0 601 1841 sc +3 1841 m +601 1841 l +601 1693 l +3 1693 l +3 1841 l + +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/imacron{569 0 -14 0 584 1525 sc +-14 1525 m +584 1525 l +584 1377 l +-14 1377 l +-14 1525 l + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/Ibreve{604 0 -11 0 615 1901 sc +-11 1901 m +107 1901 l +116 1865 136 1837 169 1818 c +202 1799 247 1790 302 1790 c +357 1790 400 1799 432 1817 c +465 1836 486 1864 497 1901 c +615 1901 l +608 1822 578 1761 525 1720 c +472 1679 397 1659 302 1659 c +206 1659 131 1679 78 1720 c +25 1761 -4 1821 -11 1901 c + +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/ibreve{569 0 -28 0 598 1608 sc +-28 1608 m +90 1608 l +97 1558 117 1520 149 1495 c +182 1470 227 1458 285 1458 c +342 1458 387 1470 419 1495 c +451 1520 471 1557 480 1608 c +598 1608 l +591 1513 562 1441 509 1393 c +456 1345 382 1321 285 1321 c +188 1321 114 1345 61 1393 c +8 1441 -21 1513 -28 1608 c + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/Iogonek{604 0 176 -395 549 1493 sc +284 0 m +403 0 l +373 -41 351 -76 336 -105 c +322 -134 315 -159 315 -180 c +315 -211 324 -234 342 -249 c +361 -264 388 -272 424 -272 c +445 -272 466 -269 487 -264 c +508 -259 528 -252 549 -242 c +549 -375 l +524 -382 500 -387 477 -390 c +454 -393 433 -395 413 -395 c +332 -395 272 -380 233 -351 c +195 -322 176 -277 176 -215 c +176 -183 185 -149 202 -114 c +220 -79 247 -41 284 0 c + +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/iogonek{569 0 150 -395 523 1556 sc +258 0 m +377 0 l +347 -41 325 -76 310 -105 c +296 -134 289 -159 289 -180 c +289 -211 298 -234 316 -249 c +335 -264 362 -272 398 -272 c +419 -272 440 -269 461 -264 c +482 -259 502 -252 523 -242 c +523 -375 l +498 -382 474 -387 451 -390 c +428 -393 407 -395 387 -395 c +306 -395 246 -380 207 -351 c +169 -322 150 -277 150 -215 c +150 -183 159 -149 176 -114 c +194 -79 221 -41 258 0 c + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/Idotaccent{604 0 201 0 405 1872 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +201 1872 m +405 1872 l +405 1667 l +201 1667 l +201 1872 l + +ce} _d +/dotlessi{569 0 193 0 377 1147 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/IJ{1208 0 201 -410 1007 1493 sc +805 1493 m +1007 1493 l +1007 104 l +1007 -76 973 -207 904 -288 c +836 -369 726 -410 575 -410 c +498 -410 l +498 -240 l +561 -240 l +650 -240 713 -215 750 -165 c +787 -115 805 -25 805 104 c +805 1493 l + +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/ij{1138 0 193 -426 945 1556 sc +761 1120 m +945 1120 l +945 -20 l +945 -163 918 -266 863 -330 c +809 -394 722 -426 601 -426 c +531 -426 l +531 -270 l +580 -270 l +650 -270 698 -254 723 -221 c +748 -189 761 -122 761 -20 c +761 1120 l + +761 1556 m +945 1556 l +945 1323 l +761 1323 l +761 1556 l + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/Jcircumflex{604 0 -106 -410 607 1901 sc +208 1901 m +396 1901 l +607 1635 l +468 1635 l +302 1813 l +136 1635 l +-3 1635 l +208 1901 l + +201 1493 m +403 1493 l +403 104 l +403 -76 369 -207 300 -288 c +232 -369 122 -410 -29 -410 c +-106 -410 l +-106 -240 l +-43 -240 l +46 -240 109 -215 146 -165 c +183 -115 201 -25 201 104 c +201 1493 l + +ce} _d +/jcircumflex{569 0 -37 -426 604 1638 sc +211 1638 m +359 1638 l +604 1262 l +465 1262 l +285 1507 l +105 1262 l +-34 1262 l +211 1638 l + +193 1120 m +377 1120 l +377 -20 l +377 -163 350 -266 295 -330 c +241 -394 154 -426 33 -426 c +-37 -426 l +-37 -270 l +12 -270 l +82 -270 130 -254 155 -221 c +180 -189 193 -122 193 -20 c +193 1120 l + +ce} _d +/Kcommaaccent{1343 0 201 -482 1386 1493 sc +727 -162 m +938 -162 l +774 -482 l +645 -482 l +727 -162 l + +201 1493 m +403 1493 l +403 862 l +1073 1493 l +1333 1493 l +592 797 l +1386 0 l +1120 0 l +403 719 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/kcommaaccent{1186 0 186 -482 1180 1556 sc +616 -162 m +827 -162 l +663 -482 l +534 -482 l +616 -162 l + +186 1556 m +371 1556 l +371 637 l +920 1120 l +1155 1120 l +561 596 l +1180 0 l +940 0 l +371 547 l +371 0 l +186 0 l +186 1556 l + +ce} _d +/kgreenlandic{1186 0 186 0 1180 1120 sc +186 1120 m +371 1120 l +371 635 l +920 1120 l +1155 1120 l +561 594 l +1180 0 l +940 0 l +371 545 l +371 0 l +186 0 l +186 1120 l + +ce} _d +/Lacute{1141 0 201 0 1130 1900 sc +421 1900 m +606 1900 l +378 1636 l +225 1636 l +421 1900 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/lacute{569 0 193 0 586 1900 sc +401 1900 m +586 1900 l +358 1636 l +205 1636 l +401 1900 l + +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/Lcommaaccent{1141 0 201 -482 1130 1493 sc +599 -162 m +810 -162 l +646 -482 l +517 -482 l +599 -162 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/lcommaaccent{569 0 136 -482 429 1556 sc +218 -162 m +429 -162 l +265 -482 l +136 -482 l +218 -162 l + +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/Lcaron{1141 0 201 0 1130 1493 sc +671 1493 m +870 1493 l +753 1117 l +600 1117 l +671 1493 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/lcaron{768 0 193 0 768 1556 sc +569 1556 m +768 1556 l +651 1180 l +498 1180 l +569 1556 l + +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/Ldot{1141 0 201 0 1130 1493 sc +780 957 m +991 957 l +991 703 l +780 703 l +780 957 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/ldot{700 0 193 0 644 1556 sc +433 953 m +644 953 l +644 699 l +433 699 l +433 953 l + +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/Lslash{1151 0 -14 0 1141 1493 sc +211 1493 m +414 1493 l +414 877 l +727 1096 l +807 985 l +414 711 l +414 170 l +1141 170 l +1141 0 l +211 0 l +211 571 l +63 465 l +-14 575 l +211 733 l +211 1493 l + +ce} _d +/lslash{582 0 2 0 584 1556 sc +199 1556 m +383 1556 l +383 954 l +508 1044 l +584 938 l +383 797 l +383 0 l +199 0 l +199 666 l +76 578 l +2 684 l +199 825 l +199 1556 l + +ce} _d +/Nacute{1532 0 201 0 1331 1900 sc +764 1900 m +949 1900 l +721 1636 l +568 1636 l +764 1900 l + +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/nacute{1298 0 186 0 1124 1645 sc +717 1645 m +916 1645 l +590 1269 l +437 1269 l +717 1645 l + +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/Ncommaaccent{1532 0 201 -482 1331 1493 sc +700 -162 m +911 -162 l +747 -482 l +618 -482 l +700 -162 l + +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/ncommaaccent{1298 0 186 -482 1124 1147 sc +588 -162 m +799 -162 l +635 -482 l +506 -482 l +588 -162 l + +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/Ncaron{1532 0 201 0 1331 1887 sc +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +663 1621 m +452 1887 l +591 1887 l +757 1709 l +923 1887 l +1062 1887 l +851 1621 l +663 1621 l + +ce} _d +/ncaron{1298 0 186 0 1124 1638 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +579 1262 m +334 1638 l +473 1638 l +653 1393 l +833 1638 l +972 1638 l +727 1262 l +579 1262 l + +ce} _d +/napostrophe{1666 0 205 0 1465 1493 sc +1465 676 m +1465 0 l +1281 0 l +1281 670 l +1281 776 1260 855 1219 908 c +1178 961 1116 987 1033 987 c +934 987 855 955 798 892 c +741 829 712 742 712 633 c +712 0 l +527 0 l +527 1120 l +712 1120 l +712 946 l +756 1013 808 1064 867 1097 c +927 1130 996 1147 1074 1147 c +1203 1147 1300 1107 1366 1027 c +1432 948 1465 831 1465 676 c + +287 1493 m +498 1493 l +498 1341 l +334 1022 l +205 1022 l +287 1341 l +287 1493 l + +ce} _d +/Eng{1532 0 201 -426 1305 1520 sc +1104 895 m +1104 1180 1002 1323 797 1323 c +678 1323 582 1280 510 1195 c +439 1110 403 994 403 846 c +403 0 l +201 0 l +201 1493 l +403 1493 l +403 1252 l +455 1341 516 1408 586 1453 c +657 1498 743 1520 845 1520 c +996 1520 1111 1467 1188 1360 c +1266 1254 1305 1098 1305 893 c +1305 -20 l +1305 -162 1278 -265 1224 -330 c +1169 -394 1082 -426 961 -426 c +874 -426 l +874 -270 l +923 -270 l +991 -270 1038 -255 1064 -225 c +1091 -195 1104 -127 1104 -20 c +1104 895 l + +ce} _d +/eng{1298 0 186 -426 1124 1147 sc +1124 676 m +1124 -20 l +1124 -163 1097 -266 1042 -330 c +988 -394 901 -426 780 -426 c +526 -426 l +526 -270 l +759 -270 l +829 -270 877 -254 902 -222 c +927 -189 940 -122 940 -20 c +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1028 c +1091 948 1124 831 1124 676 c + +ce} _d +/Omacron{1612 0 115 -29 1497 1841 sc +508 1841 m +1106 1841 l +1106 1693 l +508 1693 l +508 1841 l + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/omacron{1253 0 113 -29 1141 1525 sc +328 1525 m +926 1525 l +926 1377 l +328 1377 l +328 1525 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/Obreve{1612 0 115 -29 1497 1901 sc +494 1901 m +612 1901 l +621 1865 641 1837 674 1818 c +707 1799 752 1790 807 1790 c +862 1790 905 1799 937 1817 c +970 1836 991 1864 1002 1901 c +1120 1901 l +1113 1822 1083 1761 1030 1720 c +977 1679 902 1659 807 1659 c +711 1659 636 1679 583 1720 c +530 1761 501 1821 494 1901 c + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/obreve{1253 0 113 -29 1141 1608 sc +314 1608 m +432 1608 l +439 1558 459 1520 491 1495 c +524 1470 569 1458 627 1458 c +684 1458 729 1470 761 1495 c +793 1520 813 1557 822 1608 c +940 1608 l +933 1513 904 1441 851 1393 c +798 1345 724 1321 627 1321 c +530 1321 456 1345 403 1393 c +350 1441 321 1513 314 1608 c + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/Ohungarumlaut{1612 0 115 -29 1497 1899 sc +1056 1899 m +1241 1899 l +1013 1635 l +860 1635 l +1056 1899 l + +721 1899 m +906 1899 l +678 1635 l +525 1635 l +721 1899 l + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/ohungarumlaut{1253 0 113 -29 1141 1638 sc +924 1638 m +1102 1638 l +854 1262 l +719 1262 l +924 1638 l + +590 1638 m +760 1638 l +537 1262 l +400 1262 l +590 1638 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/OE{2191 0 115 0 2060 1493 sc +2042 1493 m +2042 1323 l +1300 1323 l +1300 881 l +2011 881 l +2011 711 l +1300 711 l +1300 170 l +2060 170 l +2060 0 l +995 0 l +706 0 487 63 338 190 c +189 317 115 503 115 748 c +115 991 189 1176 338 1303 c +487 1430 706 1493 995 1493 c +2042 1493 l + +1098 1323 m +969 1323 l +755 1323 595 1275 488 1179 c +381 1084 328 940 328 748 c +328 555 381 411 488 314 c +595 218 755 170 969 170 c +1098 170 l +1098 1323 l + +ce} _d +/oe{2095 0 113 -29 1987 1147 sc +1802 660 m +1801 761 1773 842 1718 901 c +1663 961 1590 991 1499 991 c +1397 991 1315 962 1253 904 c +1192 846 1156 764 1147 659 c +1802 660 l + +1987 606 m +1987 516 l +1141 516 l +1149 389 1187 293 1255 226 c +1323 160 1418 127 1540 127 c +1611 127 1679 136 1746 153 c +1813 170 1879 196 1944 231 c +1944 57 l +1877 29 1809 8 1740 -7 c +1671 -22 1601 -29 1530 -29 c +1423 -29 1330 -11 1249 26 c +1168 63 1101 117 1047 190 c +1000 117 941 62 871 25 c +802 -11 720 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c +720 1147 802 1128 872 1091 c +943 1054 1000 1000 1044 928 c +1096 999 1161 1054 1238 1091 c +1315 1128 1402 1147 1497 1147 c +1648 1147 1767 1098 1855 1001 c +1943 904 1987 773 1987 606 c + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +ce} _d +/Racute{1423 0 201 0 1364 1900 sc +716 1900 m +901 1900 l +673 1636 l +520 1636 l +716 1900 l + +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l + +ce} _d +/racute{842 0 186 0 916 1645 sc +717 1645 m +916 1645 l +590 1269 l +437 1269 l +717 1645 l + +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/Rcommaaccent{1423 0 201 -482 1364 1493 sc +716 -162 m +927 -162 l +763 -482 l +634 -482 l +716 -162 l + +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l + +ce} _d +/rcommaaccent{842 0 130 -482 842 1147 sc +212 -162 m +423 -162 l +259 -482 l +130 -482 l +212 -162 l + +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/Rcaron{1423 0 201 0 1364 1887 sc +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l + +543 1621 m +332 1887 l +471 1887 l +637 1709 l +803 1887 l +942 1887 l +731 1621 l +543 1621 l + +ce} _d +/rcaron{842 0 186 0 858 1638 sc +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +465 1262 m +220 1638 l +359 1638 l +539 1393 l +719 1638 l +858 1638 l +613 1262 l +465 1262 l + +ce} _d +/Sacute{1300 0 135 -29 1186 1900 sc +716 1900 m +901 1900 l +673 1636 l +520 1636 l +716 1900 l + +1096 1444 m +1096 1247 l +1019 1284 947 1311 879 1329 c +811 1347 745 1356 682 1356 c +572 1356 487 1335 427 1292 c +368 1249 338 1189 338 1110 c +338 1044 358 994 397 960 c +437 927 512 900 623 879 c +745 854 l +896 825 1007 775 1078 702 c +1150 630 1186 533 1186 412 c +1186 267 1137 158 1040 83 c +943 8 801 -29 614 -29 c +543 -29 468 -21 388 -5 c +309 11 226 35 141 66 c +141 274 l +223 228 303 193 382 170 c +461 147 538 135 614 135 c +729 135 818 158 881 203 c +944 248 975 313 975 397 c +975 470 952 528 907 569 c +862 610 789 641 686 662 c +563 686 l +412 716 303 763 236 827 c +169 891 135 980 135 1094 c +135 1226 181 1330 274 1406 c +367 1482 496 1520 659 1520 c +729 1520 800 1514 873 1501 c +946 1488 1020 1469 1096 1444 c + +ce} _d +/sacute{1067 0 111 -29 967 1645 sc +717 1645 m +916 1645 l +590 1269 l +437 1269 l +717 1645 l + +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + +ce} _d +/Scircumflex{1300 0 135 -29 1186 1901 sc +565 1901 m +753 1901 l +964 1635 l +825 1635 l +659 1813 l +493 1635 l +354 1635 l +565 1901 l + +1096 1444 m +1096 1247 l +1019 1284 947 1311 879 1329 c +811 1347 745 1356 682 1356 c +572 1356 487 1335 427 1292 c +368 1249 338 1189 338 1110 c +338 1044 358 994 397 960 c +437 927 512 900 623 879 c +745 854 l +896 825 1007 775 1078 702 c +1150 630 1186 533 1186 412 c +1186 267 1137 158 1040 83 c +943 8 801 -29 614 -29 c +543 -29 468 -21 388 -5 c +309 11 226 35 141 66 c +141 274 l +223 228 303 193 382 170 c +461 147 538 135 614 135 c +729 135 818 158 881 203 c +944 248 975 313 975 397 c +975 470 952 528 907 569 c +862 610 789 641 686 662 c +563 686 l +412 716 303 763 236 827 c +169 891 135 980 135 1094 c +135 1226 181 1330 274 1406 c +367 1482 496 1520 659 1520 c +729 1520 800 1514 873 1501 c +946 1488 1020 1469 1096 1444 c + +ce} _d +/scircumflex{1067 0 111 -29 967 1638 sc +475 1638 m +623 1638 l +868 1262 l +729 1262 l +549 1507 l +369 1262 l +230 1262 l +475 1638 l + +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + +ce} _d +/Scedilla{1300 0 135 -395 1186 1520 sc +1096 1444 m +1096 1247 l +1019 1284 947 1311 879 1329 c +811 1347 745 1356 682 1356 c +572 1356 487 1335 427 1292 c +368 1249 338 1189 338 1110 c +338 1044 358 994 397 960 c +437 927 512 900 623 879 c +745 854 l +896 825 1007 775 1078 702 c +1150 630 1186 533 1186 412 c +1186 267 1137 158 1040 83 c +943 8 801 -29 614 -29 c +543 -29 468 -21 388 -5 c +309 11 226 35 141 66 c +141 274 l +223 228 303 193 382 170 c +461 147 538 135 614 135 c +729 135 818 158 881 203 c +944 248 975 313 975 397 c +975 470 952 528 907 569 c +862 610 789 641 686 662 c +563 686 l +412 716 303 763 236 827 c +169 891 135 980 135 1094 c +135 1226 181 1330 274 1406 c +367 1482 496 1520 659 1520 c +729 1520 800 1514 873 1501 c +946 1488 1020 1469 1096 1444 c + +735 0 m +772 -41 799 -79 817 -114 c +835 -149 844 -183 844 -215 c +844 -274 824 -319 784 -349 c +744 -380 685 -395 606 -395 c +575 -395 545 -393 516 -389 c +487 -385 459 -379 430 -371 c +430 -240 l +453 -251 476 -259 501 -264 c +526 -269 554 -272 585 -272 c +624 -272 654 -264 674 -248 c +694 -232 704 -209 704 -178 c +704 -158 697 -133 682 -104 c +668 -75 646 -41 616 0 c +735 0 l + +ce} _d +/scedilla{1067 0 111 -395 967 1147 sc +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + +619 0 m +656 -41 683 -79 701 -114 c +719 -149 728 -183 728 -215 c +728 -274 708 -319 668 -349 c +628 -380 569 -395 490 -395 c +459 -395 429 -393 400 -389 c +371 -385 343 -379 314 -371 c +314 -240 l +337 -251 360 -259 385 -264 c +410 -269 438 -272 469 -272 c +508 -272 538 -264 558 -248 c +578 -232 588 -209 588 -178 c +588 -158 581 -133 566 -104 c +552 -75 530 -41 500 0 c +619 0 l + +ce} _d +/Scaron{1300 0 135 -29 1186 1901 sc +1096 1444 m +1096 1247 l +1019 1284 947 1311 879 1329 c +811 1347 745 1356 682 1356 c +572 1356 487 1335 427 1292 c +368 1249 338 1189 338 1110 c +338 1044 358 994 397 960 c +437 927 512 900 623 879 c +745 854 l +896 825 1007 775 1078 702 c +1150 630 1186 533 1186 412 c +1186 267 1137 158 1040 83 c +943 8 801 -29 614 -29 c +543 -29 468 -21 388 -5 c +309 11 226 35 141 66 c +141 274 l +223 228 303 193 382 170 c +461 147 538 135 614 135 c +729 135 818 158 881 203 c +944 248 975 313 975 397 c +975 470 952 528 907 569 c +862 610 789 641 686 662 c +563 686 l +412 716 303 763 236 827 c +169 891 135 980 135 1094 c +135 1226 181 1330 274 1406 c +367 1482 496 1520 659 1520 c +729 1520 800 1514 873 1501 c +946 1488 1020 1469 1096 1444 c + +557 1635 m +346 1901 l +485 1901 l +651 1723 l +817 1901 l +956 1901 l +745 1635 l +557 1635 l + +ce} _d +/scaron{1067 0 111 -29 967 1638 sc +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + +481 1262 m +236 1638 l +375 1638 l +555 1393 l +735 1638 l +874 1638 l +629 1262 l +481 1262 l + +551 1147 m +551 1147 l + +ce} _d +/Tcommaaccent{1251 0 -6 -395 1257 1493 sc +676 0 m +713 -41 740 -79 758 -114 c +776 -149 785 -183 785 -215 c +785 -274 765 -319 725 -349 c +685 -380 626 -395 547 -395 c +516 -395 486 -393 457 -389 c +428 -385 400 -379 371 -371 c +371 -240 l +394 -251 417 -259 442 -264 c +467 -269 495 -272 526 -272 c +565 -272 595 -264 615 -248 c +635 -232 645 -209 645 -178 c +645 -158 638 -133 623 -104 c +609 -75 587 -41 557 0 c +676 0 l + +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +-6 1323 l +-6 1493 l + +ce} _d +/tcommaaccent{803 0 55 -395 754 1438 sc +565 0 m +602 -41 629 -79 647 -114 c +665 -149 674 -183 674 -215 c +674 -274 654 -319 614 -349 c +574 -380 515 -395 436 -395 c +405 -395 375 -393 346 -389 c +317 -385 289 -379 260 -371 c +260 -240 l +283 -251 306 -259 331 -264 c +356 -269 384 -272 415 -272 c +454 -272 484 -264 504 -248 c +524 -232 534 -209 534 -178 c +534 -158 527 -133 512 -104 c +498 -75 476 -41 446 0 c +565 0 l + +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/Tcaron{1251 0 -6 0 1257 1887 sc +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +-6 1323 l +-6 1493 l + +533 1621 m +322 1887 l +461 1887 l +627 1709 l +793 1887 l +932 1887 l +721 1621 l +533 1621 l + +ce} _d +/tcaron{803 0 55 0 766 1666 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +567 1666 m +766 1666 l +649 1290 l +496 1290 l +567 1666 l + +ce} _d +/Tbar{1251 0 -6 0 1257 1493 sc +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 747 l +992 747 l +992 577 l +727 577 l +727 0 l +524 0 l +524 577 l +259 577 l +259 747 l +524 747 l +524 1323 l +-6 1323 l +-6 1493 l + +ce} _d +/tbar{803 0 55 0 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 744 l +754 744 l +754 602 l +375 602 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 27 271 80 c +217 133 190 229 190 369 c +190 602 l +55 602 l +55 744 l +190 744 l +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/Utilde{1499 0 178 -29 1321 1886 sc +746 1710 m +689 1743 l +672 1752 659 1759 648 1762 c +638 1766 629 1768 621 1768 c +597 1768 578 1760 565 1743 c +552 1726 545 1703 545 1673 c +545 1667 l +420 1667 l +420 1734 437 1788 471 1827 c +506 1866 552 1886 609 1886 c +633 1886 655 1883 675 1878 c +696 1873 722 1861 754 1843 c +811 1813 l +826 1804 840 1798 851 1794 c +862 1790 873 1788 883 1788 c +904 1788 922 1796 935 1813 c +948 1830 955 1853 955 1880 c +955 1886 l +1080 1886 l +1079 1819 1061 1766 1026 1726 c +992 1687 947 1667 891 1667 c +868 1667 847 1670 827 1675 c +808 1680 781 1692 746 1710 c + +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +ce} _d +/utilde{1298 0 174 -29 1112 1591 sc +639 1370 m +582 1425 l +567 1438 554 1448 543 1454 c +532 1461 523 1464 514 1464 c +489 1464 470 1452 458 1427 c +446 1403 439 1364 438 1309 c +313 1309 l +314 1399 332 1468 366 1517 c +400 1566 447 1591 508 1591 c +533 1591 557 1586 578 1577 c +599 1568 622 1552 647 1530 c +704 1475 l +719 1462 731 1452 742 1445 c +753 1439 763 1436 772 1436 c +797 1436 816 1448 828 1472 c +840 1497 847 1536 848 1591 c +973 1591 l +972 1501 954 1431 920 1382 c +886 1333 839 1309 778 1309 c +753 1309 729 1314 708 1323 c +687 1332 664 1348 639 1370 c + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/Umacron{1499 0 178 -29 1321 1841 sc +451 1841 m +1049 1841 l +1049 1693 l +451 1693 l +451 1841 l + +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +ce} _d +/umacron{1298 0 174 -29 1112 1525 sc +344 1525 m +942 1525 l +942 1377 l +344 1377 l +344 1525 l + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/Ubreve{1499 0 178 -29 1321 1901 sc +437 1901 m +555 1901 l +564 1865 584 1837 617 1818 c +650 1799 695 1790 750 1790 c +805 1790 848 1799 880 1817 c +913 1836 934 1864 945 1901 c +1063 1901 l +1056 1822 1026 1761 973 1720 c +920 1679 845 1659 750 1659 c +654 1659 579 1679 526 1720 c +473 1761 444 1821 437 1901 c + +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +ce} _d +/ubreve{1298 0 174 -29 1112 1608 sc +330 1608 m +448 1608 l +455 1558 475 1520 507 1495 c +540 1470 585 1458 643 1458 c +700 1458 745 1470 777 1495 c +809 1520 829 1557 838 1608 c +956 1608 l +949 1513 920 1441 867 1393 c +814 1345 740 1321 643 1321 c +546 1321 472 1345 419 1393 c +366 1441 337 1513 330 1608 c + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/Uring{1499 0 178 -29 1321 1903 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +904 1629 m +904 1671 889 1707 860 1736 c +831 1765 795 1780 752 1780 c +709 1780 672 1765 643 1736 c +614 1707 600 1672 600 1629 c +600 1586 614 1549 643 1520 c +672 1491 709 1477 752 1477 c +795 1477 831 1492 860 1521 c +889 1550 904 1586 904 1629 c + +1026 1629 m +1026 1552 999 1487 946 1434 c +893 1381 829 1354 752 1354 c +675 1354 610 1381 557 1434 c +504 1487 478 1552 478 1629 c +478 1706 504 1770 557 1823 c +610 1876 675 1903 752 1903 c +829 1903 893 1876 946 1823 c +999 1770 1026 1706 1026 1629 c + +ce} _d +/uring{1298 0 174 -29 1112 1738 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +788 1464 m +788 1506 773 1542 744 1571 c +715 1600 679 1615 636 1615 c +593 1615 556 1600 527 1571 c +498 1542 484 1507 484 1464 c +484 1421 498 1384 527 1355 c +556 1326 593 1312 636 1312 c +679 1312 715 1327 744 1356 c +773 1385 788 1421 788 1464 c + +910 1464 m +910 1387 883 1322 830 1269 c +777 1216 713 1189 636 1189 c +559 1189 494 1216 441 1269 c +388 1322 362 1387 362 1464 c +362 1541 388 1605 441 1658 c +494 1711 559 1738 636 1738 c +713 1738 777 1711 830 1658 c +883 1605 910 1541 910 1464 c + +ce} _d +/Uhungarumlaut{1499 0 178 -29 1321 1899 sc +999 1899 m +1184 1899 l +956 1635 l +803 1635 l +999 1899 l + +664 1899 m +849 1899 l +621 1635 l +468 1635 l +664 1899 l + +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +ce} _d +/uhungarumlaut{1298 0 174 -29 1118 1638 sc +940 1638 m +1118 1638 l +870 1262 l +735 1262 l +940 1638 l + +606 1638 m +776 1638 l +553 1262 l +416 1262 l +606 1638 l + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/Uogonek{1499 0 178 -395 1321 1493 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +690 0 m +809 0 l +779 -41 757 -76 742 -105 c +728 -134 721 -159 721 -180 c +721 -211 730 -234 748 -249 c +767 -264 794 -272 830 -272 c +851 -272 872 -269 893 -264 c +914 -259 934 -252 955 -242 c +955 -375 l +930 -382 906 -387 883 -390 c +860 -393 839 -395 819 -395 c +738 -395 678 -380 639 -351 c +601 -322 582 -277 582 -215 c +582 -183 591 -149 608 -114 c +626 -79 653 -41 690 0 c + +ce} _d +/uogonek{1298 0 174 -395 1256 1147 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +991 0 m +1110 0 l +1080 -41 1058 -76 1043 -105 c +1029 -134 1022 -159 1022 -180 c +1022 -211 1031 -234 1049 -249 c +1068 -264 1095 -272 1131 -272 c +1152 -272 1173 -269 1194 -264 c +1215 -259 1235 -252 1256 -242 c +1256 -375 l +1231 -382 1207 -387 1184 -390 c +1161 -393 1140 -395 1120 -395 c +1039 -395 979 -380 940 -351 c +902 -322 883 -277 883 -215 c +883 -183 892 -149 909 -114 c +927 -79 954 -41 991 0 c + +ce} _d +/Wcircumflex{2025 0 68 0 1958 1908 sc +919 1908 m +1107 1908 l +1318 1642 l +1179 1642 l +1013 1820 l +847 1642 l +708 1642 l +919 1908 l + +68 1493 m +272 1493 l +586 231 l +899 1493 l +1126 1493 l +1440 231 l +1753 1493 l +1958 1493 l +1583 0 l +1329 0 l +1014 1296 l +696 0 l +442 0 l +68 1493 l + +ce} _d +/wcircumflex{1675 0 86 0 1589 1645 sc +763 1645 m +911 1645 l +1156 1269 l +1017 1269 l +837 1514 l +657 1269 l +518 1269 l +763 1645 l + +86 1120 m +270 1120 l +500 246 l +729 1120 l +946 1120 l +1176 246 l +1405 1120 l +1589 1120 l +1296 0 l +1079 0 l +838 918 l +596 0 l +379 0 l +86 1120 l + +ce} _d +/Ycircumflex{1251 0 -4 0 1255 1908 sc +532 1908 m +720 1908 l +931 1642 l +792 1642 l +626 1820 l +460 1642 l +321 1642 l +532 1908 l + +-4 1493 m +213 1493 l +627 879 l +1038 1493 l +1255 1493 l +727 711 l +727 0 l +524 0 l +524 711 l +-4 1493 l + +ce} _d +/ycircumflex{1212 0 61 -426 1151 1645 sc +532 1645 m +680 1645 l +925 1269 l +786 1269 l +606 1514 l +426 1269 l +287 1269 l +532 1645 l + +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +956 1120 l +1151 1120 l +659 -104 l + +ce} _d +/Ydieresis{1251 0 -4 0 1255 1870 sc +-4 1493 m +213 1493 l +627 879 l +1038 1493 l +1255 1493 l +727 711 l +727 0 l +524 0 l +524 711 l +-4 1493 l + +721 1870 m +924 1870 l +924 1667 l +721 1667 l +721 1870 l + +330 1870 m +533 1870 l +533 1667 l +330 1667 l +330 1870 l + +ce} _d +/Zacute{1403 0 92 0 1311 1900 sc +716 1900 m +901 1900 l +673 1636 l +520 1636 l +716 1900 l + +115 1493 m +1288 1493 l +1288 1339 l +344 170 l +1311 170 l +1311 0 l +92 0 l +92 154 l +1036 1323 l +115 1323 l +115 1493 l + +ce} _d +/zacute{1075 0 88 0 987 1645 sc +717 1645 m +916 1645 l +590 1269 l +437 1269 l +717 1645 l + +113 1120 m +987 1120 l +987 952 l +295 147 l +987 147 l +987 0 l +88 0 l +88 168 l +780 973 l +113 973 l +113 1120 l + +ce} _d +/Zdotaccent{1403 0 92 0 1311 1872 sc +600 1872 m +804 1872 l +804 1667 l +600 1667 l +600 1872 l + +115 1493 m +1288 1493 l +1288 1339 l +344 170 l +1311 170 l +1311 0 l +92 0 l +92 154 l +1036 1323 l +115 1323 l +115 1493 l + +ce} _d +/zdotaccent{1075 0 88 0 987 1556 sc +441 1556 m +625 1556 l +625 1323 l +441 1323 l +441 1556 l + +535 1147 m +535 1147 l + +113 1120 m +987 1120 l +987 952 l +295 147 l +987 147 l +987 0 l +88 0 l +88 168 l +780 973 l +113 973 l +113 1120 l + +ce} _d +/Zcaron{1403 0 92 0 1311 1901 sc +115 1493 m +1288 1493 l +1288 1339 l +344 170 l +1311 170 l +1311 0 l +92 0 l +92 154 l +1036 1323 l +115 1323 l +115 1493 l + +608 1635 m +397 1901 l +536 1901 l +702 1723 l +868 1901 l +1007 1901 l +796 1635 l +608 1635 l + +ce} _d +/zcaron{1075 0 88 0 987 1638 sc +113 1120 m +987 1120 l +987 952 l +295 147 l +987 147 l +987 0 l +88 0 l +88 168 l +780 973 l +113 973 l +113 1120 l + +465 1262 m +220 1638 l +359 1638 l +539 1393 l +719 1638 l +858 1638 l +613 1262 l +465 1262 l + +ce} _d +/longs{721 0 47 0 760 1556 sc +408 0 m +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c +760 1556 l +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 0 l + +ce} _d +/uni0180{1300 0 32 -29 1188 1556 sc +997 559 m +997 694 969 801 914 878 c +858 955 781 993 684 993 c +587 993 510 955 454 878 c +399 801 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 914 240 c +969 317 997 424 997 559 c + +371 950 m +410 1017 459 1066 518 1098 c +577 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 577 -13 518 20 c +459 52 410 101 371 168 c +371 0 l +186 0 l +186 1284 l +32 1284 l +32 1409 l +186 1409 l +186 1556 l +371 1556 l +371 1409 l +696 1409 l +696 1284 l +371 1284 l +371 950 l + +ce} _d +/uni0181{1505 0 -105 0 1360 1493 sc +503 713 m +503 166 l +827 166 l +936 166 1016 188 1068 233 c +1121 278 1147 347 1147 440 c +1147 533 1121 602 1068 646 c +1016 691 936 713 827 713 c +503 713 l + +503 1327 m +503 877 l +802 877 l +901 877 974 895 1022 932 c +1071 969 1095 1026 1095 1102 c +1095 1177 1071 1234 1022 1271 c +974 1308 901 1327 802 1327 c +503 1327 l + +301 1493 m +817 1493 l +971 1493 1090 1461 1173 1397 c +1256 1333 1298 1242 1298 1124 c +1298 1033 1277 960 1234 906 c +1191 852 1129 818 1046 805 c +1145 784 1222 739 1277 671 c +1332 604 1360 519 1360 418 c +1360 285 1315 182 1224 109 c +1133 36 1004 0 837 0 c +301 0 l +301 1328 l +213 1328 149 1312 110 1280 c +71 1249 51 1198 51 1128 c +51 1079 l +-105 1079 l +-105 1149 l +-105 1270 -73 1357 -9 1411 c +55 1466 158 1493 301 1493 c + +ce} _d +/uni0182{1405 0 201 0 1260 1493 sc +1047 439 m +1047 532 1021 600 968 645 c +916 690 836 713 727 713 c +403 713 l +403 166 l +727 166 l +836 166 916 188 968 233 c +1021 278 1047 347 1047 439 c + +1155 1493 m +1155 1327 l +403 1327 l +403 879 l +737 879 l +904 879 1033 843 1124 770 c +1215 697 1260 587 1260 439 c +1260 292 1216 182 1128 109 c +1041 36 910 0 737 0 c +201 0 l +201 1493 l +1155 1493 l + +ce} _d +/uni0183{1300 0 186 -29 1188 1556 sc +371 950 m +410 1017 459 1066 518 1098 c +577 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 577 -13 518 20 c +459 52 410 101 371 168 c +371 0 l +186 0 l +186 1556 l +1032 1556 l +1032 1390 l +371 1391 l +371 950 l + +997 559 m +997 694 969 801 914 878 c +858 955 781 993 684 993 c +587 993 510 955 454 878 c +399 801 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 914 240 c +969 317 997 424 997 559 c + +ce} _d +/uni0184{1405 0 0 0 1260 1493 sc +1047 439 m +1047 532 1021 601 968 646 c +916 691 836 713 727 713 c +403 713 l +403 166 l +727 166 l +836 166 916 189 968 234 c +1021 279 1047 347 1047 439 c + +403 1493 m +403 879 l +737 879 l +904 879 1033 843 1124 770 c +1215 697 1260 587 1260 439 c +1260 292 1215 182 1124 109 c +1033 36 904 0 737 0 c +201 0 l +201 1092 l +0 1092 l +312 1493 l +403 1493 l + +ce} _d +/uni0185{1300 0 0 -29 1188 1557 sc +371 950 m +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c +371 0 l +186 0 l +186 1092 l +0 1092 l +290 1557 l +371 1557 l +371 950 l + +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +ce} _d +/uni0186{1440 0 115 -29 1319 1520 sc +115 1378 m +184 1425 258 1461 337 1484 c +416 1508 501 1520 590 1520 c +816 1520 994 1451 1124 1312 c +1254 1174 1319 985 1319 745 c +1319 506 1254 317 1124 178 c +994 40 816 -29 590 -29 c +502 -29 418 -17 339 7 c +260 31 186 67 115 115 c +115 326 l +183 263 255 215 332 184 c +409 153 491 137 578 137 c +749 137 879 189 970 293 c +1061 398 1106 548 1106 745 c +1106 942 1061 1093 970 1197 c +879 1302 749 1354 578 1354 c +491 1354 409 1338 332 1307 c +255 1276 183 1228 115 1165 c +115 1378 l + +ce} _d +/uni0187{1430 0 115 -29 1626 1892 sc +1319 1378 m +1319 1165 l +1251 1228 1178 1276 1101 1307 c +1024 1338 943 1354 856 1354 c +685 1354 555 1302 464 1197 c +373 1093 328 942 328 745 c +328 548 373 398 464 293 c +555 189 685 137 856 137 c +943 137 1024 153 1101 184 c +1178 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +618 -29 440 40 310 178 c +180 317 115 506 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +908 1520 992 1508 1096 1484 c +1105 1482 1113 1480 1121 1477 c +1132 1607 1165 1705 1220 1770 c +1288 1851 1398 1892 1549 1892 c +1626 1892 l +1626 1722 l +1563 1722 l +1474 1722 1411 1697 1374 1647 c +1337 1597 1319 1507 1319 1378 c + +ce} _d +/uni0188{1126 0 113 -29 1228 1556 sc +999 1150 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +723 1147 770 1143 817 1135 c +817 1150 l +817 1293 844 1396 897 1460 c +951 1524 1038 1556 1159 1556 c +1228 1556 l +1228 1400 l +1180 1400 l +1110 1400 1062 1384 1037 1351 c +1012 1319 999 1252 999 1150 c + +ce} _d +/uni0189{1587 0 10 0 1466 1493 sc +211 1493 m +627 1493 l +916 1493 1128 1433 1263 1312 c +1398 1192 1466 1004 1466 748 c +1466 491 1398 302 1262 181 c +1127 60 915 0 627 0 c +211 0 l +211 700 l +10 700 l +10 844 l +211 844 l +211 1493 l + +414 1327 m +414 844 l +750 844 l +750 700 l +414 700 l +414 166 l +657 166 l +863 166 1014 213 1109 306 c +1205 399 1253 547 1253 748 c +1253 948 1205 1094 1109 1187 c +1014 1280 863 1327 657 1327 c +414 1327 l + +ce} _d +/uni018A{1677 0 -105 0 1556 1493 sc +503 1327 m +503 166 l +747 166 l +953 166 1104 213 1199 306 c +1295 399 1343 547 1343 748 c +1343 948 1295 1094 1199 1187 c +1104 1280 953 1327 747 1327 c +503 1327 l + +301 1493 m +716 1493 l +1005 1493 1218 1433 1353 1312 c +1488 1192 1556 1004 1556 748 c +1556 491 1488 302 1352 181 c +1216 60 1004 0 716 0 c +301 0 l +301 1328 l +213 1328 149 1312 110 1280 c +71 1249 51 1198 51 1128 c +51 1079 l +-105 1079 l +-105 1149 l +-105 1270 -73 1357 -9 1411 c +55 1466 158 1493 301 1493 c + +ce} _d +/uni018B{1405 0 201 0 1260 1493 sc +414 439 m +414 257 521 166 734 166 c +1058 166 l +1058 713 l +734 713 l +625 713 545 690 492 645 c +440 600 414 532 414 439 c + +414 1493 m +1260 1493 l +1260 0 l +724 0 l +557 0 428 36 337 109 c +246 182 201 292 201 440 c +201 587 246 697 337 770 c +428 843 557 879 724 879 c +1058 879 l +1058 1327 l +414 1327 l +414 1493 l + +ce} _d +/uni018C{1300 0 113 -29 1114 1556 sc +269 1390 m +269 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c +930 1391 l +269 1390 l + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/uni018D{1253 0 113 -426 1141 1123 sc +875 66 m +980 13 1032 -60 1032 -154 c +1032 -335 885 -426 591 -426 c +444 -426 330 -403 250 -356 c +250 -203 l +318 -250 434 -273 598 -273 c +762 -273 844 -234 844 -156 c +844 -95 756 -48 580 -16 c +441 9 333 58 255 133 c +160 223 113 358 113 539 c +113 720 159 862 250 966 c +341 1071 467 1123 626 1123 c +786 1123 912 1071 1003 966 c +1095 862 1141 718 1140 535 c +1140 431 1114 336 1062 249 c +1011 162 948 101 875 66 c + +946 535 m +947 670 918 776 861 852 c +804 929 727 967 628 967 c +529 967 451 928 394 851 c +337 774 308 670 308 540 c +308 411 339 313 402 246 c +465 180 538 143 622 136 c +665 132 703 125 737 116 c +803 144 854 195 891 268 c +928 341 946 430 946 535 c + +ce} _d +/uni018E{1294 0 131 0 1093 1493 sc +1093 1493 m +1093 0 l +131 0 l +131 170 l +891 170 l +891 711 l +180 711 l +180 881 l +891 881 l +891 1323 l +149 1323 l +149 1493 l +1093 1493 l + +ce} _d +/uni018F{1612 0 117 -29 1497 1520 sc +117 780 m +1284 780 l +1284 959 1238 1099 1146 1201 c +1055 1303 918 1354 735 1354 c +642 1354 554 1338 470 1306 c +386 1274 307 1226 232 1163 c +232 1378 l +306 1425 387 1460 474 1484 c +562 1508 655 1520 752 1520 c +985 1520 1168 1452 1299 1315 c +1431 1179 1497 989 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +588 -29 418 44 297 190 c +177 337 117 533 117 780 c + +337 614 m +346 484 392 372 477 277 c +562 182 672 135 807 135 c +942 135 1051 182 1136 277 c +1221 372 1268 484 1277 614 c +337 614 l + +ce} _d +/uni0190{1258 0 164 -29 1147 1520 sc +472 805 m +385 827 317 866 270 923 c +223 980 199 1050 199 1133 c +199 1252 244 1346 333 1415 c +422 1485 544 1520 697 1520 c +756 1520 819 1515 886 1504 c +953 1493 1025 1477 1102 1456 c +1102 1276 l +1026 1301 956 1320 893 1332 c +830 1344 770 1350 715 1350 c +614 1350 536 1329 481 1288 c +427 1247 400 1189 400 1112 c +400 1037 426 980 479 940 c +532 901 608 881 707 881 c +889 881 l +889 715 l +715 715 l +605 715 519 690 457 640 c +396 591 365 522 365 434 c +365 339 398 266 464 216 c +531 166 627 141 754 141 c +827 141 896 149 963 166 c +1030 183 1091 207 1147 240 c +1147 45 l +1076 20 1008 2 941 -10 c +875 -23 811 -29 748 -29 c +561 -29 417 11 316 92 c +215 173 164 287 164 434 c +164 530 191 611 245 676 c +300 741 375 784 472 805 c + +ce} _d +/uni0191{1178 0 -106 -410 1059 1493 sc +201 1493 m +1059 1493 l +1059 1323 l +403 1323 l +403 883 l +995 883 l +995 713 l +403 713 l +403 104 l +403 -76 369 -207 300 -288 c +232 -369 122 -410 -29 -410 c +-106 -410 l +-106 -240 l +-43 -240 l +46 -240 109 -215 146 -165 c +183 -115 201 -25 201 104 c +201 1493 l + +ce} _d +/florin{721 0 -129 -426 760 1556 sc +760 1556 m +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +711 1120 l +711 977 l +408 977 l +408 -68 l +408 -193 379 -283 321 -340 c +263 -397 171 -426 45 -426 c +-129 -426 l +-129 -273 l +47 -273 l +113 -273 159 -260 184 -233 c +210 -206 223 -158 223 -89 c +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c +760 1556 l + +ce} _d +/uni0193{1587 0 115 -29 1687 1892 sc +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +921 1520 978 1516 1032 1507 c +1087 1499 1137 1487 1182 1470 c +1182 1595 1215 1697 1282 1775 c +1349 1853 1459 1892 1610 1892 c +1687 1892 l +1687 1722 l +1624 1722 l +1535 1722 1472 1697 1435 1647 c +1398 1597 1380 1507 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +ce} _d +/uni0194{1406 0 8 -430 1398 1493 sc +703 -259 m +739 -259 772 -248 801 -227 c +820 -214 829 -185 829 -141 c +829 -103 819 -60 798 -11 c +764 70 732 141 703 202 c +674 141 642 70 608 -11 c +587 -60 577 -103 577 -141 c +577 -185 586 -214 605 -227 c +634 -248 667 -259 703 -259 c + +703 631 m +1176 1493 l +1398 1493 l +816 420 l +885 287 942 162 987 46 c +1018 -33 1033 -95 1033 -139 c +1033 -232 1008 -300 958 -343 c +891 -401 806 -430 703 -430 c +600 -430 515 -401 448 -343 c +398 -300 373 -232 373 -139 c +373 -95 388 -33 419 46 c +464 162 521 287 590 420 c +8 1493 l +230 1493 l +703 631 l + +ce} _d +/uni0195{2015 0 186 0 1863 1556 sc +1356 156 m +1455 156 1533 189 1589 254 c +1647 321 1676 407 1676 510 c +1676 658 1651 769 1602 844 c +1561 907 1500 950 1419 972 c +1419 1120 l +1568 1101 1679 1043 1752 948 c +1826 851 1863 711 1863 527 c +1863 364 1815 235 1719 141 c +1623 47 1501 0 1354 0 c +1284 0 l +1159 0 1071 33 1018 100 c +966 167 940 269 940 406 c +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c +1124 406 l +1124 308 1137 242 1162 207 c +1187 173 1235 156 1305 156 c +1356 156 l + +ce} _d +/uni0196{724 0 201 0 710 1493 sc +201 1493 m +403 1493 l +403 514 l +403 385 421 295 458 245 c +495 195 558 170 647 170 c +710 170 l +710 0 l +633 0 l +482 0 372 41 303 122 c +235 203 201 334 201 514 c +201 1493 l + +ce} _d +/uni0197{604 0 10 0 594 1493 sc +201 1493 m +403 1493 l +403 747 l +594 747 l +594 577 l +403 577 l +403 0 l +201 0 l +201 577 l +10 577 l +10 747 l +201 747 l +201 1493 l + +ce} _d +/uni0198{1527 0 201 0 1527 1520 sc +1527 1096 m +1357 1096 l +1357 1217 l +1357 1253 1345 1284 1320 1311 c +1296 1338 1271 1351 1246 1351 c +1199 1351 1164 1339 1139 1316 c +592 797 l +1386 0 l +1120 0 l +403 719 l +403 0 l +201 0 l +201 1493 l +403 1493 l +403 862 l +1023 1441 l +1080 1494 1146 1520 1221 1520 c +1312 1520 1385 1489 1442 1428 c +1499 1367 1527 1300 1527 1227 c +1527 1096 l + +ce} _d +/uni0199{1186 0 185 0 1180 1556 sc +185 1150 m +185 1293 212 1396 266 1460 c +321 1524 408 1556 529 1556 c +720 1556 l +720 1400 l +552 1400 l +482 1400 434 1384 409 1351 c +384 1319 371 1252 371 1150 c +371 637 l +920 1120 l +1155 1120 l +561 596 l +1180 0 l +940 0 l +371 547 l +371 0 l +186 0 l +185 1150 l + +ce} _d +/uni019A{569 0 10 0 554 1556 sc +193 1556 m +377 1556 l +377 844 l +554 844 l +554 700 l +377 700 l +377 0 l +193 0 l +193 700 l +10 700 l +10 844 l +193 844 l +193 1556 l + +ce} _d +/uni019B{1212 0 61 0 1151 1556 sc +61 0 m +552 1074 l +481 1262 l +181 1161 l +148 1260 l +445 1359 l +370 1556 l +570 1556 l +622 1418 l +936 1522 l +969 1425 l +658 1321 l +1151 0 l +956 0 l +642 828 l +256 0 l +61 0 l + +ce} _d +/uni019C{1995 0 178 -29 1831 1493 sc +934 213 m +888 130 833 69 769 30 c +705 -9 630 -29 543 -29 c +426 -29 336 12 273 94 c +210 175 178 291 178 442 c +178 1493 l +381 1493 l +381 452 l +381 328 394 248 420 213 c +458 161 516 135 594 135 c +689 135 765 167 820 230 c +875 293 903 380 903 489 c +903 1493 l +1106 1493 l +1106 452 l +1106 344 1125 264 1163 212 c +1201 161 1260 135 1339 135 c +1421 135 1490 167 1545 230 c +1600 294 1628 380 1628 489 c +1628 1493 l +1831 1493 l +1831 -2 l +1628 -2 l +1628 172 l +1586 103 1536 53 1477 20 c +1418 -13 1349 -29 1268 -29 c +1187 -29 1117 -8 1060 33 c +1003 74 961 134 934 213 c + +ce} _d +/uni019D{1532 0 -106 -410 1331 1493 sc +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 104 l +397 -76 363 -207 294 -288 c +226 -369 116 -410 -35 -410 c +-106 -410 l +-106 -240 l +-43 -240 l +46 -240 109 -215 146 -165 c +183 -115 201 -25 201 104 c +201 1493 l + +ce} _d +/uni019E{1298 0 186 -426 1124 1147 sc +1124 676 m +1124 -426 l +940 -426 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1028 c +1091 948 1124 831 1124 676 c + +ce} _d +/uni019F{1612 0 115 -29 1497 1520 sc +115 745 m +115 981 178 1169 303 1309 c +429 1450 597 1520 806 1520 c +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 746 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 806 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c + +808 1356 m +673 1356 563 1309 478 1214 c +393 1119 346 1007 338 877 c +1278 877 l +1269 1007 1222 1119 1137 1214 c +1052 1309 943 1356 808 1356 c + +328 710 m +333 539 378 400 461 294 c +545 188 660 135 807 135 c +954 135 1068 188 1151 293 c +1234 399 1278 538 1283 710 c +328 710 l + +ce} _d +/Ohorn{1870 0 103 -29 1565 1556 sc +795 1356 m +648 1356 532 1301 445 1192 c +359 1083 316 934 316 745 c +316 557 359 408 445 299 c +532 190 648 135 795 135 c +942 135 1058 190 1143 299 c +1229 408 1272 557 1272 745 c +1272 934 1229 1083 1143 1192 c +1058 1301 942 1356 795 1356 c + +795 1520 m +1004 1520 1172 1450 1297 1309 c +1422 1169 1485 981 1485 745 c +1485 510 1422 322 1297 181 c +1172 41 1004 -29 795 -29 c +585 -29 417 41 291 181 c +166 321 103 509 103 745 c +103 981 166 1169 291 1309 c +417 1450 585 1520 795 1520 c + +1170 1291 m +1170 1410 l +1211 1380 1246 1358 1275 1343 c +1304 1329 1329 1322 1350 1322 c +1381 1322 1404 1331 1419 1349 c +1434 1368 1442 1395 1442 1431 c +1442 1452 1439 1473 1434 1494 c +1429 1515 1422 1535 1412 1556 c +1545 1556 l +1552 1531 1557 1507 1560 1484 c +1563 1461 1565 1440 1565 1420 c +1565 1339 1550 1279 1521 1240 c +1492 1202 1447 1183 1385 1183 c +1353 1183 1319 1192 1284 1209 c +1249 1227 1211 1254 1170 1291 c + +ce} _d +/ohorn{1253 0 118 -29 1235 1259 sc +840 994 m +840 1113 l +881 1083 916 1061 945 1046 c +974 1032 999 1025 1020 1025 c +1051 1025 1074 1034 1089 1052 c +1104 1071 1112 1098 1112 1134 c +1112 1155 1109 1176 1104 1197 c +1099 1218 1092 1238 1082 1259 c +1215 1259 l +1222 1234 1227 1210 1230 1187 c +1233 1164 1235 1143 1235 1123 c +1235 1042 1220 982 1191 943 c +1162 905 1117 886 1055 886 c +1023 886 989 895 954 912 c +919 930 881 957 840 994 c + +632 991 m +533 991 455 952 398 875 c +341 798 312 693 312 559 c +312 425 340 319 397 242 c +454 165 533 127 632 127 c +730 127 808 166 865 243 c +922 320 951 426 951 559 c +951 692 922 797 865 874 c +808 952 730 991 632 991 c + +632 1147 m +792 1147 918 1095 1009 991 c +1100 887 1146 743 1146 559 c +1146 376 1100 232 1009 127 c +918 23 792 -29 632 -29 c +471 -29 345 23 254 127 c +163 232 118 376 118 559 c +118 743 163 887 254 991 c +345 1095 471 1147 632 1147 c + +ce} _d +/uni01A2{1943 0 115 -29 1743 1520 sc +1541 0 m +1541 979 l +1541 1108 1523 1198 1486 1248 c +1449 1298 1390 1323 1309 1323 c +1309 182 l +1184 41 1016 -29 806 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c +882 1520 951 1511 1016 1493 c +1311 1493 l +1462 1493 1572 1452 1640 1371 c +1709 1290 1743 1159 1743 979 c +1743 0 l +1541 0 l + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 746 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +924 135 1025 169 1110 238 c +1110 1286 l +1018 1333 917 1356 807 1356 c + +ce} _d +/uni01A3{1555 0 113 -426 1369 1147 sc +1185 -426 m +1185 714 l +1185 816 1172 883 1147 916 c +1122 948 1074 964 1004 964 c +1004 128 l +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c +694 1147 754 1138 809 1120 c +1025 1120 l +1146 1120 1233 1088 1288 1024 c +1342 960 1369 857 1369 714 c +1369 -426 l +1185 -426 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +713 127 777 146 820 185 c +820 937 l +764 973 700 991 627 991 c + +ce} _d +/uni01A4{1335 0 -105 0 1265 1493 sc +503 1327 m +503 766 l +757 766 l +851 766 924 790 975 839 c +1026 888 1052 957 1052 1047 c +1052 1136 1026 1205 975 1254 c +924 1303 851 1327 757 1327 c +503 1327 l + +301 1493 m +757 1493 l +924 1493 1051 1455 1136 1379 c +1222 1304 1265 1193 1265 1047 c +1265 900 1222 788 1136 713 c +1051 638 924 600 757 600 c +503 600 l +503 0 l +301 0 l +301 1328 l +213 1328 149 1312 110 1280 c +71 1249 51 1198 51 1128 c +51 1079 l +-105 1079 l +-105 1149 l +-105 1270 -73 1357 -9 1411 c +55 1466 158 1493 301 1493 c + +ce} _d +/uni01A5{1300 0 185 -426 1188 1556 sc +371 168 m +371 -426 l +185 -426 l +185 1150 l +185 1293 212 1396 266 1460 c +321 1524 408 1556 529 1556 c +783 1556 l +783 1400 l +552 1400 l +482 1400 434 1384 409 1351 c +384 1319 371 1252 371 1150 c +371 950 l +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c + +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +ce} _d +/uni01A6{1423 0 201 -264 1364 1493 sc +909 436 m +952 421 994 390 1035 342 c +1076 294 1118 228 1159 144 c +1364 -264 l +1147 -264 l +956 119 l +907 219 859 285 812 318 c +766 351 703 367 623 367 c +403 367 l +403 0 l +201 0 l +201 1493 l +403 1493 l +403 1229 l +657 1229 l +828 1229 955 1193 1039 1122 c +1123 1051 1165 943 1165 799 c +1165 705 1143 627 1099 565 c +1056 503 992 460 909 436 c + +403 1063 m +403 504 l +657 504 l +751 504 824 528 875 577 c +926 626 952 695 952 784 c +952 873 926 942 875 990 c +824 1039 752 1063 657 1063 c +403 1063 l + +ce} _d +/uni01A7{1300 0 114 -29 1165 1520 sc +204 1444 m +356 1495 508 1520 659 1520 c +810 1520 932 1482 1025 1406 c +1118 1330 1165 1230 1165 1107 c +1165 984 1131 891 1064 827 c +997 763 888 716 737 686 c +614 662 l +511 641 437 610 392 569 c +347 528 325 468 325 389 c +325 310 356 248 419 203 c +482 158 571 135 686 135 c +836 135 994 181 1159 274 c +1159 66 l +986 3 829 -29 686 -29 c +499 -29 356 8 259 83 c +162 158 114 267 114 412 c +114 533 150 630 221 702 c +293 775 404 825 555 854 c +677 879 l +788 900 863 927 903 960 c +942 994 962 1047 962 1119 c +962 1192 932 1249 873 1292 c +813 1335 724 1356 607 1356 c +490 1356 356 1320 204 1247 c +204 1444 l + +ce} _d +/uni01A8{1067 0 100 -29 956 1147 sc +160 1087 m +211 1107 266 1122 326 1132 c +386 1142 450 1147 518 1147 c +655 1147 761 1118 836 1061 c +911 1004 948 922 948 817 c +948 730 923 662 872 612 c +821 563 739 526 625 502 c +561 487 l +447 462 373 437 339 410 c +306 383 289 345 289 295 c +289 241 312 199 359 169 c +406 140 472 125 559 125 c +624 125 689 134 755 151 c +821 169 888 196 956 231 c +956 41 l +884 18 815 0 750 -11 c +685 -23 623 -29 563 -29 c +419 -29 306 1 223 62 c +141 123 100 205 100 309 c +100 400 127 471 182 522 c +237 573 330 613 461 641 c +524 655 l +623 677 688 700 721 724 c +754 749 770 782 770 825 c +770 881 747 923 701 951 c +656 979 587 993 496 993 c +436 993 378 986 322 973 c +266 960 212 940 160 913 c +160 1087 l + +ce} _d +/uni01A9{1294 0 201 0 1163 1493 sc +433 170 m +1163 170 l +1163 0 l +201 0 l +201 170 l +680 794 l +201 1323 l +201 1493 l +1145 1493 l +1145 1323 l +433 1323 l +912 798 l +433 170 l + +ce} _d +/uni01AA{688 0 -270 -426 727 1556 sc +375 1130 m +375 -89 l +375 -158 388 -206 413 -233 c +439 -260 485 -273 551 -273 c +727 -273 l +727 -426 l +553 -426 l +427 -426 335 -397 277 -340 c +219 -283 190 -193 190 -68 c +190 1130 l +0 1130 l +-180 1130 -270 1200 -270 1340 c +-270 1484 -169 1556 33 1556 c +154 1556 241 1524 296 1460 c +331 1417 355 1359 367 1285 c +558 1285 l +558 1130 l +375 1130 l + +180 1284 m +175 1313 166 1336 155 1352 c +133 1384 85 1400 12 1400 c +-71 1400 -113 1378 -115 1335 c +-117 1301 -78 1284 1 1284 c +180 1284 l + +ce} _d +/uni01AB{803 0 55 -426 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 -20 l +754 -163 727 -266 672 -330 c +617 -394 530 -426 410 -426 c +340 -426 l +340 -270 l +388 -270 l +459 -270 507 -254 532 -222 c +557 -189 570 -122 570 -20 c +570 0 l +428 0 329 26 273 79 c +218 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/uni01AC{1251 0 24 0 1257 1493 sc +430 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +430 1323 l +342 1323 278 1308 239 1278 c +200 1248 180 1198 180 1128 c +180 1079 l +24 1079 l +24 1149 l +24 1270 56 1357 120 1411 c +184 1466 287 1493 430 1493 c + +ce} _d +/uni01AD{803 0 55 0 754 1556 sc +375 1219 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1198 l +190 1323 219 1413 277 1470 c +335 1527 427 1556 553 1556 c +727 1556 l +727 1403 l +551 1403 l +485 1403 439 1389 413 1362 c +388 1335 375 1288 375 1219 c + +ce} _d +/uni01AE{1251 0 -6 -410 1257 1493 sc +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 104 l +727 -25 745 -115 782 -165 c +819 -215 882 -240 971 -240 c +1034 -240 l +1034 -410 l +956 -410 l +805 -410 695 -369 626 -288 c +558 -207 524 -76 524 104 c +524 1323 l +-6 1323 l +-6 1493 l + +ce} _d +/Uhorn{1757 0 173 -9 1631 1556 sc +173 1513 m +376 1513 l +376 606 l +376 446 405 331 463 260 c +521 190 615 155 745 155 c +874 155 968 190 1026 260 c +1084 331 1113 446 1113 606 c +1113 1513 l +1316 1513 l +1316 581 l +1316 386 1268 239 1171 140 c +1075 41 933 -9 745 -9 c +556 -9 414 41 317 140 c +221 239 173 386 173 581 c +173 1513 l + +1236 1291 m +1236 1410 l +1277 1380 1312 1358 1341 1343 c +1370 1329 1395 1322 1416 1322 c +1447 1322 1470 1331 1485 1349 c +1500 1368 1508 1395 1508 1431 c +1508 1452 1505 1473 1500 1494 c +1495 1515 1488 1535 1478 1556 c +1611 1556 l +1618 1531 1623 1507 1626 1484 c +1629 1461 1631 1440 1631 1420 c +1631 1339 1616 1279 1587 1240 c +1558 1202 1513 1183 1451 1183 c +1419 1183 1385 1192 1350 1209 c +1315 1227 1277 1254 1236 1291 c + +ce} _d +/uhorn{1298 0 176 -29 1385 1259 sc +990 994 m +990 1113 l +1031 1083 1066 1061 1095 1046 c +1124 1032 1149 1025 1170 1025 c +1201 1025 1224 1034 1239 1052 c +1254 1071 1262 1098 1262 1134 c +1262 1155 1259 1176 1254 1197 c +1249 1218 1242 1238 1232 1259 c +1365 1259 l +1372 1234 1377 1210 1380 1187 c +1383 1164 1385 1143 1385 1123 c +1385 1042 1370 982 1341 943 c +1312 905 1267 886 1205 886 c +1173 886 1139 895 1104 912 c +1069 930 1031 957 990 994 c + +176 442 m +176 1120 l +360 1120 l +360 449 l +360 343 381 263 422 210 c +463 157 525 131 608 131 c +707 131 786 163 843 226 c +901 289 930 376 930 485 c +930 1120 l +1114 1120 l +1114 0 l +930 0 l +930 172 l +885 104 833 53 774 20 c +715 -13 647 -29 569 -29 c +440 -29 343 11 276 91 c +209 171 176 288 176 442 c + +639 1147 m +639 1147 l + +ce} _d +/uni01B1{1565 0 78 -29 1487 1482 sc +1487 1304 m +1167 1304 l +1274 1210 1352 1114 1399 1015 c +1446 916 1470 802 1470 673 c +1470 467 1406 298 1277 167 c +1149 36 984 -29 782 -29 c +579 -29 413 37 285 168 c +158 299 94 471 94 683 c +94 806 118 917 166 1016 c +215 1115 292 1211 397 1304 c +78 1304 l +78 1482 l +678 1482 l +678 1304 l +559 1239 467 1152 400 1041 c +334 930 301 808 301 673 c +301 514 345 385 433 288 c +522 191 638 143 782 143 c +926 143 1042 191 1130 288 c +1218 385 1262 513 1262 673 c +1262 808 1229 930 1163 1041 c +1097 1152 1005 1239 887 1304 c +887 1482 l +1487 1482 l +1487 1304 l + +ce} _d +/uni01B2{1476 0 201 -31 1398 1493 sc +710 141 m +854 141 970 189 1058 286 c +1146 383 1190 511 1190 671 c +1190 806 1157 928 1091 1039 c +1048 1112 989 1177 916 1235 c +916 1303 l +1095 1302 l +1202 1208 1280 1112 1327 1013 c +1374 914 1398 800 1398 671 c +1398 465 1334 296 1206 165 c +1079 34 913 -31 710 -31 c +633 -29 l +478 -25 369 16 304 93 c +235 174 201 305 201 485 c +201 1493 l +403 1493 l +403 485 l +403 356 421 266 458 216 c +495 166 558 141 647 141 c +710 141 l + +ce} _d +/uni01B3{1523 0 -4 0 1520 1520 sc +1239 1351 m +1201 1351 1175 1346 1161 1337 c +1136 1321 1118 1305 1107 1288 c +727 711 l +727 0 l +524 0 l +524 711 l +-4 1493 l +213 1493 l +627 879 l +961 1384 l +1021 1475 1105 1520 1214 1520 c +1305 1520 1378 1489 1435 1428 c +1492 1367 1520 1300 1520 1227 c +1520 1096 l +1350 1096 l +1350 1217 l +1350 1253 1338 1284 1313 1311 c +1289 1338 1264 1351 1239 1351 c + +ce} _d +/uni01B4{1496 0 61 -426 1496 1147 sc +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +888 948 l +902 983 925 1017 957 1051 c +1016 1115 1090 1147 1180 1147 c +1267 1147 1342 1115 1403 1051 c +1465 988 1496 911 1496 822 c +1496 708 l +1318 708 l +1318 822 l +1318 861 1304 894 1277 922 c +1250 950 1218 964 1180 964 c +1142 964 1109 950 1082 922 c +1069 909 1059 894 1052 877 c +659 -104 l + +ce} _d +/uni01B5{1403 0 92 0 1311 1493 sc +115 1493 m +1288 1493 l +1288 1339 l +888 844 l +1169 844 l +1169 700 l +772 700 l +344 170 l +1311 170 l +1311 0 l +92 0 l +92 154 l +533 700 l +234 700 l +234 844 l +649 844 l +1036 1323 l +115 1323 l +115 1493 l + +ce} _d +/uni01B6{1075 0 88 0 987 1120 sc +113 1120 m +987 1120 l +987 952 l +736 660 l +930 660 l +930 516 l +612 516 l +295 147 l +987 147 l +987 0 l +88 0 l +88 168 l +387 516 l +175 516 l +175 660 l +511 660 l +780 973 l +113 973 l +113 1120 l + +ce} _d +/uni01B7{1364 0 160 -63 1272 1493 sc +680 107 m +808 107 905 132 971 182 c +1038 232 1071 305 1071 400 c +1071 489 1040 558 979 607 c +917 656 831 681 721 681 c +547 681 l +547 833 l +932 1323 l +160 1323 l +160 1493 l +1184 1493 l +1184 1339 l +773 849 l +844 849 922 830 1007 793 c +1072 765 1134 715 1191 642 c +1245 573 1272 492 1272 400 c +1272 253 1221 139 1120 58 c +1019 -23 875 -63 688 -63 c +609 -63 527 -57 444 -44 c +360 -32 273 -14 184 11 c +184 206 l +255 173 332 149 417 132 c +501 115 589 107 680 107 c + +ce} _d +/uni01B8{1364 0 92 -63 1204 1493 sc +684 107 m +775 107 863 115 947 132 c +1032 149 1109 173 1180 206 c +1180 11 l +1091 -14 1004 -32 920 -44 c +837 -57 755 -63 676 -63 c +489 -63 345 -23 244 58 c +143 139 92 253 92 400 c +92 492 119 573 173 642 c +230 715 292 765 357 793 c +442 830 520 849 591 849 c +180 1339 l +180 1493 l +1204 1493 l +1204 1323 l +432 1323 l +817 833 l +817 681 l +643 681 l +533 681 447 656 385 607 c +324 558 293 489 293 400 c +293 305 326 232 393 182 c +459 132 556 107 684 107 c + +ce} _d +/uni01B9{1183 0 104 -436 1087 1120 sc +603 476 m +192 952 l +192 1120 l +1066 1120 l +1066 973 l +399 973 l +829 474 l +829 308 l +655 308 l +545 308 459 283 397 234 c +336 184 305 115 305 27 c +305 -68 338 -141 405 -191 c +471 -241 567 -266 694 -266 c +767 -266 836 -258 903 -241 c +970 -224 1031 -200 1087 -167 c +1087 -362 l +1016 -387 948 -405 881 -418 c +815 -430 751 -436 688 -436 c +501 -436 357 -396 256 -315 c +155 -234 104 -120 104 27 c +104 119 131 200 185 269 c +242 342 304 392 369 420 c +454 457 532 476 603 476 c + +ce} _d +/uni01BA{1075 0 113 -426 1000 1120 sc +639 -274 m +695 -274 749 -267 800 -253 c +853 -239 906 -217 960 -188 c +960 -367 l +902 -388 846 -403 793 -412 c +736 -421 675 -426 610 -426 c +290 -426 130 -332 130 -145 c +130 14 265 111 536 146 c +725 171 820 216 820 281 c +820 364 742 405 585 405 c +585 405 488 405 295 405 c +780 973 l +113 973 l +113 1120 l +987 1120 l +987 952 l +633 540 l +878 540 1000 464 1000 311 c +1000 127 845 19 536 -14 c +385 -30 310 -70 310 -135 c +310 -228 420 -274 639 -274 c + +ce} _d +/uni01BB{1303 0 150 0 1098 1520 sc +393 170 m +1098 170 l +1098 0 l +150 0 l +150 170 l +464 490 l +579 607 l +234 607 l +234 751 l +716 751 l +779 824 823 884 848 932 c +874 983 887 1032 887 1081 c +887 1160 859 1225 804 1275 c +748 1325 675 1350 586 1350 c +523 1350 456 1339 386 1317 c +315 1295 240 1262 160 1217 c +160 1421 l +241 1454 317 1478 388 1495 c +459 1512 523 1520 582 1520 c +737 1520 860 1481 952 1404 c +1044 1327 1090 1223 1090 1094 c +1090 1033 1079 975 1056 920 c +1035 871 1000 815 951 751 c +1055 751 l +1055 607 l +819 607 l +771 558 l +393 170 l + +ce} _d +/uni01BC{1364 0 93 -63 1273 1493 sc +294 400 m +294 205 424 107 683 107 c +942 107 1072 205 1072 400 c +1072 489 1041 558 979 607 c +918 656 832 681 722 681 c +201 681 l +201 1493 l +1130 1493 l +1130 1323 l +403 1323 l +403 848 l +774 849 l +945 850 1084 781 1192 642 c +1246 573 1273 490 1273 394 c +1273 298 1253 221 1214 164 c +1186 123 l +1173 104 1151 82 1121 58 c +1020 -23 874 -63 683 -63 c +492 -63 346 -23 245 58 c +144 139 93 253 93 400 c +294 400 l + +ce} _d +/uni01BD{1183 0 104 -436 1087 1120 sc +104 -167 m +217 -233 343 -266 480 -266 c +751 -266 886 -168 886 27 c +886 116 855 185 793 234 c +732 283 646 308 536 308 c +183 308 l +183 1120 l +976 1120 l +976 950 l +367 950 l +367 476 l +588 476 l +658 476 731 459 807 426 c +883 393 949 342 1004 271 c +1059 200 1087 117 1087 21 c +1087 -75 1067 -152 1028 -209 c +1000 -250 l +987 -269 965 -291 935 -315 c +834 -396 690 -436 503 -436 c +378 -436 245 -411 104 -362 c +104 -167 l + +ce} _d +/uni01BE{1045 0 88 -29 933 1438 sc +545 998 m +543 873 l +656 853 750 803 825 724 c +897 647 933 552 933 439 c +933 315 896 211 823 128 c +732 23 594 -29 411 -29 c +354 -29 299 -23 245 -11 c +191 0 139 18 88 41 c +88 213 l +137 186 189 165 245 149 c +297 134 350 127 403 127 c +522 127 615 165 681 241 c +719 284 738 350 739 439 c +739 504 720 562 681 613 c +624 689 531 727 403 727 c +341 727 l +343 998 l +114 998 l +114 1162 l +343 1162 l +343 1438 l +545 1438 l +545 1162 l +776 1162 l +776 998 l +545 998 l + +ce} _d +/uni01BF{1300 0 186 -426 1188 1147 sc +371 -122 m +371 -426 l +186 -426 l +186 1120 l +371 1120 l +371 950 l +406 994 468 1043 558 1098 c +612 1131 709 1147 849 1147 c +972 1147 1059 1110 1110 1035 c +1162 960 1188 871 1188 769 c +1188 486 916 189 371 -122 c + +371 60 m +788 327 997 543 997 709 c +997 798 978 864 940 908 c +903 951 844 973 764 973 c +617 973 486 893 371 734 c +371 60 l + +ce} _d +/uni01C0{604 0 201 -426 403 1493 sc +201 1493 m +403 1493 l +403 -426 l +201 -426 l +201 1493 l + +ce} _d +/uni01C1{1008 0 201 -426 807 1493 sc +605 1493 m +807 1493 l +807 -426 l +605 -426 l +605 1493 l + +201 1493 m +403 1493 l +403 -426 l +201 -426 l +201 1493 l + +ce} _d +/uni01C2{940 0 20 -426 924 1493 sc +371 1493 m +573 1493 l +573 876 l +924 876 l +924 708 l +573 708 l +573 468 l +924 468 l +924 298 l +573 298 l +573 -426 l +371 -426 l +371 298 l +20 298 l +20 468 l +371 468 l +371 708 l +20 708 l +20 876 l +371 876 l +371 1493 l + +ce} _d +/uni01C3{605 0 201 0 404 1493 sc +201 254 m +404 254 l +404 0 l +201 0 l +201 254 l + +201 1493 m +404 1493 l +404 838 l +384 481 l +222 481 l +201 838 l +201 1493 l + +ce} _d +/uni01C4{2912 0 201 0 2768 1901 sc +1572 1493 m +2745 1493 l +2745 1339 l +1801 170 l +2768 170 l +2768 0 l +1549 0 l +1549 154 l +2493 1323 l +1572 1323 l +1572 1493 l + +2065 1635 m +1854 1901 l +1993 1901 l +2159 1723 l +2325 1901 l +2464 1901 l +2253 1635 l +2065 1635 l + +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/uni01C5{2660 0 201 0 2480 1638 sc +1606 1120 m +2480 1120 l +2480 952 l +1788 147 l +2480 147 l +2480 0 l +1581 0 l +1581 168 l +2273 973 l +1606 973 l +1606 1120 l + +1958 1262 m +1713 1638 l +1852 1638 l +2032 1393 l +2212 1638 l +2351 1638 l +2106 1262 l +1958 1262 l + +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/uni01C6{2364 0 113 -29 2193 1638 sc +1319 1120 m +2193 1120 l +2193 952 l +1501 147 l +2193 147 l +2193 0 l +1294 0 l +1294 168 l +1986 973 l +1319 973 l +1319 1120 l + +1671 1262 m +1426 1638 l +1565 1638 l +1745 1393 l +1925 1638 l +2064 1638 l +1819 1262 l +1671 1262 l + +930 950 m +930 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/uni01C7{1711 0 201 -410 1572 1493 sc +1370 1493 m +1572 1493 l +1572 104 l +1572 -76 1538 -207 1469 -288 c +1401 -369 1291 -410 1140 -410 c +1063 -410 l +1063 -240 l +1126 -240 l +1215 -240 1278 -215 1315 -165 c +1352 -115 1370 -25 1370 104 c +1370 1493 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/uni01C8{1611 0 201 -426 1502 1556 sc +1318 1120 m +1502 1120 l +1502 -20 l +1502 -163 1475 -266 1420 -330 c +1366 -394 1279 -426 1158 -426 c +1088 -426 l +1088 -270 l +1137 -270 l +1207 -270 1255 -254 1280 -221 c +1305 -189 1318 -122 1318 -20 c +1318 1120 l + +1318 1556 m +1502 1556 l +1502 1323 l +1318 1323 l +1318 1556 l + +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/uni01C9{935 0 193 -426 751 1556 sc +567 1120 m +751 1120 l +751 -20 l +751 -163 724 -266 669 -330 c +615 -394 528 -426 407 -426 c +337 -426 l +337 -270 l +386 -270 l +456 -270 504 -254 529 -221 c +554 -189 567 -122 567 -20 c +567 1120 l + +567 1556 m +751 1556 l +751 1323 l +567 1323 l +567 1556 l + +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/uni01CA{1907 0 201 -410 1778 1493 sc +1576 1493 m +1778 1493 l +1778 104 l +1778 -76 1744 -207 1675 -288 c +1607 -369 1497 -410 1346 -410 c +1269 -410 l +1269 -240 l +1332 -240 l +1421 -240 1484 -215 1521 -165 c +1558 -115 1576 -25 1576 104 c +1576 1493 l + +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/uni01CB{1892 0 201 -426 1719 1556 sc +1535 1120 m +1719 1120 l +1719 -20 l +1719 -163 1692 -266 1637 -330 c +1583 -394 1496 -426 1375 -426 c +1305 -426 l +1305 -270 l +1354 -270 l +1424 -270 1472 -254 1497 -221 c +1522 -189 1535 -122 1535 -20 c +1535 1120 l + +1535 1556 m +1719 1556 l +1719 1323 l +1535 1323 l +1535 1556 l + +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/uni01CC{1633 0 186 -426 1502 1556 sc +1318 1120 m +1502 1120 l +1502 -20 l +1502 -163 1475 -266 1420 -330 c +1366 -394 1279 -426 1158 -426 c +1088 -426 l +1088 -270 l +1137 -270 l +1207 -270 1255 -254 1280 -221 c +1305 -189 1318 -122 1318 -20 c +1318 1120 l + +1318 1556 m +1502 1556 l +1502 1323 l +1318 1323 l +1318 1556 l + +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/uni01CD{1401 0 16 0 1384 1901 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +608 1635 m +397 1901 l +536 1901 l +702 1723 l +868 1901 l +1007 1901 l +796 1635 l +608 1635 l + +ce} _d +/uni01CE{1255 0 123 -29 1069 1638 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +528 1262 m +283 1638 l +422 1638 l +602 1393 l +782 1638 l +921 1638 l +676 1262 l +528 1262 l + +ce} _d +/uni01CF{604 0 -2 0 608 1901 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +209 1635 m +-2 1901 l +137 1901 l +303 1723 l +469 1901 l +608 1901 l +397 1635 l +209 1635 l + +ce} _d +/uni01D0{569 0 -32 0 606 1638 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +213 1262 m +-32 1638 l +107 1638 l +287 1393 l +467 1638 l +606 1638 l +361 1262 l +213 1262 l + +ce} _d +/uni01D1{1612 0 115 -29 1497 1901 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +713 1635 m +502 1901 l +641 1901 l +807 1723 l +973 1901 l +1112 1901 l +901 1635 l +713 1635 l + +ce} _d +/uni01D2{1253 0 113 -29 1141 1638 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +556 1262 m +311 1638 l +450 1638 l +630 1393 l +810 1638 l +949 1638 l +704 1262 l +556 1262 l + +ce} _d +/uni01D3{1499 0 178 -29 1321 1901 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +664 1635 m +453 1901 l +592 1901 l +758 1723 l +924 1901 l +1063 1901 l +852 1635 l +664 1635 l + +ce} _d +/uni01D4{1298 0 174 -29 1112 1638 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +556 1262 m +311 1638 l +450 1638 l +630 1393 l +810 1638 l +949 1638 l +704 1262 l +556 1262 l + +ce} _d +/uni01D5{1499 0 178 -29 1321 2099 sc +450 2099 m +1048 2099 l +1048 1951 l +450 1951 l +450 2099 l + +842 1838 m +1045 1838 l +1045 1635 l +842 1635 l +842 1838 l + +451 1838 m +654 1838 l +654 1635 l +451 1635 l +451 1838 l + +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +ce} _d +/uni01D6{1298 0 174 -29 1112 1841 sc +336 1841 m +934 1841 l +934 1693 l +336 1693 l +336 1841 l + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +729 1552 m +932 1552 l +932 1350 l +729 1350 l +729 1552 l + +338 1552 m +541 1552 l +541 1350 l +338 1350 l +338 1552 l + +ce} _d +/uni01D7{1499 0 178 -29 1321 2138 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +861 2138 m +1046 2138 l +818 1874 l +665 1874 l +861 2138 l + +848 1838 m +1051 1838 l +1051 1635 l +848 1635 l +848 1838 l + +457 1838 m +660 1838 l +660 1635 l +457 1635 l +457 1838 l + +ce} _d +/uni01D8{1298 0 174 -29 1112 1826 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +741 1826 m +926 1826 l +698 1562 l +545 1562 l +741 1826 l + +728 1526 m +931 1526 l +931 1323 l +728 1323 l +728 1526 l + +337 1526 m +540 1526 l +540 1323 l +337 1323 l +337 1526 l + +ce} _d +/uni01D9{1499 0 178 -29 1321 2138 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +654 1872 m +443 2138 l +582 2138 l +748 1960 l +914 2138 l +1053 2138 l +842 1872 l +654 1872 l + +842 1838 m +1045 1838 l +1045 1635 l +842 1635 l +842 1838 l + +451 1838 m +654 1838 l +654 1635 l +451 1635 l +451 1838 l + +ce} _d +/uni01DA{1298 0 174 -29 1112 1826 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +546 1560 m +335 1826 l +474 1826 l +640 1648 l +806 1826 l +945 1826 l +734 1560 l +546 1560 l + +734 1526 m +937 1526 l +937 1323 l +734 1323 l +734 1526 l + +343 1526 m +546 1526 l +546 1323 l +343 1323 l +343 1526 l + +ce} _d +/uni01DB{1499 0 178 -29 1321 2144 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +643 2144 m +839 1880 l +686 1880 l +456 2144 l +643 2144 l + +842 1844 m +1045 1844 l +1045 1641 l +842 1641 l +842 1844 l + +451 1844 m +654 1844 l +654 1641 l +451 1641 l +451 1844 l + +ce} _d +/uni01DC{1298 0 174 -29 1112 1826 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +529 1826 m +725 1562 l +572 1562 l +342 1826 l +529 1826 l + +728 1526 m +931 1526 l +931 1323 l +728 1323 l +728 1526 l + +337 1526 m +540 1526 l +540 1323 l +337 1323 l +337 1526 l + +ce} _d +/uni01DD{1260 0 113 -29 1151 1147 sc +113 512 m +113 602 l +959 602 l +951 729 913 825 844 891 c +776 958 681 991 559 991 c +488 991 420 982 353 965 c +287 948 221 922 156 887 c +156 1061 l +222 1089 290 1110 359 1125 c +428 1140 499 1147 570 1147 c +749 1147 890 1095 994 991 c +1099 887 1151 746 1151 569 c +1151 386 1101 240 1002 132 c +903 25 770 -29 602 -29 c +451 -29 332 19 244 116 c +157 213 113 345 113 512 c + +297 458 m +298 357 326 277 381 217 c +436 157 509 127 600 127 c +703 127 785 156 846 214 c +908 272 944 354 953 459 c +297 458 l + +ce} _d +/uni01DE{1401 0 16 0 1384 2099 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +402 2099 m +1000 2099 l +1000 1951 l +402 1951 l +402 2099 l + +794 1838 m +997 1838 l +997 1635 l +794 1635 l +794 1838 l + +403 1838 m +606 1838 l +606 1635 l +403 1635 l +403 1838 l + +ce} _d +/uni01DF{1255 0 123 -29 1069 1841 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +688 1552 m +891 1552 l +891 1350 l +688 1350 l +688 1552 l + +297 1552 m +500 1552 l +500 1350 l +297 1350 l +297 1552 l + +295 1841 m +893 1841 l +893 1693 l +295 1693 l +295 1841 l + +ce} _d +/uni01E0{1401 0 16 0 1384 2099 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +598 1835 m +802 1835 l +802 1630 l +598 1630 l +598 1835 l + +402 2099 m +1000 2099 l +1000 1951 l +402 1951 l +402 2099 l + +ce} _d +/uni01E1{1255 0 123 -29 1069 1780 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +489 1516 m +693 1516 l +693 1311 l +489 1311 l +489 1516 l + +293 1780 m +891 1780 l +891 1632 l +293 1632 l +293 1780 l + +ce} _d +/uni01E2{1995 0 8 0 1864 1844 sc +940 1844 m +1538 1844 l +1538 1696 l +940 1696 l +940 1844 l + +1845 1493 m +1845 1323 l +1104 1323 l +1104 881 l +1815 881 l +1815 711 l +1104 711 l +1104 170 l +1864 170 l +1864 0 l +901 0 l +901 383 l +373 383 l +213 0 l +8 0 l +633 1493 l +1845 1493 l + +772 1335 m +442 551 l +901 551 l +901 1335 l +772 1335 l + +ce} _d +/uni01E3{2011 0 123 -29 1903 1522 sc +701 1522 m +1299 1522 l +1299 1374 l +701 1374 l +701 1522 l + +1718 660 m +1717 761 1689 841 1634 901 c +1579 961 1506 991 1415 991 c +1313 991 1231 962 1169 904 c +1108 846 1072 764 1063 659 c +1718 660 l + +995 963 m +1044 1023 1104 1069 1175 1100 c +1246 1131 1325 1147 1413 1147 c +1564 1147 1683 1098 1771 1001 c +1859 904 1903 773 1903 606 c +1903 516 l +1057 516 l +1065 389 1103 292 1171 225 c +1239 158 1334 125 1456 125 c +1525 125 1593 134 1660 151 c +1727 169 1793 196 1860 231 c +1860 57 l +1793 29 1725 8 1656 -7 c +1587 -22 1517 -29 1446 -29 c +1335 -29 1238 -9 1155 31 c +1072 72 1005 132 954 211 c +905 131 845 71 773 31 c +701 -9 617 -29 522 -29 c +396 -29 298 2 228 64 c +158 127 123 214 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +681 1147 763 1131 834 1099 c +905 1067 959 1022 995 963 c + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +ce} _d +/uni01E4{1587 0 115 -29 1540 1520 sc +1419 780 m +1419 482 l +1540 482 l +1540 394 l +1419 394 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c +1219 394 l +966 394 l +966 482 l +1219 482 l +1219 614 l +889 614 l +889 780 l +1419 780 l + +ce} _d +/uni01E5{1300 0 113 -426 1274 1147 sc +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 66 1108 -0 1095 -59 c +1274 -59 l +1274 -129 l +1076 -129 l +1055 -191 1026 -243 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +854 -164 867 -147 878 -128 c +242 -128 l +242 -59 l +908 -59 l +923 -12 930 43 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/Gcaron{1587 0 115 -29 1419 1901 sc +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +748 1635 m +537 1901 l +676 1901 l +842 1723 l +1008 1901 l +1147 1901 l +936 1635 l +748 1635 l + +ce} _d +/gcaron{1300 0 113 -426 1114 1635 sc +512 1259 m +267 1635 l +406 1635 l +586 1390 l +766 1635 l +905 1635 l +660 1259 l +512 1259 l + +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/uni01E8{1343 0 201 0 1386 1901 sc +580 1635 m +369 1901 l +508 1901 l +674 1723 l +840 1901 l +979 1901 l +768 1635 l +580 1635 l + +201 1493 m +403 1493 l +403 862 l +1073 1493 l +1333 1493 l +592 797 l +1386 0 l +1120 0 l +403 719 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/uni01E9{1186 0 -23 0 1180 1901 sc +186 1556 m +371 1556 l +371 637 l +920 1120 l +1155 1120 l +561 596 l +1180 0 l +940 0 l +371 547 l +371 0 l +186 0 l +186 1556 l + +188 1635 m +-23 1901 l +116 1901 l +282 1723 l +448 1901 l +587 1901 l +376 1635 l +188 1635 l + +ce} _d +/uni01EA{1612 0 115 -395 1497 1520 sc +748 0 m +867 0 l +837 -41 815 -76 800 -105 c +786 -134 779 -159 779 -180 c +779 -211 788 -234 806 -249 c +825 -264 852 -272 888 -272 c +909 -272 930 -269 951 -264 c +972 -259 992 -252 1013 -242 c +1013 -375 l +988 -382 964 -387 941 -390 c +918 -393 897 -395 877 -395 c +796 -395 736 -380 697 -351 c +659 -322 640 -277 640 -215 c +640 -183 649 -149 666 -114 c +684 -79 711 -41 748 0 c + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/uni01EB{1253 0 113 -395 1141 1147 sc +568 0 m +687 0 l +657 -41 635 -76 620 -105 c +606 -134 599 -159 599 -180 c +599 -211 608 -234 626 -249 c +645 -264 672 -272 708 -272 c +729 -272 750 -269 771 -264 c +792 -259 812 -252 833 -242 c +833 -375 l +808 -382 784 -387 761 -390 c +738 -393 717 -395 697 -395 c +616 -395 556 -380 517 -351 c +479 -322 460 -277 460 -215 c +460 -183 469 -149 486 -114 c +504 -79 531 -41 568 0 c + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/uni01EC{1612 0 115 -395 1497 1841 sc +508 1841 m +1106 1841 l +1106 1693 l +508 1693 l +508 1841 l + +748 0 m +867 0 l +837 -41 815 -76 800 -105 c +786 -134 779 -159 779 -180 c +779 -211 788 -234 806 -249 c +825 -264 852 -272 888 -272 c +909 -272 930 -269 951 -264 c +972 -259 992 -252 1013 -242 c +1013 -375 l +988 -382 964 -387 941 -390 c +918 -393 897 -395 877 -395 c +796 -395 736 -380 697 -351 c +659 -322 640 -277 640 -215 c +640 -183 649 -149 666 -114 c +684 -79 711 -41 748 0 c + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/uni01ED{1253 0 113 -395 1141 1525 sc +328 1525 m +926 1525 l +926 1377 l +328 1377 l +328 1525 l + +568 0 m +687 0 l +657 -41 635 -76 620 -105 c +606 -134 599 -159 599 -180 c +599 -211 608 -234 626 -249 c +645 -264 672 -272 708 -272 c +729 -272 750 -269 771 -264 c +792 -259 812 -252 833 -242 c +833 -375 l +808 -382 784 -387 761 -390 c +738 -393 717 -395 697 -395 c +616 -395 556 -380 517 -351 c +479 -322 460 -277 460 -215 c +460 -183 469 -149 486 -114 c +504 -79 531 -41 568 0 c + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/uni01EE{1364 0 160 -63 1272 1901 sc +608 1635 m +397 1901 l +536 1901 l +702 1723 l +868 1901 l +1007 1901 l +796 1635 l +608 1635 l + +680 107 m +808 107 905 132 971 182 c +1038 232 1071 305 1071 400 c +1071 489 1040 558 979 607 c +917 656 831 681 721 681 c +547 681 l +547 833 l +932 1323 l +160 1323 l +160 1493 l +1184 1493 l +1184 1339 l +773 849 l +844 849 922 830 1007 793 c +1072 765 1134 715 1191 642 c +1245 573 1272 492 1272 400 c +1272 253 1221 139 1120 58 c +1019 -23 875 -63 688 -63 c +609 -63 527 -57 444 -44 c +360 -32 273 -14 184 11 c +184 206 l +255 173 332 149 417 132 c +501 115 589 107 680 107 c + +ce} _d +/uni01EF{1183 0 88 -436 1071 1638 sc +465 1262 m +220 1638 l +359 1638 l +539 1393 l +719 1638 l +858 1638 l +613 1262 l +465 1262 l + +572 476 m +643 476 721 457 806 420 c +871 392 933 342 990 269 c +1044 200 1071 119 1071 27 c +1071 -120 1020 -234 919 -315 c +818 -396 674 -436 487 -436 c +424 -436 360 -430 294 -418 c +227 -405 159 -387 88 -362 c +88 -167 l +144 -200 205 -224 272 -241 c +339 -258 408 -266 481 -266 c +608 -266 704 -241 770 -191 c +837 -141 870 -68 870 27 c +870 115 839 184 778 234 c +716 283 630 308 520 308 c +346 308 l +346 474 l +776 973 l +109 973 l +109 1120 l +983 1120 l +983 952 l +572 476 l + +ce} _d +/uni01F0{569 0 -37 -426 612 1638 sc +219 1262 m +-26 1638 l +113 1638 l +293 1393 l +473 1638 l +612 1638 l +367 1262 l +219 1262 l + +193 1120 m +377 1120 l +377 -20 l +377 -163 350 -266 295 -330 c +241 -394 154 -426 33 -426 c +-37 -426 l +-37 -270 l +12 -270 l +82 -270 130 -254 155 -221 c +180 -189 193 -122 193 -20 c +193 1120 l + +ce} _d +/uni01F1{2912 0 201 0 2768 1493 sc +1572 1493 m +2745 1493 l +2745 1339 l +1801 170 l +2768 170 l +2768 0 l +1549 0 l +1549 154 l +2493 1323 l +1572 1323 l +1572 1493 l + +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/uni01F2{2660 0 201 0 2480 1493 sc +1606 1120 m +2480 1120 l +2480 952 l +1788 147 l +2480 147 l +2480 0 l +1581 0 l +1581 168 l +2273 973 l +1606 973 l +1606 1120 l + +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/uni01F3{2364 0 113 -29 2193 1556 sc +1319 1120 m +2193 1120 l +2193 952 l +1501 147 l +2193 147 l +2193 0 l +1294 0 l +1294 168 l +1986 973 l +1319 973 l +1319 1120 l + +930 950 m +930 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/uni01F4{1587 0 115 -29 1419 1900 sc +850 1900 m +1035 1900 l +807 1636 l +654 1636 l +850 1900 l + +1219 213 m +1219 614 l +889 614 l +889 780 l +1419 780 l +1419 139 l +1341 84 1255 42 1161 13 c +1067 -15 967 -29 860 -29 c +627 -29 444 39 312 175 c +181 312 115 502 115 745 c +115 989 181 1179 312 1315 c +444 1452 627 1520 860 1520 c +957 1520 1050 1508 1137 1484 c +1225 1460 1306 1425 1380 1378 c +1380 1163 l +1305 1226 1226 1274 1142 1306 c +1058 1338 970 1354 877 1354 c +694 1354 557 1303 465 1201 c +374 1099 328 947 328 745 c +328 544 374 392 465 290 c +557 188 694 137 877 137 c +948 137 1012 143 1068 155 c +1124 168 1174 187 1219 213 c + +ce} _d +/uni01F5{1300 0 113 -426 1114 1635 sc +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +678 1635 m +877 1635 l +551 1259 l +398 1259 l +678 1635 l + +ce} _d +/uni01F6{2279 0 201 -29 2093 1493 sc +201 1493 m +403 1493 l +403 881 l +1137 881 l +1137 1493 l +1339 1493 l +1339 449 l +1339 343 1360 263 1401 210 c +1442 158 1513 132 1615 131 c +1714 131 1785 157 1829 210 c +1870 260 1891 340 1891 449 c +1891 1120 l +2093 1120 l +2093 442 l +2093 293 2060 176 1993 91 c +1929 11 1803 -29 1615 -29 c +1432 -29 1306 11 1237 91 c +1170 170 1137 287 1137 442 c +1137 711 l +403 711 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/uni01F7{1397 0 201 -426 1282 1520 sc +403 156 m +403 -426 l +201 -426 l +201 1493 l +403 1493 l +403 1308 l +440 1356 507 1409 604 1467 c +662 1502 766 1520 917 1520 c +1049 1520 1143 1480 1198 1400 c +1254 1319 1282 1223 1282 1114 c +1282 809 989 490 403 156 c + +403 351 m +852 638 1076 870 1076 1049 c +1076 1144 1056 1216 1015 1263 c +975 1310 912 1333 826 1333 c +668 1333 527 1247 403 1076 c +403 351 l + +ce} _d +/uni01F8{1532 0 201 0 1331 1899 sc +741 1899 m +937 1635 l +784 1635 l +554 1899 l +741 1899 l + +201 1493 m +473 1493 l +1135 244 l +1135 1493 l +1331 1493 l +1331 0 l +1059 0 l +397 1249 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/uni01F9{1298 0 186 0 1124 1636 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +647 1636 m +929 1262 l +776 1262 l +450 1636 l +647 1636 l + +ce} _d +/Aringacute{1401 0 16 0 1384 1907 sc +852 1626 m +852 1668 837 1704 807 1733 c +778 1763 742 1778 700 1778 c +657 1778 621 1763 592 1734 c +563 1705 549 1669 549 1626 c +549 1584 564 1548 593 1519 c +622 1490 658 1475 700 1475 c +742 1475 778 1490 807 1519 c +837 1548 852 1584 852 1626 c + +700 1294 m +428 551 l +973 551 l +700 1294 l + +549 1397 m +508 1424 478 1457 457 1495 c +436 1534 426 1577 426 1626 c +426 1703 452 1768 505 1821 c +558 1874 623 1901 700 1901 c +776 1901 841 1874 894 1820 c +948 1767 975 1702 975 1626 c +975 1579 964 1536 943 1497 c +922 1458 892 1424 852 1397 c +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +549 1397 l + +1171 1907 m +1356 1907 l +1128 1643 l +975 1643 l +1171 1907 l + +ce} _d +/aringacute{1255 0 123 -29 1244 1907 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +746 1524 m +746 1566 731 1602 702 1631 c +673 1660 637 1675 594 1675 c +551 1675 514 1660 485 1631 c +456 1602 442 1567 442 1524 c +442 1481 456 1444 485 1415 c +514 1386 551 1372 594 1372 c +637 1372 673 1387 702 1416 c +731 1445 746 1481 746 1524 c + +868 1524 m +868 1447 841 1382 788 1329 c +735 1276 671 1249 594 1249 c +517 1249 452 1276 399 1329 c +346 1382 320 1447 320 1524 c +320 1601 346 1665 399 1718 c +452 1771 517 1798 594 1798 c +671 1798 735 1771 788 1718 c +841 1665 868 1601 868 1524 c + +1059 1907 m +1244 1907 l +1016 1643 l +863 1643 l +1059 1907 l + +ce} _d +/AEacute{1995 0 8 0 1864 1900 sc +1171 1900 m +1356 1900 l +1128 1636 l +975 1636 l +1171 1900 l + +1845 1493 m +1845 1323 l +1104 1323 l +1104 881 l +1815 881 l +1815 711 l +1104 711 l +1104 170 l +1864 170 l +1864 0 l +901 0 l +901 383 l +373 383 l +213 0 l +8 0 l +633 1493 l +1845 1493 l + +772 1335 m +442 551 l +901 551 l +901 1335 l +772 1335 l + +ce} _d +/aeacute{2011 0 123 -29 1903 1635 sc +1718 660 m +1717 761 1689 841 1634 901 c +1579 961 1506 991 1415 991 c +1313 991 1231 962 1169 904 c +1108 846 1072 764 1063 659 c +1718 660 l + +995 963 m +1044 1023 1104 1069 1175 1100 c +1246 1131 1325 1147 1413 1147 c +1564 1147 1683 1098 1771 1001 c +1859 904 1903 773 1903 606 c +1903 516 l +1057 516 l +1065 389 1103 292 1171 225 c +1239 158 1334 125 1456 125 c +1525 125 1593 134 1660 151 c +1727 169 1793 196 1860 231 c +1860 57 l +1793 29 1725 8 1656 -7 c +1587 -22 1517 -29 1446 -29 c +1335 -29 1238 -9 1155 31 c +1072 72 1005 132 954 211 c +905 131 845 71 773 31 c +701 -9 617 -29 522 -29 c +396 -29 298 2 228 64 c +158 127 123 214 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +681 1147 763 1131 834 1099 c +905 1067 959 1022 995 963 c + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1008 1635 m +1207 1635 l +881 1259 l +728 1259 l +1008 1635 l + +ce} _d +/Oslashacute{1612 0 102 -70 1509 1900 sc +821 1900 m +1006 1900 l +778 1636 l +625 1636 l +821 1900 l + +1206 1112 m +489 266 l +530 223 578 191 631 168 c +685 146 744 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 820 1277 888 1264 949 c +1251 1010 1232 1065 1206 1112 c + +1124 1225 m +1083 1268 1036 1300 982 1322 c +929 1345 870 1356 807 1356 c +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 670 334 602 347 539 c +360 476 380 422 406 377 c +1124 1225 l + +272 219 m +220 287 181 365 154 453 c +128 541 115 638 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c +894 1520 974 1507 1047 1481 c +1121 1456 1187 1418 1245 1368 c +1407 1559 l +1509 1470 l +1339 1272 l +1391 1203 1430 1125 1457 1036 c +1484 947 1497 850 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +722 -29 642 -17 568 8 c +495 33 428 71 367 121 c +205 -70 l +102 18 l +272 219 l + +ce} _d +/oslashacute{1253 0 72 -94 1180 1635 sc +905 801 m +418 209 l +445 181 476 160 510 147 c +545 134 584 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 612 943 657 936 696 c +929 735 919 770 905 801 c + +834 909 m +806 936 775 957 740 970 c +706 984 668 991 627 991 c +526 991 448 952 391 873 c +335 795 307 686 307 545 c +307 497 310 455 316 418 c +323 381 333 348 346 317 c +834 909 l + +221 166 m +185 217 158 276 140 341 c +122 407 113 480 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c +689 1147 746 1138 799 1121 c +852 1104 901 1079 946 1044 c +1085 1212 l +1180 1133 l +1034 954 l +1069 903 1096 844 1114 778 c +1132 712 1141 639 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +563 -29 504 -20 450 -3 c +397 14 349 40 307 74 c +168 -94 l +72 -16 l +221 166 l + +679 1635 m +878 1635 l +552 1259 l +399 1259 l +679 1635 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-2 def /PaintType 0 def /FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def /FontBBox [-2090 -948 3673 2524] def /FontType 3 def -/Encoding [/space /exclam /b /a /e /h /i /n /r /T /t /w] def -/CharStrings 13 dict dup begin +/Encoding [/uni0200 /uni0201 /uni0202 /uni0203 /uni0204 /uni0205 /uni0206 /uni0207 /uni0208 /uni0209 /uni020A /uni020B /uni020C /uni020D /uni020E /uni020F /uni0210 /uni0211 /uni0212 /uni0213 /uni0214 /uni0215 /uni0216 /uni0217 /Scommaaccent /scommaaccent /uni021A /uni021B /uni021C /uni021D /uni021E /uni021F /uni0220 /uni0221 /uni0222 /uni0223 /uni0224 /uni0225 /uni0226 /uni0227 /uni0228 /uni0229 /uni022A /uni022B /uni022C /uni022D /uni022E /uni022F /uni0230 /uni0231 /uni0232 /uni0233 /uni0234 /uni0235 /uni0236 /dotlessj /uni0238 /uni0239 /uni023A /uni023B /uni023C /uni023D /uni023E /uni023F /uni0240 /uni0241 /uni0242 /uni0243 /uni0244 /uni0245 /uni0246 /uni0247 /uni0248 /uni0249 /uni024A /uni024B /uni024C /uni024D /uni024E /uni024F /u1F600 /u1F601 /u1F602 /u1F603 /u1F604 /u1F605 /u1F606 /u1F607 /u1F608 /u1F609 /u1F60A /u1F60B /u1F60C /u1F60D /u1F60E /u1F60F] def +/CharStrings 97 dict dup begin /.notdef 0 def -/space{651 0 0 0 0 0 sc +/uni0200{1401 0 16 0 1384 1904 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +492 1904 m +688 1640 l +535 1640 l +307 1904 l +492 1904 l + +827 1904 m +1023 1640 l +870 1640 l +642 1904 l +827 1904 l + +ce} _d +/uni0201{1255 0 123 -29 1069 1636 sc +423 1636 m +628 1260 l +493 1260 l +245 1636 l +423 1636 l + +757 1636 m +947 1260 l +810 1260 l +587 1636 l +757 1636 l + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/uni0202{1401 0 16 0 1384 1846 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +1013 1604 m +895 1604 l +886 1640 865 1667 832 1686 c +799 1705 755 1715 700 1715 c +645 1715 602 1706 569 1687 c +537 1669 516 1641 505 1604 c +387 1604 l +394 1683 424 1743 477 1784 c +530 1825 605 1846 700 1846 c +796 1846 870 1826 923 1785 c +976 1744 1006 1684 1013 1604 c + +ce} _d +/uni0203{1255 0 123 -29 1069 1608 sc +918 1321 m +800 1321 l +793 1371 773 1408 740 1433 c +708 1458 663 1471 605 1471 c +548 1471 503 1459 471 1434 c +439 1409 419 1372 410 1321 c +292 1321 l +299 1416 328 1488 381 1536 c +434 1584 508 1608 605 1608 c +702 1608 776 1584 829 1536 c +882 1488 911 1416 918 1321 c + +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/uni0204{1294 0 201 0 1163 1904 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +428 1904 m +624 1640 l +471 1640 l +243 1904 l +428 1904 l + +763 1904 m +959 1640 l +806 1640 l +578 1904 l +763 1904 l + +ce} _d +/uni0205{1260 0 113 -29 1151 1635 sc +457 1635 m +662 1259 l +527 1259 l +279 1635 l +457 1635 l + +791 1635 m +981 1259 l +844 1259 l +621 1635 l +791 1635 l + +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/uni0206{1294 0 201 0 1163 1846 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +991 1604 m +873 1604 l +864 1640 843 1667 810 1686 c +777 1705 733 1715 678 1715 c +623 1715 580 1706 547 1687 c +515 1669 494 1641 483 1604 c +365 1604 l +372 1683 402 1743 455 1784 c +508 1825 583 1846 678 1846 c +774 1846 848 1826 901 1785 c +954 1744 984 1684 991 1604 c + +ce} _d +/uni0207{1260 0 113 -29 1151 1608 sc +986 1321 m +868 1321 l +861 1371 841 1408 808 1433 c +776 1458 731 1471 673 1471 c +616 1471 571 1459 539 1434 c +507 1409 487 1372 478 1321 c +360 1321 l +367 1416 396 1488 449 1536 c +502 1584 576 1608 673 1608 c +770 1608 844 1584 897 1536 c +950 1488 979 1416 986 1321 c + +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/uni0208{604 0 -89 0 627 1904 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +96 1904 m +292 1640 l +139 1640 l +-89 1904 l +96 1904 l + +431 1904 m +627 1640 l +474 1640 l +246 1904 l +431 1904 l + +ce} _d +/uni0209{569 0 -61 0 641 1635 sc +117 1635 m +322 1259 l +187 1259 l +-61 1635 l +117 1635 l + +451 1635 m +641 1259 l +504 1259 l +281 1635 l +451 1635 l + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/uni020A{604 0 5 0 631 1846 sc +201 1493 m +403 1493 l +403 0 l +201 0 l +201 1493 l + +631 1604 m +513 1604 l +504 1640 483 1667 450 1686 c +417 1705 373 1715 318 1715 c +263 1715 220 1706 187 1687 c +155 1669 134 1641 123 1604 c +5 1604 l +12 1683 42 1743 95 1784 c +148 1825 223 1846 318 1846 c +414 1846 488 1826 541 1785 c +594 1744 624 1684 631 1604 c + +ce} _d +/uni020B{569 0 -29 0 597 1608 sc +597 1321 m +479 1321 l +472 1371 452 1408 419 1433 c +387 1458 342 1471 284 1471 c +227 1471 182 1459 150 1434 c +118 1409 98 1372 89 1321 c +-29 1321 l +-22 1416 7 1488 60 1536 c +113 1584 187 1608 284 1608 c +381 1608 455 1584 508 1536 c +561 1488 590 1416 597 1321 c + +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +285 1147 m +285 1147 l + +ce} _d +/uni020C{1612 0 115 -29 1497 1904 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +584 1904 m +780 1640 l +627 1640 l +399 1904 l +584 1904 l + +919 1904 m +1115 1640 l +962 1640 l +734 1904 l +919 1904 l + +ce} _d +/uni020D{1253 0 113 -29 1141 1636 sc +430 1636 m +635 1260 l +500 1260 l +252 1636 l +430 1636 l + +764 1636 m +954 1260 l +817 1260 l +594 1636 l +764 1636 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/uni020E{1612 0 115 -29 1497 1846 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +1109 1604 m +991 1604 l +982 1640 961 1667 928 1686 c +895 1705 851 1715 796 1715 c +741 1715 698 1706 665 1687 c +633 1669 612 1641 601 1604 c +483 1604 l +490 1683 520 1743 573 1784 c +626 1825 701 1846 796 1846 c +892 1846 966 1826 1019 1785 c +1072 1744 1102 1684 1109 1604 c + +ce} _d +/uni020F{1253 0 113 -29 1141 1608 sc +969 1321 m +851 1321 l +844 1371 824 1408 791 1433 c +759 1458 714 1471 656 1471 c +599 1471 554 1459 522 1434 c +490 1409 470 1372 461 1321 c +343 1321 l +350 1416 379 1488 432 1536 c +485 1584 559 1608 656 1608 c +753 1608 827 1584 880 1536 c +933 1488 962 1416 969 1321 c + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/uni0210{1423 0 199 0 1364 1904 sc +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l + +384 1904 m +580 1640 l +427 1640 l +199 1904 l +384 1904 l + +719 1904 m +915 1640 l +762 1640 l +534 1904 l +719 1904 l + +ce} _d +/uni0211{842 0 130 0 842 1635 sc +308 1635 m +513 1259 l +378 1259 l +130 1635 l +308 1635 l + +642 1635 m +832 1259 l +695 1259 l +472 1635 l +642 1635 l + +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/uni0212{1423 0 201 0 1364 1846 sc +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l + +953 1604 m +835 1604 l +826 1640 805 1667 772 1686 c +739 1705 695 1715 640 1715 c +585 1715 542 1706 509 1687 c +477 1669 456 1641 445 1604 c +327 1604 l +334 1683 364 1743 417 1784 c +470 1825 545 1846 640 1846 c +736 1846 810 1826 863 1785 c +916 1744 946 1684 953 1604 c + +ce} _d +/uni0213{842 0 186 0 862 1608 sc +862 1321 m +744 1321 l +737 1371 717 1408 684 1433 c +652 1458 607 1471 549 1471 c +492 1471 447 1459 415 1434 c +383 1409 363 1372 354 1321 c +236 1321 l +243 1416 272 1488 325 1536 c +378 1584 452 1608 549 1608 c +646 1608 720 1584 773 1536 c +826 1488 855 1416 862 1321 c + +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/uni0214{1499 0 178 -29 1321 1904 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +540 1904 m +736 1640 l +583 1640 l +355 1904 l +540 1904 l + +875 1904 m +1071 1640 l +918 1640 l +690 1904 l +875 1904 l + +ce} _d +/uni0215{1298 0 174 -29 1112 1636 sc +483 1636 m +688 1260 l +553 1260 l +305 1636 l +483 1636 l + +817 1636 m +1007 1260 l +870 1260 l +647 1636 l +817 1636 l + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/uni0216{1499 0 178 -29 1321 1846 sc +178 1493 m +381 1493 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 1493 l +1321 1493 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 1493 l + +1061 1604 m +943 1604 l +934 1640 913 1667 880 1686 c +847 1705 803 1715 748 1715 c +693 1715 650 1706 617 1687 c +585 1669 564 1641 553 1604 c +435 1604 l +442 1683 472 1743 525 1784 c +578 1825 653 1846 748 1846 c +844 1846 918 1826 971 1785 c +1024 1744 1054 1684 1061 1604 c + +ce} _d +/uni0217{1298 0 174 -29 1112 1608 sc +988 1321 m +870 1321 l +863 1371 843 1408 810 1433 c +778 1458 733 1471 675 1471 c +618 1471 573 1459 541 1434 c +509 1409 489 1372 480 1321 c +362 1321 l +369 1416 398 1488 451 1536 c +504 1584 578 1608 675 1608 c +772 1608 846 1584 899 1536 c +952 1488 981 1416 988 1321 c + +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/Scommaaccent{1300 0 135 -492 1186 1520 sc +562 -172 m +773 -172 l +609 -492 l +480 -492 l +562 -172 l + +1096 1444 m +1096 1247 l +1019 1284 947 1311 879 1329 c +811 1347 745 1356 682 1356 c +572 1356 487 1335 427 1292 c +368 1249 338 1189 338 1110 c +338 1044 358 994 397 960 c +437 927 512 900 623 879 c +745 854 l +896 825 1007 775 1078 702 c +1150 630 1186 533 1186 412 c +1186 267 1137 158 1040 83 c +943 8 801 -29 614 -29 c +543 -29 468 -21 388 -5 c +309 11 226 35 141 66 c +141 274 l +223 228 303 193 382 170 c +461 147 538 135 614 135 c +729 135 818 158 881 203 c +944 248 975 313 975 397 c +975 470 952 528 907 569 c +862 610 789 641 686 662 c +563 686 l +412 716 303 763 236 827 c +169 891 135 980 135 1094 c +135 1226 181 1330 274 1406 c +367 1482 496 1520 659 1520 c +729 1520 800 1514 873 1501 c +946 1488 1020 1469 1096 1444 c + +ce} _d +/scommaaccent{1067 0 111 -492 967 1147 sc +488 -172 m +699 -172 l +535 -492 l +406 -492 l +488 -172 l + +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + ce} _d -/exclam{821 0 309 0 512 1493 sc -309 254 m -512 254 l -512 0 l -309 0 l -309 254 l +/uni021A{1251 0 -6 -492 1257 1493 sc +527 -172 m +738 -172 l +574 -492 l +445 -492 l +527 -172 l -309 1493 m -512 1493 l -512 838 l -492 481 l -330 481 l -309 838 l -309 1493 l +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +-6 1323 l +-6 1493 l ce} _d -/b{1300 0 186 -29 1188 1556 sc -997 559 m -997 694 969 800 913 877 c -858 954 781 993 684 993 c -587 993 510 954 454 877 c -399 800 371 694 371 559 c -371 424 399 317 454 240 c -510 163 587 125 684 125 c -781 125 858 163 913 240 c -969 317 997 424 997 559 c +/uni021B{803 0 55 -492 754 1438 sc +444 -172 m +655 -172 l +491 -492 l +362 -492 l +444 -172 l -371 950 m -410 1017 458 1066 517 1098 c -576 1131 647 1147 729 1147 c -865 1147 975 1093 1060 985 c -1145 877 1188 735 1188 559 c -1188 383 1145 241 1060 133 c -975 25 865 -29 729 -29 c -647 -29 576 -13 517 19 c -458 52 410 101 371 168 c +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/uni021C{1284 0 156 -430 1139 1520 sc +831 674 m +1036 629 1139 509 1139 314 c +1139 239 1120 168 1083 99 c +1046 30 997 -31 935 -84 c +874 -137 800 -187 715 -232 c +630 -277 541 -316 448 -348 c +355 -380 258 -407 156 -430 c +156 -270 l +289 -233 407 -192 508 -145 c +610 -98 692 -50 753 1 c +815 52 861 104 892 156 c +923 209 938 261 938 314 c +938 381 917 437 875 481 c +834 525 776 547 701 547 c +638 547 568 530 491 496 c +328 424 l +328 595 l +576 701 l +616 718 653 738 687 759 c +721 781 755 808 790 841 c +825 874 852 914 872 960 c +893 1007 903 1057 903 1112 c +903 1149 897 1181 885 1210 c +874 1239 859 1261 840 1278 c +822 1295 800 1310 775 1321 c +750 1332 726 1340 702 1344 c +678 1348 653 1350 628 1350 c +505 1350 363 1289 201 1166 c +201 1356 l +360 1465 509 1520 646 1520 c +733 1520 811 1506 878 1477 c +946 1448 1001 1404 1042 1343 c +1083 1282 1104 1209 1104 1124 c +1104 1084 1100 1047 1092 1013 c +1085 979 1071 943 1052 905 c +1033 868 1005 830 968 791 c +931 752 886 713 831 674 c + +ce} _d +/uni021D{1068 0 71 -433 956 1147 sc +679 461 m +726 456 768 442 805 419 c +842 396 871 370 892 340 c +913 310 928 280 939 251 c +950 222 956 195 956 170 c +956 120 945 72 923 26 c +901 -20 872 -61 836 -97 c +800 -133 757 -167 706 -199 c +655 -232 604 -260 551 -283 c +499 -306 443 -328 383 -348 c +324 -369 269 -385 219 -398 c +170 -411 120 -423 71 -433 c +71 -303 l +179 -272 274 -241 355 -209 c +437 -177 504 -146 557 -115 c +610 -85 652 -54 685 -21 c +718 12 742 43 755 73 c +768 104 775 136 775 170 c +775 229 755 277 716 314 c +677 351 626 370 564 370 c +525 370 484 362 442 345 c +226 259 l +226 398 l +449 483 l +472 492 494 501 515 512 c +537 523 562 538 591 558 c +620 578 645 599 666 622 c +687 645 705 674 720 708 c +735 742 743 778 743 816 c +743 845 738 872 727 895 c +717 918 704 937 687 951 c +671 965 652 976 629 985 c +607 994 585 1001 563 1004 c +542 1007 519 1009 496 1009 c +450 1009 399 999 343 979 c +287 960 210 920 112 860 c +112 1014 l +289 1103 423 1147 512 1147 c +587 1147 655 1135 716 1111 c +777 1088 827 1052 866 1003 c +905 954 924 898 924 833 c +924 760 907 697 872 646 c +838 595 774 534 679 461 c + +ce} _d +/uni021E{1540 0 201 0 1339 1901 sc +678 1635 m +467 1901 l +606 1901 l +772 1723 l +938 1901 l +1077 1901 l +866 1635 l +678 1635 l + +201 1493 m +403 1493 l +403 881 l +1137 881 l +1137 1493 l +1339 1493 l +1339 0 l +1137 0 l +1137 711 l +403 711 l +403 0 l +201 0 l +201 1493 l + +ce} _d +/uni021F{1298 0 -16 0 1124 1901 sc +195 1635 m +-16 1901 l +123 1901 l +289 1723 l +455 1901 l +594 1901 l +383 1635 l +195 1635 l + +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c 371 0 l 186 0 l 186 1556 l 371 1556 l -371 950 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/uni0220{1506 0 201 -426 1305 1521 sc +1104 895 m +1104 1038 1078 1145 1027 1216 c +976 1287 899 1323 797 1323 c +678 1323 582 1280 510 1195 c +439 1110 403 994 403 846 c +403 0 l +201 0 l +201 1493 l +403 1493 l +403 1252 l +457 1342 518 1409 586 1454 c +654 1499 740 1521 845 1520 c +996 1520 1111 1467 1188 1360 c +1266 1254 1305 1098 1305 893 c +1305 -426 l +1104 -426 l +1104 895 l + +ce} _d +/uni0221{1716 0 113 -144 1604 1556 sc +1206 130 m +1217 127 1235 125 1260 125 c +1367 125 1420 183 1420 300 c +1420 330 1403 345 1368 346 c +1333 346 1279 274 1206 130 c + +1114 314 m +1187 437 1269 498 1360 498 c +1523 498 1604 429 1604 292 c +1604 78 1490 -29 1262 -29 c +1213 -29 1171 -24 1136 -14 c +1113 -62 1094 -105 1078 -144 c +882 -144 l +901 -98 933 -26 978 72 c +946 112 930 144 930 168 c +891 101 843 52 784 20 c +725 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1066 891 1017 930 950 c +930 1556 l +1114 1556 l +1114 314 l + +386 878 m +331 801 303 694 303 559 c +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 801 846 878 c +790 955 713 993 616 993 c +519 993 442 955 386 878 c + +ce} _d +/uni0222{1430 0 113 -29 1317 1520 sc +715 709 m +592 709 496 683 425 632 c +354 581 318 510 318 420 c +318 330 354 259 425 208 c +496 157 592 131 715 131 c +838 131 935 157 1006 208 c +1077 260 1113 331 1113 420 c +1113 510 1078 581 1007 632 c +936 683 839 709 715 709 c + +657 1260 m +570 1258 499 1236 442 1195 c +385 1154 357 1107 357 1054 c +357 1008 389 961 452 913 c +491 883 579 868 715 868 c +827 868 915 890 978 933 c +1042 976 1074 1043 1074 1134 c +1074 1239 1049 1306 998 1335 c +925 1386 846 1415 759 1420 c +759 1520 l +911 1520 1037 1485 1137 1416 c +1230 1351 1276 1257 1276 1133 c +1276 1048 1248 975 1191 916 c +1134 857 1055 816 954 795 c +1069 772 1158 728 1221 662 c +1285 596 1317 515 1317 420 c +1317 275 1265 164 1161 87 c +1058 10 909 -29 715 -29 c +521 -29 372 10 269 87 c +165 164 113 275 113 420 c +113 515 145 596 209 662 c +274 728 363 772 477 795 c +363 816 284 849 239 896 c +182 955 154 1014 154 1073 c +154 1160 197 1231 284 1286 c +361 1335 486 1360 657 1360 c +657 1260 l + +ce} _d +/uni0223{1250 0 113 -29 1137 1295 sc +625 709 m +529 709 453 683 398 632 c +343 581 316 510 316 420 c +316 330 343 259 398 208 c +453 157 529 131 625 131 c +721 131 797 157 852 208 c +907 260 935 331 935 420 c +935 510 907 581 852 632 c +797 683 722 709 625 709 c + +1024 1295 m +1063 1252 1082 1198 1082 1133 c +1082 1031 1065 959 1030 916 c +982 857 915 816 828 795 c +925 772 1001 728 1056 662 c +1110 596 1137 515 1137 420 c +1137 275 1093 164 1004 87 c +916 10 790 -29 625 -29 c +460 -29 334 10 246 87 c +157 164 113 275 113 420 c +113 515 140 596 195 662 c +250 728 326 772 423 795 c +327 818 259 859 220 916 c +185 966 168 1038 168 1133 c +168 1192 189 1246 231 1295 c +392 1295 l +363 1252 349 1191 349 1114 c +349 1037 373 976 422 933 c +470 890 538 868 625 868 c +712 868 779 890 828 933 c +877 976 902 1037 902 1114 c +902 1193 887 1254 858 1295 c +1024 1295 l + +ce} _d +/uni0224{1403 0 92 -426 1311 1493 sc +1311 -20 m +1311 -213 1258 -336 1153 -389 c +1105 -414 1043 -426 967 -426 c +713 -426 l +713 -270 l +946 -270 l +1016 -270 1064 -254 1089 -222 c +1114 -189 1127 -122 1127 -20 c +1127 0 l +92 0 l +92 154 l +1036 1323 l +115 1323 l +115 1493 l +1288 1493 l +1288 1339 l +344 170 l +1311 170 l +1311 -20 l + +ce} _d +/uni0225{1075 0 88 -426 987 1120 sc +987 -20 m +987 -213 934 -336 829 -389 c +781 -414 719 -426 643 -426 c +389 -426 l +389 -270 l +622 -270 l +692 -270 740 -254 765 -222 c +790 -189 803 -122 803 -20 c +803 0 l +88 0 l +88 168 l +780 973 l +113 973 l +113 1120 l +987 1120 l +987 952 l +295 147 l +987 147 l +987 -20 l + +ce} _d +/uni0226{1401 0 16 0 1384 1872 sc +598 1872 m +802 1872 l +802 1667 l +598 1667 l +598 1872 l + +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l ce} _d -/a{1255 0 123 -29 1069 1147 sc +/uni0227{1255 0 123 -29 1069 1556 sc +492 1556 m +676 1556 l +676 1323 l +492 1323 l +492 1556 l + +586 1147 m +586 1147 l + 702 563 m 553 563 450 546 393 512 c 336 478 307 420 307 338 c @@ -119,7 +14190,39 @@ ce} _d 1029 937 1069 810 1069 639 c ce} _d -/e{1260 0 113 -29 1151 1147 sc +/uni0228{1294 0 201 -395 1163 1493 sc +201 1493 m +1145 1493 l +1145 1323 l +403 1323 l +403 881 l +1114 881 l +1114 711 l +403 711 l +403 170 l +1163 170 l +1163 0 l +201 0 l +201 1493 l + +758 0 m +795 -41 822 -79 840 -114 c +858 -149 867 -183 867 -215 c +867 -274 847 -319 807 -349 c +767 -380 708 -395 629 -395 c +598 -395 568 -393 539 -389 c +510 -385 482 -379 453 -371 c +453 -240 l +476 -251 499 -259 524 -264 c +549 -269 577 -272 608 -272 c +647 -272 677 -264 697 -248 c +717 -232 727 -209 727 -178 c +727 -158 720 -133 705 -104 c +691 -75 669 -41 639 0 c +758 0 l + +ce} _d +/uni0229{1260 0 113 -395 1151 1147 sc 1151 606 m 1151 516 l 305 516 l @@ -144,11 +14247,417 @@ ce} _d 356 846 320 764 311 659 c 967 660 l +719 0 m +756 -41 783 -79 801 -114 c +819 -149 828 -183 828 -215 c +828 -274 808 -319 768 -349 c +728 -380 669 -395 590 -395 c +559 -395 529 -393 500 -389 c +471 -385 443 -379 414 -371 c +414 -240 l +437 -251 460 -259 485 -264 c +510 -269 538 -272 569 -272 c +608 -272 638 -264 658 -248 c +678 -232 688 -209 688 -178 c +688 -158 681 -133 666 -104 c +652 -75 630 -41 600 0 c +719 0 l + ce} _d -/h{1298 0 186 0 1124 1556 sc -1124 676 m -1124 0 l -940 0 l +/uni022A{1612 0 115 -29 1497 2099 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +500 2099 m +1098 2099 l +1098 1951 l +500 1951 l +500 2099 l + +892 1838 m +1095 1838 l +1095 1635 l +892 1635 l +892 1838 l + +501 1838 m +704 1838 l +704 1635 l +501 1635 l +501 1838 l + +ce} _d +/uni022B{1253 0 113 -29 1141 1841 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +721 1552 m +924 1552 l +924 1350 l +721 1350 l +721 1552 l + +330 1552 m +533 1552 l +533 1350 l +330 1350 l +330 1552 l + +328 1841 m +926 1841 l +926 1693 l +328 1693 l +328 1841 l + +ce} _d +/uni022C{1612 0 115 -29 1497 2099 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +805 1685 m +748 1718 l +731 1727 718 1734 707 1737 c +697 1741 688 1743 680 1743 c +656 1743 637 1735 624 1718 c +611 1701 604 1678 604 1648 c +604 1642 l +479 1642 l +479 1709 496 1763 530 1802 c +565 1841 611 1861 668 1861 c +692 1861 714 1858 734 1853 c +755 1848 781 1836 813 1818 c +870 1788 l +885 1779 899 1773 910 1769 c +921 1765 932 1763 942 1763 c +963 1763 981 1771 994 1788 c +1007 1805 1014 1828 1014 1855 c +1014 1861 l +1139 1861 l +1138 1794 1120 1741 1085 1701 c +1051 1662 1006 1642 950 1642 c +927 1642 906 1645 886 1650 c +867 1655 840 1667 805 1685 c + +507 2099 m +1105 2099 l +1105 1951 l +507 1951 l +507 2099 l + +ce} _d +/uni022D{1253 0 113 -29 1141 1769 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +625 1355 m +568 1388 l +551 1397 538 1404 527 1407 c +517 1411 508 1413 500 1413 c +476 1413 457 1405 444 1388 c +431 1371 424 1348 424 1318 c +424 1312 l +299 1312 l +299 1379 316 1433 350 1472 c +385 1511 431 1531 488 1531 c +512 1531 534 1528 554 1523 c +575 1518 601 1506 633 1488 c +690 1458 l +705 1449 719 1443 730 1439 c +741 1435 752 1433 762 1433 c +783 1433 801 1441 814 1458 c +827 1475 834 1498 834 1525 c +834 1531 l +959 1531 l +958 1464 940 1411 905 1371 c +871 1332 826 1312 770 1312 c +747 1312 726 1315 706 1320 c +687 1325 660 1337 625 1355 c + +327 1769 m +925 1769 l +925 1621 l +327 1621 l +327 1769 l + +ce} _d +/uni022E{1612 0 115 -29 1497 1872 sc +705 1872 m +909 1872 l +909 1667 l +705 1667 l +705 1872 l + +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +ce} _d +/uni022F{1253 0 113 -29 1141 1556 sc +533 1556 m +717 1556 l +717 1323 l +533 1323 l +533 1556 l + +627 1147 m +627 1147 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/uni0230{1612 0 115 -29 1497 2099 sc +807 1356 m +660 1356 544 1301 457 1192 c +371 1083 328 934 328 745 c +328 557 371 408 457 299 c +544 190 660 135 807 135 c +954 135 1070 190 1155 299 c +1241 408 1284 557 1284 745 c +1284 934 1241 1083 1155 1192 c +1070 1301 954 1356 807 1356 c + +807 1520 m +1016 1520 1184 1450 1309 1309 c +1434 1169 1497 981 1497 745 c +1497 510 1434 322 1309 181 c +1184 41 1016 -29 807 -29 c +597 -29 429 41 303 181 c +178 321 115 509 115 745 c +115 981 178 1169 303 1309 c +429 1450 597 1520 807 1520 c + +704 1835 m +908 1835 l +908 1630 l +704 1630 l +704 1835 l + +508 2099 m +1106 2099 l +1106 1951 l +508 1951 l +508 2099 l + +ce} _d +/uni0231{1253 0 113 -29 1141 1841 sc +533 1556 m +717 1556 l +717 1323 l +533 1323 l +533 1556 l + +627 1147 m +627 1147 l + +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +328 1841 m +926 1841 l +926 1693 l +328 1693 l +328 1841 l + +ce} _d +/uni0232{1251 0 -4 0 1255 1841 sc +327 1841 m +925 1841 l +925 1693 l +327 1693 l +327 1841 l + +-4 1493 m +213 1493 l +627 879 l +1038 1493 l +1255 1493 l +727 711 l +727 0 l +524 0 l +524 711 l +-4 1493 l + +ce} _d +/uni0233{1212 0 61 -426 1151 1525 sc +307 1525 m +905 1525 l +905 1377 l +307 1377 l +307 1525 l + +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +956 1120 l +1151 1120 l +659 -104 l + +ce} _d +/uni0234{972 0 138 -144 860 1550 sc +462 130 m +473 127 491 125 516 125 c +623 125 676 183 676 300 c +676 330 659 345 624 346 c +589 346 535 274 462 130 c + +370 314 m +443 437 525 498 616 498 c +779 498 860 429 860 292 c +860 78 746 -29 518 -29 c +469 -29 427 -24 392 -14 c +369 -62 350 -105 334 -144 c +138 -144 l +157 -98 189 -26 234 72 c +201 112 185 162 186 222 c +186 1550 l +370 1550 l +370 314 l + +ce} _d +/uni0235{1726 0 186 -144 1614 1147 sc +1216 130 m +1227 127 1245 125 1270 125 c +1377 125 1430 183 1430 300 c +1430 330 1413 345 1378 346 c +1343 346 1289 274 1216 130 c + +1124 314 m +1197 437 1279 498 1370 498 c +1533 498 1614 429 1614 292 c +1614 78 1500 -29 1272 -29 c +1223 -29 1181 -24 1146 -14 c +1123 -62 1104 -105 1088 -144 c +892 -144 l +911 -98 943 -26 988 72 c +955 112 939 162 940 222 c 940 670 l 940 776 919 855 878 908 c 837 961 775 987 692 987 c @@ -156,20 +14665,601 @@ ce} _d 400 829 371 742 371 633 c 371 0 l 186 0 l -186 1556 l -371 1556 l +186 1120 l +371 1120 l 371 946 l 415 1013 467 1064 526 1097 c 586 1130 655 1147 733 1147 c -862 1147 959 1107 1025 1027 c +862 1147 959 1107 1025 1028 c 1091 948 1124 831 1124 676 c +1124 314 l + +ce} _d +/uni0236{977 0 55 -144 865 1438 sc +467 130 m +478 127 496 125 521 125 c +628 125 681 183 681 300 c +681 330 664 345 629 346 c +594 346 540 274 467 130 c + +375 314 m +448 437 530 498 621 498 c +784 498 865 429 865 292 c +865 78 751 -29 523 -29 c +474 -29 432 -24 396 -14 c +374 -62 355 -105 339 -144 c +143 -144 l +162 -98 194 -26 239 72 c +206 112 190 162 190 222 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l +375 1120 l +754 1120 l +754 977 l +375 977 l +375 314 l ce} _d -/i{569 0 193 0 377 1556 sc +/dotlessj{569 0 -37 -426 377 1120 sc 193 1120 m 377 1120 l +377 -20 l +377 -163 350 -266 295 -330 c +241 -394 154 -426 33 -426 c +-37 -426 l +-37 -270 l +12 -270 l +82 -270 130 -254 155 -221 c +180 -189 193 -122 193 -20 c +193 1120 l + +ce} _d +/uni0238{2044 0 113 -29 1932 1556 sc +386 878 m +331 801 303 694 303 559 c +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 955 386 878 c + +571 1147 m +734 1147 854 1081 930 950 c +930 1556 l +1115 1556 l +1115 950 l +1191 1081 1310 1147 1473 1147 c +1609 1147 1719 1093 1804 985 c +1889 877 1932 735 1932 559 c +1932 383 1889 241 1804 133 c +1719 25 1609 -29 1473 -29 c +1310 -29 1191 37 1115 168 c +1115 0 l +930 0 l +930 168 l +854 37 734 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c + +1658 240 m +1713 317 1741 424 1741 559 c +1741 694 1713 801 1658 878 c +1602 955 1525 993 1428 993 c +1331 993 1254 955 1198 878 c +1143 801 1115 694 1115 559 c +1115 424 1143 317 1198 240 c +1254 163 1331 125 1428 125 c +1525 125 1602 163 1658 240 c + +ce} _d +/uni0239{2044 0 113 -426 1932 1147 sc +1658 240 m +1713 317 1741 424 1741 559 c +1741 694 1713 801 1658 878 c +1602 955 1525 993 1428 993 c +1331 993 1254 955 1198 878 c +1143 801 1115 694 1115 559 c +1115 424 1143 317 1198 240 c +1254 163 1331 125 1428 125 c +1525 125 1602 163 1658 240 c + +1473 -29 m +1310 -29 1191 37 1115 168 c +1115 -426 l +930 -426 l +930 168 l +854 37 734 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +734 1147 854 1081 930 950 c +930 1120 l +1115 1120 l +1115 950 l +1191 1081 1310 1147 1473 1147 c +1609 1147 1719 1093 1804 985 c +1889 877 1932 735 1932 559 c +1932 383 1889 241 1804 133 c +1719 25 1609 -29 1473 -29 c + +386 878 m +331 801 303 694 303 559 c +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 955 386 878 c + +ce} _d +/uni023A{1401 0 -3 -70 1404 1559 sc +586 1493 m +815 1493 l +949 1142 l +1302 1559 l +1404 1470 l +1004 997 l +1384 0 l +1174 0 l +1038 383 l +484 383 l +279 142 l +229 0 l +159 0 l +100 -70 l +18 0 l +16 0 l +17 1 l +-3 18 l +44 74 l +586 1493 l + +700 1294 m +426 551 l +448 551 l +815 984 l +700 1294 l + +626 551 m +975 551 l +869 838 l +626 551 l + +ce} _d +/uni023B{1430 0 12 -70 1418 1559 sc +114 -70 m +12 18 l +232 279 l +154 404 115 559 115 745 c +115 985 180 1174 310 1312 c +440 1451 618 1520 844 1520 c +933 1520 1017 1508 1096 1484 c +1137 1472 1176 1457 1215 1439 c +1316 1559 l +1418 1470 l +1319 1352 l +1319 1165 l +1288 1194 1257 1219 1224 1241 c +444 319 l +450 310 457 302 464 294 c +555 189 685 137 856 137 c +943 137 1025 153 1102 184 c +1179 215 1251 263 1319 326 c +1319 115 l +1248 67 1173 31 1094 7 c +1015 -17 932 -29 844 -29 c +623 -29 447 37 318 170 c +114 -70 l + +375 447 m +1103 1307 l +1102 1307 l +1025 1338 943 1354 856 1354 c +685 1354 555 1302 464 1198 c +373 1093 328 942 328 745 c +328 630 344 530 375 447 c + +ce} _d +/uni023C{1126 0 9 -94 1117 1212 sc +105 -94 m +9 -16 l +198 214 l +141 308 113 423 113 559 c +113 742 163 885 264 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1130 c +873 1123 903 1115 933 1104 c +1022 1212 l +1117 1133 l +999 990 l +999 905 l +982 914 966 923 950 930 c +393 256 l +398 251 402 245 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 895 1 840 -11 c +785 -23 726 -29 664 -29 c +504 -29 375 19 277 115 c +105 -94 l + +332 376 m +826 974 l +779 985 731 991 684 991 c +565 991 472 953 406 878 c +340 802 307 696 307 559 c +307 490 315 429 332 376 c + +ce} _d +/uni023D{1141 0 10 0 1130 1493 sc +201 1493 m +403 1493 l +403 844 l +594 844 l +594 700 l +403 700 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 700 l +10 700 l +10 844 l +201 844 l +201 1493 l + +ce} _d +/uni023E{1251 0 -78 -70 1329 1559 sc +1257 1385 m +1257 1323 l +1205 1323 l +727 759 l +727 0 l +524 0 l +524 519 l +25 -70 l +-78 18 l +524 729 l +524 1323 l +-6 1323 l +-6 1493 l +1171 1493 l +1227 1559 l +1329 1470 l +1257 1385 l + +727 969 m +1027 1323 l +727 1323 l +727 969 l + +ce} _d +/uni023F{1067 0 111 -496 1049 1147 sc +778 295 m +778 345 761 383 728 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 366 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +738 612 831 572 884 522 c +939 471 967 400 967 309 c +967 205 926 123 844 62 c +786 19 713 -8 625 -21 c +784 -180 l +931 -306 l +959 -330 998 -342 1049 -342 c +1049 -496 l +1010 -496 l +954 -496 888 -476 812 -435 c +765 -410 711 -367 652 -306 c +415 -63 l +395 -43 373 -28 349 -17 c +338 -15 327 -13 316 -12 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 152 c +378 134 443 125 508 125 c +595 125 661 140 708 170 c +755 199 778 241 778 295 c + +ce} _d +/uni0240{1075 0 88 -496 1075 1120 sc +113 1120 m +987 1120 l +987 952 l +297 149 l +358 138 405 117 438 84 c +702 -180 l +849 -306 l +877 -330 916 -342 967 -342 c +1075 -342 l +1075 -496 l +928 -496 l +872 -496 806 -476 730 -435 c +683 -410 629 -367 570 -306 c +333 -63 l +292 -21 242 0 182 0 c +88 0 l +88 156 l +88 168 l +780 973 l +113 973 l +113 1120 l + +ce} _d +/uni0241{1235 0 80 0 1165 1493 sc +657 602 m +618 602 l +618 0 l +416 0 l +416 768 l +657 768 l +751 768 824 792 875 840 c +926 889 952 958 952 1048 c +952 1139 926 1208 875 1255 c +824 1303 751 1327 657 1327 c +403 1327 l +357 1327 305 1320 246 1306 c +188 1292 133 1270 80 1241 c +80 1423 l +181 1470 289 1493 403 1493 c +657 1493 l +820 1493 945 1456 1033 1383 c +1121 1310 1165 1198 1165 1048 c +1165 905 1121 794 1032 717 c +944 640 819 602 657 602 c + +ce} _d +/uni0242{981 0 80 0 911 1147 sc +303 422 m +403 422 l +497 422 570 446 621 494 c +672 543 698 612 698 702 c +698 796 673 865 622 908 c +565 956 492 980 403 980 c +354 980 301 973 244 959 c +187 945 132 924 80 895 c +80 1077 l +181 1124 289 1147 403 1147 c +570 1147 696 1110 779 1037 c +867 960 911 848 911 702 c +911 560 876 451 805 374 c +734 298 634 260 505 260 c +505 0 l +303 0 l +303 422 l + +ce} _d +/uni0243{1405 0 10 0 1260 1493 sc +403 713 m +403 512 l +750 512 l +750 368 l +403 368 l +403 166 l +727 166 l +836 166 916 188 968 233 c +1021 278 1047 347 1047 440 c +1047 533 1021 602 968 646 c +916 691 836 713 727 713 c +403 713 l + +403 1327 m +403 877 l +702 877 l +801 877 874 895 922 932 c +971 969 995 1026 995 1102 c +995 1177 971 1234 922 1271 c +874 1308 801 1327 702 1327 c +403 1327 l + +201 1493 m +717 1493 l +871 1493 990 1461 1073 1397 c +1156 1333 1198 1242 1198 1124 c +1198 1033 1177 960 1134 906 c +1091 852 1029 818 946 805 c +1045 784 1122 739 1177 671 c +1232 604 1260 519 1260 418 c +1260 285 1215 182 1124 109 c +1033 36 904 0 737 0 c +201 0 l +201 368 l +10 368 l +10 512 l +201 512 l +201 1493 l + +ce} _d +/uni0244{1499 0 12 -29 1486 1493 sc +178 1493 m +381 1493 l +381 875 l +1118 875 l +1118 1493 l +1321 1493 l +1321 875 l +1486 875 l +1486 711 l +1321 711 l +1321 561 l +1321 366 1273 219 1176 120 c +1080 21 938 -29 750 -29 c +561 -29 419 21 322 120 c +226 219 178 366 178 561 c +178 711 l +12 711 l +12 875 l +178 875 l +178 1493 l + +1118 711 m +381 711 l +381 586 l +381 426 410 311 468 240 c +526 170 620 135 750 135 c +879 135 973 170 1031 240 c +1089 311 1118 426 1118 586 c +1118 711 l + +ce} _d +/uni0245{1401 0 16 0 1384 1493 sc +229 0 m +16 0 l +586 1493 l +815 1493 l +1384 0 l +1174 0 l +700 1294 l +229 0 l + +ce} _d +/uni0246{1294 0 201 -190 1163 1683 sc +952 1683 m +1122 1683 l +1057 1493 l +1145 1493 l +1145 1323 l +999 1323 l +848 881 l +1114 881 l +1114 711 l +790 711 l +605 170 l +1163 170 l +1163 0 l +547 0 l +482 -190 l +312 -190 l 377 0 l -193 0 l +201 0 l +201 1493 l +887 1493 l +952 1683 l + +435 170 m +620 711 l +403 711 l +403 170 l +435 170 l + +678 881 m +829 1323 l +403 1323 l +403 881 l +678 881 l + +ce} _d +/uni0247{1260 0 113 -190 1151 1310 sc +967 660 m +966 761 937 841 882 901 c +873 912 863 921 852 930 c +741 660 l +967 660 l + +1151 606 m +1151 516 l +682 516 l +534 155 l +583 136 640 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +612 -29 538 -18 472 4 c +392 -190 l +222 -190 l +331 75 l +309 90 289 108 270 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +699 1147 734 1144 767 1138 c +838 1310 l +1008 1310 l +916 1086 l +954 1063 989 1035 1020 1002 c +1107 905 1151 773 1151 606 c + +571 659 m +706 989 l +693 990 679 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +571 659 l + +401 246 m +512 516 l +305 516 l +312 401 344 311 401 246 c + +ce} _d +/uni0248{604 0 -106 -410 594 1493 sc +594 631 m +403 631 l +403 104 l +403 -76 369 -207 300 -288 c +232 -369 122 -410 -29 -410 c +-106 -410 l +-106 -240 l +-43 -240 l +46 -240 109 -215 146 -165 c +183 -115 201 -25 201 104 c +201 631 l +10 631 l +10 797 l +201 797 l +201 1493 l +403 1493 l +403 797 l +594 797 l +594 631 l + +ce} _d +/uni0249{569 0 -37 -426 540 1556 sc +193 1120 m +377 1120 l +377 616 l +540 616 l +540 452 l +377 452 l +377 -20 l +377 -163 350 -266 295 -330 c +241 -394 154 -426 33 -426 c +-37 -426 l +-37 -270 l +12 -270 l +82 -270 130 -254 155 -221 c +180 -189 193 -122 193 -20 c +193 452 l +12 452 l +12 616 l +193 616 l 193 1120 l 193 1556 m @@ -179,34 +15269,115 @@ ce} _d 193 1556 l ce} _d -/n{1298 0 186 0 1124 1147 sc -1124 676 m -1124 0 l -940 0 l -940 670 l -940 776 919 855 878 908 c -837 961 775 987 692 987 c -593 987 514 955 457 892 c -400 829 371 742 371 633 c -371 0 l -186 0 l -186 1120 l -371 1120 l -371 946 l -415 1013 467 1064 526 1097 c -586 1130 655 1147 733 1147 c -862 1147 959 1107 1025 1027 c -1091 948 1124 831 1124 676 c +/uni024A{1600 0 115 -410 1712 1521 sc +1203 1261 m +1203 1493 l +1399 1493 l +1399 104 l +1399 -25 1417 -115 1454 -165 c +1491 -215 1554 -240 1643 -240 c +1712 -240 l +1712 -410 l +1635 -410 l +1484 -410 1374 -369 1305 -288 c +1237 -207 1203 -76 1203 104 c +1203 231 l +1152 143 1087 78 1008 35 c +929 -8 835 -29 725 -29 c +545 -29 398 42 285 184 c +172 327 115 514 115 746 c +115 978 172 1165 285 1307 c +398 1450 545 1521 725 1521 c +835 1521 929 1500 1008 1457 c +1087 1414 1152 1349 1203 1261 c + +325 745 m +325 555 364 406 442 297 c +520 189 627 135 763 135 c +900 135 1007 189 1085 297 c +1164 406 1203 555 1203 745 c +1203 935 1164 1084 1085 1192 c +1007 1301 900 1355 763 1355 c +627 1355 520 1301 442 1192 c +364 1084 325 935 325 745 c + +ce} _d +/uni024B{1300 0 113 -426 1344 1147 sc +1344 -426 m +1274 -426 l +1153 -426 1066 -394 1011 -330 c +957 -266 930 -163 930 -20 c +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c +930 1120 l +1114 1120 l +1114 -20 l +1114 -122 1127 -189 1152 -221 c +1177 -254 1225 -270 1295 -270 c +1344 -270 l +1344 -426 l + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/uni024C{1423 0 10 0 1364 1493 sc +909 700 m +952 685 994 654 1035 606 c +1076 558 1118 492 1159 408 c +1364 0 l +1147 0 l +956 383 l +907 483 859 549 812 582 c +766 615 703 631 623 631 c +403 631 l +403 0 l +201 0 l +201 631 l +10 631 l +10 797 l +201 797 l +201 1493 l +657 1493 l +828 1493 955 1457 1039 1386 c +1123 1315 1165 1207 1165 1063 c +1165 969 1143 891 1099 829 c +1056 767 992 724 909 700 c + +403 1327 m +403 797 l +657 797 l +754 797 828 819 877 864 c +927 909 952 976 952 1063 c +952 1150 927 1216 877 1260 c +828 1305 754 1327 657 1327 c +403 1327 l ce} _d -/r{842 0 186 0 842 1147 sc -842 948 m -821 960 799 969 774 974 c -750 980 723 983 694 983 c -590 983 510 949 454 881 c -399 814 371 717 371 590 c +/uni024D{842 0 14 0 842 1147 sc +542 616 m +542 452 l +371 452 l 371 0 l 186 0 l +186 452 l +14 452 l +14 616 l +186 616 l 186 1120 l 371 1120 l 371 946 l @@ -215,230 +15386,1105 @@ ce} _d 761 1147 775 1146 790 1144 c 805 1143 822 1140 841 1137 c 842 948 l +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 880 c +399 811 371 723 371 616 c +542 616 l ce} _d -/T{1251 0 -6 0 1257 1493 sc --6 1493 m -1257 1493 l -1257 1323 l -727 1323 l +/uni024E{1251 0 -10 0 1260 1493 sc +-4 1493 m +213 1493 l +364 1269 l +888 1269 l +1038 1493 l +1255 1493 l +1104 1269 l +1260 1269 l +1260 1105 l +993 1105 l +727 711 l 727 0 l 524 0 l -524 1323 l --6 1323 l --6 1493 l +524 711 l +258 1105 l +-10 1105 l +-10 1269 l +147 1269 l +-4 1493 l + +778 1105 m +475 1105 l +627 879 l +778 1105 l ce} _d -/t{803 0 55 0 754 1438 sc -375 1438 m -375 1120 l -754 1120 l -754 977 l -375 977 l -375 369 l -375 278 387 219 412 193 c -437 167 488 154 565 154 c -754 154 l -754 0 l -565 0 l -423 0 325 26 271 79 c -217 132 190 229 190 369 c -190 977 l -55 977 l -55 1120 l -190 1120 l -190 1438 l -375 1438 l +/uni024F{1212 0 11 -426 1205 1120 sc +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +309 516 l +11 516 l +11 659 l +251 659 l +61 1120 l +256 1120 l +440 659 l +772 659 l +956 1120 l +1151 1120 l +966 659 l +1205 659 l +1205 516 l +908 516 l +659 -104 l + +715 516 m +497 516 l +606 244 l +715 516 l ce} _d -/w{1675 0 86 0 1589 1120 sc -86 1120 m -270 1120 l -500 246 l -729 1120 l -946 1120 l -1176 246 l -1405 1120 l -1589 1120 l -1296 0 l -1079 0 l -838 918 l -596 0 l -379 0 l -86 1120 l +/u1F600{2135 0 170 -150 1965 1646 sc +642 1023 m +642 1062 655 1095 682 1122 c +709 1149 743 1163 782 1163 c +821 1163 854 1149 881 1122 c +908 1095 922 1062 922 1023 c +922 984 908 951 881 923 c +854 896 821 882 782 882 c +743 882 709 896 682 923 c +655 951 642 984 642 1023 c + +1220 1023 m +1220 1062 1234 1095 1261 1122 c +1288 1149 1321 1163 1360 1163 c +1399 1163 1432 1149 1459 1122 c +1487 1095 1501 1062 1501 1023 c +1501 984 1487 951 1459 923 c +1432 896 1399 882 1360 882 c +1321 882 1287 896 1260 923 c +1233 951 1220 984 1220 1023 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1136 255 m +1195 262 1249 280 1300 308 c +1300 601 l +1136 601 l +1136 255 l + +1000 255 m +1000 601 l +836 601 l +836 308 l +887 280 941 262 1000 255 c + +1436 419 m +1484 474 1517 534 1536 601 c +1436 601 l +1436 419 l + +700 417 m +700 601 l +599 601 l +618 533 651 472 700 417 c ce} _d -end readonly def +/u1F601{2135 0 170 -150 1965 1646 sc +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c -/BuildGlyph { - exch begin - CharStrings exch - 2 copy known not {pop /.notdef} if - true 3 1 roll get exec - end -} _d +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c -/BuildChar { - 1 index /Encoding get exch get - 1 index /BuildGlyph get exec -} _d +1126 908 m +1126 1001 1149 1080 1196 1145 c +1243 1211 1299 1244 1364 1244 c +1429 1244 1485 1211 1532 1145 c +1579 1080 1602 1001 1602 908 c +1467 908 l +1467 963 1457 1011 1436 1050 c +1416 1090 1392 1110 1364 1110 c +1336 1110 1312 1090 1291 1050 c +1271 1011 1261 963 1261 908 c +1126 908 l -FontName currentdict end definefont pop -%!PS-Adobe-3.0 Resource-Font -%%Creator: Converted from TrueType to Type 3 by Matplotlib. -10 dict begin -/FontName /WenQuanYiZenHei def -/PaintType 0 def -/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def -/FontBBox [-129 -304 1076 986] def -/FontType 3 def -/Encoding [/uni51E0 /uni6C49 /uni4E2A /uni5B57] def -/CharStrings 5 dict dup begin -/.notdef 0 def -/uni51E0{1024 0 34 -126 1004 818 sc -935 257 m -947 232 970 219 1004 217 c -974 -26 l -973 -40 968 -51 960 -60 c -955 -63 950 -65 943 -65 c -729 -65 l -697 -65 670 -55 649 -34 c -628 -14 617 12 615 43 c -614 75 613 193 613 397 c -613 602 614 719 616 748 c -393 748 l -394 395 l -391 256 361 144 302 58 c -250 -21 184 -82 104 -126 c -89 -96 66 -77 34 -68 c -119 -34 188 23 240 103 c -292 184 318 281 318 395 c -317 818 l -692 818 l -692 55 l -692 42 695 30 702 20 c -709 11 719 6 730 6 c -886 6 l -898 7 905 12 908 23 c -911 42 912 62 913 81 c -935 257 l - -ce} _d -/uni6C49{1024 0 17 -119 990 820 sc -612 211 m -536 349 488 512 468 699 c -447 699 426 698 405 697 c -407 719 407 741 405 763 c -445 761 485 760 526 760 c -871 760 l -851 534 793 348 696 202 c -769 91 867 2 990 -65 c -963 -76 942 -94 928 -119 c -819 -56 727 31 653 143 c -561 27 446 -58 307 -112 c -289 -89 268 -69 243 -53 c -392 -2 515 86 612 211 c - -535 700 m -552 534 592 391 655 271 c -735 396 782 539 796 700 c -535 700 l - -151 -118 m -123 -102 88 -93 47 -92 c -76 -38 107 24 138 93 c -169 162 215 283 274 454 c -315 433 l -199 54 l -151 -118 l - -230 457 m -166 408 l -17 544 l -80 594 l -230 457 l - -248 626 m -202 677 152 724 97 768 c -157 820 l -214 773 268 723 317 670 c -248 626 l - -ce} _d -/uni4E2A{1024 0 14 -123 980 833 sc -547 -123 m -520 -120 492 -120 464 -123 c -467 -72 468 -21 468 30 c -468 362 l -468 413 467 465 464 516 c -492 513 520 513 547 516 c -545 465 544 413 544 362 c -544 30 l -544 -21 545 -72 547 -123 c - -980 427 m -955 410 939 387 931 358 c -846 384 767 429 695 494 c -624 559 563 631 514 711 c -383 520 236 378 71 285 c -59 314 40 337 14 354 c -113 405 204 471 285 550 c -367 630 433 724 484 833 c -499 822 515 813 531 805 c -537 808 l -542 800 l -549 796 557 792 564 789 c -555 775 l -614 672 682 590 759 531 c -824 484 898 450 980 427 c - -ce} _d -/uni5B57{1024 0 32 -132 982 872 sc -982 285 m -980 264 980 243 982 222 c -943 224 904 225 865 225 c -555 225 l -555 -29 l -555 -54 545 -76 525 -95 c -496 -120 444 -132 368 -131 c -376 -98 368 -68 344 -43 c -366 -46 392 -47 422 -47 c -452 -48 470 -46 475 -42 c -480 -38 483 -27 483 -9 c -483 225 l -148 225 l -109 225 71 224 32 222 c -34 243 34 264 32 285 c -71 283 109 282 148 282 c -483 282 l -483 355 l -648 506 l -317 506 l -278 506 239 505 200 503 c -203 524 203 545 200 566 c -239 564 278 563 317 563 c -761 563 l -769 498 l -748 493 730 483 714 469 c -555 323 l -555 282 l -865 282 l -904 282 943 283 982 285 c - -131 562 m -59 562 l -59 753 l -468 752 l -390 807 l -435 872 l -542 798 l -510 752 l -925 752 l -925 562 l -852 562 l -852 695 l -131 695 l -131 562 l +534 908 m +534 1001 557 1080 604 1145 c +651 1211 707 1244 772 1244 c +837 1244 893 1211 940 1145 c +987 1080 1010 1001 1010 908 c +875 908 l +875 963 865 1011 844 1050 c +824 1090 800 1110 772 1110 c +744 1110 720 1090 699 1050 c +679 1011 669 963 669 908 c +534 908 l + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1136 255 m +1195 262 1249 280 1300 308 c +1300 601 l +1136 601 l +1136 255 l + +1000 255 m +1000 601 l +836 601 l +836 308 l +887 280 941 262 1000 255 c + +1436 419 m +1484 474 1517 534 1536 601 c +1436 601 l +1436 419 l + +700 417 m +700 601 l +599 601 l +618 533 651 472 700 417 c + +ce} _d +/u1F602{2393 0 95 -150 2297 1646 sc +828 417 m +828 601 l +727 601 l +746 533 779 472 828 417 c + +1564 419 m +1612 474 1645 534 1664 601 c +1564 601 l +1564 419 l + +1128 255 m +1128 601 l +964 601 l +964 308 l +1015 280 1069 262 1128 255 c + +1264 255 m +1323 262 1377 280 1428 308 c +1428 601 l +1264 601 l +1264 255 l + +574 736 m +1817 736 l +1817 564 1756 418 1635 297 c +1514 176 1368 116 1196 116 c +1024 116 877 176 756 297 c +635 418 574 564 574 736 c + +662 908 m +662 1001 685 1080 732 1145 c +779 1211 835 1244 900 1244 c +965 1244 1021 1211 1068 1145 c +1115 1080 1138 1001 1138 908 c +1003 908 l +1003 963 993 1011 972 1050 c +952 1090 928 1110 900 1110 c +872 1110 848 1090 827 1050 c +807 1011 797 963 797 908 c +662 908 l + +1254 908 m +1254 1001 1277 1080 1324 1145 c +1371 1211 1427 1244 1492 1244 c +1557 1244 1613 1211 1660 1145 c +1707 1080 1730 1001 1730 908 c +1595 908 l +1595 963 1585 1011 1564 1050 c +1544 1090 1520 1110 1492 1110 c +1464 1110 1440 1090 1419 1050 c +1399 1011 1389 963 1389 908 c +1254 908 l + +298 746 m +298 995 386 1207 561 1382 c +736 1558 948 1646 1196 1646 c +1445 1646 1656 1558 1831 1382 c +2006 1207 2093 995 2093 746 c +2093 727 2092 708 2091 690 c +2184 659 l +2223 646 2251 631 2267 615 c +2287 594 2297 572 2297 547 c +2297 522 2288 500 2270 482 c +2252 464 2230 455 2205 455 c +2180 455 2157 465 2136 486 c +2119 503 2105 530 2093 568 c +2082 600 l +2054 414 1970 251 1831 112 c +1656 -63 1445 -150 1196 -150 c +948 -150 736 -63 561 112 c +422 251 338 413 309 598 c +299 568 l +287 530 273 503 256 486 c +235 465 212 455 187 455 c +162 455 140 464 122 482 c +104 500 95 522 95 547 c +95 572 105 594 125 615 c +140 632 168 646 208 659 c +300 689 l +299 708 298 727 298 746 c + +433 746 m +433 535 507 356 656 207 c +805 59 985 -15 1196 -15 c +1407 -15 1587 59 1735 207 c +1884 356 1958 535 1958 746 c +1958 759 1958 772 1957 785 c +1939 768 1918 759 1893 759 c +1868 759 1845 769 1824 790 c +1807 807 1793 834 1781 872 c +1736 1008 l +1872 963 l +1899 954 1921 944 1937 934 c +1906 1067 1839 1184 1736 1287 c +1588 1436 1408 1511 1196 1511 c +985 1511 805 1436 656 1287 c +553 1184 486 1066 455 934 c +471 945 493 954 520 963 c +656 1008 l +611 872 l +599 834 585 807 568 790 c +547 769 524 759 499 759 c +474 759 452 768 434 786 c +433 773 433 759 433 746 c + +ce} _d +/u1F603{2135 0 170 -150 1965 1646 sc +642 1023 m +642 1062 655 1095 682 1122 c +709 1149 743 1163 782 1163 c +821 1163 854 1149 881 1122 c +908 1095 922 1062 922 1023 c +922 984 908 951 881 923 c +854 896 821 882 782 882 c +743 882 709 896 682 923 c +655 951 642 984 642 1023 c + +1220 1023 m +1220 1062 1234 1095 1261 1122 c +1288 1149 1321 1163 1360 1163 c +1399 1163 1432 1149 1459 1122 c +1487 1095 1501 1062 1501 1023 c +1501 984 1487 951 1459 923 c +1432 896 1399 882 1360 882 c +1321 882 1287 896 1260 923 c +1233 951 1220 984 1220 1023 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1536 601 m +599 601 l +621 523 663 453 724 392 c +819 297 933 250 1068 250 c +1203 251 1317 298 1412 393 c +1473 454 1515 524 1536 601 c + +ce} _d +/u1F604{2135 0 170 -150 1965 1646 sc +534 908 m +534 1001 557 1080 604 1145 c +651 1211 707 1244 772 1244 c +837 1244 893 1211 940 1145 c +987 1080 1010 1001 1010 908 c +875 908 l +875 963 865 1011 844 1050 c +824 1090 800 1110 772 1110 c +744 1110 720 1090 699 1050 c +679 1011 669 963 669 908 c +534 908 l + +1126 908 m +1126 1001 1149 1080 1196 1145 c +1243 1211 1299 1244 1364 1244 c +1429 1244 1485 1211 1532 1145 c +1579 1080 1602 1001 1602 908 c +1467 908 l +1467 963 1457 1011 1436 1050 c +1416 1090 1392 1110 1364 1110 c +1336 1110 1312 1090 1291 1050 c +1271 1011 1261 963 1261 908 c +1126 908 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1536 601 m +599 601 l +621 523 663 453 724 392 c +819 297 933 250 1068 251 c +1203 251 1317 298 1412 393 c +1473 454 1515 524 1536 601 c + +ce} _d +/u1F605{2135 0 170 -150 1965 1646 sc +1704 1068 m +1769 940 l +1788 903 1797 873 1796 850 c +1795 821 1786 798 1769 781 c +1751 763 1729 754 1704 754 c +1679 754 1657 763 1639 781 c +1621 799 1612 823 1612 852 c +1612 875 1621 905 1639 940 c +1704 1068 l + +534 908 m +534 1001 557 1080 604 1145 c +651 1211 707 1244 772 1244 c +837 1244 893 1211 940 1145 c +987 1080 1010 1001 1010 908 c +875 908 l +875 963 865 1011 844 1050 c +824 1090 800 1110 772 1110 c +744 1110 720 1090 699 1050 c +679 1011 669 963 669 908 c +534 908 l + +1126 908 m +1126 1001 1149 1080 1196 1145 c +1243 1211 1299 1244 1364 1244 c +1429 1244 1485 1211 1532 1145 c +1579 1080 1602 1001 1602 908 c +1467 908 l +1467 963 1457 1011 1436 1050 c +1416 1090 1392 1110 1364 1110 c +1336 1110 1312 1090 1291 1050 c +1271 1011 1261 963 1261 908 c +1126 908 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1536 601 m +599 601 l +621 523 663 453 724 392 c +819 297 933 250 1068 251 c +1203 251 1317 298 1412 393 c +1473 454 1515 524 1536 601 c + +ce} _d +/u1F606{2135 0 170 -150 1965 1646 sc +1148 1063 m +1443 1269 l +1520 1159 l +1322 1020 l +1520 882 l +1443 772 l +1148 978 l +1148 1063 l + +988 1063 m +988 978 l +693 772 l +616 882 l +814 1020 l +616 1159 l +693 1269 l +988 1063 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +446 736 m +1689 736 l +1689 564 1628 418 1507 297 c +1386 176 1240 116 1068 116 c +896 116 749 176 628 297 c +507 418 446 564 446 736 c + +1536 601 m +599 601 l +621 523 663 453 724 392 c +819 297 933 250 1068 250 c +1203 251 1317 298 1412 393 c +1473 454 1515 524 1536 601 c + +ce} _d +/u1F607{2135 0 143 -150 1992 1891 sc +541 465 m +656 536 l +675 507 697 479 724 454 c +819 360 934 313 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1608 1287 c +1594 1301 l +1441 1263 1266 1244 1068 1244 c +871 1244 695 1263 542 1301 c +528 1287 l +379 1138 304 957 305 746 c + +1220 1021 m +1220 1060 1234 1093 1261 1120 c +1288 1147 1321 1161 1360 1161 c +1399 1161 1432 1147 1459 1120 c +1487 1093 1501 1060 1501 1021 c +1501 982 1487 949 1459 921 c +1432 894 1399 880 1360 880 c +1321 880 1287 894 1260 921 c +1233 949 1220 982 1220 1021 c + +642 1021 m +642 1060 655 1093 682 1120 c +709 1147 743 1161 782 1161 c +821 1161 854 1147 881 1120 c +908 1093 922 1060 922 1021 c +922 982 908 948 881 921 c +854 894 821 880 782 880 c +743 880 709 894 682 921 c +655 949 642 982 642 1021 c + +143 1567 m +143 1656 233 1733 414 1796 c +595 1859 813 1891 1068 1891 c +1324 1891 1542 1859 1722 1796 c +1902 1733 1992 1656 1992 1567 c +1992 1480 1908 1406 1739 1345 c +1890 1177 1965 977 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c +170 977 246 1177 397 1345 c +228 1406 143 1480 143 1567 c + +1642 1439 m +1767 1475 1830 1518 1830 1567 c +1830 1621 1756 1667 1607 1705 c +1459 1743 1279 1762 1068 1762 c +857 1762 677 1743 528 1705 c +379 1667 305 1621 305 1567 c +305 1518 368 1475 494 1439 c +657 1577 848 1646 1068 1646 c +1289 1646 1480 1577 1642 1439 c + +1466 1401 m +1349 1474 1216 1511 1068 1511 c +921 1511 788 1474 671 1401 c +788 1382 921 1373 1068 1373 c +1216 1373 1349 1382 1466 1401 c + +ce} _d +/u1F608{2135 0 170 -150 1965 1840 sc +722 1383 m +1025 1171 l +948 1060 l +645 1273 l +722 1383 l + +1220 949 m +1220 988 1234 1021 1261 1048 c +1288 1075 1321 1089 1360 1089 c +1399 1089 1432 1075 1459 1048 c +1487 1021 1501 988 1501 949 c +1501 910 1487 877 1459 849 c +1432 822 1399 808 1360 808 c +1321 808 1287 822 1260 849 c +1233 877 1220 910 1220 949 c + +642 949 m +642 988 655 1021 682 1048 c +709 1075 743 1089 782 1089 c +821 1089 854 1075 881 1048 c +908 1021 922 988 922 949 c +922 910 908 876 881 849 c +854 822 821 808 782 808 c +743 808 709 822 682 849 c +655 877 642 910 642 949 c + +1414 1383 m +1491 1273 l +1188 1060 l +1111 1171 l +1414 1383 l + +541 465 m +656 536 l +675 507 697 479 724 454 c +819 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +787 1604 m +875 1632 969 1646 1068 1646 c +1167 1646 1261 1632 1349 1604 c +1792 1840 l +1792 1280 l +1907 1126 1965 948 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c +170 948 228 1126 344 1280 c +344 1840 l +787 1604 l + +ce} _d +/u1F609{2135 0 170 -150 1965 1646 sc +541 465 m +656 536 l +675 507 697 479 724 454 c +819 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +642 1021 m +642 1060 655 1093 682 1120 c +709 1147 743 1161 782 1161 c +821 1161 854 1147 881 1120 c +908 1093 922 1060 922 1021 c +922 982 908 948 881 921 c +854 894 821 880 782 880 c +743 880 709 894 682 921 c +655 949 642 982 642 1021 c + +1148 1063 m +1443 1269 l +1520 1159 l +1322 1020 l +1520 882 l +1443 772 l +1148 978 l +1148 1063 l + +ce} _d +/u1F60A{2135 0 170 -150 1965 1646 sc +541 465 m +656 536 l +675 507 697 479 724 454 c +819 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +534 908 m +534 1001 557 1080 604 1145 c +651 1211 707 1244 772 1244 c +837 1244 893 1211 940 1145 c +987 1080 1010 1001 1010 908 c +875 908 l +875 963 865 1011 844 1050 c +824 1090 800 1110 772 1110 c +744 1110 720 1090 699 1050 c +679 1011 669 963 669 908 c +534 908 l + +1126 908 m +1126 1001 1149 1080 1196 1145 c +1243 1211 1299 1244 1364 1244 c +1429 1244 1485 1211 1532 1145 c +1579 1080 1602 1001 1602 908 c +1467 908 l +1467 963 1457 1011 1436 1050 c +1416 1090 1392 1110 1364 1110 c +1336 1110 1312 1090 1291 1050 c +1271 1011 1261 963 1261 908 c +1126 908 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +ce} _d +/u1F60B{2135 0 170 -150 1965 1646 sc +1126 908 m +1126 1001 1149 1080 1196 1145 c +1243 1211 1299 1244 1364 1244 c +1429 1244 1485 1211 1532 1145 c +1579 1080 1602 1001 1602 908 c +1467 908 l +1467 963 1457 1011 1436 1050 c +1416 1090 1392 1110 1364 1110 c +1336 1110 1312 1090 1291 1050 c +1271 1011 1261 963 1261 908 c +1126 908 l + +534 908 m +534 1001 557 1080 604 1145 c +651 1211 707 1244 772 1244 c +837 1244 893 1211 940 1145 c +987 1080 1010 1001 1010 908 c +875 908 l +875 963 865 1011 844 1050 c +824 1090 800 1110 772 1110 c +744 1110 720 1090 699 1050 c +679 1011 669 963 669 908 c +534 908 l + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1159 -15 1243 -1 1322 26 c +1255 58 1199 110 1156 182 c +1127 179 1098 177 1068 177 c +896 177 749 237 628 358 c +507 479 446 625 446 797 c +581 797 l +581 662 628 548 723 453 c +818 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1507 549 1554 663 1554 797 c +1689 797 l +1689 655 1648 531 1566 424 c +1603 357 1621 291 1621 225 c +1621 221 l +1760 366 1830 541 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +1466 320 m +1413 275 1356 241 1295 217 c +1316 190 1339 169 1364 153 c +1388 138 1409 130 1427 130 c +1438 130 1447 133 1456 138 c +1478 151 1489 176 1490 214 c +1490 217 1490 221 1489 225 c +1488 256 1480 287 1466 320 c + +ce} _d +/u1F60C{2135 0 170 -150 1965 1646 sc +541 465 m +656 536 l +675 507 697 479 724 454 c +819 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +1602 1160 m +1602 1067 1579 988 1532 922 c +1485 857 1429 824 1364 824 c +1299 824 1243 857 1196 922 c +1149 988 1126 1067 1126 1160 c +1261 1160 l +1261 1105 1271 1057 1291 1017 c +1312 978 1336 958 1364 958 c +1392 958 1416 978 1436 1017 c +1457 1057 1467 1105 1467 1160 c +1602 1160 l + +1010 1160 m +1010 1067 987 988 940 922 c +893 857 837 824 772 824 c +707 824 651 857 604 922 c +557 988 534 1067 534 1160 c +669 1160 l +669 1105 679 1057 699 1017 c +720 978 744 958 772 958 c +800 958 824 978 844 1017 c +865 1057 875 1105 875 1160 c +1010 1160 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +ce} _d +/u1F60D{2135 0 170 -150 1965 1646 sc +1446 1216 m +1463 1216 1480 1211 1497 1202 c +1537 1177 1557 1144 1557 1102 c +1556 1059 1539 1016 1506 975 c +1338 768 l +1336 768 l +1177 958 l +1136 1003 1115 1051 1115 1102 c +1114 1116 1117 1131 1124 1148 c +1149 1193 1183 1216 1225 1216 c +1252 1215 1277 1204 1298 1183 c +1317 1162 1329 1135 1336 1102 c +1337 1102 l +1337 1111 1341 1126 1350 1145 c +1373 1192 1405 1216 1446 1216 c + +690 1216 m +731 1216 763 1192 786 1145 c +795 1126 799 1111 799 1102 c +800 1102 l +807 1135 819 1162 838 1183 c +859 1204 884 1215 911 1216 c +953 1216 987 1193 1012 1148 c +1019 1131 1022 1116 1021 1102 c +1021 1051 1000 1003 959 958 c +800 768 l +798 768 l +630 975 l +597 1016 580 1059 579 1102 c +579 1144 599 1177 639 1202 c +656 1211 673 1216 690 1216 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c + +541 465 m +656 536 l +675 507 697 479 724 454 c +819 359 933 312 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +ce} _d +/u1F60E{2135 0 170 -150 1965 1646 sc +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 876 1802 994 1746 1101 c +1636 1101 l +1636 1022 l +1636 955 1608 898 1553 851 c +1498 804 1431 780 1352 780 c +1273 780 1206 804 1151 851 c +1096 898 1068 955 1068 1022 c +1068 955 1040 898 985 851 c +930 804 863 780 784 780 c +705 780 638 804 583 851 c +528 898 500 955 500 1022 c +500 1101 l +390 1101 l +333 994 305 876 305 746 c + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +541 465 m +656 536 l +675 507 697 479 724 454 c +819 360 934 313 1068 312 c +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +896 177 749 237 628 358 c +595 392 566 428 541 465 c + +482 1236 m +1654 1236 l +1639 1253 1624 1270 1608 1287 c +1460 1436 1280 1511 1068 1511 c +857 1511 677 1436 528 1287 c +512 1270 497 1253 482 1236 c + +ce} _d +/u1F60F{2135 0 170 -150 1965 1646 sc +1176 908 m +1176 1043 l +1602 1043 l +1602 908 l +1176 908 l + +534 908 m +534 1043 l +960 1043 l +960 908 l +534 908 l + +1068 312 m +1203 312 1317 359 1412 454 c +1438 480 1461 508 1480 537 c +1595 465 l +1570 427 1541 391 1508 358 c +1387 237 1240 177 1068 177 c +1068 312 l + +170 746 m +170 995 258 1207 433 1382 c +608 1558 820 1646 1068 1646 c +1317 1646 1528 1558 1703 1382 c +1878 1207 1965 995 1965 746 c +1965 498 1878 287 1703 112 c +1528 -63 1317 -150 1068 -150 c +820 -150 608 -63 433 112 c +258 287 170 498 170 746 c + +305 746 m +305 535 379 356 528 207 c +677 59 857 -15 1068 -15 c +1279 -15 1459 59 1607 207 c +1756 356 1830 535 1830 746 c +1830 957 1756 1138 1607 1287 c +1459 1436 1279 1511 1068 1511 c +857 1511 677 1436 528 1287 c +379 1138 305 957 305 746 c ce} _d end readonly def @@ -460,61 +16506,692 @@ FontName currentdict end definefont pop end %%EndProlog mpldict begin -18 180 translate -576 432 0 0 clipbox +0 0 translate +0 0 576 432 rectclip gsave 0 0 m 576 0 l 576 432 l 0 432 l cl -1.000 setgray +1 setgray fill grestore -0.000 setgray -/DejaVuSans 27.000 selectfont +0 setgray +/Cmr10-0 16.000 selectfont +gsave + +195.836 362.531 translate +0 rotate +0 0 m /T glyphshow +11.5625 0 m /h glyphshow +20.4531 0 m /e glyphshow +27.5625 0 m /r glyphshow +33.8281 0 m /e glyphshow +40.9375 0 m /space glyphshow +46.2656 0 m /a glyphshow +54.2656 0 m /r glyphshow +60.5312 0 m /e glyphshow +67.6406 0 m /space glyphshow +72.9688 0 m /b glyphshow +81.8594 0 m /a glyphshow +89.8594 0 m /s glyphshow +96.1719 0 m /i glyphshow +100.609 0 m /c glyphshow +107.719 0 m /space glyphshow +113.047 0 m /c glyphshow +120.156 0 m /h glyphshow +129.047 0 m /a glyphshow +137.047 0 m /r glyphshow +143.312 0 m /a glyphshow +151.312 0 m /c glyphshow +158.422 0 m /t glyphshow +164.641 0 m /e glyphshow +171.75 0 m /r glyphshow +178.016 0 m /s glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +34.5859 347.781 translate +0 rotate +0 0 m /A glyphshow +12 0 m /B glyphshow +23.3281 0 m /C glyphshow +34.8906 0 m /D glyphshow +47.1094 0 m /E glyphshow +58 0 m /F glyphshow +68.4375 0 m /G glyphshow +80.9844 0 m /H glyphshow +92.9844 0 m /I glyphshow +98.7656 0 m /J glyphshow +106.984 0 m /K glyphshow +119.422 0 m /L glyphshow +129.422 0 m /M glyphshow +144.078 0 m /N glyphshow +156.078 0 m /O glyphshow +168.516 0 m /P glyphshow +179.406 0 m /Q glyphshow +191.844 0 m /R glyphshow +203.625 0 m /S glyphshow +212.516 0 m /T glyphshow +224.078 0 m /U glyphshow +236.078 0 m /V glyphshow +248.078 0 m /W glyphshow +264.516 0 m /X glyphshow +276.516 0 m /Y glyphshow +288.516 0 m /Z glyphshow +298.297 0 m /space glyphshow +303.625 0 m /a glyphshow +311.625 0 m /b glyphshow +320.516 0 m /c glyphshow +327.625 0 m /d glyphshow +336.516 0 m /e glyphshow +343.625 0 m /f glyphshow +348.516 0 m /g glyphshow +356.516 0 m /h glyphshow +365.406 0 m /i glyphshow +369.844 0 m /j glyphshow +374.734 0 m /k glyphshow +383.172 0 m /l glyphshow +387.609 0 m /m glyphshow +400.938 0 m /n glyphshow +409.828 0 m /o glyphshow +417.828 0 m /p glyphshow +426.719 0 m /q glyphshow +435.156 0 m /r glyphshow +441.422 0 m /s glyphshow +447.734 0 m /t glyphshow +453.953 0 m /u glyphshow +462.844 0 m /v glyphshow +471.281 0 m /w glyphshow +482.844 0 m /x glyphshow +491.281 0 m /y glyphshow +499.719 0 m /z glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +122.281 332.484 translate +0 rotate +0 0 m /zero glyphshow +8 0 m /one glyphshow +16 0 m /two glyphshow +24 0 m /three glyphshow +32 0 m /four glyphshow +40 0 m /five glyphshow +48 0 m /six glyphshow +56 0 m /seven glyphshow +64 0 m /eight glyphshow +72 0 m /nine glyphshow +80 0 m /space glyphshow +85.3281 0 m /exclam glyphshow +89.7656 0 m /quotedblright glyphshow +97.7656 0 m /numbersign glyphshow +111.094 0 m /dollar glyphshow +119.094 0 m /percent glyphshow +132.422 0 m /ampersand glyphshow +144.859 0 m /quoteright glyphshow +149.297 0 m /parenleft glyphshow +155.516 0 m /parenright glyphshow +161.734 0 m /asterisk glyphshow +169.734 0 m /plus glyphshow +182.172 0 m /comma glyphshow +186.609 0 m /hyphen glyphshow +191.938 0 m /period glyphshow +196.375 0 m /slash glyphshow +204.375 0 m /colon glyphshow +208.812 0 m /semicolon glyphshow +213.25 0 m /exclamdown glyphshow +217.688 0 m /equal glyphshow +230.125 0 m /questiondown glyphshow +237.688 0 m /question glyphshow +245.25 0 m /at glyphshow +257.688 0 m /bracketleft glyphshow +262.125 0 m /quotedblleft glyphshow +270.125 0 m /bracketright glyphshow +274.562 0 m /circumflex glyphshow +282.562 0 m /dotaccent glyphshow +287 0 m /quoteleft glyphshow +291.438 0 m /emdash glyphshow +299.438 0 m /endash glyphshow +315.438 0 m /hungarumlaut glyphshow +323.438 0 m /tilde glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +203.922 317.203 translate +0 rotate +0 0 m /a glyphshow +8 0 m /n glyphshow +16.8906 0 m /d glyphshow +25.7812 0 m /space glyphshow +31.1094 0 m /a glyphshow +39.1094 0 m /c glyphshow +46.2188 0 m /c glyphshow +53.3281 0 m /e glyphshow +60.4375 0 m /n glyphshow +69.3281 0 m /t glyphshow +75.5469 0 m /e glyphshow +82.6562 0 m /d glyphshow +91.5469 0 m /space glyphshow +96.875 0 m /c glyphshow +103.984 0 m /h glyphshow +112.875 0 m /a glyphshow +120.875 0 m /r glyphshow +127.141 0 m /a glyphshow +135.141 0 m /c glyphshow +142.25 0 m /t glyphshow +148.469 0 m /e glyphshow +155.578 0 m /r glyphshow +161.844 0 m /s glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +144.602 299.047 translate +0 rotate +0 0 m /Aring glyphshow +10.9531 0 m /AE glyphshow +26.5469 0 m /Ccedilla glyphshow +37.7188 0 m /Egrave glyphshow +47.8281 0 m /Eacute glyphshow +57.9375 0 m /Ecircumflex glyphshow +68.0469 0 m /Edieresis glyphshow +78.1562 0 m /Igrave glyphshow +82.875 0 m /Iacute glyphshow +87.5938 0 m /Icircumflex glyphshow +92.3125 0 m /Idieresis glyphshow +97.0312 0 m /Eth glyphshow +109.438 0 m /Ntilde glyphshow +121.406 0 m /Ograve glyphshow +134 0 m /Oacute glyphshow +146.594 0 m /Ocircumflex glyphshow +159.188 0 m /Otilde glyphshow +171.781 0 m /Odieresis glyphshow +184.375 0 m /multiply glyphshow +197.781 0 m /Oslash glyphshow +210.375 0 m /Ugrave glyphshow +222.094 0 m /Uacute glyphshow +233.812 0 m /Ucircumflex glyphshow +245.531 0 m /Udieresis glyphshow +257.25 0 m /Yacute glyphshow +267.031 0 m /Thorn glyphshow +276.719 0 m /germandbls glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +136.82 281.703 translate +0 rotate +0 0 m /agrave glyphshow +9.8125 0 m /aacute glyphshow +19.625 0 m /acircumflex glyphshow +29.4375 0 m /atilde glyphshow +39.25 0 m /adieresis glyphshow +49.0625 0 m /aring glyphshow +58.875 0 m /ae glyphshow +74.5938 0 m /ccedilla glyphshow +83.3906 0 m /egrave glyphshow +93.2344 0 m /eacute glyphshow +103.078 0 m /ecircumflex glyphshow +112.922 0 m /edieresis glyphshow +122.766 0 m /igrave glyphshow +127.219 0 m /iacute glyphshow +131.672 0 m /icircumflex glyphshow +136.125 0 m /idieresis glyphshow +140.578 0 m /eth glyphshow +150.375 0 m /ntilde glyphshow +160.516 0 m /ograve glyphshow +170.312 0 m /oacute glyphshow +180.109 0 m /ocircumflex glyphshow +189.906 0 m /otilde glyphshow +199.703 0 m /odieresis glyphshow +209.5 0 m /divide glyphshow +222.906 0 m /oslash glyphshow +232.703 0 m /ugrave glyphshow +242.844 0 m /uacute glyphshow +252.984 0 m /ucircumflex glyphshow +263.125 0 m /udieresis glyphshow +273.266 0 m /yacute glyphshow +282.734 0 m /thorn glyphshow +292.891 0 m /ydieresis glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +121.945 263.234 translate +0 rotate +0 0 m /Amacron glyphshow +10.9531 0 m /amacron glyphshow +20.7656 0 m /Abreve glyphshow +31.7188 0 m /abreve glyphshow +41.5312 0 m /Aogonek glyphshow +52.4844 0 m /aogonek glyphshow +62.2969 0 m /Cacute glyphshow +73.4688 0 m /cacute glyphshow +82.2656 0 m /Ccircumflex glyphshow +93.4375 0 m /ccircumflex glyphshow +102.234 0 m /Cdotaccent glyphshow +113.406 0 m /cdotaccent glyphshow +122.203 0 m /Ccaron glyphshow +133.375 0 m /ccaron glyphshow +142.172 0 m /Dcaron glyphshow +154.5 0 m /dcaron glyphshow +164.656 0 m /Dcroat glyphshow +177.062 0 m /dcroat glyphshow +187.219 0 m /Emacron glyphshow +197.328 0 m /emacron glyphshow +207.172 0 m /Ebreve glyphshow +217.281 0 m /ebreve glyphshow +227.125 0 m /Edotaccent glyphshow +237.234 0 m /edotaccent glyphshow +247.078 0 m /Eogonek glyphshow +257.188 0 m /eogonek glyphshow +267.031 0 m /Ecaron glyphshow +277.141 0 m /ecaron glyphshow +286.984 0 m /Gcircumflex glyphshow +299.391 0 m /gcircumflex glyphshow +309.547 0 m /Gbreve glyphshow +321.953 0 m /gbreve glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +164.969 245.047 translate +0 rotate +0 0 m /Gdotaccent glyphshow +12.4062 0 m /gdotaccent glyphshow +22.5625 0 m /Gcommaaccent glyphshow +34.9688 0 m /gcommaaccent glyphshow +45.125 0 m /Hcircumflex glyphshow +57.1562 0 m /hcircumflex glyphshow +67.2969 0 m /Hbar glyphshow +81.9531 0 m /hbar glyphshow +93.0781 0 m /Itilde glyphshow +97.7969 0 m /itilde glyphshow +102.25 0 m /Imacron glyphshow +106.969 0 m /imacron glyphshow +111.422 0 m /Ibreve glyphshow +116.141 0 m /ibreve glyphshow +120.594 0 m /Iogonek glyphshow +125.312 0 m /iogonek glyphshow +129.766 0 m /Idotaccent glyphshow +134.484 0 m /dotlessi glyphshow +138.938 0 m /IJ glyphshow +148.375 0 m /ij glyphshow +157.266 0 m /Jcircumflex glyphshow +161.984 0 m /jcircumflex glyphshow +166.438 0 m /Kcommaaccent glyphshow +176.938 0 m /kcommaaccent glyphshow +186.203 0 m /kgreenlandic glyphshow +195.469 0 m /Lacute glyphshow +204.391 0 m /lacute glyphshow +208.844 0 m /Lcommaaccent glyphshow +217.766 0 m /lcommaaccent glyphshow +222.219 0 m /Lcaron glyphshow +231.141 0 m /lcaron glyphshow +237.141 0 m /Ldot glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +123.125 226.188 translate +0 rotate +0 0 m /ldot glyphshow +5.46875 0 m /Lslash glyphshow +14.4688 0 m /lslash glyphshow +19.0156 0 m /Nacute glyphshow +30.9844 0 m /nacute glyphshow +41.125 0 m /Ncommaaccent glyphshow +53.0938 0 m /ncommaaccent glyphshow +63.2344 0 m /Ncaron glyphshow +75.2031 0 m /ncaron glyphshow +85.3438 0 m /napostrophe glyphshow +98.3594 0 m /Eng glyphshow +110.328 0 m /eng glyphshow +120.469 0 m /Omacron glyphshow +133.062 0 m /omacron glyphshow +142.859 0 m /Obreve glyphshow +155.453 0 m /obreve glyphshow +165.25 0 m /Ohungarumlaut glyphshow +177.844 0 m /ohungarumlaut glyphshow +187.641 0 m /OE glyphshow +204.766 0 m /oe glyphshow +221.141 0 m /Racute glyphshow +232.266 0 m /racute glyphshow +238.844 0 m /Rcommaaccent glyphshow +249.969 0 m /rcommaaccent glyphshow +256.547 0 m /Rcaron glyphshow +267.672 0 m /rcaron glyphshow +274.25 0 m /Sacute glyphshow +284.406 0 m /sacute glyphshow +292.75 0 m /Scircumflex glyphshow +302.906 0 m /scircumflex glyphshow +311.25 0 m /Scedilla glyphshow +321.406 0 m /scedilla glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +128.219 207.516 translate +0 rotate +0 0 m /Scaron glyphshow +10.1562 0 m /scaron glyphshow +18.5 0 m /Tcommaaccent glyphshow +28.2812 0 m /tcommaaccent glyphshow +34.5625 0 m /Tcaron glyphshow +44.3438 0 m /tcaron glyphshow +50.625 0 m /Tbar glyphshow +60.4062 0 m /tbar glyphshow +66.6875 0 m /Utilde glyphshow +78.4062 0 m /utilde glyphshow +88.5469 0 m /Umacron glyphshow +100.266 0 m /umacron glyphshow +110.406 0 m /Ubreve glyphshow +122.125 0 m /ubreve glyphshow +132.266 0 m /Uring glyphshow +143.984 0 m /uring glyphshow +154.125 0 m /Uhungarumlaut glyphshow +165.844 0 m /uhungarumlaut glyphshow +175.984 0 m /Uogonek glyphshow +187.703 0 m /uogonek glyphshow +197.844 0 m /Wcircumflex glyphshow +213.672 0 m /wcircumflex glyphshow +226.766 0 m /Ycircumflex glyphshow +236.547 0 m /ycircumflex glyphshow +246.016 0 m /Ydieresis glyphshow +255.797 0 m /Zacute glyphshow +266.766 0 m /zacute glyphshow +275.172 0 m /Zdotaccent glyphshow +286.141 0 m /zdotaccent glyphshow +294.547 0 m /Zcaron glyphshow +305.516 0 m /zcaron glyphshow +313.922 0 m /longs glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +120.906 189.406 translate +0 rotate +0 0 m /uni0180 glyphshow +10.1562 0 m /uni0181 glyphshow +21.9219 0 m /uni0182 glyphshow +32.9062 0 m /uni0183 glyphshow +43.0625 0 m /uni0184 glyphshow +54.0469 0 m /uni0185 glyphshow +64.2031 0 m /uni0186 glyphshow +75.4531 0 m /uni0187 glyphshow +86.625 0 m /uni0188 glyphshow +95.4219 0 m /uni0189 glyphshow +107.828 0 m /uni018A glyphshow +120.938 0 m /uni018B glyphshow +131.922 0 m /uni018C glyphshow +142.078 0 m /uni018D glyphshow +151.875 0 m /uni018E glyphshow +161.984 0 m /uni018F glyphshow +174.578 0 m /uni0190 glyphshow +184.406 0 m /uni0191 glyphshow +193.609 0 m /florin glyphshow +199.25 0 m /uni0193 glyphshow +211.656 0 m /uni0194 glyphshow +222.641 0 m /uni0195 glyphshow +238.391 0 m /uni0196 glyphshow +244.047 0 m /uni0197 glyphshow +248.766 0 m /uni0198 glyphshow +260.703 0 m /uni0199 glyphshow +269.969 0 m /uni019A glyphshow +274.422 0 m /uni019B glyphshow +283.891 0 m /uni019C glyphshow +299.484 0 m /uni019D glyphshow +311.453 0 m /uni019E glyphshow +321.594 0 m /uni019F glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +124.211 173.891 translate +0 rotate +0 0 m /Ohorn glyphshow +14.6094 0 m /ohorn glyphshow +24.4062 0 m /uni01A2 glyphshow +39.5938 0 m /uni01A3 glyphshow +51.75 0 m /uni01A4 glyphshow +62.1875 0 m /uni01A5 glyphshow +72.3438 0 m /uni01A6 glyphshow +83.4688 0 m /uni01A7 glyphshow +93.625 0 m /uni01A8 glyphshow +101.969 0 m /uni01A9 glyphshow +112.078 0 m /uni01AA glyphshow +117.453 0 m /uni01AB glyphshow +123.734 0 m /uni01AC glyphshow +133.516 0 m /uni01AD glyphshow +139.797 0 m /uni01AE glyphshow +149.578 0 m /Uhorn glyphshow +163.312 0 m /uhorn glyphshow +173.453 0 m /uni01B1 glyphshow +185.688 0 m /uni01B2 glyphshow +197.219 0 m /uni01B3 glyphshow +209.125 0 m /uni01B4 glyphshow +220.812 0 m /uni01B5 glyphshow +231.781 0 m /uni01B6 glyphshow +240.188 0 m /uni01B7 glyphshow +250.844 0 m /uni01B8 glyphshow +261.5 0 m /uni01B9 glyphshow +270.75 0 m /uni01BA glyphshow +279.156 0 m /uni01BB glyphshow +289.344 0 m /uni01BC glyphshow +300 0 m /uni01BD glyphshow +309.25 0 m /uni01BE glyphshow +317.422 0 m /uni01BF glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +110.68 153.734 translate +0 rotate +0 0 m /uni01C0 glyphshow +4.71875 0 m /uni01C1 glyphshow +12.5938 0 m /uni01C2 glyphshow +19.9375 0 m /uni01C3 glyphshow +24.6719 0 m /uni01C4 glyphshow +47.4219 0 m /uni01C5 glyphshow +68.2031 0 m /uni01C6 glyphshow +86.6719 0 m /uni01C7 glyphshow +100.047 0 m /uni01C8 glyphshow +112.641 0 m /uni01C9 glyphshow +119.953 0 m /uni01CA glyphshow +134.859 0 m /uni01CB glyphshow +149.641 0 m /uni01CC glyphshow +162.406 0 m /uni01CD glyphshow +173.359 0 m /uni01CE glyphshow +183.172 0 m /uni01CF glyphshow +187.891 0 m /uni01D0 glyphshow +192.344 0 m /uni01D1 glyphshow +204.938 0 m /uni01D2 glyphshow +214.734 0 m /uni01D3 glyphshow +226.453 0 m /uni01D4 glyphshow +236.594 0 m /uni01D5 glyphshow +248.312 0 m /uni01D6 glyphshow +258.453 0 m /uni01D7 glyphshow +270.172 0 m /uni01D8 glyphshow +280.312 0 m /uni01D9 glyphshow +292.031 0 m /uni01DA glyphshow +302.172 0 m /uni01DB glyphshow +313.891 0 m /uni01DC glyphshow +324.031 0 m /uni01DD glyphshow +333.875 0 m /uni01DE glyphshow +344.828 0 m /uni01DF glyphshow +grestore +/DejaVuSans-1 16.000 selectfont +gsave + +90.0078 134 translate +0 rotate +0 0 m /uni01E0 glyphshow +10.9531 0 m /uni01E1 glyphshow +20.7656 0 m /uni01E2 glyphshow +36.3594 0 m /uni01E3 glyphshow +52.0781 0 m /uni01E4 glyphshow +64.4844 0 m /uni01E5 glyphshow +74.6406 0 m /Gcaron glyphshow +87.0469 0 m /gcaron glyphshow +97.2031 0 m /uni01E8 glyphshow +107.703 0 m /uni01E9 glyphshow +116.969 0 m /uni01EA glyphshow +129.562 0 m /uni01EB glyphshow +139.359 0 m /uni01EC glyphshow +151.953 0 m /uni01ED glyphshow +161.75 0 m /uni01EE glyphshow +172.406 0 m /uni01EF glyphshow +181.656 0 m /uni01F0 glyphshow +186.109 0 m /uni01F1 glyphshow +208.859 0 m /uni01F2 glyphshow +229.641 0 m /uni01F3 glyphshow +248.109 0 m /uni01F4 glyphshow +260.516 0 m /uni01F5 glyphshow +270.672 0 m /uni01F6 glyphshow +288.484 0 m /uni01F7 glyphshow +299.406 0 m /uni01F8 glyphshow +311.375 0 m /uni01F9 glyphshow +321.516 0 m /Aringacute glyphshow +332.469 0 m /aringacute glyphshow +342.281 0 m /AEacute glyphshow +357.875 0 m /aeacute glyphshow +373.594 0 m /Oslashacute glyphshow +386.188 0 m /oslashacute glyphshow +grestore +/DejaVuSans-2 16.000 selectfont +gsave + +138.602 115.719 translate +0 rotate +0 0 m /uni0200 glyphshow +10.9531 0 m /uni0201 glyphshow +20.7656 0 m /uni0202 glyphshow +31.7188 0 m /uni0203 glyphshow +41.5312 0 m /uni0204 glyphshow +51.6406 0 m /uni0205 glyphshow +61.4844 0 m /uni0206 glyphshow +71.5938 0 m /uni0207 glyphshow +81.4375 0 m /uni0208 glyphshow +86.1562 0 m /uni0209 glyphshow +90.6094 0 m /uni020A glyphshow +95.3281 0 m /uni020B glyphshow +99.7812 0 m /uni020C glyphshow +112.375 0 m /uni020D glyphshow +122.172 0 m /uni020E glyphshow +134.766 0 m /uni020F glyphshow +144.562 0 m /uni0210 glyphshow +155.688 0 m /uni0211 glyphshow +162.266 0 m /uni0212 glyphshow +173.391 0 m /uni0213 glyphshow +179.969 0 m /uni0214 glyphshow +191.688 0 m /uni0215 glyphshow +201.828 0 m /uni0216 glyphshow +213.547 0 m /uni0217 glyphshow +223.688 0 m /Scommaaccent glyphshow +233.844 0 m /scommaaccent glyphshow +242.188 0 m /uni021A glyphshow +251.969 0 m /uni021B glyphshow +258.25 0 m /uni021C glyphshow +268.281 0 m /uni021D glyphshow +276.625 0 m /uni021E glyphshow +288.656 0 m /uni021F glyphshow +grestore +/DejaVuSans-2 16.000 selectfont gsave -86.4 205.2 translate +118.953 95.4688 translate 0 rotate -0.000000 0 m /T glyphshow -16.492676 0 m /h glyphshow -33.604980 0 m /e glyphshow -50.216309 0 m /r glyphshow -61.316895 0 m /e glyphshow -77.928223 0 m /space glyphshow -86.510742 0 m /a glyphshow -103.056152 0 m /r glyphshow -114.156738 0 m /e glyphshow -130.768066 0 m /space glyphshow +0 0 m /uni0220 glyphshow +11.7656 0 m /uni0221 glyphshow +25.1719 0 m /uni0222 glyphshow +36.3438 0 m /uni0223 glyphshow +46.1094 0 m /uni0224 glyphshow +57.0781 0 m /uni0225 glyphshow +65.4844 0 m /uni0226 glyphshow +76.4375 0 m /uni0227 glyphshow +86.25 0 m /uni0228 glyphshow +96.3594 0 m /uni0229 glyphshow +106.203 0 m /uni022A glyphshow +118.797 0 m /uni022B glyphshow +128.594 0 m /uni022C glyphshow +141.188 0 m /uni022D glyphshow +150.984 0 m /uni022E glyphshow +163.578 0 m /uni022F glyphshow +173.375 0 m /uni0230 glyphshow +185.969 0 m /uni0231 glyphshow +195.766 0 m /uni0232 glyphshow +205.547 0 m /uni0233 glyphshow +215.016 0 m /uni0234 glyphshow +222.609 0 m /uni0235 glyphshow +236.094 0 m /uni0236 glyphshow +243.734 0 m /dotlessj glyphshow +248.188 0 m /uni0238 glyphshow +264.156 0 m /uni0239 glyphshow +280.125 0 m /uni023A glyphshow +291.078 0 m /uni023B glyphshow +302.25 0 m /uni023C glyphshow +311.047 0 m /uni023D glyphshow +319.969 0 m /uni023E glyphshow +329.75 0 m /uni023F glyphshow grestore -/WenQuanYiZenHei 27.000 selectfont +/DejaVuSans-2 16.000 selectfont gsave -86.4 205.2 translate +79.4297 76.8125 translate 0 rotate -139.350586 0 m /uni51E0 glyphshow -166.350586 0 m /uni4E2A glyphshow -193.350586 0 m /uni6C49 glyphshow -220.350586 0 m /uni5B57 glyphshow +0 0 m /uni0240 glyphshow +8.40625 0 m /uni0241 glyphshow +18.0625 0 m /uni0242 glyphshow +25.7344 0 m /uni0243 glyphshow +36.7188 0 m /uni0244 glyphshow +48.4375 0 m /uni0245 glyphshow +59.3906 0 m /uni0246 glyphshow +69.5 0 m /uni0247 glyphshow +79.3438 0 m /uni0248 glyphshow +84.0625 0 m /uni0249 glyphshow +88.5156 0 m /uni024A glyphshow +101.016 0 m /uni024B glyphshow +111.172 0 m /uni024C glyphshow +122.297 0 m /uni024D glyphshow +128.875 0 m /uni024E glyphshow +138.656 0 m /uni024F glyphshow +148.125 0 m /u1F600 glyphshow +164.812 0 m /u1F601 glyphshow +181.5 0 m /u1F602 glyphshow +200.203 0 m /u1F603 glyphshow +216.891 0 m /u1F604 glyphshow +233.578 0 m /u1F605 glyphshow +250.266 0 m /u1F606 glyphshow +266.953 0 m /u1F607 glyphshow +283.641 0 m /u1F608 glyphshow +300.328 0 m /u1F609 glyphshow +317.016 0 m /u1F60A glyphshow +333.703 0 m /u1F60B glyphshow +350.391 0 m /u1F60C glyphshow +367.078 0 m /u1F60D glyphshow +383.766 0 m /u1F60E glyphshow +400.453 0 m /u1F60F glyphshow grestore -/DejaVuSans 27.000 selectfont +/Cmr10-0 16.000 selectfont gsave -86.4 205.2 translate +248.008 61.4844 translate 0 rotate -247.350586 0 m /space glyphshow -255.933105 0 m /i glyphshow -263.434570 0 m /n glyphshow -280.546875 0 m /space glyphshow -289.129395 0 m /b glyphshow -306.268066 0 m /e glyphshow -322.879395 0 m /t glyphshow -333.465820 0 m /w glyphshow -355.548340 0 m /e glyphshow -372.159668 0 m /e glyphshow -388.770996 0 m /n glyphshow -405.883301 0 m /exclam glyphshow +0 0 m /i glyphshow +4.4375 0 m /n glyphshow +13.3281 0 m /space glyphshow +18.6562 0 m /b glyphshow +27.5469 0 m /e glyphshow +34.6562 0 m /t glyphshow +40.875 0 m /w glyphshow +52.4375 0 m /e glyphshow +59.5469 0 m /e glyphshow +66.6562 0 m /n glyphshow +75.5469 0 m /exclam glyphshow grestore end diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps index 472824330da4..61a6802918dd 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps @@ -1,13 +1,14 @@ %!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 %%Title: multi_font_type42.eps -%%Creator: Matplotlib v3.6.0.dev2856+g4848cedd6d.d20220807, https://matplotlib.org/ -%%CreationDate: Sun Aug 7 16:45:01 2022 +%%Creator: Matplotlib v3.11.0.dev2222+g4230aa142, https://matplotlib.org/ +%%CreationDate: Fri Apr 3 00:28:30 2026 %%Orientation: portrait -%%BoundingBox: 18 180 594 612 -%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%BoundingBox: 0 0 576 432 +%%HiResBoundingBox: 0.000000 0.000000 576.000000 432.000000 %%EndComments %%BeginProlog -/mpldict 12 dict def +/mpldict 11 dict def mpldict begin /_d { bind def } bind def /m { moveto } _d @@ -16,345 +17,17966 @@ mpldict begin /c { curveto } _d /cl { closepath } _d /ce { closepath eofill } _d -/box { - m - 1 index 0 r - 0 exch r - neg 0 r - cl - } _d -/clipbox { - box - clip - newpath - } _d /sc { setcachedevice } _d -%!PS-TrueTypeFont-1.0-2.22937 -%%Title: unknown -%%Creator: Converted from TrueType to type 42 by PPR -15 dict begin -/FontName /DejaVuSans def -/PaintType 0 def -/FontMatrix[1 0 0 1 0 0]def -/FontBBox[-1021 -463 1793 1232]def -/FontType 42 def -/Encoding StandardEncoding def -/FontInfo 10 dict dup begin -/FamilyName (unknown) def -/FullName (unknown) def -/Weight (unknown) def -/Version (unknown) def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -130 def -/UnderlineThickness 90 def + + %%!PS-TrueTypeFont-1.0-1.0000000 + 10 dict begin + /FontType 42 def + /FontMatrix [1 0 0 1 0 0] def + /FontName /Cmr10-0 def + /FontInfo 7 dict dup begin + /FullName (cmr10) def + /FamilyName (cmr10) def + /Version (1.1/12-Nov-94) def + /ItalicAngle 0.0 def + /isFixedPitch false def + /UnderlinePosition -133 def + /UnderlineThickness 20 def + end readonly def + /Encoding StandardEncoding def + /FontBBox [-90 -512 2066 1536] def + /PaintType 0 def + /CIDMap 0 def + /CharStrings 133 dict dup begin +/.notdef 0 def +/.null 1 def +/nonmarkingreturn 2 def +/Xi 3 def +/dotlessi 4 def +/oslash 5 def +/Delta 6 def +/Theta 7 def +/germandbls 8 def +/Phi 9 def +/Sigma 10 def +/Upsilon 11 def +/Omega 12 def +/OE 13 def +/Lambda 14 def +/Psi 15 def +/ae 16 def +/Pi 17 def +/oe 18 def +/Gamma 19 def +/Oslash 20 def +/comma 21 def +/nine 22 def +/M 23 def +/Z 24 def +/quoteleft 25 def +/dotaccent 26 def +/g 27 def +/t 28 def +/exclam 29 def +/period 30 def +/semicolon 31 def +/B 32 def +/O 33 def +/quotedblright 34 def +/ring 35 def +/i 36 def +/v 37 def +/ff 38 def +/numbersign 39 def +/zero 40 def +/equal 41 def +/D 42 def +/Q 43 def +/k 44 def +/x 45 def +/hungarumlaut 46 def +/ffl 47 def +/percent 48 def +/two 49 def +/question 50 def +/F 51 def +/fl 52 def +/questiondown 53 def +/caron 54 def +/S 55 def +/m 56 def +/z 57 def +/quoteright 58 def +/four 59 def +/H 60 def +/U 61 def +/bracketleft 62 def +/acute 63 def +/AE 64 def +/b 65 def +/o 66 def +/polishlcross 67 def +/parenright 68 def +/six 69 def +/J 70 def +/W 71 def +/bracketright 72 def +/d 73 def +/q 74 def +/tilde 75 def +/plus 76 def +/eight 77 def +/L 78 def +/Y 79 def +/f 80 def +/breve 81 def +/s 82 def +/space 83 def +/hyphen 84 def +/colon 85 def +/A 86 def +/N 87 def +/quotedblleft 88 def +/h 89 def +/u 90 def +/dieresis 91 def +/slash 92 def +/C 93 def +/ffi 94 def +/P 95 def +/j 96 def +/cedilla 97 def +/w 98 def +/dollar 99 def +/one 100 def +/E 101 def +/dotlessj 102 def +/exclamdown 103 def +/fi 104 def +/R 105 def +/l 106 def +/y 107 def +/hardspace 108 def +/ampersand 109 def +/three 110 def +/at 111 def +/G 112 def +/T 113 def +/grave 114 def +/a 115 def +/n 116 def +/emdash 117 def +/endash 118 def +/parenleft 119 def +/five 120 def +/I 121 def +/V 122 def +/c 123 def +/circumflex 124 def +/p 125 def +/asterisk 126 def +/seven 127 def +/K 128 def +/X 129 def +/e 130 def +/macron 131 def +/r 132 def end readonly def -/sfnts[<0001000000090080000300106376742000691D390000009C000001FE6670676D -7134766A0000029C000000AB676C7966118399D500000348000007B668656164085DC286 -00000B0000000036686865610D9F077C00000B3800000024686D74783A5706B700000B5C -0000003C6C6F636108C30B6500000B98000000206D617870047C067100000BB800000020 -707265703B07F10000000BD800000568013500B800CB00CB00C100AA009C01A600B80066 -0000007100CB00A002B20085007500B800C301CB0189022D00CB00A600F000D300AA0087 -00CB03AA0400014A003300CB000000D9050200F4015400B4009C01390114013907060400 -044E04B4045204B804E704CD0037047304CD04600473013303A2055605A60556053903C5 -021200C9001F00B801DF007300BA03E9033303BC0444040E00DF03CD03AA00E503AA0404 -000000CB008F00A4007B00B80014016F007F027B0252008F00C705CD009A009A006F00CB -00CD019E01D300F000BA018300D5009803040248009E01D500C100CB00F600830354027F -00000333026600D300C700A400CD008F009A0073040005D5010A00FE022B00A400B4009C -00000062009C0000001D032D05D505D505D505F0007F007B005400A406B80614072301D3 -00B800CB00A601C301EC069300A000D3035C037103DB0185042304A80448008F01390114 -01390360008F05D5019A0614072306660179046004600460047B009C00000277046001AA -00E904600762007B00C5007F027B000000B4025205CD006600BC00660077061000CD013B -01850389008F007B0000001D00CD074A042F009C009C0000077D006F0000006F0335006A -006F007B00AE00B2002D0396008F027B00F600830354063705F6008F009C04E10266008F -018D02F600CD03440029006604EE00730000140000960000B707060504030201002C2010 -B002254964B040515820C859212D2CB002254964B040515820C859212D2C20100720B000 -50B00D7920B8FFFF5058041B0559B0051CB0032508B0042523E120B00050B00D7920B8FF -FF5058041B0559B0051CB0032508E12D2C4B505820B0FD454459212D2CB002254560442D -2C4B5358B00225B0022545445921212D2C45442D2CB00225B0022549B00525B005254960 -B0206368208A108A233A8A10653A2D00000201350000020005D5000300090035400F0700 -8304810208070501030400000A10FC4BB00B5458B90000FFC038593CEC32393931002FE4 -FCCC3001B6000B200B500B035D253315231133110323030135CBCBCB14A215FEFE05D5FD -71FE9B0165000001FFFA000004E905D50007004A400E0602950081040140031C00400508 -10D4E4FCE431002FF4EC3230014BB00A5458BD00080040000100080008FFC03811373859 -401300091F00100110021F071009400970099F09095D03211521112311210604EFFDEECB -FDEE05D5AAFAD5052B000002007BFFE3042D047B000A002500BC4027191F0B17090E00A9 -1706B90E1120861FBA1CB923B8118C170C001703180D09080B1F030814452610FCECCCD4 -EC323211393931002FC4E4F4FCF4EC10C6EE10EE11391139123930406E301D301E301F30 -20302130223F27401D401E401F402040214022501D501E501F5020502150225027702785 -1D871E871F8720872185229027A027F0271E301E301F30203021401E401F40204021501E -501F50205021601E601F60206021701E701F70207021801E801F80208021185D015D0122 -061514163332363D01371123350E01232226353436332135342623220607353E01333216 -02BEDFAC816F99B9B8B83FBC88ACCBFDFB0102A79760B65465BE5AF3F00233667B6273D9 -B4294CFD81AA6661C1A2BDC0127F8B2E2EAA2727FC00000200BAFFE304A40614000B001C -0038401903B90C0F09B918158C0FB81B971900121247180C06081A461D10FCEC3232F4EC -31002FECE4F4C4EC10C6EE30B6601E801EA01E03015D013426232206151416333236013E -01333200111002232226271523113303E5A79292A7A79292A7FD8E3AB17BCC00FFFFCC7B -B13AB9B9022FCBE7E7CBCBE7E702526461FEBCFEF8FEF8FEBC6164A8061400020071FFE3 -047F047B0014001B00704024001501098608880515A90105B90C01BB18B912B80C8C1C1B -1502081508004B02120F451C10FCECF4ECC4111239310010E4F4ECE410EE10EE10F4EE11 -12393040293F1D701DA01DD01DF01D053F003F013F023F153F1B052C072F082F092C0A6F -006F016F026F156F1B095D71015D0115211E0133323637150E0123200011100033320007 -2E0123220607047FFCB20CCDB76AC76263D06BFEF4FEC70129FCE20107B802A5889AB90E -025E5ABEC73434AE2A2C0138010A01130143FEDDC497B4AE9E00000100BA000004640614 -001300344019030900030E0106870E11B80C970A010208004E0D09080B461410FCEC32F4 -EC31002F3CECF4C4EC1112173930B2601501015D0111231134262322061511231133113E -013332160464B87C7C95ACB9B942B375C1C602A4FD5C029E9F9EBEA4FD870614FD9E6564 -EF00000200C100000179061400030007002B400E06BE04B100BC020501080400460810FC -3CEC3231002FE4FCEC30400B1009400950096009700905015D1333112311331523C1B8B8 -B8B80460FBA00614E900000100BA00000464047B001300364019030900030E0106870E11 -B80CBC0A010208004E0D09080B461410FCEC32F4EC31002F3CE4F4C4EC1112173930B460 -15CF1502015D0111231134262322061511231133153E013332160464B87C7C95ACB9B942 -B375C1C602A4FD5C029E9F9EBEA4FD870460AE6564EF000100BA0000034A047B00110030 -4014060B0700110B03870EB809BC070A06080008461210FCC4EC3231002FE4F4ECC4D4CC -11123930B450139F1302015D012E012322061511231133153E0133321617034A1F492C9C -A7B9B93ABA85132E1C03B41211CBBEFDB20460AE6663050500010037000002F2059E0013 -003840190E05080F03A9001101BC08870A0B08090204000810120E461410FC3CC4FC3CC4 -32393931002FECF43CC4EC3211393930B2AF1501015D01112115211114163B0115232226 -3511233533110177017BFE854B73BDBDD5A28787059EFEC28FFDA0894E9A9FD202608F01 -3E0000010056000006350460000C01EB404905550605090A0904550A0903550A0B0A0255 -01020B0B0A061107080705110405080807021103020C000C011100000C420A0502030603 -00BF0B080C0B0A09080605040302010B07000D10D44BB00A544BB011545B4BB012545B4B -B013545B4BB00B545B58B9000000403859014BB00C544BB00D545B4BB010545B58B90000 -FFC03859CC173931002F3CEC32321739304B5358071005ED071008ED071008ED071005ED -071008ED071005ED0705ED071008ED59220140FF050216021605220A350A49024905460A -400A5B025B05550A500A6E026E05660A79027F0279057F05870299029805940ABC02BC05 -CE02C703CF051D0502090306040B050A080B09040B050C1502190316041A051B081B0914 -0B150C2500250123022703210425052206220725082709240A210B230C39033604360839 -0C300E460248034604400442054006400740084409440A440B400E400E56005601560250 -0451055206520750085309540A550B6300640165026A0365046A056A066A076E09610B67 -0C6F0E7500750179027D0378047D057A067F067A077F07780879097F097B0A760B7D0C87 -0288058F0E97009701940293039C049B05980698079908402F960C9F0EA600A601A402A4 -03AB04AB05A906A907AB08A40CAF0EB502B103BD04BB05B809BF0EC402C303CC04CA0579 -5D005D13331B01331B013301230B012356B8E6E5D9E6E5B8FEDBD9F1F2D90460FC96036A -FC96036AFBA00396FC6A00000001000000025999D203C60C5F0F3CF5001F080000000000 -D17E0EE400000000D17E0EE4F7D6FC4C0E5909DC00000008000000000000000000010000 -076DFE1D00000EFEF7D6FA510E5900010000000000000000000000000000000F04CD0066 -0000000002AA0000028B00000335013504E3FFFA04E7007B051400BA04EC0071051200BA -023900C1051200BA034A00BA03230037068B0056000000000000000000000031006900FF -014B01B501F102190255028C02C903DB00010000000F0354002B0068000C000200100099 -000800000415021600080004B8028040FFFBFE03FA1403F92503F83203F79603F60E03F5 -FE03F4FE03F32503F20E03F19603F02503EF8A4105EFFE03EE9603ED9603ECFA03EBFA03 -EAFE03E93A03E84203E7FE03E63203E5E45305E59603E48A4105E45303E3E22F05E3FA03 -E22F03E1FE03E0FE03DF3203DE1403DD9603DCFE03DB1203DA7D03D9BB03D8FE03D68A41 -05D67D03D5D44705D57D03D44703D3D21B05D3FE03D21B03D1FE03D0FE03CFFE03CEFE03 -CD9603CCCB1E05CCFE03CB1E03CA3203C9FE03C6851105C61C03C51603C4FE03C3FE03C2 -FE03C1FE03C0FE03BFFE03BEFE03BDFE03BCFE03BBFE03BA1103B9862505B9FE03B8B7BB -05B8FE03B7B65D05B7BB03B78004B6B52505B65D40FF03B64004B52503B4FE03B39603B2 -FE03B1FE03B0FE03AFFE03AE6403AD0E03ACAB2505AC6403ABAA1205AB2503AA1203A98A -4105A9FA03A8FE03A7FE03A6FE03A51203A4FE03A3A20E05A33203A20E03A16403A08A41 -05A096039FFE039E9D0C059EFE039D0C039C9B19059C64039B9A10059B19039A1003990A -0398FE0397960D0597FE03960D03958A410595960394930E05942803930E0392FA039190 -BB0591FE03908F5D0590BB039080048F8E25058F5D038F40048E25038DFE038C8B2E058C -FE038B2E038A8625058A410389880B05891403880B038786250587640386851105862503 -85110384FE038382110583FE0382110381FE0380FE037FFE0340FF7E7D7D057EFE037D7D -037C64037B5415057B25037AFE0379FE03780E03770C03760A0375FE0374FA0373FA0372 -FA0371FA0370FE036FFE036EFE036C21036BFE036A1142056A530369FE03687D03671142 -0566FE0365FE0364FE0363FE0362FE03613A0360FA035E0C035DFE035BFE035AFE035958 -0A0559FA03580A035716190557320356FE03555415055542035415035301100553180352 -1403514A130551FE03500B034FFE034E4D10054EFE034D10034CFE034B4A13054BFE034A -4910054A1303491D0D05491003480D0347FE0346960345960344FE0343022D0543FA0342 -BB03414B0340FE033FFE033E3D12053E14033D3C0F053D12033C3B0D053C40FF0F033B0D -033AFE0339FE033837140538FA033736100537140336350B05361003350B03341E03330D -0332310B0532FE03310B03302F0B05300D032F0B032E2D09052E10032D09032C32032B2A -25052B64032A2912052A25032912032827250528410327250326250B05260F03250B0324 -FE0323FE03220F03210110052112032064031FFA031E1D0D051E64031D0D031C1142051C -FE031BFA031A42031911420519FE031864031716190517FE031601100516190315FE0314 -FE0313FE031211420512FE0311022D05114203107D030F64030EFE030D0C16050DFE030C -0110050C16030BFE030A100309FE0308022D0508FE030714030664030401100504FE0340 -1503022D0503FE0302011005022D0301100300FE0301B80164858D012B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B002B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B -2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B1D00>]def -/CharStrings 13 dict dup begin + /sfnts[<00010000000d0080000300504f532f321350119e00004b940000004e636d61700ed90eff000000dc000000ea6376 +74204d184f4a000001c8000000da6670676d0211c261000002a4000001d8676c7966725a810d000008e40000405668656164 +5f1a847b0000047c00000036686865610d5f069100004b7000000024686d7478ab9523030000493c000002146c6f63619a5d +a8d6000004b40000010c6d617870017400cb00004b50000000206e616d651c3b34c20000077000000174706f737474c85e0b +000005e80000018670726570ef569262000005c00000002800000001000300010000000c000400de00000004000400010000 +007effff00000020ffff00000001000400000053001d0022002700630030006d003a00770044007e004c00150054001e005c +002800640031006e003b00780045007f004d00160055001f0067002900350032006f00560020005d002a006500330070003c +007900460080004e001700570021005f002b006900370071003d007a00470081004f0018003e00580048007c001a00190073 +0041007b004900820050001b005900240060002c006a003800740042007d004a00840052001c005a00250062002d006b0039 +00750076002e004b000000060008000e001d002b0042fe5afe73ffd30000037303a0057705a401cd00e100db00d500b000a8 +00a600a400980093008d007f006d006a0068005e00560052004e004a00480042003d003b003700350033002f002107fe07ee +05ec05c305b005a0057b0552050804df040803fe03e9031902fc02f402e302aa026d025a0227021f01e901c10185017f016d +012500ee00e100df00db00d900d500cf00c500c300c100be00ba00b800b400b200ae00a600a400a200a000960091008f008b +0087007f007d00790073006f006a0062005200480042003b00350021000040161514131211100f0e0d0c0b0a090807060504 +030201002cb200800043208a628a234266562d2cb22a0000435478b0002b58173959b0002b58173c59b0002b58b00a2a59b0 +014310b0002b58173c59b0002b58b00a2a592d2c2b2d2c2bb0022a2d2cb0022a2d2cb00162b0002342b10103254220462068 +6164b0032546206820b0044323612064b140408a545821212121b100211c5950582121b1000425204668b007254561b00051 +58211bb0054338591b6164595358232f23f91b2f23e959b0012b2d2cb00162b0002342b101032542204620686164b0032546 +206861645358232f23f91b2f23e959b0012b2d2cb00162b0002342b1010525423fe9b0012b2d2cb00162b0002342b1010325 +423ff9b0012b2d2c111217392d2cc12d2cb2000100432020b004438a45b003436169604460422d2c4520b0032342b2010205 +43764323438a23616960b004234218b00b2a2d2cb0002342184569b0406120b000515821b0411bb04061b0005158b0461bb0 +485959b00523424520b001234269b0022342b00c2a182d2c204568442d2cba00110005ffc0422b2d2cb2110500422b2d2c20 +20b102038a4223b0016142466820b0405458b0406059b00423422d2cb1020343114312173931002d2c2e2d2cc52d2c3fb014 +2a2d00010000000100003b6a6a965f0f3cf500030800000000007c259dc0000000007c259dc0ffa6fe000812060000000006 +0002000000000000000000000000000000690069006900690069006900690069006900690069006900690069006900690069 +006900a00110016401af01ea020e02b302f80336035903a70412046a04d104d105100550055005ea0632066e06bf075907b8 +0824085d085d0904096e09db0a2f0a2f0a9a0a9a0b210b9a0be50c1e0c5f0cad0cf60d180d180d180d710dbb0dbb0df60e72 +0eba0f230f450fab1007103b107e10fb1130117d11d211d2124e124e126612a112ee133613a013f314431443146b14da14da +15271583158315e8169716c6172817281766176617de180718651865190a19961a3f1abb1b031b031b7d1bd11be91c011c42 +1cc21ce81d301d851da41e061e731eba1f1a1f8a1fe11fe1202b401e072703220b1f080f04275d0e0d076a0c5d0755055123 +4a055d5d2b0d35008db8033c851d2b2b0002000000000000ff7b001400000000000000000000000000000000000000000085 +000000010002010200d700a100a801030089010401050106009f00b00107010800a0010900b1010a0091000f001c0030003d +00b600dc004a005700040011001e0025003200b500dd004c0059010b00060013002000270034004e005b00df010c00080015 +0022002900c100a200e100360050005d00b70017002b0038003e008d009000450052010d000c0019002d003a004000470054 +00d9000e001b002f003c004900db005600030010001d0024003100b4004b0058008e00120026010e0033004d00de005a0007 +00140028010f00a300c00035004f005c0110000900160023002a003700430044005100b300b2000b0018002c0039004600d8 +0053000d001a002e003b004800da005502586905546865746103506869055369676d6107557073696c6f6e064c616d626461 +035073690250690547616d6d610266660366666c0c706f6c6973686c63726f73730366666908646f746c6573736a09686172 +647370616365000000000007005a000300010409000000be00000003000104090001000a00be0003000104090002000e00c8 +0003000104090003002000d60003000104090004000a00be0003000104090005001a00f60003000104090006000a01100043 +006f0070007900720069006700680074002000280043002900200031003900390034002c00200042006100730069006c0020 +004b002e0020004d0061006c00790073006800650076002e00200041006c006c002000520069006700680074007300200052 +0065007300650072007600650064002e00300031003200420061004b006f004d006100200046006f006e0074007300200043 +006f006c006c0065006300740069006f006e002c0020004c006500760065006c002d0042002e0063006d0072003100300052 +006500670075006c006100720046006f006e0074004d006f006e006700650072003a0063006d0072003100300031002e0031 +002f00310032002d004e006f0076002d003900340043006d00720031003000030056000004fe0577000f001b0029004a4042 +27220225140b011a080209201c0225161e0c040816120218100216141a14060e020208160009100703041f0e0218016a1714 +12051a011c01026a12110c06022b0f032b31002b2a3033033316171e01332132363736373303011133152135331123352115 +031321132326272623212207060766103b0518088864021065870818053b10fc2f3b02a43b3bfd5cf8110472113c05150fe3 +fdfce30f1505015e9d1b080606081b9dfea2022301686868fe986868020c0148feb88c180c0c188c000100acfe7301a400e1 +00180020401806010e0f08090107010416070b00026b11060405011a0f032b31003f2b301334373e013d0106232226353436 +3332161514060706232226cb084a5221312f42422f493e5b5308050a14fe8f090847ba671921422f3040875272cd52041200 +00020056ffd303a805540028003a003b403300010b02093225130c00080801220126290b140602231c090007030408014e18 +2f010500014e251f010650370f0006033c0f032b31002b2b303716333236373e01350e0123222e0135343e0133321e021514 +02062322263534363332161514062301323e013d02342623220e0215141616e7388b4e87252b1823835577bb6470c67e7ca7 +582374e7a379a8392a29393a280108546b305a7f5165300d166256426a4d57c592536a81d3777bd57d87d5ee749efeb7dc72 +732a39392a283a019c71a85327089af24776864f74a47d00000100480000070c057700250037402b211b0b03131e0917090f +0c080c000916100701041225180b0a041b220956131b0005692204000602270f032b2b2b3f3f3f3f3f2a3033353235113423 +3521321709013633211522151114331521353235110106232227011114331548d3d30183190801be01be08190183d3d3fdac +d3fe0809191a09fe0ed34879042d414817fb790487174841fb9b4148484104a8fae61717050afba07948000100730000047b +0577001e0033402b1d0c02071509130107220e0c0108050115200009010702041c1413110d0c0b060509351d03010501200f +032b31002b2b3033223d0134370121220e0115231321321d01140701213236373e02373303891604031cfee090bb5f3c1703 +b41702fce3012d6dae3b292e0e073c23171c080404f04cae9301d517180904fb13283a297c7462fdd5000001008f031f018d +058d001b001f401714010f160001060104090c190c026b12031005011d0f032b31003f2b3001222635343e01373633321615 +14070e0215141736333216151406011d4a442c533608040a1509314a26021d39313f40031f87524e91832f04130908092b75 +8642150a2741302e4200000100ac0479018d055a000c001840110a0f030c000801044807000005010e0f032b31002b301334 +3633321e01151406232226ac442d1c361e422e2d4404e92d441e361d2c44440000030039fe5a03e103a0003b004b0057005b +4052210802514c0914011a11021f0151260f0a15082a01264c28100603012f194809010740263807000704042a014e3c0917 +011f1d02633444110521110259244e0106542c0803040a0602633c00110603590f032b2b31002b2b30173436372e01353437 +2635343e023332173e0133321615140623222635343722071e0115140e01232227061514163b01321e0115140e0123222626 +37141e0133323e013534262b01220606013235342623220615141616396a49292b3d5e3762783f7a612a733e2c3826191a26 +196a4c252d609d53705d1d4736a87ac48598d35a5bd49873709e46459e6fc48ca82f4f30011bae4a64644a1c4ca0496d171f +5e35604a5c774070522b472d313f2c19262619260f49256b35578b4d3d283236512c84795a773535775a455d2d2d5d456b3f +2d5101d8f86b8b8b6b446e4600010027ffe902a804ec001b0037402e1401010f090a0103010122080a03080f241909000702 +04070a016a151201050c01030106015b07000d06021d0f032b31002e2b2b303711233532363533112115211114163332363d +013315140e01232226d1aa867e3b0121fedf394a463e3b2f5c427b8ff6023535fa92fe8748fdcf5580874e797d407d509300 +000200ac0000018d05ba000c001d002040181b01030f0a09100701041216010f014807000a05011f0f032b31002e2b303734 +3633321e0115140623222613033534363332161d010314062b012226ac442d1c361e422e2d445454452c2d43520c0619050b +712e421e361c2d4444015403b00c2b3b3b2b0cfc50070c0d000100ac0000018d00e1000c00184011030f0a09000701044807 +000005010e0f032b31002b3037343633321e01151406232226ac442d1c361e422e2d44712e421e361c2d4444000200acfe73 +019303730018002600294021240f1d0a000805010d0f0709010702041607190a000321016b1005050501280f032b31003f2b +30133437363d010623222635343633321615140e01070623222603343e0133321e01151406232226cb048f1e232f42422f47 +2f23473106090a141f1e351e1d351e412f2e43fe8f09049bd10a12422f30408356488c86350612047d1d342020341d2e4343 +00030046000005350577001700250036003a403432010622080c02080f0126272501061c010122000908070304070102124c +132000050f014d0c2c04062601551804080603380f032b2b2b30333532351134233521321e0115140607321e0115140e0123 +2514163321323e0135342e0123213521323e0235342e022321220e011546d3d302f168d48bcd8959bc7885d46bfe5a323601 +04508950427b4ffe7701333e6d572f23445c35fefc25291a484104654148519f6c7da91a62a45c70ac5d892c15548d504e95 +60362d556b3c3562502d061d1e0000020073ffd305c505a40011002a0023401c2024090c000815240009000702044a0e1b00 +054a27040006022c0f032b31002b30052224023534123e0133321e011215140204011e0133323e0137361110272e01232206 +070e0215141616031dc3fec9b065b5fe9291ffb662b0fec8fdec3cb068438169257a7a3cb36364b43c30351615392dcf0157 +bd8c0112d47c7dd6fef691bdfea9cf010a5e6f365e39b801420127ac566868564397a2575db1aa0000020044031f02cf058d +001a00350034402a2a012401220702090f0f0c0b080104331827221d1b042013096b2e2000050c00026b1305040602370f03 +2b2b31002e2e2b301334373e013534270623222635343633321e0115140607062322262534373e0135342706232226353436 +33321e01151406070623222662084c56021d392e42422e33401b615408040b14018e084e54021e382f42422f32401b615308 +050a14033b090842c066150a27422f304044662f71d64a04120a090844bd67150a27422f304044662f71d54b04120002003f +000001fe055a000f001c0025401c1a0f130c000801040a0a0009170f02100901035c0a051405011e0f032b31003f3f2b3033 +35323635113426233525111416331501343633321e011514062322263f465a3d5a01274e41fe98442c1d361e442d2c444816 +2b022f4f244816fd002b164804e92d441e361d2c444400010027ffe904100373001d002140170c1c09140a060a130f0c0b09 +07063715050105011f0f032b31003f3f3f2e3021012e0123352115221514171b01363534262335211522060701062b012201 +f4fed5134f4001ae7302e6cf063b2801543f5f1afeec08190f1902f026154848310804fdbe020a1011272d48483a39fd4817 +00020073fe730635058d0047004b004f4044271e024b2e021f130c0a064930024237021f0a030a06020445073b07220c180c +340107014b4a49484241403e38302f27261e1d1c140c0b030200162e2b100b05014d0f032b31003f3f3f3f2b300134371321 +22263534363321132122263534363321133e013332161d010321133e013332161d0103213216151406232103213216151406 +232103062322263534371321030623222601211321015602a6fe9c11161611017d51fe321116161101e7ac03150f1118a801 +81ac03150f1118a8016610151510fe815201d110151510fe17aa0b1e111802a6fe7faa0b1e11180114018251fe7ffe9c0404 +026c1a0f1118013c18110f1a027f0d11181108fd94027f0d11181108fd941a0f1118fec418110f1afd811e18110404026cfd +811e1802d7013c0000020050ffd303ae0554000f00200023401c1827070c00081027000900070204550c140005551c030006 +02220f032b31002b300522021134123633321e021514020627323612353402262322060215141e01160200fbb541c1ae87ac +5a2141beaf72701a1a6f7374701a0b306b2d019d011db2013adb84d1ef83b0fecdd735ea011ca09a0104d3d4fefd9a72cad7 +930000020073011005c502f0000d001b002040191f150e00061f07000006020418011101320a030a05011d0f032b31002b30 +132226353436332132161514062301222635343633213216151406239a1116161105060f16160ffafa1116161105060f1616 +0f01101a0f111818110f1a018e18110f1a1a0f11180000020044000005a8057700120028002a402424010622080c02081701 +0122000908070204070102124c0d1d00055313040006022a0f032b2b2b30333532351134233521321e011215140e02232514 +163b013236373e01353426272e012b01220e011544d3d302f38ce8a4595aa9e787fe983236cb69bd3e412c2c413dbc6bcb25 +291a48410465414879caff008682f5c572892c155b5156d58f95e058575d061d1e0000030073fe7305d105a400260031004e +004f404539012c12094424090c000827362c00064d1b02270112250009180715102007000704042509392925033e2f091d01 +12014c0e3e14054d016b2f3201064c4a04000603500f032b2b31003f2b2b30052224023534123e0133321e01121514020607 +1e01333236353436333215140623222e0227062732372e0123220615141627343e01333216173e03353426272e0123220607 +0e011514121726031dc3fec9b065b5fe9291ffb66263bd7e2752494d6e0f07177383485e311c0b5a665c56134f5031444176 +2c4f2f596828445c331739443bb66567b53c463794ad162dcf0157bd8c0112d47c7dd6fef6918dfef4d43b5b656a4c070c1b +93f6476d903b1f3b2f586949303443772d50317362338b99ab568efa64596d6b5b66fa8cdafe9b4a2a000001003500000417 +058d002a004140392524230c0412010915011222140a0208291f1c031e01012200090a0702040b0c211f1d1815130f07122a +010a010226015d0b051505012c0f032b2b3f2b2b30333532363511342e0123352511253635342623352115220f01011e0133 +152135323534270307151416331535465a213e41012f011329221801818b8b920105364954fe684625c57d5b4548162b0433 +37310b4816fc34f1272118194848797ffe8e4d2c48482b1f2f01186ce42b16480001001900000421037300330044403c2c2b +1211040901091a011b180c0309220b0a0a08322623032501012200090a070204332f2c2b29261f1b191512110f0c0a051035 +2401010501350f032b31002b2b30333532363f01032e0123352115220615141f0137363534262335211522060f01011e0133 +152135323635342f0107061514163315194e8a30baf224554d01aa1b2f06a47b19211b01794f8b30a2010627544efe56192e +06b893172217483d3ced013b2d1548481817080bd59e1e1e192448483d3ccffea62d14484818170909f4bc1a221924480002 +01020419035c059a000a001500204016150a0e0c030c15140b0a0907063e1200010501170f032b31003f3f2e2e3001133633 +321e011514070325133633321e011514070301026f123113281710bf01216f123113281710bf0433013334142612151afefa +1a013334142612151afefa0000040073ff8d063506000029003600470054005c405324070303311a02014f4802091601271f +310806271a0500063f0e021b2a4f010627012648370206040424014c5202012d34020922016a434c040503015f523b100607 +016a092d010600015f3412100604560f032b2a31002b2a3005343701062322271615140e0223222e0135343e013332171633 +32363736333216151407010623222613323635342e0123220615141601222e0135343e0133321e0115140e010627323e0135 +3426232206151416011d0603a07b94a093291f406544608a45458a604c3fa5e279d2430a16121706fbdf091511188565682b +5c4666424403ef608a45458a605978371f3f6545455e2a68656642444a0c09056645536279428d7d5180c05d5bc1803da26b +641016130b09f9d70d1a0355f17743ab79e38681e5fc9180c15d5bc18087be57428c7f51367baa4376f0e38582e400010066 +00000398055400320044403b2e2918030d2b091210020d22200c0408310103012b140009110702042901091509310132302e +034f240909051b012a100303481501030602340f032b2b31002b2b3033353437013e0335342e012322060736333216151406 +23222635343e0233321e0115140e020f0133323637363733036604013e485a58333e7b57598e1d080e2e41412e30413a6d89 +4d75ca764e7abe1ee8c591c30618193c3a37050601604e6a8a8f5054995c6b55023e312f41432d4d87693863b57959a083a6 +1cdf05051aa3fe93000200730000035205a4000c003a0035402d221b021803091827290c00083801030f0a0910070204552d +1400050e0148070001061b014e1f250106033c0f032b31002b2b3025343633321e0115140623222613353412373e0135342e +012322060733321615140623222635343e0133321e01151406070e011d0114062b0122260156442d1c361e422e2d4454625a +1e1c265e554f89260c29393a28283a639e5361b5753833758d0c0619050b712e421e361c2d44440154688601025d20593154 +602c403f392a283a3a28547f44337e663c6f2452ec8464070c0d0001003f000004e105770026003b40321b01061609100106 +22080c02080a01221622100602041d0009090701031226011d016a1c1a06052301511504080602280f032b2b3f2e2b2b3033 +353235113423352113232e032b01220e01151133323e0135331123342e012b01111421153fd3d30469393b0e2f5a9784bb25 +291a916962253b3b256269910106484104654148fe2b81955522061d1efdf1256269fdd9696225fdf141480000020073fe5c +03520400002c00390036402c1c013719090f3037000619272a07000702040c23091c014e26200105100148342d0106551500 +0006033b0f032b31003f2e2b2b30173436373e023d0134363b0132161d011406070615141e01333236372322263534363332 +1615140e0123222613343633321e01151406232226732e2e456135090719070b4b4a2b1247495ca92e0a293a3a29283a7ab8 +5991c3e3442d1c361e422e2d448d3b6f25388ea45864060d0c076882fe653a704b5d383c433a29283a3a285e7f3a8a04a92d +441e361d2c44440000010073ffd303fe05a400490045403c3627131204052f0a091a011c012f23200c0c08440148010a2240 +091207020444361c1312050e070924015e3c0e0105330116016a07011206024b0f032b2b31002b2b301711343b0132161514 +1633323e0135342e0127252e0135343e0133321737363b0132161511142b012235342e01272623220e0115141617051e0315 +140e0123222e012707062b0122731219060af9ca477843325e3bfef684a76cb76acd795406080e060b111813233e2463a346 +76466d58010a42754e2c66b96e448e78315606090c121d01de100a06c9dd5285473e75560e4123d9876aba6c8b85060908fe +251212347c722464477a43588f174110536e87486fc5781e3e3187060001003d000006850389003f0047403c0f0b02110c02 +37012627140a0e0801042f091e090a0a00091d011f015b182314052e01300111015b2a3415063f010a01020b015b3b051506 +03410f032b31003f3f3f3f2b30333532363511342e01233525153e013332173e0133321e0115111416331521353236351134 +2623220615111416331521353236351134262322061511141633153d465a213e41012929a15fec29299e5e5d7f405b45fe2b +465a3a5a769a5a46fe2b465a3a5a77995a4548162b022f37310b4816c85870c0566a3c7b5dfe142b164848162b01e6677ebe +79fe6c2b164848162b01e66481be79fe6c2b164800010039000003350373001f0033402b1e0d02071709150107260f0a0108 +050117250009010702041d1615130e0d0c060509391e03010501210f032b31002b2b3033223d0134370123220e0215231321 +32161d01140701333236373e01373303501706023cb8577149213b1702ae090d04fdc3c459772a271d083b23171008060308 +163d6b5e01520d0a0c0608fcf9162a278461fe79000100ac031f01aa058d001a001f40170701090f0f0c01080104180c0002 +6b13050405011c0f032b31002e2b301334373e013534270623222635343633321e011514060706232226cb084e54021e382f +42422f32401b615308050a14033b090844bd67150a27422f304044662f71d54b041200020039000003c5055400160019002f +4026180102160122080a0a0601041905110917010212100a02070112011901570c161d05011b0f032b2b3f2e2e2b30133501 +363b013215113315231514163315213532363535252111390279070e1e17c9c9784ffdcc4f78fe2701e501524803b00a17fc +5d48c92a174848172ac94802d5000001003f000005be05770023003a402f220d1f000601041a09120c080c00091913070104 +12231b1109040e0c091e0151160e02052001510c04080602250f032b2b2b3f3f3f3f2b303335323511342335211522151121 +113423352115221511143315213532351121111433153fd3d30265d3025cd30264d2d2fd9cd3fda4d34841046541484841fe +0e01f241484841fb9b41484841022bfdd54148000001003fffd305be05770022002d40240d221f0900070104160c050c1704 +02121506021209096819120005510900000602240f032b2b2b3f3f2b3001113423352115221511141e0133323e0135113423 +352115221511140e02232226260112d30265d33f957875b260d301edd2437fac6188f39001cf031f41484841fce972cb7f7b +cc7502df79484879fd195fb6935488ea000100f2fe00020a06000007002040191f02030006001f0507000802040703026705 +01100501090f032b31002b301311211523113315f20118c6c6fe00080052f8a4520000020035ffe9042d058d0019002a0033 +402a080127270c0a040818011d26150910070204070c000955112100050601191807035b1a000506022c0f032b31003f3f2b +303311342e01233525113e0233321e0215140e012322262707371e0133323e013534272e0223220607d5213e41012f225b68 +365c9d744179d17d4e9230465a22804e6a83342d1445552f528c2904bc37310b4816fd7f26391e4a82a95a7cd67f50427bc9 +4b5f7aba67ad5a284427574900020039ffe903c50396001000240023401c1b26090a00081124000900070204540d14000554 +2104000602260f032b31002b3005222e0135343e0233321e0115140606273236353426272e02232206070e01151416160200 +7bd27a437da6617ecf787ad17aa46e162517474f2a40732626152879177dd27c5eae894d85df7e7bd37d3ceeb86787372233 +1b3a363a8b6073b77c0000010073fe0002540600001b0016400f0e000a0202126014060005011d0f032b2b2e2e3013223534 +3700111001263534363b013217161a0115140a0106070623851204015efea6080b0713060493c45b316ba4720406fe001209 +040156028b028b0152050c070b0474feb4fe88c491fee7feefe75a0400020056ffd303a80554002c00420040403817011a24 +091a25090c0008100121013626240a14082d23000900070304170150293201054e0d1300063c013f0121014e3a0519060344 +0f032b31002b2b3005222e023534123633321e011514062322263534363b012e0123220e04153e0133321e02151406062732 +3e0235342e0123220e0115141714060714161602007faa5d247ef5a8467945392a283a3a280b1a5f333e6954381f08248453 +5b966c396bc27b4f602d0b166265536b3102010124662d87d7ec79a20146d63567492a393a29283a2523365c6f8e7c5e546b +4a83a85678d980414877795874a47d70ab4f1b0e03040358b47c0001004cffd303b8057700230028401f0327150900070104 +1c0c0c0d0152100810050b010001472019110602250f032b31003f2e2b30371e0133323e013511342135211522061511140e +0123222e0135343633321e0115140623ba257643425b2dfeee0268455871b35f539861443321361f44327334375b873f03c5 +414848162bfc33629854437f5433441f3820324400010025ffd30812057700300039402f211202221f131007050422060c0a +0801042b190b03132f092809201c19181513110c0b0a070b2c2205010501320f032b31003f3f2a2b3005012e012335211522 +1d010901272e0123352115221514161509013635342623352115220701062b0122270901062b0122027dfe5e105c4a0225ac +014701172f105c4a0224ae040146013302723e01b6a226fe7007170f1707feaefeac07180e181705052b164848370afc1003 +5e922b16484837030c02fc1703b8060d3630484879fb3316160413fbed160001002dfe00014606000007002040191f060300 +06001f010700080204050102670703040501090f032b31002b3013353311233521112dc7c70119fe0052075c52f800000002 +0044ffe9043b058d001c002e003d403317012820090b012827080a04081a01000120261909120702041809120c1801110124 +010c015b12191705552c04000602300f032b31003f3f2b2b3005222e0135343e013332161711342e0123352511141e013315 +05350606251e0133323637112e0223220e0115141601f479c86f79d07d4b8631213d41012f213d41fecb3592fee423754555 +8e2318495b336b8234111783d6787cd57e3f3801aa37310b4816fb2d36310b4817813d44c94350624e01eb2d472679bc6752 +7a0000020044fe73043b0389001b002b0034402c110114012325160a0c0805011c2608091007020400071b01010120151403 +5b1605150655280d0006022d10032b31003f2b300135323635110e0123222e0235343e013332161737331114163315013236 +37112e0123220e02151416160266465a3092505c9e754178d17957942c49365a45fdc55b8f2215845c466e49243a78fe7347 +182a017b404e4c82ab587ad77e6252b4fb732a184701ac795c016862904a7c904052c186000100aa04930354055800130025 +401e030111010b1c090c0a08010113011b070d0a0602043c0a00000501150f032b31002b3013373633321e01333237170706 +23222e01232207aa3b5050275d5d27524c293b514f275d5d27524c04b6465c2e2e5c23455d2f2e5d00010073ff5605c504aa +001f0026401e06011f011f0f160a0601041b0b1303021217011e01670e070a0501210f032b2b2e2e2b301322263534363321 +1134363332161511213216151406232111140623222635119a11161611025a18110f1a025a0f16160ffda61a0f111801d71a +0f0f1a025c10151510fda41a0f0f1afda410151510025c0000030056ffd303a80554001e002e003c0043403a302f2b2a1303 +0637230937250b0c000823231b09000702042b2a02333b0964172600051301660f3304060301663b071006641f000006043e +0f032b2b31002b2b3013343637272e0135343e0133321e0115140e0107171e0115140e012322262637141e0133323635342e +0127250e010613173e0135342e0123220e01151456a27f4c465867ab615ba96d41714075516377c46d6ac57b6f59925077c2 +1f3722feed406b3e8df8566e4d7c473e7e5201377bbd3f312e9954629e5a4a8a5f45765e214b35ac5f6fb66456a36b51864c +8b73274d3f14b22268820229a0328e59457340305f4060000001003f000004a8057700160024401c10010122000908070104 +15080c150907010412510c04000501180f032b2b3f2e2b303335323511342335211520151114163b01323e013733033fd3d3 +0298fefa32369e99a645123b394841046541484841fb9b2c1570c1a0fde700010017000005e705770021002f402718011916 +0b0308220a0c0a080104100009211917130d090107120b011001531e04050501230f032b2b3f2e2b302135323511012e0123 +3521152215141709013635342623352115220607011114331501cfd3fe521e6c5302418f04017201520d472c01bc508927fe +73d3484101a602c3291448482f040cfd9d022b131629254848393cfd75fe5a41480000010042000002e305a40028003d4034 +19011b070914011b270e0c0408200122010522070a0a0802040009282219035211170106070102050120015c24080706022a +10032b31003f2b2b3033353236351123353335343e023332161514062322263534372623220e011d01331523111416331542 +455a9d9d375d7941456f3527273737211a3c552aece5784e48162b02a248f54273563152442735352740160b557a3cf148fd +5e2a174800010044ffe902e103960040004c4042302f240f0e0604072908091701190129291d0a0c083b013f010826390912 +0702041a0a3b302f190f0e060b2c09211a0265340b010501010601652c13030602420f032b2b31003f2b2b301711343b0132 +17123332363534262f012e0235343e0133321737343b0132161511142b01223534262322061514161f011e0215140e022322 +2707142b01224412190c0439db61836646894571485f9858694e3b0a0f060a101912776b5c8761418b467947335b7c44805b +4b0b0c1206014e1010fed7585c425d111b0f3e67445a73333833050b06fef413136b8244533949101a104c74494a6d482256 +510500010017017b023501fa00030017401019020000060104400301000501050f032b31002b301335211517021e017b7f7f +000200ac0000018d0373000c001a0022401b180f110a0008030f0a090007020415010d014807000a05011c0f032b31002b30 +37343633321e0115140623222611343e0133321e01151406232226ac442d1c361e422e2d441e351e1d351e412f2e43712e42 +1e361c2d444402be1d342020341d2e43430000020042000005bc05ba001c001f00314028221e1500061b100d030f01012200 +090a0702041f071f1e1d1c181514131009310e01010501210f032b31002e2e2b303335323701363b013217011e0133152135 +323d0103210306151416331503210342b72a01ae061b1a1b0601c1136b50fdc5aa6ffe0f5c0261382301c1e1487904e31616 +fae52b164848370a0140fef8070e3331480210028e000001003f000005be0577001f003240261b0b1809100c080c00091107 +0103121f0f0a030c1c091a0169130c0105691c04000602210f032b2b2b3f3f3f3f2e2e303335323511262335213217011134 +2335211522151114062b01222701111433153fd3439001880a0402d5d301e7d21005190a04fca4d3487904620c4808fbd503 +7279484879fb5c060c0804f2fbc779480002012f031f03ba058d001b00370035402b32011c013013020f15000b060104250c +080c19130d0b041f11093528026b2e1f10056b1103000602390f032b2b31003f3f2b3001222635343637363332161514070e +021514173633321e0115140621222635343e0137363332161514070e021514173633321615140601bc49445f5508050b1308 +314c25021e3820331e41015e4a442c533608040a1509314a26021d39313f40031f875271d44c04130909082b788342150a27 +1e32212f4187524e91832f0413090a072b758642150a2741302e42000001003d0000044c058d0028003340290c0120270f0a +0408010418090b0c0009170119015b121d140528010a01020b015b24051506022a0f032b31003f3f3f2b3033353236351134 +2e01233525113e0133321615111416331521353236351134262322061511141633153d465a213e4101302b9a5d8e8f5a46fe +2b465a3a5a77995a4548162b043337310b4816fd405567888cfe142b164848162b01e66481be79fe6c2b16480001003dffe9 +044c03890023003240281e0121010c261d09120701041c09160a070a1c0115011d015b1610150506015b0700040602250f03 +2b31003f3f3f2b303711342e0123352511141e013332363511342e0123352511141e01331505350e01232226dd213e410136 +174b526e82223d410135213e41fed126885293adf401c437310b4816fd6b50592cb875016c37310b4816fd3136310b4817ad +4d607d0000010073fe00038b0600000f0013400b0d06380a00000501110f032b31002e2e30133437013e013332161d010106 +232226730202c804140d1217fd380c1b1118fe29060207b60c0d161308f84a19180000010073ffd3055205a4003a0038402f +302a0b033203091c011f013222230c0c080322120900070204301f0208380927016a0d0808054a38170006023c0f032b2b31 +002b2b30251e0133323e0235343b013215140e02232224260235341236243332161737363b0132161511142b012235342e01 +2726232206070e0115141601d346d2715fa279411219105296c36a93fef9c36d6dc301079369c048770c020f060a1025132f +4930739571cf474b3a3adb59674b86aa5e10146bc7975477cf011093930110d0755b53a8060a07fdd712123b92833270665a +60f58b8bf60000020044000004fe0577001500230032402a1f010622080c02082417100006020400090701021215011b1209 +4a0c1b00051601511204010602250f032b2b2b3f2b30333532351134233521321e0115140e012321111433150321323e0135 +34262b01220e011544d3d302d970e09192de71feb8d3d90116718c4193abae25291a4841046541485cb07572ac59fe0a4148 +02bc418970a792061d1e0002ffa6fe5c01b2055a001c0029003d40310a0002270209270f200c00080227110700070204170c +0a0b0a1d0b02061a0924015d0d06080500014e1a140106022b0f032b2b31003f3f2e2b2b3013163332363511342e01233525 +11140e0123222635343633321615140613343633321e01151406232226292f3b513f284543013f4e864f59903a28283a238a +442d1c361e422e2d44fea817a560032237310b4816fc044f8d555850283a3a281f3406382d441e361d2c444400010025ffe9 +05a003730032003140252d1a0d031331092a09220a140a060a211d1a191715130e0d0c09070c312305010501340f032b3100 +3f3f3f3f3f2a3021012e012335211522151e01151b01272e0123352115221514171b01363534262335211522060703062b01 +22270b01062b012201c5fef712444101a6790101c5aa1b10464001947902cdba04492d015c3e5915f40718101807cfcf0a16 +0f1902e92d15484833030606fdd801e1472d154848330807fdbf020a100d2b3148484138fd4e17170248fdb8170000030073 +ff8d038b0600004f0058005f005c4053595841321e1914110f080a4018092a013c39025a0140212b0c1608500100014e0118 +2201090e070204280c4801543c3b034e2f350905504e41035a190003682a280a0622015e1e1411044e0b05030603610f032b +31003f2b2b300535222e013534363332161514062b0122272e01231e0233112e0327263d023436373e0133353315321e0115 +14062322263534363b013217332e0223111e01171e011d011406070e012315353e0235342e012727110e02151401db6da358 +3a28283a3a28020e070203010e577c423c3337331d723c362b8e3d4866a65c3a28283a3a28020e06051258793e546a2f3c3f +3b3732893b4573403e6f4b483f7544735f6cb76a283a3a28283a020101416c3b025611101a221c74a102024b8f3a2b485e5c +61a969283a3a28283a023d5a32fde11331303ca15704529c3b32485fa6095580434f754e13d5020c07497143c700000100b2 +0000035e055400110024401d0a010401220706110601040009110701031209015a0d04010501130f032b2b3f2b3033352035 +1106233532373332161511142115be01006aa2fb801d070d0100484104333348830b07fb474148000001003f000005370577 +002d0044403d1a0106151c01210102090f010622080c02080a012c012215211406260101220009080703042c09070104121c +016a1b19020522015114040806022f0f032b2b2b2a3033353235113423352113232e022b01220e01151133323e0135331123 +342e012b011114163b01323e023733033fd3d30486393b164faeaec925291a976867273c3c276768973236d98eaf6135173b +56484104654148fe2bb1a03c061d1efe0c236369fdda686423fdd72c152d69a794fde700000200acfe46018d04000010001d +001f40160f141b000601040e0618011101480b010a05011f0f032b31002e2e2b3013351334363b0132161513151406232226 +11343633321e01151406232226ac54090719070b52432d2c45442d1c361e422e2d44feac0c03b0060d0c07fc500c2b3b3b05 +0e2d441e361d2c44440000020044ffd305db05770030003d004940402f1e01032c170939010622080c0208100127322c0106 +17272409000703040009070102123001352d096a201a000510014a0c3504063101532d040806033f0f032b2b2b3f2b2b3033 +3532351134233521321e0115140e01071e011f011e013332363534363b013215140e012322263d0134262b01111433150333 +32363534262b01220e011544d3d3028373fdaa67a1545c8a0e1d142c4b403f0d0713142c533994d58e67f6d3d3dbaab2b0ac +7325291a48410465414853aa7656875b14208c5ab67b797746070b1b386b46938eb66692fde7414802d789a4a388061d1e00 +0001003f0000020e058d0010001a40120b0c000910010a01025c0b05140501120f032b31003f3f30333532363511342e0123 +352511141633153f465a213e4101305a4548162b043337310b4816fafc2b164800010027fe5c04100373002f002e40240227 +23070007010429111a0a0b0a1b19140c0505120e010a010001542d26130501310f032b2b3f3f2e2e2b30131633323f01012e +0123352115221514171b013635342e0123352115220607010e0223222635343633321e011514068d272f815246fecd134e41 +01ae7102e4cd061b2b1b01543f5f1afe9e1c4b6c404b7134261a29172cfeb01fc3ac02f026154848310804fdd001f8101319 +251448483a39fc9c426f476349263417281b213400030056ffd305d105ba003a00460053005940513d2f15100302061e2a09 +0a274e0c00074001210147011e22200a070835013b0137012a2233091a0703042f211f1b041210014a520927013d016a0d4a +11054003025f520601060201554300010603550f032b2b2b2b2b3013343f012e0135343e01333216151406071e03173e013f +013635342623352115220f010e01071e0133323e013533140e012322270623222626053237260227070615141616133e0135 +342e0123220e0115145652f4272b4783565e578b711d425565333d5b53330b583801c9b9474244714443874a3b673d3c4c82 +4dc6a9abc95aac6b0183a88d65bc3b52582c5f7b5d7814372e344520010a7352fe66d76951975fac685bc27b477e868a4148 +8d8b5a0c1730264848797076af504d653c653c4c88519c9c4c8f937d69011483545c9e458b5f032b67ac4f31624a476731b1 +00010056ffd303a80554004b00504047250119124400020b0302091f1c0219252c0c0408350124120b010603233d09000703 +041211021622094a3906000535014e311604061c0f00034b222801064748410006044d0f032b2b31002b2a30371e01333236 +35342e022b01223d01343337323e01353426232206073e0133321615140623222635343e0133321e0215140e01071e021514 +0e0123222e0135343633321e0115140623c330a25d77641532573f88121271485f2c5c5e4e8e2a0406042e3e3e2e2d406aa7 +543e8a70474d865059a0617cca7060c17b443321371f46319e4644cb813a74643c131210096c9b46627e3b3c0101402d2c40 +402c58824325456c4556926a1a11649c5a71b76749926633441f3820324400020073ffe905c505a4004800590056404d2301 +512b474202183d020933270a0c00082720510006150149011201272b181a063d2600090007040441012e2709450147016a0f +2e09052315025b274d01065b561c00066a38050006045b0f032b2b31002b2a3005222e01023534123e0133321e0112151402 +232226270e0123222e0135343e01333216173332161511141633323635342e0223220e0215141e0233323e01373332161514 +070401323637112e0223220e0215141616031d92f9ba6565baf99291fab9644c8948780f30894b76ba6a6aba76579a2f6706 +0a1e245e355ca3e98484e9a55d5ba8ea8461c3bc595c060b0dfec0fea5528a26194d673542643e2039741777d101098d8d01 +09d17676d1fef78dbcfef948413e4b7fd27270d37f624e0b07fde72a4bf29a80fbbf7071bdf88282f3c3701f3a2c0c070e04 +9601506b5201a23455334f788b3b54ba800000010073ffd305e105a4003f0044403a36300c0b090538030922012501382229 +0c0c08160103221909100702041309360a02073e090c012d01251602510f0719054a3e1d000602410f032b2b31003f2b2b30 +251e013332363d013421352115220615111406232226270e012322240235341236243332161737363b0132161511142b0122 +35342e012726232206070e01151001d74ad57472baff00024b414c0b0713591a32d675c7feb9be6ec50106936abd4a770c02 +0f060a1025132f4930739572d0474b3adb5a66746bac414848162bfe6c070b5a235654cd0157c5910112d0755b53a8060a07 +fdd712123b92833270665a60f58bfec60001004a0000057b057700220026401d19010922110c02080104130f000922120f01 +0412511e05000501240f032b2b3f2e2e2b3021353236351134262b0122070e010723132113232e0127262b01220e01151114 +163315015a67c2313756a94725200b3b2704e3273c0c1d2648a85625291ac26648172a04652c154a25a37b01d5fe2b849b24 +4a061d1efb9b2a17480000020052ffe903f203960032003e0047403e0b010904090926170a00081101240127043b14062c1f +0236262f091007030429096a252200053b012c015b1b0503060e0133011401570b00190603400f032b31003f2b2b30373436 +243335342e01232207321615140623222635343633321e01151114163332363d013315140e01232226270e01232226263714 +1633323e013d0122060652c0010d7934623b884727333a28293ac47653a86b222422213c30512f3c57052694544e9765a66a +48426c405dc380c97a993f543b6f473d3b27293a3a296c69478458fe332843442783832e53315d404d5b2e634f486242723f +d53d82000001003d0000044c038900280035402b0b010c0120270f0a0c08010418090a0a0009170119015b121d140528010a +01020b015b24051506022a0f032b31003f3f3f2b30333532363511342e01233525153e013332161511141633152135323635 +1134262322061511141633153d465a213e41012929a15f8e8f5a46fe2b465a3a5a77995a4548162b022f37310b4816c85870 +888cfe142b164848162b01e66481be79fe6c2b16480000010000020603fe023b000300174010270200000601043603010005 +01050f032b31002b301135211503fe020635350000010000020607fe023b000300174010270200000601042b030100050105 +0f032b31002b301135211507fe0206353500000100c7fe0002a8060000200016400f1f0d1b100212601705000501220f032b +2b2e2e30012e010a0135341a013637343b0132161514070e0202151001161514062b0122027b72a56934346ba66f0a13060a +0464855124015c060b05130afe045ae90108012091930120010ae857040b07090462e0fdfef193fd75feae060b050d000001 +0066ffd30398055400460043403b443e00030d0409201b022b01121d2911062c13020d272f0a040804233709000703042624 +02513307010541013b00021b1202682c17150602480f032b31002b2b30371e02333236353426272e0123220e020723222635 +1134363b01163332373332161d0114230e01232227113e0133321e0115140e0123222e01353436333216151406232226b215 +5777409470050b135f4545633e300617050f0d07068a9b988d06070c0446d3715256446b5371b36079d07a65a9613c2d2d3d +3d2d0712e93c6237e6a447612d486c2a383e020d0602ac050b42420a06130a5d6817fe7d372f82d16d7bd27c68b0632e3a3b +2d2c3d0300010035000002ae0577000f001a4013080c00090f0907010412510c04000501110f032b2b3f3f30333532351134 +2335211522151114331535dddd0279dddd4841046541484841fb9b41480000010027ffd305d70577001e002a402115011613 +07030422060c0a0801040d1d0914100d0c0907062f1605010501200f032b31003f2e2b3005012e012335211522151e011509 +01363534262335211522060701062b012202d1fe1d12664f0233aa010101890173045c3901ba4d7519fe31061b1a1b170505 +2b16484835030504fbeb03dd0811322e48483940fb33160000010044ffe903520396002a003040282824140e04161e091624 +080a00081e24000900070204280114014e0b111105551a040006022c0f032b31002b2b3005222e0135343e01333216151406 +232226353436372623220e0115141e0133323637343b0132161d01060601fe7cca7473cb7c78c5392929392e224780627830 +3b83636188191019060a1fb81781d77979de855e6b283b3b282435072d82c05e63b97977600c0b0706798e00000100ec044e +0312058d0005001840100504000313020c3f0301000501070f032b31003f2a300127090107270114280114011229e9044e2b +0114feec2bcd00020035fe73042d0389001d002d003e403408012a21090a010b012a250e0a0c081801212616091007020409 +0a000755122600051d011e010901020a015b19051d06022f0f032b31003f3f2b2b301335323635113426233525153e013332 +1e0115140e012322271114163315031e0133323e0235342e012322060735465a5050012f38955479c26d79d17d95675b45a0 +24804c476d4a233b7956538b29fe7347182a03db381c48167f3e4183d5777cd67f79fe9a2a18470254495f4c808d4252bf83 +554900010085028d0379060000370030402935302b281c19140f0c000a13250103013534332d2b2a22211918110f0e06050f +3a1f090b0501390f032b31002a301322263534372d012635343633321705032734363332161d010325363332161514070d01 +1615140623222725131514062322263537130506c117251d0127fed91d2418100e01041e02251816252001040e1019231dfe +d901271d2319110dfefc2025161825021efefc0c034c2718220f8c890f221a260bbe014c0417201e1904feb4be0b261a220f +898c0f2218270abefeb5041820211704014bbe0a00010073ffd303e1056800200024401d0e010c011701141207150601041e +09140c0603124e1b00000501220f032b2b3f2b3025343e023713232007060723133315141633211514060701060211140623 +22260164284d6d41bbeafe940b1b183b433cfa78017d0101fee668343a28293a3572dad5cd5a01040a219c01ae0625163502 +0202fe749afe88fee7283a3a0001003f000005e30577002a0041403a2625240c040601091401151209030622080c0a08291f +1c031e01012200090a0702042a221f1d1815130f0907010b122701510c040805012c0f032b2b2b2b30333532351134233521 +15221511013635342623352115220709011e013315213532363534270107111433153fd3d30265d302791a372301bda87ffe +9501c1375363fde831431afe91e5d34841046541484841fd6802601c1c2021484879fea4fd6751284848142519270220ddfe +854148000001002f000005cf057700310046403e2a2911100408010919011a170b0308220a0c0a0830242103230101220009 +0a070204312d2a2927241d1c1a181411100e0b09040312302201010501330f032b31002b2b303335323709012e0123352115 +220615141701133635342623352115220709011e0133152135323635342709010615141633152fd0530154fe87206d54023d +256002010cec08532e01f4d053fee701b1236b53fdc2216503febdfed906522d487401fa023e261548481d1a0404fe680160 +120b2a30484875fe5dfd6c271448481d1a060201eefe490c102a304800020039ffe903520396002000280033402b1e1c020f +14092626080a000828220f000614240009000703040d015c1e22080521015411041006022a0f032b31002b2b3005222e0135 +343e0133321e021514232115141633323e01373e013b01321506060121342e0123220601fe7dd1776dc3785e8b5a2e15fdaf +899b3f6b4f0e020b0712151dc0fe7901d32b6451747f1783db7a78d8853f70985b1b16aaf4386439070b1a749502234d9e69 +d90000010035000002e903890023002e40260b01150c021b270f0a0c0801040a0a00091a1812031223010a01020b015d2005 +150501250f032b2b3f3f2b30333532363511342e01233525153e0133321615140623222635343723220e0115111433153546 +5a213e410125217a573d6035272636270c53692cc748162b022f37310b4816c8596f483b25373626371778b251feb0414800 +0000060001000000000000000000055400560000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000023700ac040000560754004804e3 +00730237008f023700ac04000039031b0027023700ac023700ac023700ac05aa00460637007304000044000000000237003f +043700270000000006aa00730400005006370073061b0044063700730437003504370019040001020000000006aa00730400 +006603c700730537003f0000000003c70073000000000471007306aa003d038d0039023700ac040000390600003f0600003f +023700f20000000000000000047100350400003900000000031b007304000056041b004c083700250237002d047100440437 +0044040000aa06370073040000560500003f0600001702710042000000000327004402aa000002aa0017023700ac06000042 +0600003f0400012f0471003d0471003d000000000400007305c7007300000000057100440271ffa60000000005c700250400 +0073040000b20571003f00000000023700ac0000000005e300440237003f0437002700000000063700560400005606370073 +0646007305c7004a00000000040000520471003d0400000008000000031b00c70400006602e3003506000027038d00440400 +00ec0471003504000085040000730637003f0600002f038d0039000000000321003500010000008500600004000000000002 +000c00060016000000c40062000400010001000005a4fe4600000837ffa6ff8e081200010000000000000000000000000000 +0085000003e7019000050000019a01710000fe5a019a0171000004a2006602120000020b0500000000000000000000000000 +000000000000000000000000000000400020007e05a4fe5a000006000200000000>]def + FontName currentdict end definefont pop + + %%!PS-TrueTypeFont-1.0-2.3499908 + 10 dict begin + /FontType 42 def + /FontMatrix [1 0 0 1 0 0] def + /FontName /DejaVuSans-0 def + /FontInfo 7 dict dup begin + /FullName (DejaVu Sans) def + /FamilyName (DejaVu Sans) def + /Version (Version 2.35) def + /ItalicAngle 0.0 def + /isFixedPitch false def + /UnderlinePosition -130 def + /UnderlineThickness 90 def + end readonly def + /Encoding StandardEncoding def + /FontBBox [-2090 -948 3673 2524] def + /PaintType 0 def + /CIDMap 0 def + /CharStrings 5970 dict dup begin /.notdef 0 def +/.null 1 def +/nonmarkingreturn 2 def /space 3 def /exclam 4 def -/b 7 def -/a 6 def -/e 8 def -/h 9 def -/i 10 def -/n 11 def -/r 12 def -/T 5 def -/t 13 def -/w 14 def +/quotedbl 5 def +/numbersign 6 def +/dollar 7 def +/percent 8 def +/ampersand 9 def +/quotesingle 10 def +/parenleft 11 def +/parenright 12 def +/asterisk 13 def +/plus 14 def +/comma 15 def +/hyphen 16 def +/period 17 def +/slash 18 def +/zero 19 def +/one 20 def +/two 21 def +/three 22 def +/four 23 def +/five 24 def +/six 25 def +/seven 26 def +/eight 27 def +/nine 28 def +/colon 29 def +/semicolon 30 def +/less 31 def +/equal 32 def +/greater 33 def +/question 34 def +/at 35 def +/A 36 def +/B 37 def +/C 38 def +/D 39 def +/E 40 def +/F 41 def +/G 42 def +/H 43 def +/I 44 def +/J 45 def +/K 46 def +/L 47 def +/M 48 def +/N 49 def +/O 50 def +/P 51 def +/Q 52 def +/R 53 def +/S 54 def +/T 55 def +/U 56 def +/V 57 def +/W 58 def +/X 59 def +/Y 60 def +/Z 61 def +/bracketleft 62 def +/backslash 63 def +/bracketright 64 def +/asciicircum 65 def +/underscore 66 def +/grave 67 def +/a 68 def +/b 69 def +/c 70 def +/d 71 def +/e 72 def +/f 73 def +/g 74 def +/h 75 def +/i 76 def +/j 77 def +/k 78 def +/l 79 def +/m 80 def +/n 81 def +/o 82 def +/p 83 def +/q 84 def +/r 85 def +/s 86 def +/t 87 def +/u 88 def +/v 89 def +/w 90 def +/x 91 def +/y 92 def +/z 93 def +/braceleft 94 def +/bar 95 def +/braceright 96 def +/asciitilde 97 def +/nonbreakingspace 98 def +/exclamdown 99 def +/cent 100 def +/sterling 101 def +/currency 102 def +/yen 103 def +/brokenbar 104 def +/section 105 def +/dieresis 106 def +/copyright 107 def +/ordfeminine 108 def +/guillemotleft 109 def +/logicalnot 110 def +/sfthyphen 111 def +/registered 112 def +/macron 113 def +/degree 114 def +/plusminus 115 def +/twosuperior 116 def +/threesuperior 117 def +/acute 118 def +/mu 119 def +/paragraph 120 def +/periodcentered 121 def +/cedilla 122 def +/onesuperior 123 def +/ordmasculine 124 def +/guillemotright 125 def +/onequarter 126 def +/onehalf 127 def +/threequarters 128 def +/questiondown 129 def +/Agrave 130 def +/Aacute 131 def +/Acircumflex 132 def +/Atilde 133 def +/Adieresis 134 def +/Aring 135 def +/AE 136 def +/Ccedilla 137 def +/Egrave 138 def +/Eacute 139 def +/Ecircumflex 140 def +/Edieresis 141 def +/Igrave 142 def +/Iacute 143 def +/Icircumflex 144 def +/Idieresis 145 def +/Eth 146 def +/Ntilde 147 def +/Ograve 148 def +/Oacute 149 def +/Ocircumflex 150 def +/Otilde 151 def +/Odieresis 152 def +/multiply 153 def +/Oslash 154 def +/Ugrave 155 def +/Uacute 156 def +/Ucircumflex 157 def +/Udieresis 158 def +/Yacute 159 def +/Thorn 160 def +/germandbls 161 def +/agrave 162 def +/aacute 163 def +/acircumflex 164 def +/atilde 165 def +/adieresis 166 def +/aring 167 def +/ae 168 def +/ccedilla 169 def +/egrave 170 def +/eacute 171 def +/ecircumflex 172 def +/edieresis 173 def +/igrave 174 def +/iacute 175 def +/icircumflex 176 def +/idieresis 177 def +/eth 178 def +/ntilde 179 def +/ograve 180 def +/oacute 181 def +/ocircumflex 182 def +/otilde 183 def +/odieresis 184 def +/divide 185 def +/oslash 186 def +/ugrave 187 def +/uacute 188 def +/ucircumflex 189 def +/udieresis 190 def +/yacute 191 def +/thorn 192 def +/ydieresis 193 def +/Amacron 194 def +/amacron 195 def +/Abreve 196 def +/abreve 197 def +/Aogonek 198 def +/aogonek 199 def +/Cacute 200 def +/cacute 201 def +/Ccircumflex 202 def +/ccircumflex 203 def +/Cdotaccent 204 def +/cdotaccent 205 def +/Ccaron 206 def +/ccaron 207 def +/Dcaron 208 def +/dcaron 209 def +/Dcroat 210 def +/dcroat 211 def +/Emacron 212 def +/emacron 213 def +/Ebreve 214 def +/ebreve 215 def +/Edotaccent 216 def +/edotaccent 217 def +/Eogonek 218 def +/eogonek 219 def +/Ecaron 220 def +/ecaron 221 def +/Gcircumflex 222 def +/gcircumflex 223 def +/Gbreve 224 def +/gbreve 225 def +/Gdotaccent 226 def +/gdotaccent 227 def +/Gcommaaccent 228 def +/gcommaaccent 229 def +/Hcircumflex 230 def +/hcircumflex 231 def +/Hbar 232 def +/hbar 233 def +/Itilde 234 def +/itilde 235 def +/Imacron 236 def +/imacron 237 def +/Ibreve 238 def +/ibreve 239 def +/Iogonek 240 def +/iogonek 241 def +/Idotaccent 242 def +/dotlessi 243 def +/IJ 244 def +/ij 245 def +/Jcircumflex 246 def +/jcircumflex 247 def +/Kcommaaccent 248 def +/kcommaaccent 249 def +/kgreenlandic 250 def +/Lacute 251 def +/lacute 252 def +/Lcommaaccent 253 def +/lcommaaccent 254 def +/Lcaron 255 def +/lcaron 256 def +/Ldot 257 def +/ldot 258 def +/Lslash 259 def +/lslash 260 def +/Nacute 261 def +/nacute 262 def +/Ncommaaccent 263 def +/ncommaaccent 264 def +/Ncaron 265 def +/ncaron 266 def +/napostrophe 267 def +/Eng 268 def +/eng 269 def +/Omacron 270 def +/omacron 271 def +/Obreve 272 def +/obreve 273 def +/Ohungarumlaut 274 def +/ohungarumlaut 275 def +/OE 276 def +/oe 277 def +/Racute 278 def +/racute 279 def +/Rcommaaccent 280 def +/rcommaaccent 281 def +/Rcaron 282 def +/rcaron 283 def +/Sacute 284 def +/sacute 285 def +/Scircumflex 286 def +/scircumflex 287 def +/Scedilla 288 def +/scedilla 289 def +/Scaron 290 def +/scaron 291 def +/Tcommaaccent 292 def +/tcommaaccent 293 def +/Tcaron 294 def +/tcaron 295 def +/Tbar 296 def +/tbar 297 def +/Utilde 298 def +/utilde 299 def +/Umacron 300 def +/umacron 301 def +/Ubreve 302 def +/ubreve 303 def +/Uring 304 def +/uring 305 def +/Uhungarumlaut 306 def +/uhungarumlaut 307 def +/Uogonek 308 def +/uogonek 309 def +/Wcircumflex 310 def +/wcircumflex 311 def +/Ycircumflex 312 def +/ycircumflex 313 def +/Ydieresis 314 def +/Zacute 315 def +/zacute 316 def +/Zdotaccent 317 def +/zdotaccent 318 def +/Zcaron 319 def +/zcaron 320 def +/longs 321 def +/uni0180 322 def +/uni0181 323 def +/uni0182 324 def +/uni0183 325 def +/uni0184 326 def +/uni0185 327 def +/uni0186 328 def +/uni0187 329 def +/uni0188 330 def +/uni0189 331 def +/uni018A 332 def +/uni018B 333 def +/uni018C 334 def +/uni018D 335 def +/uni018E 336 def +/uni018F 337 def +/uni0190 338 def +/uni0191 339 def +/florin 340 def +/uni0193 341 def +/uni0194 342 def +/uni0195 343 def +/uni0196 344 def +/uni0197 345 def +/uni0198 346 def +/uni0199 347 def +/uni019A 348 def +/uni019B 349 def +/uni019C 350 def +/uni019D 351 def +/uni019E 352 def +/uni019F 353 def +/Ohorn 354 def +/ohorn 355 def +/uni01A2 356 def +/uni01A3 357 def +/uni01A4 358 def +/uni01A5 359 def +/uni01A6 360 def +/uni01A7 361 def +/uni01A8 362 def +/uni01A9 363 def +/uni01AA 364 def +/uni01AB 365 def +/uni01AC 366 def +/uni01AD 367 def +/uni01AE 368 def +/Uhorn 369 def +/uhorn 370 def +/uni01B1 371 def +/uni01B2 372 def +/uni01B3 373 def +/uni01B4 374 def +/uni01B5 375 def +/uni01B6 376 def +/uni01B7 377 def +/uni01B8 378 def +/uni01B9 379 def +/uni01BA 380 def +/uni01BB 381 def +/uni01BC 382 def +/uni01BD 383 def +/uni01BE 384 def +/uni01BF 385 def +/uni01C0 386 def +/uni01C1 387 def +/uni01C2 388 def +/uni01C3 389 def +/uni01C4 390 def +/uni01C5 391 def +/uni01C6 392 def +/uni01C7 393 def +/uni01C8 394 def +/uni01C9 395 def +/uni01CA 396 def +/uni01CB 397 def +/uni01CC 398 def +/uni01CD 399 def +/uni01CE 400 def +/uni01CF 401 def +/uni01D0 402 def +/uni01D1 403 def +/uni01D2 404 def +/uni01D3 405 def +/uni01D4 406 def +/uni01D5 407 def +/uni01D6 408 def +/uni01D7 409 def +/uni01D8 410 def +/uni01D9 411 def +/uni01DA 412 def +/uni01DB 413 def +/uni01DC 414 def +/uni01DD 415 def +/uni01DE 416 def +/uni01DF 417 def +/uni01E0 418 def +/uni01E1 419 def +/uni01E2 420 def +/uni01E3 421 def +/uni01E4 422 def +/uni01E5 423 def +/Gcaron 424 def +/gcaron 425 def +/uni01E8 426 def +/uni01E9 427 def +/uni01EA 428 def +/uni01EB 429 def +/uni01EC 430 def +/uni01ED 431 def +/uni01EE 432 def +/uni01EF 433 def +/uni01F0 434 def +/uni01F1 435 def +/uni01F2 436 def +/uni01F3 437 def +/uni01F4 438 def +/uni01F5 439 def +/uni01F6 440 def +/uni01F7 441 def +/uni01F8 442 def +/uni01F9 443 def +/Aringacute 444 def +/aringacute 445 def +/AEacute 446 def +/aeacute 447 def +/Oslashacute 448 def +/oslashacute 449 def +/uni0200 450 def +/uni0201 451 def +/uni0202 452 def +/uni0203 453 def +/uni0204 454 def +/uni0205 455 def +/uni0206 456 def +/uni0207 457 def +/uni0208 458 def +/uni0209 459 def +/uni020A 460 def +/uni020B 461 def +/uni020C 462 def +/uni020D 463 def +/uni020E 464 def +/uni020F 465 def +/uni0210 466 def +/uni0211 467 def +/uni0212 468 def +/uni0213 469 def +/uni0214 470 def +/uni0215 471 def +/uni0216 472 def +/uni0217 473 def +/Scommaaccent 474 def +/scommaaccent 475 def +/uni021A 476 def +/uni021B 477 def +/uni021C 478 def +/uni021D 479 def +/uni021E 480 def +/uni021F 481 def +/uni0220 482 def +/uni0221 483 def +/uni0222 484 def +/uni0223 485 def +/uni0224 486 def +/uni0225 487 def +/uni0226 488 def +/uni0227 489 def +/uni0228 490 def +/uni0229 491 def +/uni022A 492 def +/uni022B 493 def +/uni022C 494 def +/uni022D 495 def +/uni022E 496 def +/uni022F 497 def +/uni0230 498 def +/uni0231 499 def +/uni0232 500 def +/uni0233 501 def +/uni0234 502 def +/uni0235 503 def +/uni0236 504 def +/dotlessj 505 def +/uni0238 506 def +/uni0239 507 def +/uni023A 508 def +/uni023B 509 def +/uni023C 510 def +/uni023D 511 def +/uni023E 512 def +/uni023F 513 def +/uni0240 514 def +/uni0241 515 def +/uni0242 516 def +/uni0243 517 def +/uni0244 518 def +/uni0245 519 def +/uni0246 520 def +/uni0247 521 def +/uni0248 522 def +/uni0249 523 def +/uni024A 524 def +/uni024B 525 def +/uni024C 526 def +/uni024D 527 def +/uni024E 528 def +/uni024F 529 def +/uni0250 530 def +/uni0251 531 def +/uni0252 532 def +/uni0253 533 def +/uni0254 534 def +/uni0255 535 def +/uni0256 536 def +/uni0257 537 def +/uni0258 538 def +/uni0259 539 def +/uni025A 540 def +/uni025B 541 def +/uni025C 542 def +/uni025D 543 def +/uni025E 544 def +/uni025F 545 def +/uni0260 546 def +/uni0261 547 def +/uni0262 548 def +/uni0263 549 def +/uni0264 550 def +/uni0265 551 def +/uni0266 552 def +/uni0267 553 def +/uni0268 554 def +/uni0269 555 def +/uni026A 556 def +/uni026B 557 def +/uni026C 558 def +/uni026D 559 def +/uni026E 560 def +/uni026F 561 def +/uni0270 562 def +/uni0271 563 def +/uni0272 564 def +/uni0273 565 def +/uni0274 566 def +/uni0275 567 def +/uni0276 568 def +/uni0277 569 def +/uni0278 570 def +/uni0279 571 def +/uni027A 572 def +/uni027B 573 def +/uni027C 574 def +/uni027D 575 def +/uni027E 576 def +/uni027F 577 def +/uni0280 578 def +/uni0281 579 def +/uni0282 580 def +/uni0283 581 def +/uni0284 582 def +/uni0285 583 def +/uni0286 584 def +/uni0287 585 def +/uni0288 586 def +/uni0289 587 def +/uni028A 588 def +/uni028B 589 def +/uni028C 590 def +/uni028D 591 def +/uni028E 592 def +/uni028F 593 def +/uni0290 594 def +/uni0291 595 def +/uni0292 596 def +/uni0293 597 def +/uni0294 598 def +/uni0295 599 def +/uni0296 600 def +/uni0297 601 def +/uni0298 602 def +/uni0299 603 def +/uni029A 604 def +/uni029B 605 def +/uni029C 606 def +/uni029D 607 def +/uni029E 608 def +/uni029F 609 def +/uni02A0 610 def +/uni02A1 611 def +/uni02A2 612 def +/uni02A3 613 def +/uni02A4 614 def +/uni02A5 615 def +/uni02A6 616 def +/uni02A7 617 def +/uni02A8 618 def +/uni02A9 619 def +/uni02AA 620 def +/uni02AB 621 def +/uni02AC 622 def +/uni02AD 623 def +/uni02AE 624 def +/uni02AF 625 def +/uni02B0 626 def +/uni02B1 627 def +/uni02B2 628 def +/uni02B3 629 def +/uni02B4 630 def +/uni02B5 631 def +/uni02B6 632 def +/uni02B7 633 def +/uni02B8 634 def +/uni02B9 635 def +/uni02BA 636 def +/uni02BB 637 def +/uni02BC 638 def +/uni02BD 639 def +/uni02BE 640 def +/uni02BF 641 def +/uni02C0 642 def +/uni02C1 643 def +/uni02C2 644 def +/uni02C3 645 def +/uni02C4 646 def +/uni02C5 647 def +/circumflex 648 def +/caron 649 def +/uni02C8 650 def +/uni02C9 651 def +/uni02CA 652 def +/uni02CB 653 def +/uni02CC 654 def +/uni02CD 655 def +/uni02CE 656 def +/uni02CF 657 def +/uni02D0 658 def +/uni02D1 659 def +/uni02D2 660 def +/uni02D3 661 def +/uni02D4 662 def +/uni02D5 663 def +/uni02D6 664 def +/uni02D7 665 def +/breve 666 def +/dotaccent 667 def +/ring 668 def +/ogonek 669 def +/tilde 670 def +/hungarumlaut 671 def +/uni02DE 672 def +/uni02DF 673 def +/uni02E0 674 def +/uni02E1 675 def +/uni02E2 676 def +/uni02E3 677 def +/uni02E4 678 def +/uni02E5 679 def +/uni02E6 680 def +/uni02E7 681 def +/uni02E8 682 def +/uni02E9 683 def +/uni02EC 684 def +/uni02ED 685 def +/uni02EE 686 def +/uni02F3 687 def +/uni02F7 688 def +/gravecomb 689 def +/acutecomb 690 def +/uni0302 691 def +/tildecomb 692 def +/uni0304 693 def +/uni0305 694 def +/uni0306 695 def +/uni0307 696 def +/uni0308 697 def +/hookabovecomb 698 def +/uni030A 699 def +/uni030B 700 def +/uni030C 701 def +/uni030D 702 def +/uni030E 703 def +/uni030F 704 def +/uni0310 705 def +/uni0311 706 def +/uni0312 707 def +/uni0313 708 def +/uni0314 709 def +/uni0315 710 def +/uni0316 711 def +/uni0317 712 def +/uni0318 713 def +/uni0319 714 def +/uni031A 715 def +/uni031B 716 def +/uni031C 717 def +/uni031D 718 def +/uni031E 719 def +/uni031F 720 def +/uni0320 721 def +/uni0321 722 def +/uni0322 723 def +/dotbelowcomb 724 def +/uni0324 725 def +/uni0325 726 def +/uni0326 727 def +/uni0327 728 def +/uni0328 729 def +/uni0329 730 def +/uni032A 731 def +/uni032B 732 def +/uni032C 733 def +/uni032D 734 def +/uni032E 735 def +/uni032F 736 def +/uni0330 737 def +/uni0331 738 def +/uni0332 739 def +/uni0333 740 def +/uni0334 741 def +/uni0335 742 def +/uni0336 743 def +/uni0337 744 def +/uni0338 745 def +/uni0339 746 def +/uni033A 747 def +/uni033B 748 def +/uni033C 749 def +/uni033D 750 def +/uni033E 751 def +/uni033F 752 def +/uni0340 753 def +/uni0341 754 def +/uni0342 755 def +/uni0343 756 def +/uni0344 757 def +/uni0345 758 def +/uni0346 759 def +/uni0347 760 def +/uni0348 761 def +/uni0349 762 def +/uni034A 763 def +/uni034B 764 def +/uni034C 765 def +/uni034D 766 def +/uni034E 767 def +/uni034F 768 def +/uni0351 769 def +/uni0352 770 def +/uni0353 771 def +/uni0357 772 def +/uni0358 773 def +/uni035A 774 def +/uni035C 775 def +/uni035D 776 def +/uni035E 777 def +/uni035F 778 def +/uni0360 779 def +/uni0361 780 def +/uni0362 781 def +/uni0370 782 def +/uni0371 783 def +/uni0372 784 def +/uni0373 785 def +/uni0374 786 def +/uni0375 787 def +/uni0376 788 def +/uni0377 789 def +/uni037A 790 def +/uni037B 791 def +/uni037C 792 def +/uni037D 793 def +/uni037E 794 def +/tonos 795 def +/dieresistonos 796 def +/Alphatonos 797 def +/anoteleia 798 def +/Epsilontonos 799 def +/Etatonos 800 def +/Iotatonos 801 def +/Omicrontonos 802 def +/Upsilontonos 803 def +/Omegatonos 804 def +/iotadieresistonos 805 def +/Alpha 806 def +/Beta 807 def +/Gamma 808 def +/uni0394 809 def +/Epsilon 810 def +/Zeta 811 def +/Eta 812 def +/Theta 813 def +/Iota 814 def +/Kappa 815 def +/Lambda 816 def +/Mu 817 def +/Nu 818 def +/Xi 819 def +/Omicron 820 def +/Pi 821 def +/Rho 822 def +/Sigma 823 def +/Tau 824 def +/Upsilon 825 def +/Phi 826 def +/Chi 827 def +/Psi 828 def +/Omega 829 def +/Iotadieresis 830 def +/Upsilondieresis 831 def +/alphatonos 832 def +/epsilontonos 833 def +/etatonos 834 def +/iotatonos 835 def +/upsilondieresistonos 836 def +/alpha 837 def +/beta 838 def +/gamma 839 def +/delta 840 def +/epsilon 841 def +/zeta 842 def +/eta 843 def +/theta 844 def +/iota 845 def +/kappa 846 def +/lambda 847 def +/uni03BC 848 def +/nu 849 def +/xi 850 def +/omicron 851 def +/pi 852 def +/rho 853 def +/sigma1 854 def +/sigma 855 def +/tau 856 def +/upsilon 857 def +/phi 858 def +/chi 859 def +/psi 860 def +/omega 861 def +/iotadieresis 862 def +/upsilondieresis 863 def +/omicrontonos 864 def +/upsilontonos 865 def +/omegatonos 866 def +/uni03CF 867 def +/uni03D0 868 def +/theta1 869 def +/Upsilon1 870 def +/uni03D3 871 def +/uni03D4 872 def +/phi1 873 def +/omega1 874 def +/uni03D7 875 def +/uni03D8 876 def +/uni03D9 877 def +/uni03DA 878 def +/uni03DB 879 def +/uni03DC 880 def +/uni03DD 881 def +/uni03DE 882 def +/uni03DF 883 def +/uni03E0 884 def +/uni03E1 885 def +/uni03E2 886 def +/uni03E3 887 def +/uni03E4 888 def +/uni03E5 889 def +/uni03E6 890 def +/uni03E7 891 def +/uni03E8 892 def +/uni03E9 893 def +/uni03EA 894 def +/uni03EB 895 def +/uni03EC 896 def +/uni03ED 897 def +/uni03EE 898 def +/uni03EF 899 def +/uni03F0 900 def +/uni03F1 901 def +/uni03F2 902 def +/uni03F3 903 def +/uni03F4 904 def +/uni03F5 905 def +/uni03F6 906 def +/uni03F7 907 def +/uni03F8 908 def +/uni03F9 909 def +/uni03FA 910 def +/uni03FB 911 def +/uni03FC 912 def +/uni03FD 913 def +/uni03FE 914 def +/uni03FF 915 def +/uni0400 916 def +/uni0401 917 def +/uni0402 918 def +/uni0403 919 def +/uni0404 920 def +/uni0405 921 def +/uni0406 922 def +/uni0407 923 def +/uni0408 924 def +/uni0409 925 def +/uni040A 926 def +/uni040B 927 def +/uni040C 928 def +/uni040D 929 def +/uni040E 930 def +/uni040F 931 def +/uni0410 932 def +/uni0411 933 def +/uni0412 934 def +/uni0413 935 def +/uni0414 936 def +/uni0415 937 def +/uni0416 938 def +/uni0417 939 def +/uni0418 940 def +/uni0419 941 def +/uni041A 942 def +/uni041B 943 def +/uni041C 944 def +/uni041D 945 def +/uni041E 946 def +/uni041F 947 def +/uni0420 948 def +/uni0421 949 def +/uni0422 950 def +/uni0423 951 def +/uni0424 952 def +/uni0425 953 def +/uni0426 954 def +/uni0427 955 def +/uni0428 956 def +/uni0429 957 def +/uni042A 958 def +/uni042B 959 def +/uni042C 960 def +/uni042D 961 def +/uni042E 962 def +/uni042F 963 def +/uni0430 964 def +/uni0431 965 def +/uni0432 966 def +/uni0433 967 def +/uni0434 968 def +/uni0435 969 def +/uni0436 970 def +/uni0437 971 def +/uni0438 972 def +/uni0439 973 def +/uni043A 974 def +/uni043B 975 def +/uni043C 976 def +/uni043D 977 def +/uni043E 978 def +/uni043F 979 def +/uni0440 980 def +/uni0441 981 def +/uni0442 982 def +/uni0443 983 def +/uni0444 984 def +/uni0445 985 def +/uni0446 986 def +/uni0447 987 def +/uni0448 988 def +/uni0449 989 def +/uni044A 990 def +/uni044B 991 def +/uni044C 992 def +/uni044D 993 def +/uni044E 994 def +/uni044F 995 def +/uni0450 996 def +/uni0451 997 def +/uni0452 998 def +/uni0453 999 def +/uni0454 1000 def +/uni0455 1001 def +/uni0456 1002 def +/uni0457 1003 def +/uni0458 1004 def +/uni0459 1005 def +/uni045A 1006 def +/uni045B 1007 def +/uni045C 1008 def +/uni045D 1009 def +/uni045E 1010 def +/uni045F 1011 def +/uni0460 1012 def +/uni0461 1013 def +/uni0462 1014 def +/uni0463 1015 def +/uni0464 1016 def +/uni0465 1017 def +/uni0466 1018 def +/uni0467 1019 def +/uni0468 1020 def +/uni0469 1021 def +/uni046A 1022 def +/uni046B 1023 def +/uni046C 1024 def +/uni046D 1025 def +/uni046E 1026 def +/uni046F 1027 def +/uni0470 1028 def +/uni0471 1029 def +/uni0472 1030 def +/uni0473 1031 def +/uni0474 1032 def +/uni0475 1033 def +/uni0476 1034 def +/uni0477 1035 def +/uni0478 1036 def +/uni0479 1037 def +/uni047A 1038 def +/uni047B 1039 def +/uni047C 1040 def +/uni047D 1041 def +/uni047E 1042 def +/uni047F 1043 def +/uni0480 1044 def +/uni0481 1045 def +/uni0482 1046 def +/uni0483 1047 def +/uni0484 1048 def +/uni0485 1049 def +/uni0486 1050 def +/uni0487 1051 def +/uni0488 1052 def +/uni0489 1053 def +/uni048A 1054 def +/uni048B 1055 def +/uni048C 1056 def +/uni048D 1057 def +/uni048E 1058 def +/uni048F 1059 def +/uni0490 1060 def +/uni0491 1061 def +/uni0492 1062 def +/uni0493 1063 def +/uni0494 1064 def +/uni0495 1065 def +/uni0496 1066 def +/uni0497 1067 def +/uni0498 1068 def +/uni0499 1069 def +/uni049A 1070 def +/uni049B 1071 def +/uni049C 1072 def +/uni049D 1073 def +/uni049E 1074 def +/uni049F 1075 def +/uni04A0 1076 def +/uni04A1 1077 def +/uni04A2 1078 def +/uni04A3 1079 def +/uni04A4 1080 def +/uni04A5 1081 def +/uni04A6 1082 def +/uni04A7 1083 def +/uni04A8 1084 def +/uni04A9 1085 def +/uni04AA 1086 def +/uni04AB 1087 def +/uni04AC 1088 def +/uni04AD 1089 def +/uni04AE 1090 def +/uni04AF 1091 def +/uni04B0 1092 def +/uni04B1 1093 def +/uni04B2 1094 def +/uni04B3 1095 def +/uni04B4 1096 def +/uni04B5 1097 def +/uni04B6 1098 def +/uni04B7 1099 def +/uni04B8 1100 def +/uni04B9 1101 def +/uni04BA 1102 def +/uni04BB 1103 def +/uni04BC 1104 def +/uni04BD 1105 def +/uni04BE 1106 def +/uni04BF 1107 def +/uni04C0 1108 def +/uni04C1 1109 def +/uni04C2 1110 def +/uni04C3 1111 def +/uni04C4 1112 def +/uni04C5 1113 def +/uni04C6 1114 def +/uni04C7 1115 def +/uni04C8 1116 def +/uni04C9 1117 def +/uni04CA 1118 def +/uni04CB 1119 def +/uni04CC 1120 def +/uni04CD 1121 def +/uni04CE 1122 def +/uni04CF 1123 def +/uni04D0 1124 def +/uni04D1 1125 def +/uni04D2 1126 def +/uni04D3 1127 def +/uni04D4 1128 def +/uni04D5 1129 def +/uni04D6 1130 def +/uni04D7 1131 def +/uni04D8 1132 def +/uni04D9 1133 def +/uni04DA 1134 def +/uni04DB 1135 def +/uni04DC 1136 def +/uni04DD 1137 def +/uni04DE 1138 def +/uni04DF 1139 def +/uni04E0 1140 def +/uni04E1 1141 def +/uni04E2 1142 def +/uni04E3 1143 def +/uni04E4 1144 def +/uni04E5 1145 def +/uni04E6 1146 def +/uni04E7 1147 def +/uni04E8 1148 def +/uni04E9 1149 def +/uni04EA 1150 def +/uni04EB 1151 def +/uni04EC 1152 def +/uni04ED 1153 def +/uni04EE 1154 def +/uni04EF 1155 def +/uni04F0 1156 def +/uni04F1 1157 def +/uni04F2 1158 def +/uni04F3 1159 def +/uni04F4 1160 def +/uni04F5 1161 def +/uni04F6 1162 def +/uni04F7 1163 def +/uni04F8 1164 def +/uni04F9 1165 def +/uni04FA 1166 def +/uni04FB 1167 def +/uni04FC 1168 def +/uni04FD 1169 def +/uni04FE 1170 def +/uni04FF 1171 def +/uni0500 1172 def +/uni0501 1173 def +/uni0502 1174 def +/uni0503 1175 def +/uni0504 1176 def +/uni0505 1177 def +/uni0506 1178 def +/uni0507 1179 def +/uni0508 1180 def +/uni0509 1181 def +/uni050A 1182 def +/uni050B 1183 def +/uni050C 1184 def +/uni050D 1185 def +/uni050E 1186 def +/uni050F 1187 def +/uni0510 1188 def +/uni0511 1189 def +/uni0512 1190 def +/uni0513 1191 def +/uni0514 1192 def +/uni0515 1193 def +/uni0516 1194 def +/uni0517 1195 def +/uni0518 1196 def +/uni0519 1197 def +/uni051A 1198 def +/uni051B 1199 def +/uni051C 1200 def +/uni051D 1201 def +/uni051E 1202 def +/uni051F 1203 def +/uni0520 1204 def +/uni0521 1205 def +/uni0522 1206 def +/uni0523 1207 def +/uni0524 1208 def +/uni0525 1209 def +/uni0531 1210 def +/uni0532 1211 def +/uni0533 1212 def +/uni0534 1213 def +/uni0535 1214 def +/uni0536 1215 def +/uni0537 1216 def +/uni0538 1217 def +/uni0539 1218 def +/uni053A 1219 def +/uni053B 1220 def +/uni053C 1221 def +/uni053D 1222 def +/uni053E 1223 def +/uni053F 1224 def +/uni0540 1225 def +/uni0541 1226 def +/uni0542 1227 def +/uni0543 1228 def +/uni0544 1229 def +/uni0545 1230 def +/uni0546 1231 def +/uni0547 1232 def +/uni0548 1233 def +/uni0549 1234 def +/uni054A 1235 def +/uni054B 1236 def +/uni054C 1237 def +/uni054D 1238 def +/uni054E 1239 def +/uni054F 1240 def +/uni0550 1241 def +/uni0551 1242 def +/uni0552 1243 def +/uni0553 1244 def +/uni0554 1245 def +/uni0555 1246 def +/uni0556 1247 def +/uni0559 1248 def +/uni055A 1249 def +/uni055B 1250 def +/uni055C 1251 def +/uni055D 1252 def +/uni055E 1253 def +/uni055F 1254 def +/uni0561 1255 def +/uni0562 1256 def +/uni0563 1257 def +/uni0564 1258 def +/uni0565 1259 def +/uni0566 1260 def +/uni0567 1261 def +/uni0568 1262 def +/uni0569 1263 def +/uni056A 1264 def +/uni056B 1265 def +/uni056C 1266 def +/uni056D 1267 def +/uni056E 1268 def +/uni056F 1269 def +/uni0570 1270 def +/uni0571 1271 def +/uni0572 1272 def +/uni0573 1273 def +/uni0574 1274 def +/uni0575 1275 def +/uni0576 1276 def +/uni0577 1277 def +/uni0578 1278 def +/uni0579 1279 def +/uni057A 1280 def +/uni057B 1281 def +/uni057C 1282 def +/uni057D 1283 def +/uni057E 1284 def +/uni057F 1285 def +/uni0580 1286 def +/uni0581 1287 def +/uni0582 1288 def +/uni0583 1289 def +/uni0584 1290 def +/uni0585 1291 def +/uni0586 1292 def +/uni0587 1293 def +/uni0589 1294 def +/uni058A 1295 def +/uni05B0 1296 def +/uni05B1 1297 def +/uni05B2 1298 def +/uni05B3 1299 def +/uni05B4 1300 def +/uni05B5 1301 def +/uni05B6 1302 def +/uni05B7 1303 def +/uni05B8 1304 def +/uni05B9 1305 def +/uni05BA 1306 def +/uni05BB 1307 def +/uni05BC 1308 def +/uni05BD 1309 def +/uni05BE 1310 def +/uni05BF 1311 def +/uni05C0 1312 def +/uni05C1 1313 def +/uni05C2 1314 def +/uni05C3 1315 def +/uni05C6 1316 def +/uni05C7 1317 def +/uni05D0 1318 def +/uni05D1 1319 def +/uni05D2 1320 def +/uni05D3 1321 def +/uni05D4 1322 def +/uni05D5 1323 def +/uni05D6 1324 def +/uni05D7 1325 def +/uni05D8 1326 def +/uni05D9 1327 def +/uni05DA 1328 def +/uni05DB 1329 def +/uni05DC 1330 def +/uni05DD 1331 def +/uni05DE 1332 def +/uni05DF 1333 def +/uni05E0 1334 def +/uni05E1 1335 def +/uni05E2 1336 def +/uni05E3 1337 def +/uni05E4 1338 def +/uni05E5 1339 def +/uni05E6 1340 def +/uni05E7 1341 def +/uni05E8 1342 def +/uni05E9 1343 def +/uni05EA 1344 def +/uni05F0 1345 def +/uni05F1 1346 def +/uni05F2 1347 def +/uni05F3 1348 def +/uni05F4 1349 def +/uni0606 1350 def +/uni0607 1351 def +/uni0609 1352 def +/uni060A 1353 def +/uni060C 1354 def +/uni0615 1355 def +/uni061B 1356 def +/uni061F 1357 def +/uni0621 1358 def +/uni0622 1359 def +/uni0623 1360 def +/uni0624 1361 def +/uni0625 1362 def +/uni0626 1363 def +/uni0627 1364 def +/uni0628 1365 def +/uni0629 1366 def +/uni062A 1367 def +/uni062B 1368 def +/uni062C 1369 def +/uni062D 1370 def +/uni062E 1371 def +/uni062F 1372 def +/uni0630 1373 def +/uni0631 1374 def +/uni0632 1375 def +/uni0633 1376 def +/uni0634 1377 def +/uni0635 1378 def +/uni0636 1379 def +/uni0637 1380 def +/uni0638 1381 def +/uni0639 1382 def +/uni063A 1383 def +/uni0640 1384 def +/uni0641 1385 def +/uni0642 1386 def +/uni0643 1387 def +/uni0644 1388 def +/uni0645 1389 def +/uni0646 1390 def +/uni0647 1391 def +/uni0648 1392 def +/uni0649 1393 def +/uni064A 1394 def +/uni064B 1395 def +/uni064C 1396 def +/uni064D 1397 def +/uni064E 1398 def +/uni064F 1399 def +/uni0650 1400 def +/uni0651 1401 def +/uni0652 1402 def +/uni0653 1403 def +/uni0654 1404 def +/uni0655 1405 def +/uni0657 1406 def +/uni065A 1407 def +/uni0660 1408 def +/uni0661 1409 def +/uni0662 1410 def +/uni0663 1411 def +/uni0664 1412 def +/uni0665 1413 def +/uni0666 1414 def +/uni0667 1415 def +/uni0668 1416 def +/uni0669 1417 def +/uni066A 1418 def +/uni066B 1419 def +/uni066C 1420 def +/uni066D 1421 def +/uni066E 1422 def +/uni066F 1423 def +/uni0670 1424 def +/uni0674 1425 def +/uni0679 1426 def +/uni067A 1427 def +/uni067B 1428 def +/uni067C 1429 def +/uni067D 1430 def +/uni067E 1431 def +/uni067F 1432 def +/uni0680 1433 def +/uni0681 1434 def +/uni0682 1435 def +/uni0683 1436 def +/uni0684 1437 def +/uni0685 1438 def +/uni0686 1439 def +/uni0687 1440 def +/uni0688 1441 def +/uni0689 1442 def +/uni068A 1443 def +/uni068B 1444 def +/uni068C 1445 def +/uni068D 1446 def +/uni068E 1447 def +/uni068F 1448 def +/uni0690 1449 def +/uni0691 1450 def +/uni0692 1451 def +/uni0693 1452 def +/uni0694 1453 def +/uni0695 1454 def +/uni0696 1455 def +/uni0697 1456 def +/uni0698 1457 def +/uni0699 1458 def +/uni069A 1459 def +/uni069B 1460 def +/uni069C 1461 def +/uni069D 1462 def +/uni069E 1463 def +/uni069F 1464 def +/uni06A0 1465 def +/uni06A1 1466 def +/uni06A2 1467 def +/uni06A3 1468 def +/uni06A4 1469 def +/uni06A5 1470 def +/uni06A6 1471 def +/uni06A7 1472 def +/uni06A8 1473 def +/uni06A9 1474 def +/uni06AA 1475 def +/uni06AB 1476 def +/uni06AC 1477 def +/uni06AD 1478 def +/uni06AE 1479 def +/uni06AF 1480 def +/uni06B0 1481 def +/uni06B1 1482 def +/uni06B2 1483 def +/uni06B3 1484 def +/uni06B4 1485 def +/uni06B5 1486 def +/uni06B6 1487 def +/uni06B7 1488 def +/uni06B8 1489 def +/uni06B9 1490 def +/uni06BA 1491 def +/uni06BB 1492 def +/uni06BC 1493 def +/uni06BD 1494 def +/uni06BE 1495 def +/uni06BF 1496 def +/uni06C6 1497 def +/uni06C7 1498 def +/uni06C8 1499 def +/uni06CB 1500 def +/uni06CC 1501 def +/uni06CE 1502 def +/uni06D0 1503 def +/uni06D5 1504 def +/uni06F0 1505 def +/uni06F1 1506 def +/uni06F2 1507 def +/uni06F3 1508 def +/uni06F4 1509 def +/uni06F5 1510 def +/uni06F6 1511 def +/uni06F7 1512 def +/uni06F8 1513 def +/uni06F9 1514 def +/uni07C0 1515 def +/uni07C1 1516 def +/uni07C2 1517 def +/uni07C3 1518 def +/uni07C4 1519 def +/uni07C5 1520 def +/uni07C6 1521 def +/uni07C7 1522 def +/uni07C8 1523 def +/uni07C9 1524 def +/uni07CA 1525 def +/uni07CB 1526 def +/uni07CC 1527 def +/uni07CD 1528 def +/uni07CE 1529 def +/uni07CF 1530 def +/uni07D0 1531 def +/uni07D1 1532 def +/uni07D2 1533 def +/uni07D3 1534 def +/uni07D4 1535 def +/uni07D5 1536 def +/uni07D6 1537 def +/uni07D7 1538 def +/uni07D8 1539 def +/uni07D9 1540 def +/uni07DA 1541 def +/uni07DB 1542 def +/uni07DC 1543 def +/uni07DD 1544 def +/uni07DE 1545 def +/uni07DF 1546 def +/uni07E0 1547 def +/uni07E1 1548 def +/uni07E2 1549 def +/uni07E3 1550 def +/uni07E4 1551 def +/uni07E5 1552 def +/uni07E6 1553 def +/uni07E7 1554 def +/uni07EB 1555 def +/uni07EC 1556 def +/uni07ED 1557 def +/uni07EE 1558 def +/uni07EF 1559 def +/uni07F0 1560 def +/uni07F1 1561 def +/uni07F2 1562 def +/uni07F3 1563 def +/uni07F4 1564 def +/uni07F5 1565 def +/uni07F8 1566 def +/uni07F9 1567 def +/uni07FA 1568 def +/uni0E3F 1569 def +/uni0E81 1570 def +/uni0E82 1571 def +/uni0E84 1572 def +/uni0E87 1573 def +/uni0E88 1574 def +/uni0E8A 1575 def +/uni0E8D 1576 def +/uni0E94 1577 def +/uni0E95 1578 def +/uni0E96 1579 def +/uni0E97 1580 def +/uni0E99 1581 def +/uni0E9A 1582 def +/uni0E9B 1583 def +/uni0E9C 1584 def +/uni0E9D 1585 def +/uni0E9E 1586 def +/uni0E9F 1587 def +/uni0EA1 1588 def +/uni0EA2 1589 def +/uni0EA3 1590 def +/uni0EA5 1591 def +/uni0EA7 1592 def +/uni0EAA 1593 def +/uni0EAB 1594 def +/uni0EAD 1595 def +/uni0EAE 1596 def +/uni0EAF 1597 def +/uni0EB0 1598 def +/uni0EB1 1599 def +/uni0EB2 1600 def +/uni0EB3 1601 def +/uni0EB4 1602 def +/uni0EB5 1603 def +/uni0EB6 1604 def +/uni0EB7 1605 def +/uni0EB8 1606 def +/uni0EB9 1607 def +/uni0EBB 1608 def +/uni0EBC 1609 def +/uni0EBD 1610 def +/uni0EC0 1611 def +/uni0EC1 1612 def +/uni0EC2 1613 def +/uni0EC3 1614 def +/uni0EC4 1615 def +/uni0EC6 1616 def +/uni0EC8 1617 def +/uni0EC9 1618 def +/uni0ECA 1619 def +/uni0ECB 1620 def +/uni0ECC 1621 def +/uni0ECD 1622 def +/uni0ED0 1623 def +/uni0ED1 1624 def +/uni0ED2 1625 def +/uni0ED3 1626 def +/uni0ED4 1627 def +/uni0ED5 1628 def +/uni0ED6 1629 def +/uni0ED7 1630 def +/uni0ED8 1631 def +/uni0ED9 1632 def +/uni0EDC 1633 def +/uni0EDD 1634 def +/uni10A0 1635 def +/uni10A1 1636 def +/uni10A2 1637 def +/uni10A3 1638 def +/uni10A4 1639 def +/uni10A5 1640 def +/uni10A6 1641 def +/uni10A7 1642 def +/uni10A8 1643 def +/uni10A9 1644 def +/uni10AA 1645 def +/uni10AB 1646 def +/uni10AC 1647 def +/uni10AD 1648 def +/uni10AE 1649 def +/uni10AF 1650 def +/uni10B0 1651 def +/uni10B1 1652 def +/uni10B2 1653 def +/uni10B3 1654 def +/uni10B4 1655 def +/uni10B5 1656 def +/uni10B6 1657 def +/uni10B7 1658 def +/uni10B8 1659 def +/uni10B9 1660 def +/uni10BA 1661 def +/uni10BB 1662 def +/uni10BC 1663 def +/uni10BD 1664 def +/uni10BE 1665 def +/uni10BF 1666 def +/uni10C0 1667 def +/uni10C1 1668 def +/uni10C2 1669 def +/uni10C3 1670 def +/uni10C4 1671 def +/uni10C5 1672 def +/uni10D0 1673 def +/uni10D1 1674 def +/uni10D2 1675 def +/uni10D3 1676 def +/uni10D4 1677 def +/uni10D5 1678 def +/uni10D6 1679 def +/uni10D7 1680 def +/uni10D8 1681 def +/uni10D9 1682 def +/uni10DA 1683 def +/uni10DB 1684 def +/uni10DC 1685 def +/uni10DD 1686 def +/uni10DE 1687 def +/uni10DF 1688 def +/uni10E0 1689 def +/uni10E1 1690 def +/uni10E2 1691 def +/uni10E3 1692 def +/uni10E4 1693 def +/uni10E5 1694 def +/uni10E6 1695 def +/uni10E7 1696 def +/uni10E8 1697 def +/uni10E9 1698 def +/uni10EA 1699 def +/uni10EB 1700 def +/uni10EC 1701 def +/uni10ED 1702 def +/uni10EE 1703 def +/uni10EF 1704 def +/uni10F0 1705 def +/uni10F1 1706 def +/uni10F2 1707 def +/uni10F3 1708 def +/uni10F4 1709 def +/uni10F5 1710 def +/uni10F6 1711 def +/uni10F7 1712 def +/uni10F8 1713 def +/uni10F9 1714 def +/uni10FA 1715 def +/uni10FB 1716 def +/uni10FC 1717 def +/uni1401 1718 def +/uni1402 1719 def +/uni1403 1720 def +/uni1404 1721 def +/uni1405 1722 def +/uni1406 1723 def +/uni1407 1724 def +/uni1409 1725 def +/uni140A 1726 def +/uni140B 1727 def +/uni140C 1728 def +/uni140D 1729 def +/uni140E 1730 def +/uni140F 1731 def +/uni1410 1732 def +/uni1411 1733 def +/uni1412 1734 def +/uni1413 1735 def +/uni1414 1736 def +/uni1415 1737 def +/uni1416 1738 def +/uni1417 1739 def +/uni1418 1740 def +/uni1419 1741 def +/uni141A 1742 def +/uni141B 1743 def +/uni141D 1744 def +/uni141E 1745 def +/uni141F 1746 def +/uni1420 1747 def +/uni1421 1748 def +/uni1422 1749 def +/uni1423 1750 def +/uni1424 1751 def +/uni1425 1752 def +/uni1426 1753 def +/uni1427 1754 def +/uni1428 1755 def +/uni1429 1756 def +/uni142A 1757 def +/uni142B 1758 def +/uni142C 1759 def +/uni142D 1760 def +/uni142E 1761 def +/uni142F 1762 def +/uni1430 1763 def +/uni1431 1764 def +/uni1432 1765 def +/uni1433 1766 def +/uni1434 1767 def +/uni1435 1768 def +/uni1437 1769 def +/uni1438 1770 def +/uni1439 1771 def +/uni143A 1772 def +/uni143B 1773 def +/uni143C 1774 def +/uni143D 1775 def +/uni143E 1776 def +/uni143F 1777 def +/uni1440 1778 def +/uni1441 1779 def +/uni1442 1780 def +/uni1443 1781 def +/uni1444 1782 def +/uni1445 1783 def +/uni1446 1784 def +/uni1447 1785 def +/uni1448 1786 def +/uni1449 1787 def +/uni144A 1788 def +/uni144C 1789 def +/uni144D 1790 def +/uni144E 1791 def +/uni144F 1792 def +/uni1450 1793 def +/uni1451 1794 def +/uni1452 1795 def +/uni1454 1796 def +/uni1455 1797 def +/uni1456 1798 def +/uni1457 1799 def +/uni1458 1800 def +/uni1459 1801 def +/uni145A 1802 def +/uni145B 1803 def +/uni145C 1804 def +/uni145D 1805 def +/uni145E 1806 def +/uni145F 1807 def +/uni1460 1808 def +/uni1461 1809 def +/uni1462 1810 def +/uni1463 1811 def +/uni1464 1812 def +/uni1465 1813 def +/uni1466 1814 def +/uni1467 1815 def +/uni1468 1816 def +/uni1469 1817 def +/uni146A 1818 def +/uni146B 1819 def +/uni146C 1820 def +/uni146D 1821 def +/uni146E 1822 def +/uni146F 1823 def +/uni1470 1824 def +/uni1471 1825 def +/uni1472 1826 def +/uni1473 1827 def +/uni1474 1828 def +/uni1475 1829 def +/uni1476 1830 def +/uni1477 1831 def +/uni1478 1832 def +/uni1479 1833 def +/uni147A 1834 def +/uni147B 1835 def +/uni147C 1836 def +/uni147D 1837 def +/uni147E 1838 def +/uni147F 1839 def +/uni1480 1840 def +/uni1481 1841 def +/uni1482 1842 def +/uni1483 1843 def +/uni1484 1844 def +/uni1485 1845 def +/uni1486 1846 def +/uni1487 1847 def +/uni1488 1848 def +/uni1489 1849 def +/uni148A 1850 def +/uni148B 1851 def +/uni148C 1852 def +/uni148D 1853 def +/uni148E 1854 def +/uni148F 1855 def +/uni1490 1856 def +/uni1491 1857 def +/uni1492 1858 def +/uni1493 1859 def +/uni1494 1860 def +/uni1495 1861 def +/uni1496 1862 def +/uni1497 1863 def +/uni1498 1864 def +/uni1499 1865 def +/uni149A 1866 def +/uni149B 1867 def +/uni149C 1868 def +/uni149D 1869 def +/uni149E 1870 def +/uni149F 1871 def +/uni14A0 1872 def +/uni14A1 1873 def +/uni14A2 1874 def +/uni14A3 1875 def +/uni14A4 1876 def +/uni14A5 1877 def +/uni14A6 1878 def +/uni14A7 1879 def +/uni14A8 1880 def +/uni14A9 1881 def +/uni14AA 1882 def +/uni14AB 1883 def +/uni14AC 1884 def +/uni14AD 1885 def +/uni14AE 1886 def +/uni14AF 1887 def +/uni14B0 1888 def +/uni14B1 1889 def +/uni14B2 1890 def +/uni14B3 1891 def +/uni14B4 1892 def +/uni14B5 1893 def +/uni14B6 1894 def +/uni14B7 1895 def +/uni14B8 1896 def +/uni14B9 1897 def +/uni14BA 1898 def +/uni14BB 1899 def +/uni14BC 1900 def +/uni14BD 1901 def +/uni14C0 1902 def +/uni14C1 1903 def +/uni14C2 1904 def +/uni14C3 1905 def +/uni14C4 1906 def +/uni14C5 1907 def +/uni14C6 1908 def +/uni14C7 1909 def +/uni14C8 1910 def +/uni14C9 1911 def +/uni14CA 1912 def +/uni14CB 1913 def +/uni14CC 1914 def +/uni14CD 1915 def +/uni14CE 1916 def +/uni14CF 1917 def +/uni14D0 1918 def +/uni14D1 1919 def +/uni14D2 1920 def +/uni14D3 1921 def +/uni14D4 1922 def +/uni14D5 1923 def +/uni14D6 1924 def +/uni14D7 1925 def +/uni14D8 1926 def +/uni14D9 1927 def +/uni14DA 1928 def +/uni14DB 1929 def +/uni14DC 1930 def +/uni14DD 1931 def +/uni14DE 1932 def +/uni14DF 1933 def +/uni14E0 1934 def +/uni14E1 1935 def +/uni14E2 1936 def +/uni14E3 1937 def +/uni14E4 1938 def +/uni14E5 1939 def +/uni14E6 1940 def +/uni14E7 1941 def +/uni14E8 1942 def +/uni14E9 1943 def +/uni14EA 1944 def +/uni14EC 1945 def +/uni14ED 1946 def +/uni14EE 1947 def +/uni14EF 1948 def +/uni14F0 1949 def +/uni14F1 1950 def +/uni14F2 1951 def +/uni14F3 1952 def +/uni14F4 1953 def +/uni14F5 1954 def +/uni14F6 1955 def +/uni14F7 1956 def +/uni14F8 1957 def +/uni14F9 1958 def +/uni14FA 1959 def +/uni14FB 1960 def +/uni14FC 1961 def +/uni14FD 1962 def +/uni14FE 1963 def +/uni14FF 1964 def +/uni1500 1965 def +/uni1501 1966 def +/uni1502 1967 def +/uni1503 1968 def +/uni1504 1969 def +/uni1505 1970 def +/uni1506 1971 def +/uni1507 1972 def +/uni1510 1973 def +/uni1511 1974 def +/uni1512 1975 def +/uni1513 1976 def +/uni1514 1977 def +/uni1515 1978 def +/uni1516 1979 def +/uni1517 1980 def +/uni1518 1981 def +/uni1519 1982 def +/uni151A 1983 def +/uni151B 1984 def +/uni151C 1985 def +/uni151D 1986 def +/uni151E 1987 def +/uni151F 1988 def +/uni1520 1989 def +/uni1521 1990 def +/uni1522 1991 def +/uni1523 1992 def +/uni1524 1993 def +/uni1525 1994 def +/uni1526 1995 def +/uni1527 1996 def +/uni1528 1997 def +/uni1529 1998 def +/uni152A 1999 def +/uni152B 2000 def +/uni152C 2001 def +/uni152D 2002 def +/uni152E 2003 def +/uni152F 2004 def +/uni1530 2005 def +/uni1531 2006 def +/uni1532 2007 def +/uni1533 2008 def +/uni1534 2009 def +/uni1535 2010 def +/uni1536 2011 def +/uni1537 2012 def +/uni1538 2013 def +/uni1539 2014 def +/uni153A 2015 def +/uni153B 2016 def +/uni153C 2017 def +/uni153D 2018 def +/uni153E 2019 def +/uni1540 2020 def +/uni1541 2021 def +/uni1542 2022 def +/uni1543 2023 def +/uni1544 2024 def +/uni1545 2025 def +/uni1546 2026 def +/uni1547 2027 def +/uni1548 2028 def +/uni1549 2029 def +/uni154A 2030 def +/uni154B 2031 def +/uni154C 2032 def +/uni154D 2033 def +/uni154E 2034 def +/uni154F 2035 def +/uni1550 2036 def +/uni1552 2037 def +/uni1553 2038 def +/uni1554 2039 def +/uni1555 2040 def +/uni1556 2041 def +/uni1557 2042 def +/uni1558 2043 def +/uni1559 2044 def +/uni155A 2045 def +/uni155B 2046 def +/uni155C 2047 def +/uni155D 2048 def +/uni155E 2049 def +/uni155F 2050 def +/uni1560 2051 def +/uni1561 2052 def +/uni1562 2053 def +/uni1563 2054 def +/uni1564 2055 def +/uni1565 2056 def +/uni1566 2057 def +/uni1567 2058 def +/uni1568 2059 def +/uni1569 2060 def +/uni156A 2061 def +/uni1574 2062 def +/uni1575 2063 def +/uni1576 2064 def +/uni1577 2065 def +/uni1578 2066 def +/uni1579 2067 def +/uni157A 2068 def +/uni157B 2069 def +/uni157C 2070 def +/uni157D 2071 def +/uni157E 2072 def +/uni157F 2073 def +/uni1580 2074 def +/uni1581 2075 def +/uni1582 2076 def +/uni1583 2077 def +/uni1584 2078 def +/uni1585 2079 def +/uni158A 2080 def +/uni158B 2081 def +/uni158C 2082 def +/uni158D 2083 def +/uni158E 2084 def +/uni158F 2085 def +/uni1590 2086 def +/uni1591 2087 def +/uni1592 2088 def +/uni1593 2089 def +/uni1594 2090 def +/uni1595 2091 def +/uni1596 2092 def +/uni15A0 2093 def +/uni15A1 2094 def +/uni15A2 2095 def +/uni15A3 2096 def +/uni15A4 2097 def +/uni15A5 2098 def +/uni15A6 2099 def +/uni15A7 2100 def +/uni15A8 2101 def +/uni15A9 2102 def +/uni15AA 2103 def +/uni15AB 2104 def +/uni15AC 2105 def +/uni15AD 2106 def +/uni15AE 2107 def +/uni15AF 2108 def +/uni15DE 2109 def +/uni15E1 2110 def +/uni1646 2111 def +/uni1647 2112 def +/uni166E 2113 def +/uni166F 2114 def +/uni1670 2115 def +/uni1671 2116 def +/uni1672 2117 def +/uni1673 2118 def +/uni1674 2119 def +/uni1675 2120 def +/uni1676 2121 def +/uni1680 2122 def +/uni1681 2123 def +/uni1682 2124 def +/uni1683 2125 def +/uni1684 2126 def +/uni1685 2127 def +/uni1686 2128 def +/uni1687 2129 def +/uni1688 2130 def +/uni1689 2131 def +/uni168A 2132 def +/uni168B 2133 def +/uni168C 2134 def +/uni168D 2135 def +/uni168E 2136 def +/uni168F 2137 def +/uni1690 2138 def +/uni1691 2139 def +/uni1692 2140 def +/uni1693 2141 def +/uni1694 2142 def +/uni1695 2143 def +/uni1696 2144 def +/uni1697 2145 def +/uni1698 2146 def +/uni1699 2147 def +/uni169A 2148 def +/uni169B 2149 def +/uni169C 2150 def +/uni1D00 2151 def +/uni1D01 2152 def +/uni1D02 2153 def +/uni1D03 2154 def +/uni1D04 2155 def +/uni1D05 2156 def +/uni1D06 2157 def +/uni1D07 2158 def +/uni1D08 2159 def +/uni1D09 2160 def +/uni1D0A 2161 def +/uni1D0B 2162 def +/uni1D0C 2163 def +/uni1D0D 2164 def +/uni1D0E 2165 def +/uni1D0F 2166 def +/uni1D10 2167 def +/uni1D11 2168 def +/uni1D12 2169 def +/uni1D13 2170 def +/uni1D14 2171 def +/uni1D16 2172 def +/uni1D17 2173 def +/uni1D18 2174 def +/uni1D19 2175 def +/uni1D1A 2176 def +/uni1D1B 2177 def +/uni1D1C 2178 def +/uni1D1D 2179 def +/uni1D1E 2180 def +/uni1D1F 2181 def +/uni1D20 2182 def +/uni1D21 2183 def +/uni1D22 2184 def +/uni1D23 2185 def +/uni1D26 2186 def +/uni1D27 2187 def +/uni1D28 2188 def +/uni1D29 2189 def +/uni1D2A 2190 def +/uni1D2B 2191 def +/uni1D2C 2192 def +/uni1D2D 2193 def +/uni1D2E 2194 def +/uni1D30 2195 def +/uni1D31 2196 def +/uni1D32 2197 def +/uni1D33 2198 def +/uni1D34 2199 def +/uni1D35 2200 def +/uni1D36 2201 def +/uni1D37 2202 def +/uni1D38 2203 def +/uni1D39 2204 def +/uni1D3A 2205 def +/uni1D3B 2206 def +/uni1D3C 2207 def +/uni1D3D 2208 def +/uni1D3E 2209 def +/uni1D3F 2210 def +/uni1D40 2211 def +/uni1D41 2212 def +/uni1D42 2213 def +/uni1D43 2214 def +/uni1D44 2215 def +/uni1D45 2216 def +/uni1D46 2217 def +/uni1D47 2218 def +/uni1D48 2219 def +/uni1D49 2220 def +/uni1D4A 2221 def +/uni1D4B 2222 def +/uni1D4C 2223 def +/uni1D4D 2224 def +/uni1D4E 2225 def +/uni1D4F 2226 def +/uni1D50 2227 def +/uni1D51 2228 def +/uni1D52 2229 def +/uni1D53 2230 def +/uni1D54 2231 def +/uni1D55 2232 def +/uni1D56 2233 def +/uni1D57 2234 def +/uni1D58 2235 def +/uni1D59 2236 def +/uni1D5A 2237 def +/uni1D5B 2238 def +/uni1D5D 2239 def +/uni1D5E 2240 def +/uni1D5F 2241 def +/uni1D60 2242 def +/uni1D61 2243 def +/uni1D62 2244 def +/uni1D63 2245 def +/uni1D64 2246 def +/uni1D65 2247 def +/uni1D66 2248 def +/uni1D67 2249 def +/uni1D68 2250 def +/uni1D69 2251 def +/uni1D6A 2252 def +/uni1D77 2253 def +/uni1D78 2254 def +/uni1D7B 2255 def +/uni1D7D 2256 def +/uni1D85 2257 def +/uni1D9B 2258 def +/uni1D9C 2259 def +/uni1D9D 2260 def +/uni1D9E 2261 def +/uni1D9F 2262 def +/uni1DA0 2263 def +/uni1DA1 2264 def +/uni1DA2 2265 def +/uni1DA3 2266 def +/uni1DA4 2267 def +/uni1DA5 2268 def +/uni1DA6 2269 def +/uni1DA7 2270 def +/uni1DA8 2271 def +/uni1DA9 2272 def +/uni1DAA 2273 def +/uni1DAB 2274 def +/uni1DAC 2275 def +/uni1DAD 2276 def +/uni1DAE 2277 def +/uni1DAF 2278 def +/uni1DB0 2279 def +/uni1DB1 2280 def +/uni1DB2 2281 def +/uni1DB3 2282 def +/uni1DB4 2283 def +/uni1DB5 2284 def +/uni1DB6 2285 def +/uni1DB7 2286 def +/uni1DB8 2287 def +/uni1DB9 2288 def +/uni1DBA 2289 def +/uni1DBB 2290 def +/uni1DBC 2291 def +/uni1DBD 2292 def +/uni1DBE 2293 def +/uni1DBF 2294 def +/uni1DC4 2295 def +/uni1DC5 2296 def +/uni1DC6 2297 def +/uni1DC7 2298 def +/uni1DC8 2299 def +/uni1DC9 2300 def +/uni1E00 2301 def +/uni1E01 2302 def +/uni1E02 2303 def +/uni1E03 2304 def +/uni1E04 2305 def +/uni1E05 2306 def +/uni1E06 2307 def +/uni1E07 2308 def +/uni1E08 2309 def +/uni1E09 2310 def +/uni1E0A 2311 def +/uni1E0B 2312 def +/uni1E0C 2313 def +/uni1E0D 2314 def +/uni1E0E 2315 def +/uni1E0F 2316 def +/uni1E10 2317 def +/uni1E11 2318 def +/uni1E12 2319 def +/uni1E13 2320 def +/uni1E14 2321 def +/uni1E15 2322 def +/uni1E16 2323 def +/uni1E17 2324 def +/uni1E18 2325 def +/uni1E19 2326 def +/uni1E1A 2327 def +/uni1E1B 2328 def +/uni1E1C 2329 def +/uni1E1D 2330 def +/uni1E1E 2331 def +/uni1E1F 2332 def +/uni1E20 2333 def +/uni1E21 2334 def +/uni1E22 2335 def +/uni1E23 2336 def +/uni1E24 2337 def +/uni1E25 2338 def +/uni1E26 2339 def +/uni1E27 2340 def +/uni1E28 2341 def +/uni1E29 2342 def +/uni1E2A 2343 def +/uni1E2B 2344 def +/uni1E2C 2345 def +/uni1E2D 2346 def +/uni1E2E 2347 def +/uni1E2F 2348 def +/uni1E30 2349 def +/uni1E31 2350 def +/uni1E32 2351 def +/uni1E33 2352 def +/uni1E34 2353 def +/uni1E35 2354 def +/uni1E36 2355 def +/uni1E37 2356 def +/uni1E38 2357 def +/uni1E39 2358 def +/uni1E3A 2359 def +/uni1E3B 2360 def +/uni1E3C 2361 def +/uni1E3D 2362 def +/uni1E3E 2363 def +/uni1E3F 2364 def +/uni1E40 2365 def +/uni1E41 2366 def +/uni1E42 2367 def +/uni1E43 2368 def +/uni1E44 2369 def +/uni1E45 2370 def +/uni1E46 2371 def +/uni1E47 2372 def +/uni1E48 2373 def +/uni1E49 2374 def +/uni1E4A 2375 def +/uni1E4B 2376 def +/uni1E4C 2377 def +/uni1E4D 2378 def +/uni1E4E 2379 def +/uni1E4F 2380 def +/uni1E50 2381 def +/uni1E51 2382 def +/uni1E52 2383 def +/uni1E53 2384 def +/uni1E54 2385 def +/uni1E55 2386 def +/uni1E56 2387 def +/uni1E57 2388 def +/uni1E58 2389 def +/uni1E59 2390 def +/uni1E5A 2391 def +/uni1E5B 2392 def +/uni1E5C 2393 def +/uni1E5D 2394 def +/uni1E5E 2395 def +/uni1E5F 2396 def +/uni1E60 2397 def +/uni1E61 2398 def +/uni1E62 2399 def +/uni1E63 2400 def +/uni1E64 2401 def +/uni1E65 2402 def +/uni1E66 2403 def +/uni1E67 2404 def +/uni1E68 2405 def +/uni1E69 2406 def +/uni1E6A 2407 def +/uni1E6B 2408 def +/uni1E6C 2409 def +/uni1E6D 2410 def +/uni1E6E 2411 def +/uni1E6F 2412 def +/uni1E70 2413 def +/uni1E71 2414 def +/uni1E72 2415 def +/uni1E73 2416 def +/uni1E74 2417 def +/uni1E75 2418 def +/uni1E76 2419 def +/uni1E77 2420 def +/uni1E78 2421 def +/uni1E79 2422 def +/uni1E7A 2423 def +/uni1E7B 2424 def +/uni1E7C 2425 def +/uni1E7D 2426 def +/uni1E7E 2427 def +/uni1E7F 2428 def +/Wgrave 2429 def +/wgrave 2430 def +/Wacute 2431 def +/wacute 2432 def +/Wdieresis 2433 def +/wdieresis 2434 def +/uni1E86 2435 def +/uni1E87 2436 def +/uni1E88 2437 def +/uni1E89 2438 def +/uni1E8A 2439 def +/uni1E8B 2440 def +/uni1E8C 2441 def +/uni1E8D 2442 def +/uni1E8E 2443 def +/uni1E8F 2444 def +/uni1E90 2445 def +/uni1E91 2446 def +/uni1E92 2447 def +/uni1E93 2448 def +/uni1E94 2449 def +/uni1E95 2450 def +/uni1E96 2451 def +/uni1E97 2452 def +/uni1E98 2453 def +/uni1E99 2454 def +/uni1E9A 2455 def +/uni1E9B 2456 def +/uni1E9C 2457 def +/uni1E9D 2458 def +/uni1E9E 2459 def +/uni1E9F 2460 def +/uni1EA0 2461 def +/uni1EA1 2462 def +/uni1EA2 2463 def +/uni1EA3 2464 def +/uni1EA4 2465 def +/uni1EA5 2466 def +/uni1EA6 2467 def +/uni1EA7 2468 def +/uni1EA8 2469 def +/uni1EA9 2470 def +/uni1EAA 2471 def +/uni1EAB 2472 def +/uni1EAC 2473 def +/uni1EAD 2474 def +/uni1EAE 2475 def +/uni1EAF 2476 def +/uni1EB0 2477 def +/uni1EB1 2478 def +/uni1EB2 2479 def +/uni1EB3 2480 def +/uni1EB4 2481 def +/uni1EB5 2482 def +/uni1EB6 2483 def +/uni1EB7 2484 def +/uni1EB8 2485 def +/uni1EB9 2486 def +/uni1EBA 2487 def +/uni1EBB 2488 def +/uni1EBC 2489 def +/uni1EBD 2490 def +/uni1EBE 2491 def +/uni1EBF 2492 def +/uni1EC0 2493 def +/uni1EC1 2494 def +/uni1EC2 2495 def +/uni1EC3 2496 def +/uni1EC4 2497 def +/uni1EC5 2498 def +/uni1EC6 2499 def +/uni1EC7 2500 def +/uni1EC8 2501 def +/uni1EC9 2502 def +/uni1ECA 2503 def +/uni1ECB 2504 def +/uni1ECC 2505 def +/uni1ECD 2506 def +/uni1ECE 2507 def +/uni1ECF 2508 def +/uni1ED0 2509 def +/uni1ED1 2510 def +/uni1ED2 2511 def +/uni1ED3 2512 def +/uni1ED4 2513 def +/uni1ED5 2514 def +/uni1ED6 2515 def +/uni1ED7 2516 def +/uni1ED8 2517 def +/uni1ED9 2518 def +/uni1EDA 2519 def +/uni1EDB 2520 def +/uni1EDC 2521 def +/uni1EDD 2522 def +/uni1EDE 2523 def +/uni1EDF 2524 def +/uni1EE0 2525 def +/uni1EE1 2526 def +/uni1EE2 2527 def +/uni1EE3 2528 def +/uni1EE4 2529 def +/uni1EE5 2530 def +/uni1EE6 2531 def +/uni1EE7 2532 def +/uni1EE8 2533 def +/uni1EE9 2534 def +/uni1EEA 2535 def +/uni1EEB 2536 def +/uni1EEC 2537 def +/uni1EED 2538 def +/uni1EEE 2539 def +/uni1EEF 2540 def +/uni1EF0 2541 def +/uni1EF1 2542 def +/Ygrave 2543 def +/ygrave 2544 def +/uni1EF4 2545 def +/uni1EF5 2546 def +/uni1EF6 2547 def +/uni1EF7 2548 def +/uni1EF8 2549 def +/uni1EF9 2550 def +/uni1EFA 2551 def +/uni1EFB 2552 def +/uni1F00 2553 def +/uni1F01 2554 def +/uni1F02 2555 def +/uni1F03 2556 def +/uni1F04 2557 def +/uni1F05 2558 def +/uni1F06 2559 def +/uni1F07 2560 def +/uni1F08 2561 def +/uni1F09 2562 def +/uni1F0A 2563 def +/uni1F0B 2564 def +/uni1F0C 2565 def +/uni1F0D 2566 def +/uni1F0E 2567 def +/uni1F0F 2568 def +/uni1F10 2569 def +/uni1F11 2570 def +/uni1F12 2571 def +/uni1F13 2572 def +/uni1F14 2573 def +/uni1F15 2574 def +/uni1F18 2575 def +/uni1F19 2576 def +/uni1F1A 2577 def +/uni1F1B 2578 def +/uni1F1C 2579 def +/uni1F1D 2580 def +/uni1F20 2581 def +/uni1F21 2582 def +/uni1F22 2583 def +/uni1F23 2584 def +/uni1F24 2585 def +/uni1F25 2586 def +/uni1F26 2587 def +/uni1F27 2588 def +/uni1F28 2589 def +/uni1F29 2590 def +/uni1F2A 2591 def +/uni1F2B 2592 def +/uni1F2C 2593 def +/uni1F2D 2594 def +/uni1F2E 2595 def +/uni1F2F 2596 def +/uni1F30 2597 def +/uni1F31 2598 def +/uni1F32 2599 def +/uni1F33 2600 def +/uni1F34 2601 def +/uni1F35 2602 def +/uni1F36 2603 def +/uni1F37 2604 def +/uni1F38 2605 def +/uni1F39 2606 def +/uni1F3A 2607 def +/uni1F3B 2608 def +/uni1F3C 2609 def +/uni1F3D 2610 def +/uni1F3E 2611 def +/uni1F3F 2612 def +/uni1F40 2613 def +/uni1F41 2614 def +/uni1F42 2615 def +/uni1F43 2616 def +/uni1F44 2617 def +/uni1F45 2618 def +/uni1F48 2619 def +/uni1F49 2620 def +/uni1F4A 2621 def +/uni1F4B 2622 def +/uni1F4C 2623 def +/uni1F4D 2624 def +/uni1F50 2625 def +/uni1F51 2626 def +/uni1F52 2627 def +/uni1F53 2628 def +/uni1F54 2629 def +/uni1F55 2630 def +/uni1F56 2631 def +/uni1F57 2632 def +/uni1F59 2633 def +/uni1F5B 2634 def +/uni1F5D 2635 def +/uni1F5F 2636 def +/uni1F60 2637 def +/uni1F61 2638 def +/uni1F62 2639 def +/uni1F63 2640 def +/uni1F64 2641 def +/uni1F65 2642 def +/uni1F66 2643 def +/uni1F67 2644 def +/uni1F68 2645 def +/uni1F69 2646 def +/uni1F6A 2647 def +/uni1F6B 2648 def +/uni1F6C 2649 def +/uni1F6D 2650 def +/uni1F6E 2651 def +/uni1F6F 2652 def +/uni1F70 2653 def +/uni1F71 2654 def +/uni1F72 2655 def +/uni1F73 2656 def +/uni1F74 2657 def +/uni1F75 2658 def +/uni1F76 2659 def +/uni1F77 2660 def +/uni1F78 2661 def +/uni1F79 2662 def +/uni1F7A 2663 def +/uni1F7B 2664 def +/uni1F7C 2665 def +/uni1F7D 2666 def +/uni1F80 2667 def +/uni1F81 2668 def +/uni1F82 2669 def +/uni1F83 2670 def +/uni1F84 2671 def +/uni1F85 2672 def +/uni1F86 2673 def +/uni1F87 2674 def +/uni1F88 2675 def +/uni1F89 2676 def +/uni1F8A 2677 def +/uni1F8B 2678 def +/uni1F8C 2679 def +/uni1F8D 2680 def +/uni1F8E 2681 def +/uni1F8F 2682 def +/uni1F90 2683 def +/uni1F91 2684 def +/uni1F92 2685 def +/uni1F93 2686 def +/uni1F94 2687 def +/uni1F95 2688 def +/uni1F96 2689 def +/uni1F97 2690 def +/uni1F98 2691 def +/uni1F99 2692 def +/uni1F9A 2693 def +/uni1F9B 2694 def +/uni1F9C 2695 def +/uni1F9D 2696 def +/uni1F9E 2697 def +/uni1F9F 2698 def +/uni1FA0 2699 def +/uni1FA1 2700 def +/uni1FA2 2701 def +/uni1FA3 2702 def +/uni1FA4 2703 def +/uni1FA5 2704 def +/uni1FA6 2705 def +/uni1FA7 2706 def +/uni1FA8 2707 def +/uni1FA9 2708 def +/uni1FAA 2709 def +/uni1FAB 2710 def +/uni1FAC 2711 def +/uni1FAD 2712 def +/uni1FAE 2713 def +/uni1FAF 2714 def +/uni1FB0 2715 def +/uni1FB1 2716 def +/uni1FB2 2717 def +/uni1FB3 2718 def +/uni1FB4 2719 def +/uni1FB6 2720 def +/uni1FB7 2721 def +/uni1FB8 2722 def +/uni1FB9 2723 def +/uni1FBA 2724 def +/uni1FBB 2725 def +/uni1FBC 2726 def +/uni1FBD 2727 def +/uni1FBE 2728 def +/uni1FBF 2729 def +/uni1FC0 2730 def +/uni1FC1 2731 def +/uni1FC2 2732 def +/uni1FC3 2733 def +/uni1FC4 2734 def +/uni1FC6 2735 def +/uni1FC7 2736 def +/uni1FC8 2737 def +/uni1FC9 2738 def +/uni1FCA 2739 def +/uni1FCB 2740 def +/uni1FCC 2741 def +/uni1FCD 2742 def +/uni1FCE 2743 def +/uni1FCF 2744 def +/uni1FD0 2745 def +/uni1FD1 2746 def +/uni1FD2 2747 def +/uni1FD3 2748 def +/uni1FD6 2749 def +/uni1FD7 2750 def +/uni1FD8 2751 def +/uni1FD9 2752 def +/uni1FDA 2753 def +/uni1FDB 2754 def +/uni1FDD 2755 def +/uni1FDE 2756 def +/uni1FDF 2757 def +/uni1FE0 2758 def +/uni1FE1 2759 def +/uni1FE2 2760 def +/uni1FE3 2761 def +/uni1FE4 2762 def +/uni1FE5 2763 def +/uni1FE6 2764 def +/uni1FE7 2765 def +/uni1FE8 2766 def +/uni1FE9 2767 def +/uni1FEA 2768 def +/uni1FEB 2769 def +/uni1FEC 2770 def +/uni1FED 2771 def +/uni1FEE 2772 def +/uni1FEF 2773 def +/uni1FF2 2774 def +/uni1FF3 2775 def +/uni1FF4 2776 def +/uni1FF6 2777 def +/uni1FF7 2778 def +/uni1FF8 2779 def +/uni1FF9 2780 def +/uni1FFA 2781 def +/uni1FFB 2782 def +/uni1FFC 2783 def +/uni1FFD 2784 def +/uni1FFE 2785 def +/uni2000 2786 def +/uni2001 2787 def +/uni2002 2788 def +/uni2003 2789 def +/uni2004 2790 def +/uni2005 2791 def +/uni2006 2792 def +/uni2007 2793 def +/uni2008 2794 def +/uni2009 2795 def +/uni200A 2796 def +/uni200B 2797 def +/uni200C 2798 def +/uni200D 2799 def +/uni200E 2800 def +/uni200F 2801 def +/uni2010 2802 def +/uni2011 2803 def +/figuredash 2804 def +/endash 2805 def +/emdash 2806 def +/uni2015 2807 def +/uni2016 2808 def +/underscoredbl 2809 def +/quoteleft 2810 def +/quoteright 2811 def +/quotesinglbase 2812 def +/quotereversed 2813 def +/quotedblleft 2814 def +/quotedblright 2815 def +/quotedblbase 2816 def +/uni201F 2817 def +/dagger 2818 def +/daggerdbl 2819 def +/bullet 2820 def +/uni2023 2821 def +/onedotenleader 2822 def +/twodotenleader 2823 def +/ellipsis 2824 def +/uni2027 2825 def +/uni2028 2826 def +/uni2029 2827 def +/uni202A 2828 def +/uni202B 2829 def +/uni202C 2830 def +/uni202D 2831 def +/uni202E 2832 def +/uni202F 2833 def +/perthousand 2834 def +/uni2031 2835 def +/minute 2836 def +/second 2837 def +/uni2034 2838 def +/uni2035 2839 def +/uni2036 2840 def +/uni2037 2841 def +/uni2038 2842 def +/guilsinglleft 2843 def +/guilsinglright 2844 def +/uni203B 2845 def +/exclamdbl 2846 def +/uni203D 2847 def +/uni203E 2848 def +/uni203F 2849 def +/uni2040 2850 def +/uni2041 2851 def +/uni2042 2852 def +/uni2043 2853 def +/fraction 2854 def +/uni2045 2855 def +/uni2046 2856 def +/uni2047 2857 def +/uni2048 2858 def +/uni2049 2859 def +/uni204A 2860 def +/uni204B 2861 def +/uni204C 2862 def +/uni204D 2863 def +/uni204E 2864 def +/uni204F 2865 def +/uni2050 2866 def +/uni2051 2867 def +/uni2052 2868 def +/uni2053 2869 def +/uni2054 2870 def +/uni2055 2871 def +/uni2056 2872 def +/uni2057 2873 def +/uni2058 2874 def +/uni2059 2875 def +/uni205A 2876 def +/uni205B 2877 def +/uni205C 2878 def +/uni205D 2879 def +/uni205E 2880 def +/uni205F 2881 def +/uni2060 2882 def +/uni2061 2883 def +/uni2062 2884 def +/uni2063 2885 def +/uni2064 2886 def +/uni206A 2887 def +/uni206B 2888 def +/uni206C 2889 def +/uni206D 2890 def +/uni206E 2891 def +/uni206F 2892 def +/uni2070 2893 def +/uni2071 2894 def +/uni2074 2895 def +/uni2075 2896 def +/uni2076 2897 def +/uni2077 2898 def +/uni2078 2899 def +/uni2079 2900 def +/uni207A 2901 def +/uni207B 2902 def +/uni207C 2903 def +/uni207D 2904 def +/uni207E 2905 def +/uni207F 2906 def +/uni2080 2907 def +/uni2081 2908 def +/uni2082 2909 def +/uni2083 2910 def +/uni2084 2911 def +/uni2085 2912 def +/uni2086 2913 def +/uni2087 2914 def +/uni2088 2915 def +/uni2089 2916 def +/uni208A 2917 def +/uni208B 2918 def +/uni208C 2919 def +/uni208D 2920 def +/uni208E 2921 def +/uni2090 2922 def +/uni2091 2923 def +/uni2092 2924 def +/uni2093 2925 def +/uni2094 2926 def +/uni2095 2927 def +/uni2096 2928 def +/uni2097 2929 def +/uni2098 2930 def +/uni2099 2931 def +/uni209A 2932 def +/uni209B 2933 def +/uni209C 2934 def +/uni20A0 2935 def +/colonmonetary 2936 def +/uni20A2 2937 def +/franc 2938 def +/lira 2939 def +/uni20A5 2940 def +/uni20A6 2941 def +/peseta 2942 def +/uni20A8 2943 def +/uni20A9 2944 def +/uni20AA 2945 def +/dong 2946 def +/Euro 2947 def +/uni20AD 2948 def +/uni20AE 2949 def +/uni20AF 2950 def +/uni20B0 2951 def +/uni20B1 2952 def +/uni20B2 2953 def +/uni20B3 2954 def +/uni20B4 2955 def +/uni20B5 2956 def +/uni20B8 2957 def +/uni20B9 2958 def +/uni20BA 2959 def +/uni20BD 2960 def +/uni20D0 2961 def +/uni20D1 2962 def +/uni20D6 2963 def +/uni20D7 2964 def +/uni20DB 2965 def +/uni20DC 2966 def +/uni20E1 2967 def +/uni2100 2968 def +/uni2101 2969 def +/uni2102 2970 def +/uni2103 2971 def +/uni2104 2972 def +/uni2105 2973 def +/uni2106 2974 def +/uni2107 2975 def +/uni2108 2976 def +/uni2109 2977 def +/uni210B 2978 def +/uni210C 2979 def +/uni210D 2980 def +/uni210E 2981 def +/uni210F 2982 def +/uni2110 2983 def +/Ifraktur 2984 def +/uni2112 2985 def +/uni2113 2986 def +/uni2114 2987 def +/uni2115 2988 def +/uni2116 2989 def +/uni2117 2990 def +/weierstrass 2991 def +/uni2119 2992 def +/uni211A 2993 def +/uni211B 2994 def +/Rfraktur 2995 def +/uni211D 2996 def +/prescription 2997 def +/uni211F 2998 def +/uni2120 2999 def +/uni2121 3000 def +/trademark 3001 def +/uni2123 3002 def +/uni2124 3003 def +/uni2125 3004 def +/uni2126 3005 def +/uni2127 3006 def +/uni2128 3007 def +/uni2129 3008 def +/uni212A 3009 def +/uni212B 3010 def +/uni212C 3011 def +/uni212D 3012 def +/estimated 3013 def +/uni212F 3014 def +/uni2130 3015 def +/uni2131 3016 def +/uni2132 3017 def +/uni2133 3018 def +/uni2134 3019 def +/aleph 3020 def +/uni2136 3021 def +/uni2137 3022 def +/uni2138 3023 def +/uni2139 3024 def +/uni213A 3025 def +/uni213B 3026 def +/uni213C 3027 def +/uni213D 3028 def +/uni213E 3029 def +/uni213F 3030 def +/uni2140 3031 def +/uni2141 3032 def +/uni2142 3033 def +/uni2143 3034 def +/uni2144 3035 def +/uni2145 3036 def +/uni2146 3037 def +/uni2147 3038 def +/uni2148 3039 def +/uni2149 3040 def +/uni214B 3041 def +/uni214E 3042 def +/uni2150 3043 def +/uni2151 3044 def +/uni2152 3045 def +/onethird 3046 def +/twothirds 3047 def +/uni2155 3048 def +/uni2156 3049 def +/uni2157 3050 def +/uni2158 3051 def +/uni2159 3052 def +/uni215A 3053 def +/oneeighth 3054 def +/threeeighths 3055 def +/fiveeighths 3056 def +/seveneighths 3057 def +/uni215F 3058 def +/uni2160 3059 def +/uni2161 3060 def +/uni2162 3061 def +/uni2163 3062 def +/uni2164 3063 def +/uni2165 3064 def +/uni2166 3065 def +/uni2167 3066 def +/uni2168 3067 def +/uni2169 3068 def +/uni216A 3069 def +/uni216B 3070 def +/uni216C 3071 def +/uni216D 3072 def +/uni216E 3073 def +/uni216F 3074 def +/uni2170 3075 def +/uni2171 3076 def +/uni2172 3077 def +/uni2173 3078 def +/uni2174 3079 def +/uni2175 3080 def +/uni2176 3081 def +/uni2177 3082 def +/uni2178 3083 def +/uni2179 3084 def +/uni217A 3085 def +/uni217B 3086 def +/uni217C 3087 def +/uni217D 3088 def +/uni217E 3089 def +/uni217F 3090 def +/uni2180 3091 def +/uni2181 3092 def +/uni2182 3093 def +/uni2183 3094 def +/uni2184 3095 def +/uni2185 3096 def +/uni2189 3097 def +/arrowleft 3098 def +/arrowup 3099 def +/arrowright 3100 def +/arrowdown 3101 def +/arrowboth 3102 def +/arrowupdn 3103 def +/uni2196 3104 def +/uni2197 3105 def +/uni2198 3106 def +/uni2199 3107 def +/uni219A 3108 def +/uni219B 3109 def +/uni219C 3110 def +/uni219D 3111 def +/uni219E 3112 def +/uni219F 3113 def +/uni21A0 3114 def +/uni21A1 3115 def +/uni21A2 3116 def +/uni21A3 3117 def +/uni21A4 3118 def +/uni21A5 3119 def +/uni21A6 3120 def +/uni21A7 3121 def +/arrowupdnbse 3122 def +/uni21A9 3123 def +/uni21AA 3124 def +/uni21AB 3125 def +/uni21AC 3126 def +/uni21AD 3127 def +/uni21AE 3128 def +/uni21AF 3129 def +/uni21B0 3130 def +/uni21B1 3131 def +/uni21B2 3132 def +/uni21B3 3133 def +/uni21B4 3134 def +/carriagereturn 3135 def +/uni21B6 3136 def +/uni21B7 3137 def +/uni21B8 3138 def +/uni21B9 3139 def +/uni21BA 3140 def +/uni21BB 3141 def +/uni21BC 3142 def +/uni21BD 3143 def +/uni21BE 3144 def +/uni21BF 3145 def +/uni21C0 3146 def +/uni21C1 3147 def +/uni21C2 3148 def +/uni21C3 3149 def +/uni21C4 3150 def +/uni21C5 3151 def +/uni21C6 3152 def +/uni21C7 3153 def +/uni21C8 3154 def +/uni21C9 3155 def +/uni21CA 3156 def +/uni21CB 3157 def +/uni21CC 3158 def +/uni21CD 3159 def +/uni21CE 3160 def +/uni21CF 3161 def +/arrowdblleft 3162 def +/arrowdblup 3163 def +/arrowdblright 3164 def +/arrowdbldown 3165 def +/arrowdblboth 3166 def +/uni21D5 3167 def +/uni21D6 3168 def +/uni21D7 3169 def +/uni21D8 3170 def +/uni21D9 3171 def +/uni21DA 3172 def +/uni21DB 3173 def +/uni21DC 3174 def +/uni21DD 3175 def +/uni21DE 3176 def +/uni21DF 3177 def +/uni21E0 3178 def +/uni21E1 3179 def +/uni21E2 3180 def +/uni21E3 3181 def +/uni21E4 3182 def +/uni21E5 3183 def +/uni21E6 3184 def +/uni21E7 3185 def +/uni21E8 3186 def +/uni21E9 3187 def +/uni21EA 3188 def +/uni21EB 3189 def +/uni21EC 3190 def +/uni21ED 3191 def +/uni21EE 3192 def +/uni21EF 3193 def +/uni21F0 3194 def +/uni21F1 3195 def +/uni21F2 3196 def +/uni21F3 3197 def +/uni21F4 3198 def +/uni21F5 3199 def +/uni21F6 3200 def +/uni21F7 3201 def +/uni21F8 3202 def +/uni21F9 3203 def +/uni21FA 3204 def +/uni21FB 3205 def +/uni21FC 3206 def +/uni21FD 3207 def +/uni21FE 3208 def +/uni21FF 3209 def +/universal 3210 def +/uni2201 3211 def +/partialdiff 3212 def +/existential 3213 def +/uni2204 3214 def +/emptyset 3215 def +/Delta 3216 def +/gradient 3217 def +/element 3218 def +/notelement 3219 def +/uni220A 3220 def +/suchthat 3221 def +/uni220C 3222 def +/uni220D 3223 def +/uni220E 3224 def +/product 3225 def +/uni2210 3226 def +/summation 3227 def +/minus 3228 def +/uni2213 3229 def +/uni2214 3230 def +/uni2215 3231 def +/uni2216 3232 def +/asteriskmath 3233 def +/uni2218 3234 def +/uni2219 3235 def +/radical 3236 def +/uni221B 3237 def +/uni221C 3238 def +/proportional 3239 def +/infinity 3240 def +/orthogonal 3241 def +/angle 3242 def +/uni2221 3243 def +/uni2222 3244 def +/uni2223 3245 def +/uni2224 3246 def +/uni2225 3247 def +/uni2226 3248 def +/logicaland 3249 def +/logicalor 3250 def +/intersection 3251 def +/union 3252 def +/integral 3253 def +/uni222C 3254 def +/uni222D 3255 def +/uni222E 3256 def +/uni222F 3257 def +/uni2230 3258 def +/uni2231 3259 def +/uni2232 3260 def +/uni2233 3261 def +/therefore 3262 def +/uni2235 3263 def +/uni2236 3264 def +/uni2237 3265 def +/uni2238 3266 def +/uni2239 3267 def +/uni223A 3268 def +/uni223B 3269 def +/similar 3270 def +/uni223D 3271 def +/uni223E 3272 def +/uni223F 3273 def +/uni2240 3274 def +/uni2241 3275 def +/uni2242 3276 def +/uni2243 3277 def +/uni2244 3278 def +/congruent 3279 def +/uni2246 3280 def +/uni2247 3281 def +/approxequal 3282 def +/uni2249 3283 def +/uni224A 3284 def +/uni224B 3285 def +/uni224C 3286 def +/uni224D 3287 def +/uni224E 3288 def +/uni224F 3289 def +/uni2250 3290 def +/uni2251 3291 def +/uni2252 3292 def +/uni2253 3293 def +/uni2254 3294 def +/uni2255 3295 def +/uni2256 3296 def +/uni2257 3297 def +/uni2258 3298 def +/uni2259 3299 def +/uni225A 3300 def +/uni225B 3301 def +/uni225C 3302 def +/uni225D 3303 def +/uni225E 3304 def +/uni225F 3305 def +/notequal 3306 def +/equivalence 3307 def +/uni2262 3308 def +/uni2263 3309 def +/lessequal 3310 def +/greaterequal 3311 def +/uni2266 3312 def +/uni2267 3313 def +/uni2268 3314 def +/uni2269 3315 def +/uni226A 3316 def +/uni226B 3317 def +/uni226C 3318 def +/uni226D 3319 def +/uni226E 3320 def +/uni226F 3321 def +/uni2270 3322 def +/uni2271 3323 def +/uni2272 3324 def +/uni2273 3325 def +/uni2274 3326 def +/uni2275 3327 def +/uni2276 3328 def +/uni2277 3329 def +/uni2278 3330 def +/uni2279 3331 def +/uni227A 3332 def +/uni227B 3333 def +/uni227C 3334 def +/uni227D 3335 def +/uni227E 3336 def +/uni227F 3337 def +/uni2280 3338 def +/uni2281 3339 def +/propersubset 3340 def +/propersuperset 3341 def +/notsubset 3342 def +/uni2285 3343 def +/reflexsubset 3344 def +/reflexsuperset 3345 def +/uni2288 3346 def +/uni2289 3347 def +/uni228A 3348 def +/uni228B 3349 def +/uni228C 3350 def +/uni228D 3351 def +/uni228E 3352 def +/uni228F 3353 def +/uni2290 3354 def +/uni2291 3355 def +/uni2292 3356 def +/uni2293 3357 def +/uni2294 3358 def +/circleplus 3359 def +/uni2296 3360 def +/circlemultiply 3361 def +/uni2298 3362 def +/uni2299 3363 def +/uni229A 3364 def +/uni229B 3365 def +/uni229C 3366 def +/uni229D 3367 def +/uni229E 3368 def +/uni229F 3369 def +/uni22A0 3370 def +/uni22A1 3371 def +/uni22A2 3372 def +/uni22A3 3373 def +/uni22A4 3374 def +/perpendicular 3375 def +/uni22A6 3376 def +/uni22A7 3377 def +/uni22A8 3378 def +/uni22A9 3379 def +/uni22AA 3380 def +/uni22AB 3381 def +/uni22AC 3382 def +/uni22AD 3383 def +/uni22AE 3384 def +/uni22AF 3385 def +/uni22B0 3386 def +/uni22B1 3387 def +/uni22B2 3388 def +/uni22B3 3389 def +/uni22B4 3390 def +/uni22B5 3391 def +/uni22B6 3392 def +/uni22B7 3393 def +/uni22B8 3394 def +/uni22B9 3395 def +/uni22BA 3396 def +/uni22BB 3397 def +/uni22BC 3398 def +/uni22BD 3399 def +/uni22BE 3400 def +/uni22BF 3401 def +/uni22C0 3402 def +/uni22C1 3403 def +/uni22C2 3404 def +/uni22C3 3405 def +/uni22C4 3406 def +/dotmath 3407 def +/uni22C6 3408 def +/uni22C7 3409 def +/uni22C8 3410 def +/uni22C9 3411 def +/uni22CA 3412 def +/uni22CB 3413 def +/uni22CC 3414 def +/uni22CD 3415 def +/uni22CE 3416 def +/uni22CF 3417 def +/uni22D0 3418 def +/uni22D1 3419 def +/uni22D2 3420 def +/uni22D3 3421 def +/uni22D4 3422 def +/uni22D5 3423 def +/uni22D6 3424 def +/uni22D7 3425 def +/uni22D8 3426 def +/uni22D9 3427 def +/uni22DA 3428 def +/uni22DB 3429 def +/uni22DC 3430 def +/uni22DD 3431 def +/uni22DE 3432 def +/uni22DF 3433 def +/uni22E0 3434 def +/uni22E1 3435 def +/uni22E2 3436 def +/uni22E3 3437 def +/uni22E4 3438 def +/uni22E5 3439 def +/uni22E6 3440 def +/uni22E7 3441 def +/uni22E8 3442 def +/uni22E9 3443 def +/uni22EA 3444 def +/uni22EB 3445 def +/uni22EC 3446 def +/uni22ED 3447 def +/uni22EE 3448 def +/uni22EF 3449 def +/uni22F0 3450 def +/uni22F1 3451 def +/uni22F2 3452 def +/uni22F3 3453 def +/uni22F4 3454 def +/uni22F5 3455 def +/uni22F6 3456 def +/uni22F7 3457 def +/uni22F8 3458 def +/uni22F9 3459 def +/uni22FA 3460 def +/uni22FB 3461 def +/uni22FC 3462 def +/uni22FD 3463 def +/uni22FE 3464 def +/uni22FF 3465 def +/uni2300 3466 def +/uni2301 3467 def +/house 3468 def +/uni2303 3469 def +/uni2304 3470 def +/uni2305 3471 def +/uni2306 3472 def +/uni2307 3473 def +/uni2308 3474 def +/uni2309 3475 def +/uni230A 3476 def +/uni230B 3477 def +/uni230C 3478 def +/uni230D 3479 def +/uni230E 3480 def +/uni230F 3481 def +/revlogicalnot 3482 def +/uni2311 3483 def +/uni2318 3484 def +/uni2319 3485 def +/uni231C 3486 def +/uni231D 3487 def +/uni231E 3488 def +/uni231F 3489 def +/integraltp 3490 def +/integralbt 3491 def +/uni2324 3492 def +/uni2325 3493 def +/uni2326 3494 def +/uni2327 3495 def +/uni2328 3496 def +/uni232B 3497 def +/uni232C 3498 def +/uni2373 3499 def +/uni2374 3500 def +/uni2375 3501 def +/uni237A 3502 def +/uni237D 3503 def +/uni2387 3504 def +/uni2394 3505 def +/uni239B 3506 def +/uni239C 3507 def +/uni239D 3508 def +/uni239E 3509 def +/uni239F 3510 def +/uni23A0 3511 def +/uni23A1 3512 def +/uni23A2 3513 def +/uni23A3 3514 def +/uni23A4 3515 def +/uni23A5 3516 def +/uni23A6 3517 def +/uni23A7 3518 def +/uni23A8 3519 def +/uni23A9 3520 def +/uni23AA 3521 def +/uni23AB 3522 def +/uni23AC 3523 def +/uni23AD 3524 def +/uni23AE 3525 def +/uni23CE 3526 def +/uni23CF 3527 def +/uni23E3 3528 def +/uni23E5 3529 def +/uni23E8 3530 def +/uni2422 3531 def +/uni2423 3532 def +/uni2460 3533 def +/uni2461 3534 def +/uni2462 3535 def +/uni2463 3536 def +/uni2464 3537 def +/uni2465 3538 def +/uni2466 3539 def +/uni2467 3540 def +/uni2468 3541 def +/uni2469 3542 def +/SF100000 3543 def +/uni2501 3544 def +/SF110000 3545 def +/uni2503 3546 def +/uni2504 3547 def +/uni2505 3548 def +/uni2506 3549 def +/uni2507 3550 def +/uni2508 3551 def +/uni2509 3552 def +/uni250A 3553 def +/uni250B 3554 def +/SF010000 3555 def +/uni250D 3556 def +/uni250E 3557 def +/uni250F 3558 def +/SF030000 3559 def +/uni2511 3560 def +/uni2512 3561 def +/uni2513 3562 def +/SF020000 3563 def +/uni2515 3564 def +/uni2516 3565 def +/uni2517 3566 def +/SF040000 3567 def +/uni2519 3568 def +/uni251A 3569 def +/uni251B 3570 def +/SF080000 3571 def +/uni251D 3572 def +/uni251E 3573 def +/uni251F 3574 def +/uni2520 3575 def +/uni2521 3576 def +/uni2522 3577 def +/uni2523 3578 def +/SF090000 3579 def +/uni2525 3580 def +/uni2526 3581 def +/uni2527 3582 def +/uni2528 3583 def +/uni2529 3584 def +/uni252A 3585 def +/uni252B 3586 def +/SF060000 3587 def +/uni252D 3588 def +/uni252E 3589 def +/uni252F 3590 def +/uni2530 3591 def +/uni2531 3592 def +/uni2532 3593 def +/uni2533 3594 def +/SF070000 3595 def +/uni2535 3596 def +/uni2536 3597 def +/uni2537 3598 def +/uni2538 3599 def +/uni2539 3600 def +/uni253A 3601 def +/uni253B 3602 def +/SF050000 3603 def +/uni253D 3604 def +/uni253E 3605 def +/uni253F 3606 def +/uni2540 3607 def +/uni2541 3608 def +/uni2542 3609 def +/uni2543 3610 def +/uni2544 3611 def +/uni2545 3612 def +/uni2546 3613 def +/uni2547 3614 def +/uni2548 3615 def +/uni2549 3616 def +/uni254A 3617 def +/uni254B 3618 def +/uni254C 3619 def +/uni254D 3620 def +/uni254E 3621 def +/uni254F 3622 def +/SF430000 3623 def +/SF240000 3624 def +/SF510000 3625 def +/SF520000 3626 def +/SF390000 3627 def +/SF220000 3628 def +/SF210000 3629 def +/SF250000 3630 def +/SF500000 3631 def +/SF490000 3632 def +/SF380000 3633 def +/SF280000 3634 def +/SF270000 3635 def +/SF260000 3636 def +/SF360000 3637 def +/SF370000 3638 def +/SF420000 3639 def +/SF190000 3640 def +/SF200000 3641 def +/SF230000 3642 def +/SF470000 3643 def +/SF480000 3644 def +/SF410000 3645 def +/SF450000 3646 def +/SF460000 3647 def +/SF400000 3648 def +/SF540000 3649 def +/SF530000 3650 def +/SF440000 3651 def +/uni256D 3652 def +/uni256E 3653 def +/uni256F 3654 def +/uni2570 3655 def +/uni2571 3656 def +/uni2572 3657 def +/uni2573 3658 def +/uni2574 3659 def +/uni2575 3660 def +/uni2576 3661 def +/uni2577 3662 def +/uni2578 3663 def +/uni2579 3664 def +/uni257A 3665 def +/uni257B 3666 def +/uni257C 3667 def +/uni257D 3668 def +/uni257E 3669 def +/uni257F 3670 def +/upblock 3671 def +/uni2581 3672 def +/uni2582 3673 def +/uni2583 3674 def +/dnblock 3675 def +/uni2585 3676 def +/uni2586 3677 def +/uni2587 3678 def +/block 3679 def +/uni2589 3680 def +/uni258A 3681 def +/uni258B 3682 def +/lfblock 3683 def +/uni258D 3684 def +/uni258E 3685 def +/uni258F 3686 def +/rtblock 3687 def +/ltshade 3688 def +/shade 3689 def +/dkshade 3690 def +/uni2594 3691 def +/uni2595 3692 def +/uni2596 3693 def +/uni2597 3694 def +/uni2598 3695 def +/uni2599 3696 def +/uni259A 3697 def +/uni259B 3698 def +/uni259C 3699 def +/uni259D 3700 def +/uni259E 3701 def +/uni259F 3702 def +/filledbox 3703 def +/H22073 3704 def +/uni25A2 3705 def +/uni25A3 3706 def +/uni25A4 3707 def +/uni25A5 3708 def +/uni25A6 3709 def +/uni25A7 3710 def +/uni25A8 3711 def +/uni25A9 3712 def +/H18543 3713 def +/H18551 3714 def +/filledrect 3715 def +/uni25AD 3716 def +/uni25AE 3717 def +/uni25AF 3718 def +/uni25B0 3719 def +/uni25B1 3720 def +/triagup 3721 def +/uni25B3 3722 def +/uni25B4 3723 def +/uni25B5 3724 def +/uni25B6 3725 def +/uni25B7 3726 def +/uni25B8 3727 def +/uni25B9 3728 def +/triagrt 3729 def +/uni25BB 3730 def +/triagdn 3731 def +/uni25BD 3732 def +/uni25BE 3733 def +/uni25BF 3734 def +/uni25C0 3735 def +/uni25C1 3736 def +/uni25C2 3737 def +/uni25C3 3738 def +/triaglf 3739 def +/uni25C5 3740 def +/uni25C6 3741 def +/uni25C7 3742 def +/uni25C8 3743 def +/uni25C9 3744 def +/lozenge 3745 def +/circle 3746 def +/uni25CC 3747 def +/uni25CD 3748 def +/uni25CE 3749 def +/H18533 3750 def +/uni25D0 3751 def +/uni25D1 3752 def +/uni25D2 3753 def +/uni25D3 3754 def +/uni25D4 3755 def +/uni25D5 3756 def +/uni25D6 3757 def +/uni25D7 3758 def +/invbullet 3759 def +/invcircle 3760 def +/uni25DA 3761 def +/uni25DB 3762 def +/uni25DC 3763 def +/uni25DD 3764 def +/uni25DE 3765 def +/uni25DF 3766 def +/uni25E0 3767 def +/uni25E1 3768 def +/uni25E2 3769 def +/uni25E3 3770 def +/uni25E4 3771 def +/uni25E5 3772 def +/openbullet 3773 def +/uni25E7 3774 def +/uni25E8 3775 def +/uni25E9 3776 def +/uni25EA 3777 def +/uni25EB 3778 def +/uni25EC 3779 def +/uni25ED 3780 def +/uni25EE 3781 def +/uni25EF 3782 def +/uni25F0 3783 def +/uni25F1 3784 def +/uni25F2 3785 def +/uni25F3 3786 def +/uni25F4 3787 def +/uni25F5 3788 def +/uni25F6 3789 def +/uni25F7 3790 def +/uni25F8 3791 def +/uni25F9 3792 def +/uni25FA 3793 def +/uni25FB 3794 def +/uni25FC 3795 def +/uni25FD 3796 def +/uni25FE 3797 def +/uni25FF 3798 def +/uni2600 3799 def +/uni2601 3800 def +/uni2602 3801 def +/uni2603 3802 def +/uni2604 3803 def +/uni2605 3804 def +/uni2606 3805 def +/uni2607 3806 def +/uni2608 3807 def +/uni2609 3808 def +/uni260A 3809 def +/uni260B 3810 def +/uni260C 3811 def +/uni260D 3812 def +/uni260E 3813 def +/uni260F 3814 def +/uni2610 3815 def +/uni2611 3816 def +/uni2612 3817 def +/uni2613 3818 def +/uni2614 3819 def +/uni2615 3820 def +/uni2616 3821 def +/uni2617 3822 def +/uni2618 3823 def +/uni2619 3824 def +/uni261A 3825 def +/uni261B 3826 def +/uni261C 3827 def +/uni261D 3828 def +/uni261E 3829 def +/uni261F 3830 def +/uni2620 3831 def +/uni2621 3832 def +/uni2622 3833 def +/uni2623 3834 def +/uni2624 3835 def +/uni2625 3836 def +/uni2626 3837 def +/uni2627 3838 def +/uni2628 3839 def +/uni2629 3840 def +/uni262A 3841 def +/uni262B 3842 def +/uni262C 3843 def +/uni262D 3844 def +/uni262E 3845 def +/uni262F 3846 def +/uni2630 3847 def +/uni2631 3848 def +/uni2632 3849 def +/uni2633 3850 def +/uni2634 3851 def +/uni2635 3852 def +/uni2636 3853 def +/uni2637 3854 def +/uni2638 3855 def +/uni2639 3856 def +/smileface 3857 def +/invsmileface 3858 def +/sun 3859 def +/uni263D 3860 def +/uni263E 3861 def +/uni263F 3862 def +/female 3863 def +/uni2641 3864 def +/male 3865 def +/uni2643 3866 def +/uni2644 3867 def +/uni2645 3868 def +/uni2646 3869 def +/uni2647 3870 def +/uni2648 3871 def +/uni2649 3872 def +/uni264A 3873 def +/uni264B 3874 def +/uni264C 3875 def +/uni264D 3876 def +/uni264E 3877 def +/uni264F 3878 def +/uni2650 3879 def +/uni2651 3880 def +/uni2652 3881 def +/uni2653 3882 def +/uni2654 3883 def +/uni2655 3884 def +/uni2656 3885 def +/uni2657 3886 def +/uni2658 3887 def +/uni2659 3888 def +/uni265A 3889 def +/uni265B 3890 def +/uni265C 3891 def +/uni265D 3892 def +/uni265E 3893 def +/uni265F 3894 def +/spade 3895 def +/uni2661 3896 def +/uni2662 3897 def +/club 3898 def +/uni2664 3899 def +/heart 3900 def +/diamond 3901 def +/uni2667 3902 def +/uni2668 3903 def +/uni2669 3904 def +/musicalnote 3905 def +/musicalnotedbl 3906 def +/uni266C 3907 def +/uni266D 3908 def +/uni266E 3909 def +/uni266F 3910 def +/uni2670 3911 def +/uni2671 3912 def +/uni2672 3913 def +/uni2673 3914 def +/uni2674 3915 def +/uni2675 3916 def +/uni2676 3917 def +/uni2677 3918 def +/uni2678 3919 def +/uni2679 3920 def +/uni267A 3921 def +/uni267B 3922 def +/uni267C 3923 def +/uni267D 3924 def +/uni267E 3925 def +/uni267F 3926 def +/uni2680 3927 def +/uni2681 3928 def +/uni2682 3929 def +/uni2683 3930 def +/uni2684 3931 def +/uni2685 3932 def +/uni2686 3933 def +/uni2687 3934 def +/uni2688 3935 def +/uni2689 3936 def +/uni268A 3937 def +/uni268B 3938 def +/uni268C 3939 def +/uni268D 3940 def +/uni268E 3941 def +/uni268F 3942 def +/uni2690 3943 def +/uni2691 3944 def +/uni2692 3945 def +/uni2693 3946 def +/uni2694 3947 def +/uni2695 3948 def +/uni2696 3949 def +/uni2697 3950 def +/uni2698 3951 def +/uni2699 3952 def +/uni269A 3953 def +/uni269B 3954 def +/uni269C 3955 def +/uni269E 3956 def +/uni269F 3957 def +/uni26A0 3958 def +/uni26A1 3959 def +/uni26A2 3960 def +/uni26A3 3961 def +/uni26A4 3962 def +/uni26A5 3963 def +/uni26A6 3964 def +/uni26A7 3965 def +/uni26A8 3966 def +/uni26A9 3967 def +/uni26AA 3968 def +/uni26AB 3969 def +/uni26AC 3970 def +/uni26AD 3971 def +/uni26AE 3972 def +/uni26AF 3973 def +/uni26B0 3974 def +/uni26B1 3975 def +/uni26B2 3976 def +/uni26B3 3977 def +/uni26B4 3978 def +/uni26B5 3979 def +/uni26B6 3980 def +/uni26B7 3981 def +/uni26B8 3982 def +/uni26C0 3983 def +/uni26C1 3984 def +/uni26C2 3985 def +/uni26C3 3986 def +/uni26E2 3987 def +/uni2701 3988 def +/uni2702 3989 def +/uni2703 3990 def +/uni2704 3991 def +/uni2706 3992 def +/uni2707 3993 def +/uni2708 3994 def +/uni2709 3995 def +/uni270C 3996 def +/uni270D 3997 def +/uni270E 3998 def +/uni270F 3999 def +/uni2710 4000 def +/uni2711 4001 def +/uni2712 4002 def +/uni2713 4003 def +/uni2714 4004 def +/uni2715 4005 def +/uni2716 4006 def +/uni2717 4007 def +/uni2718 4008 def +/uni2719 4009 def +/uni271A 4010 def +/uni271B 4011 def +/uni271C 4012 def +/uni271D 4013 def +/uni271E 4014 def +/uni271F 4015 def +/uni2720 4016 def +/uni2721 4017 def +/uni2722 4018 def +/uni2723 4019 def +/uni2724 4020 def +/uni2725 4021 def +/uni2726 4022 def +/uni2727 4023 def +/uni2729 4024 def +/uni272A 4025 def +/uni272B 4026 def +/uni272C 4027 def +/uni272D 4028 def +/uni272E 4029 def +/uni272F 4030 def +/uni2730 4031 def +/uni2731 4032 def +/uni2732 4033 def +/uni2733 4034 def +/uni2734 4035 def +/uni2735 4036 def +/uni2736 4037 def +/uni2737 4038 def +/uni2738 4039 def +/uni2739 4040 def +/uni273A 4041 def +/uni273B 4042 def +/uni273C 4043 def +/uni273D 4044 def +/uni273E 4045 def +/uni273F 4046 def +/uni2740 4047 def +/uni2741 4048 def +/uni2742 4049 def +/uni2743 4050 def +/uni2744 4051 def +/uni2745 4052 def +/uni2746 4053 def +/uni2747 4054 def +/uni2748 4055 def +/uni2749 4056 def +/uni274A 4057 def +/uni274B 4058 def +/uni274D 4059 def +/uni274F 4060 def +/uni2750 4061 def +/uni2751 4062 def +/uni2752 4063 def +/uni2756 4064 def +/uni2758 4065 def +/uni2759 4066 def +/uni275A 4067 def +/uni275B 4068 def +/uni275C 4069 def +/uni275D 4070 def +/uni275E 4071 def +/uni2761 4072 def +/uni2762 4073 def +/uni2763 4074 def +/uni2764 4075 def +/uni2765 4076 def +/uni2766 4077 def +/uni2767 4078 def +/uni2768 4079 def +/uni2769 4080 def +/uni276A 4081 def +/uni276B 4082 def +/uni276C 4083 def +/uni276D 4084 def +/uni276E 4085 def +/uni276F 4086 def +/uni2770 4087 def +/uni2771 4088 def +/uni2772 4089 def +/uni2773 4090 def +/uni2774 4091 def +/uni2775 4092 def +/uni2776 4093 def +/uni2777 4094 def +/uni2778 4095 def +/uni2779 4096 def +/uni277A 4097 def +/uni277B 4098 def +/uni277C 4099 def +/uni277D 4100 def +/uni277E 4101 def +/uni277F 4102 def +/uni2780 4103 def +/uni2781 4104 def +/uni2782 4105 def +/uni2783 4106 def +/uni2784 4107 def +/uni2785 4108 def +/uni2786 4109 def +/uni2787 4110 def +/uni2788 4111 def +/uni2789 4112 def +/uni278A 4113 def +/uni278B 4114 def +/uni278C 4115 def +/uni278D 4116 def +/uni278E 4117 def +/uni278F 4118 def +/uni2790 4119 def +/uni2791 4120 def +/uni2792 4121 def +/uni2793 4122 def +/uni2794 4123 def +/uni2798 4124 def +/uni2799 4125 def +/uni279A 4126 def +/uni279B 4127 def +/uni279C 4128 def +/uni279D 4129 def +/uni279E 4130 def +/uni279F 4131 def +/uni27A0 4132 def +/uni27A1 4133 def +/uni27A2 4134 def +/uni27A3 4135 def +/uni27A4 4136 def +/uni27A5 4137 def +/uni27A6 4138 def +/uni27A7 4139 def +/uni27A8 4140 def +/uni27A9 4141 def +/uni27AA 4142 def +/uni27AB 4143 def +/uni27AC 4144 def +/uni27AD 4145 def +/uni27AE 4146 def +/uni27AF 4147 def +/uni27B1 4148 def +/uni27B2 4149 def +/uni27B3 4150 def +/uni27B4 4151 def +/uni27B5 4152 def +/uni27B6 4153 def +/uni27B7 4154 def +/uni27B8 4155 def +/uni27B9 4156 def +/uni27BA 4157 def +/uni27BB 4158 def +/uni27BC 4159 def +/uni27BD 4160 def +/uni27BE 4161 def +/uni27C5 4162 def +/uni27C6 4163 def +/uni27E0 4164 def +/uni27E6 4165 def +/uni27E7 4166 def +/uni27E8 4167 def +/uni27E9 4168 def +/uni27EA 4169 def +/uni27EB 4170 def +/uni27F0 4171 def +/uni27F1 4172 def +/uni27F2 4173 def +/uni27F3 4174 def +/uni27F4 4175 def +/uni27F5 4176 def +/uni27F6 4177 def +/uni27F7 4178 def +/uni27F8 4179 def +/uni27F9 4180 def +/uni27FA 4181 def +/uni27FB 4182 def +/uni27FC 4183 def +/uni27FD 4184 def +/uni27FE 4185 def +/uni27FF 4186 def +/uni2800 4187 def +/uni2801 4188 def +/uni2802 4189 def +/uni2803 4190 def +/uni2804 4191 def +/uni2805 4192 def +/uni2806 4193 def +/uni2807 4194 def +/uni2808 4195 def +/uni2809 4196 def +/uni280A 4197 def +/uni280B 4198 def +/uni280C 4199 def +/uni280D 4200 def +/uni280E 4201 def +/uni280F 4202 def +/uni2810 4203 def +/uni2811 4204 def +/uni2812 4205 def +/uni2813 4206 def +/uni2814 4207 def +/uni2815 4208 def +/uni2816 4209 def +/uni2817 4210 def +/uni2818 4211 def +/uni2819 4212 def +/uni281A 4213 def +/uni281B 4214 def +/uni281C 4215 def +/uni281D 4216 def +/uni281E 4217 def +/uni281F 4218 def +/uni2820 4219 def +/uni2821 4220 def +/uni2822 4221 def +/uni2823 4222 def +/uni2824 4223 def +/uni2825 4224 def +/uni2826 4225 def +/uni2827 4226 def +/uni2828 4227 def +/uni2829 4228 def +/uni282A 4229 def +/uni282B 4230 def +/uni282C 4231 def +/uni282D 4232 def +/uni282E 4233 def +/uni282F 4234 def +/uni2830 4235 def +/uni2831 4236 def +/uni2832 4237 def +/uni2833 4238 def +/uni2834 4239 def +/uni2835 4240 def +/uni2836 4241 def +/uni2837 4242 def +/uni2838 4243 def +/uni2839 4244 def +/uni283A 4245 def +/uni283B 4246 def +/uni283C 4247 def +/uni283D 4248 def +/uni283E 4249 def +/uni283F 4250 def +/uni2840 4251 def +/uni2841 4252 def +/uni2842 4253 def +/uni2843 4254 def +/uni2844 4255 def +/uni2845 4256 def +/uni2846 4257 def +/uni2847 4258 def +/uni2848 4259 def +/uni2849 4260 def +/uni284A 4261 def +/uni284B 4262 def +/uni284C 4263 def +/uni284D 4264 def +/uni284E 4265 def +/uni284F 4266 def +/uni2850 4267 def +/uni2851 4268 def +/uni2852 4269 def +/uni2853 4270 def +/uni2854 4271 def +/uni2855 4272 def +/uni2856 4273 def +/uni2857 4274 def +/uni2858 4275 def +/uni2859 4276 def +/uni285A 4277 def +/uni285B 4278 def +/uni285C 4279 def +/uni285D 4280 def +/uni285E 4281 def +/uni285F 4282 def +/uni2860 4283 def +/uni2861 4284 def +/uni2862 4285 def +/uni2863 4286 def +/uni2864 4287 def +/uni2865 4288 def +/uni2866 4289 def +/uni2867 4290 def +/uni2868 4291 def +/uni2869 4292 def +/uni286A 4293 def +/uni286B 4294 def +/uni286C 4295 def +/uni286D 4296 def +/uni286E 4297 def +/uni286F 4298 def +/uni2870 4299 def +/uni2871 4300 def +/uni2872 4301 def +/uni2873 4302 def +/uni2874 4303 def +/uni2875 4304 def +/uni2876 4305 def +/uni2877 4306 def +/uni2878 4307 def +/uni2879 4308 def +/uni287A 4309 def +/uni287B 4310 def +/uni287C 4311 def +/uni287D 4312 def +/uni287E 4313 def +/uni287F 4314 def +/uni2880 4315 def +/uni2881 4316 def +/uni2882 4317 def +/uni2883 4318 def +/uni2884 4319 def +/uni2885 4320 def +/uni2886 4321 def +/uni2887 4322 def +/uni2888 4323 def +/uni2889 4324 def +/uni288A 4325 def +/uni288B 4326 def +/uni288C 4327 def +/uni288D 4328 def +/uni288E 4329 def +/uni288F 4330 def +/uni2890 4331 def +/uni2891 4332 def +/uni2892 4333 def +/uni2893 4334 def +/uni2894 4335 def +/uni2895 4336 def +/uni2896 4337 def +/uni2897 4338 def +/uni2898 4339 def +/uni2899 4340 def +/uni289A 4341 def +/uni289B 4342 def +/uni289C 4343 def +/uni289D 4344 def +/uni289E 4345 def +/uni289F 4346 def +/uni28A0 4347 def +/uni28A1 4348 def +/uni28A2 4349 def +/uni28A3 4350 def +/uni28A4 4351 def +/uni28A5 4352 def +/uni28A6 4353 def +/uni28A7 4354 def +/uni28A8 4355 def +/uni28A9 4356 def +/uni28AA 4357 def +/uni28AB 4358 def +/uni28AC 4359 def +/uni28AD 4360 def +/uni28AE 4361 def +/uni28AF 4362 def +/uni28B0 4363 def +/uni28B1 4364 def +/uni28B2 4365 def +/uni28B3 4366 def +/uni28B4 4367 def +/uni28B5 4368 def +/uni28B6 4369 def +/uni28B7 4370 def +/uni28B8 4371 def +/uni28B9 4372 def +/uni28BA 4373 def +/uni28BB 4374 def +/uni28BC 4375 def +/uni28BD 4376 def +/uni28BE 4377 def +/uni28BF 4378 def +/uni28C0 4379 def +/uni28C1 4380 def +/uni28C2 4381 def +/uni28C3 4382 def +/uni28C4 4383 def +/uni28C5 4384 def +/uni28C6 4385 def +/uni28C7 4386 def +/uni28C8 4387 def +/uni28C9 4388 def +/uni28CA 4389 def +/uni28CB 4390 def +/uni28CC 4391 def +/uni28CD 4392 def +/uni28CE 4393 def +/uni28CF 4394 def +/uni28D0 4395 def +/uni28D1 4396 def +/uni28D2 4397 def +/uni28D3 4398 def +/uni28D4 4399 def +/uni28D5 4400 def +/uni28D6 4401 def +/uni28D7 4402 def +/uni28D8 4403 def +/uni28D9 4404 def +/uni28DA 4405 def +/uni28DB 4406 def +/uni28DC 4407 def +/uni28DD 4408 def +/uni28DE 4409 def +/uni28DF 4410 def +/uni28E0 4411 def +/uni28E1 4412 def +/uni28E2 4413 def +/uni28E3 4414 def +/uni28E4 4415 def +/uni28E5 4416 def +/uni28E6 4417 def +/uni28E7 4418 def +/uni28E8 4419 def +/uni28E9 4420 def +/uni28EA 4421 def +/uni28EB 4422 def +/uni28EC 4423 def +/uni28ED 4424 def +/uni28EE 4425 def +/uni28EF 4426 def +/uni28F0 4427 def +/uni28F1 4428 def +/uni28F2 4429 def +/uni28F3 4430 def +/uni28F4 4431 def +/uni28F5 4432 def +/uni28F6 4433 def +/uni28F7 4434 def +/uni28F8 4435 def +/uni28F9 4436 def +/uni28FA 4437 def +/uni28FB 4438 def +/uni28FC 4439 def +/uni28FD 4440 def +/uni28FE 4441 def +/uni28FF 4442 def +/uni2906 4443 def +/uni2907 4444 def +/uni290A 4445 def +/uni290B 4446 def +/uni2940 4447 def +/uni2941 4448 def +/uni2983 4449 def +/uni2984 4450 def +/uni29CE 4451 def +/uni29CF 4452 def +/uni29D0 4453 def +/uni29D1 4454 def +/uni29D2 4455 def +/uni29D3 4456 def +/uni29D4 4457 def +/uni29D5 4458 def +/uni29EB 4459 def +/uni29FA 4460 def +/uni29FB 4461 def +/uni2A00 4462 def +/uni2A01 4463 def +/uni2A02 4464 def +/uni2A0C 4465 def +/uni2A0D 4466 def +/uni2A0E 4467 def +/uni2A0F 4468 def +/uni2A10 4469 def +/uni2A11 4470 def +/uni2A12 4471 def +/uni2A13 4472 def +/uni2A14 4473 def +/uni2A15 4474 def +/uni2A16 4475 def +/uni2A17 4476 def +/uni2A18 4477 def +/uni2A19 4478 def +/uni2A1A 4479 def +/uni2A1B 4480 def +/uni2A1C 4481 def +/uni2A2F 4482 def +/uni2A6A 4483 def +/uni2A6B 4484 def +/uni2A7D 4485 def +/uni2A7E 4486 def +/uni2A7F 4487 def +/uni2A80 4488 def +/uni2A81 4489 def +/uni2A82 4490 def +/uni2A83 4491 def +/uni2A84 4492 def +/uni2A85 4493 def +/uni2A86 4494 def +/uni2A87 4495 def +/uni2A88 4496 def +/uni2A89 4497 def +/uni2A8A 4498 def +/uni2A8B 4499 def +/uni2A8C 4500 def +/uni2A8D 4501 def +/uni2A8E 4502 def +/uni2A8F 4503 def +/uni2A90 4504 def +/uni2A91 4505 def +/uni2A92 4506 def +/uni2A93 4507 def +/uni2A94 4508 def +/uni2A95 4509 def +/uni2A96 4510 def +/uni2A97 4511 def +/uni2A98 4512 def +/uni2A99 4513 def +/uni2A9A 4514 def +/uni2A9B 4515 def +/uni2A9C 4516 def +/uni2A9D 4517 def +/uni2A9E 4518 def +/uni2A9F 4519 def +/uni2AA0 4520 def +/uni2AAE 4521 def +/uni2AAF 4522 def +/uni2AB0 4523 def +/uni2AB1 4524 def +/uni2AB2 4525 def +/uni2AB3 4526 def +/uni2AB4 4527 def +/uni2AB5 4528 def +/uni2AB6 4529 def +/uni2AB7 4530 def +/uni2AB8 4531 def +/uni2AB9 4532 def +/uni2ABA 4533 def +/uni2AF9 4534 def +/uni2AFA 4535 def +/uni2B00 4536 def +/uni2B01 4537 def +/uni2B02 4538 def +/uni2B03 4539 def +/uni2B04 4540 def +/uni2B05 4541 def +/uni2B06 4542 def +/uni2B07 4543 def +/uni2B08 4544 def +/uni2B09 4545 def +/uni2B0A 4546 def +/uni2B0B 4547 def +/uni2B0C 4548 def +/uni2B0D 4549 def +/uni2B0E 4550 def +/uni2B0F 4551 def +/uni2B10 4552 def +/uni2B11 4553 def +/uni2B12 4554 def +/uni2B13 4555 def +/uni2B14 4556 def +/uni2B15 4557 def +/uni2B16 4558 def +/uni2B17 4559 def +/uni2B18 4560 def +/uni2B19 4561 def +/uni2B1A 4562 def +/uni2B1F 4563 def +/uni2B20 4564 def +/uni2B21 4565 def +/uni2B22 4566 def +/uni2B23 4567 def +/uni2B24 4568 def +/uni2B53 4569 def +/uni2B54 4570 def +/uni2C60 4571 def +/uni2C61 4572 def +/uni2C62 4573 def +/uni2C63 4574 def +/uni2C64 4575 def +/uni2C65 4576 def +/uni2C66 4577 def +/uni2C67 4578 def +/uni2C68 4579 def +/uni2C69 4580 def +/uni2C6A 4581 def +/uni2C6B 4582 def +/uni2C6C 4583 def +/uni2C6D 4584 def +/uni2C6E 4585 def +/uni2C6F 4586 def +/uni2C70 4587 def +/uni2C71 4588 def +/uni2C72 4589 def +/uni2C73 4590 def +/uni2C74 4591 def +/uni2C75 4592 def +/uni2C76 4593 def +/uni2C77 4594 def +/uni2C79 4595 def +/uni2C7A 4596 def +/uni2C7B 4597 def +/uni2C7C 4598 def +/uni2C7D 4599 def +/uni2C7E 4600 def +/uni2C7F 4601 def +/uni2D00 4602 def +/uni2D01 4603 def +/uni2D02 4604 def +/uni2D03 4605 def +/uni2D04 4606 def +/uni2D05 4607 def +/uni2D06 4608 def +/uni2D07 4609 def +/uni2D08 4610 def +/uni2D09 4611 def +/uni2D0A 4612 def +/uni2D0B 4613 def +/uni2D0C 4614 def +/uni2D0D 4615 def +/uni2D0E 4616 def +/uni2D0F 4617 def +/uni2D10 4618 def +/uni2D11 4619 def +/uni2D12 4620 def +/uni2D13 4621 def +/uni2D14 4622 def +/uni2D15 4623 def +/uni2D16 4624 def +/uni2D17 4625 def +/uni2D18 4626 def +/uni2D19 4627 def +/uni2D1A 4628 def +/uni2D1B 4629 def +/uni2D1C 4630 def +/uni2D1D 4631 def +/uni2D1E 4632 def +/uni2D1F 4633 def +/uni2D20 4634 def +/uni2D21 4635 def +/uni2D22 4636 def +/uni2D23 4637 def +/uni2D24 4638 def +/uni2D25 4639 def +/uni2D30 4640 def +/uni2D31 4641 def +/uni2D32 4642 def +/uni2D33 4643 def +/uni2D34 4644 def +/uni2D35 4645 def +/uni2D36 4646 def +/uni2D37 4647 def +/uni2D38 4648 def +/uni2D39 4649 def +/uni2D3A 4650 def +/uni2D3B 4651 def +/uni2D3C 4652 def +/uni2D3D 4653 def +/uni2D3E 4654 def +/uni2D3F 4655 def +/uni2D40 4656 def +/uni2D41 4657 def +/uni2D42 4658 def +/uni2D43 4659 def +/uni2D44 4660 def +/uni2D45 4661 def +/uni2D46 4662 def +/uni2D47 4663 def +/uni2D48 4664 def +/uni2D49 4665 def +/uni2D4A 4666 def +/uni2D4B 4667 def +/uni2D4C 4668 def +/uni2D4D 4669 def +/uni2D4E 4670 def +/uni2D4F 4671 def +/uni2D50 4672 def +/uni2D51 4673 def +/uni2D52 4674 def +/uni2D53 4675 def +/uni2D54 4676 def +/uni2D55 4677 def +/uni2D56 4678 def +/uni2D57 4679 def +/uni2D58 4680 def +/uni2D59 4681 def +/uni2D5A 4682 def +/uni2D5B 4683 def +/uni2D5C 4684 def +/uni2D5D 4685 def +/uni2D5E 4686 def +/uni2D5F 4687 def +/uni2D60 4688 def +/uni2D61 4689 def +/uni2D62 4690 def +/uni2D63 4691 def +/uni2D64 4692 def +/uni2D65 4693 def +/uni2D6F 4694 def +/uni2E18 4695 def +/uni2E1F 4696 def +/uni2E22 4697 def +/uni2E23 4698 def +/uni2E24 4699 def +/uni2E25 4700 def +/uni2E2E 4701 def +/uni4DC0 4702 def +/uni4DC1 4703 def +/uni4DC2 4704 def +/uni4DC3 4705 def +/uni4DC4 4706 def +/uni4DC5 4707 def +/uni4DC6 4708 def +/uni4DC7 4709 def +/uni4DC8 4710 def +/uni4DC9 4711 def +/uni4DCA 4712 def +/uni4DCB 4713 def +/uni4DCC 4714 def +/uni4DCD 4715 def +/uni4DCE 4716 def +/uni4DCF 4717 def +/uni4DD0 4718 def +/uni4DD1 4719 def +/uni4DD2 4720 def +/uni4DD3 4721 def +/uni4DD4 4722 def +/uni4DD5 4723 def +/uni4DD6 4724 def +/uni4DD7 4725 def +/uni4DD8 4726 def +/uni4DD9 4727 def +/uni4DDA 4728 def +/uni4DDB 4729 def +/uni4DDC 4730 def +/uni4DDD 4731 def +/uni4DDE 4732 def +/uni4DDF 4733 def +/uni4DE0 4734 def +/uni4DE1 4735 def +/uni4DE2 4736 def +/uni4DE3 4737 def +/uni4DE4 4738 def +/uni4DE5 4739 def +/uni4DE6 4740 def +/uni4DE7 4741 def +/uni4DE8 4742 def +/uni4DE9 4743 def +/uni4DEA 4744 def +/uni4DEB 4745 def +/uni4DEC 4746 def +/uni4DED 4747 def +/uni4DEE 4748 def +/uni4DEF 4749 def +/uni4DF0 4750 def +/uni4DF1 4751 def +/uni4DF2 4752 def +/uni4DF3 4753 def +/uni4DF4 4754 def +/uni4DF5 4755 def +/uni4DF6 4756 def +/uni4DF7 4757 def +/uni4DF8 4758 def +/uni4DF9 4759 def +/uni4DFA 4760 def +/uni4DFB 4761 def +/uni4DFC 4762 def +/uni4DFD 4763 def +/uni4DFE 4764 def +/uni4DFF 4765 def +/uniA4D0 4766 def +/uniA4D1 4767 def +/uniA4D2 4768 def +/uniA4D3 4769 def +/uniA4D4 4770 def +/uniA4D5 4771 def +/uniA4D6 4772 def +/uniA4D7 4773 def +/uniA4D8 4774 def +/uniA4D9 4775 def +/uniA4DA 4776 def +/uniA4DB 4777 def +/uniA4DC 4778 def +/uniA4DD 4779 def +/uniA4DE 4780 def +/uniA4DF 4781 def +/uniA4E0 4782 def +/uniA4E1 4783 def +/uniA4E2 4784 def +/uniA4E3 4785 def +/uniA4E4 4786 def +/uniA4E5 4787 def +/uniA4E6 4788 def +/uniA4E7 4789 def +/uniA4E8 4790 def +/uniA4E9 4791 def +/uniA4EA 4792 def +/uniA4EB 4793 def +/uniA4EC 4794 def +/uniA4ED 4795 def +/uniA4EE 4796 def +/uniA4EF 4797 def +/uniA4F0 4798 def +/uniA4F1 4799 def +/uniA4F2 4800 def +/uniA4F3 4801 def +/uniA4F4 4802 def +/uniA4F5 4803 def +/uniA4F6 4804 def +/uniA4F7 4805 def +/uniA4F8 4806 def +/uniA4F9 4807 def +/uniA4FA 4808 def +/uniA4FB 4809 def +/uniA4FC 4810 def +/uniA4FD 4811 def +/uniA4FE 4812 def +/uniA4FF 4813 def +/uniA644 4814 def +/uniA645 4815 def +/uniA646 4816 def +/uniA647 4817 def +/uniA64C 4818 def +/uniA64D 4819 def +/uniA650 4820 def +/uniA651 4821 def +/uniA654 4822 def +/uniA655 4823 def +/uniA656 4824 def +/uniA657 4825 def +/uniA662 4826 def +/uniA663 4827 def +/uniA664 4828 def +/uniA665 4829 def +/uniA666 4830 def +/uniA667 4831 def +/uniA668 4832 def +/uniA669 4833 def +/uniA66A 4834 def +/uniA66B 4835 def +/uniA66C 4836 def +/uniA66D 4837 def +/uniA66E 4838 def +/uniA68A 4839 def +/uniA68B 4840 def +/uniA68C 4841 def +/uniA68D 4842 def +/uniA694 4843 def +/uniA695 4844 def +/uniA708 4845 def +/uniA709 4846 def +/uniA70A 4847 def +/uniA70B 4848 def +/uniA70C 4849 def +/uniA70D 4850 def +/uniA70E 4851 def +/uniA70F 4852 def +/uniA710 4853 def +/uniA711 4854 def +/uniA712 4855 def +/uniA713 4856 def +/uniA714 4857 def +/uniA715 4858 def +/uniA716 4859 def +/uniA71B 4860 def +/uniA71C 4861 def +/uniA71D 4862 def +/uniA71E 4863 def +/uniA71F 4864 def +/uniA722 4865 def +/uniA723 4866 def +/uniA724 4867 def +/uniA725 4868 def +/uniA726 4869 def +/uniA727 4870 def +/uniA728 4871 def +/uniA729 4872 def +/uniA72A 4873 def +/uniA72B 4874 def +/uniA730 4875 def +/uniA731 4876 def +/uniA732 4877 def +/uniA733 4878 def +/uniA734 4879 def +/uniA735 4880 def +/uniA736 4881 def +/uniA737 4882 def +/uniA738 4883 def +/uniA739 4884 def +/uniA73A 4885 def +/uniA73B 4886 def +/uniA73C 4887 def +/uniA73D 4888 def +/uniA73E 4889 def +/uniA73F 4890 def +/uniA740 4891 def +/uniA741 4892 def +/uniA746 4893 def +/uniA747 4894 def +/uniA748 4895 def +/uniA749 4896 def +/uniA74A 4897 def +/uniA74B 4898 def +/uniA74E 4899 def +/uniA74F 4900 def +/uniA750 4901 def +/uniA751 4902 def +/uniA752 4903 def +/uniA753 4904 def +/uniA756 4905 def +/uniA757 4906 def +/uniA764 4907 def +/uniA765 4908 def +/uniA766 4909 def +/uniA767 4910 def +/uniA780 4911 def +/uniA781 4912 def +/uniA782 4913 def +/uniA783 4914 def +/uniA789 4915 def +/uniA78A 4916 def +/uniA78B 4917 def +/uniA78C 4918 def +/uniA78D 4919 def +/uniA78E 4920 def +/uniA790 4921 def +/uniA791 4922 def +/uniA7A0 4923 def +/uniA7A1 4924 def +/uniA7A2 4925 def +/uniA7A3 4926 def +/uniA7A4 4927 def +/uniA7A5 4928 def +/uniA7A6 4929 def +/uniA7A7 4930 def +/uniA7A8 4931 def +/uniA7A9 4932 def +/uniA7AA 4933 def +/uniA7F8 4934 def +/uniA7F9 4935 def +/uniA7FA 4936 def +/uniA7FB 4937 def +/uniA7FC 4938 def +/uniA7FD 4939 def +/uniA7FE 4940 def +/uniA7FF 4941 def +/uni02E5.5 4942 def +/uni02E6.5 4943 def +/uni02E7.5 4944 def +/uni02E8.5 4945 def +/uni02E9.5 4946 def +/uni02E5.4 4947 def +/uni02E6.4 4948 def +/uni02E7.4 4949 def +/uni02E8.4 4950 def +/uni02E9.4 4951 def +/uni02E5.3 4952 def +/uni02E6.3 4953 def +/uni02E7.3 4954 def +/uni02E8.3 4955 def +/uni02E9.3 4956 def +/uni02E5.2 4957 def +/uni02E6.2 4958 def +/uni02E7.2 4959 def +/uni02E8.2 4960 def +/uni02E9.2 4961 def +/uni02E5.1 4962 def +/uni02E6.1 4963 def +/uni02E7.1 4964 def +/uni02E8.1 4965 def +/uni02E9.1 4966 def +/stem 4967 def +/uniF000 4968 def +/uniF001 4969 def +/uniF002 4970 def +/uniF003 4971 def +/uniF400 4972 def +/uniF401 4973 def +/uniF402 4974 def +/uniF403 4975 def +/uniF404 4976 def +/uniF405 4977 def +/uniF406 4978 def +/uniF407 4979 def +/uniF408 4980 def +/uniF409 4981 def +/uniF40A 4982 def +/uniF40B 4983 def +/uniF40C 4984 def +/uniF40D 4985 def +/uniF40E 4986 def +/uniF40F 4987 def +/uniF410 4988 def +/uniF411 4989 def +/uniF412 4990 def +/uniF413 4991 def +/uniF414 4992 def +/uniF415 4993 def +/uniF416 4994 def +/uniF417 4995 def +/uniF418 4996 def +/uniF419 4997 def +/uniF41A 4998 def +/uniF41B 4999 def +/uniF41C 5000 def +/uniF41D 5001 def +/uniF41E 5002 def +/uniF41F 5003 def +/uniF420 5004 def +/uniF421 5005 def +/uniF422 5006 def +/uniF423 5007 def +/uniF424 5008 def +/uniF425 5009 def +/uniF426 5010 def +/uniF428 5011 def +/uniF429 5012 def +/uniF42A 5013 def +/uniF42B 5014 def +/uniF42C 5015 def +/uniF42D 5016 def +/uniF42E 5017 def +/uniF42F 5018 def +/uniF430 5019 def +/uniF431 5020 def +/uniF432 5021 def +/uniF433 5022 def +/uniF434 5023 def +/uniF435 5024 def +/uniF436 5025 def +/uniF437 5026 def +/uniF438 5027 def +/uniF439 5028 def +/uniF43A 5029 def +/uniF43B 5030 def +/uniF43C 5031 def +/uniF43D 5032 def +/uniF43E 5033 def +/uniF43F 5034 def +/uniF440 5035 def +/uniF441 5036 def +/uniF6C5 5037 def +/uniFB00 5038 def +/fi 5039 def +/fl 5040 def +/uniFB03 5041 def +/uniFB04 5042 def +/uniFB05 5043 def +/uniFB06 5044 def +/uniFB13 5045 def +/uniFB14 5046 def +/uniFB15 5047 def +/uniFB16 5048 def +/uniFB17 5049 def +/uniFB1D 5050 def +/uniFB1E 5051 def +/uniFB1F 5052 def +/uniFB20 5053 def +/uniFB21 5054 def +/uniFB22 5055 def +/uniFB23 5056 def +/uniFB24 5057 def +/uniFB25 5058 def +/uniFB26 5059 def +/uniFB27 5060 def +/uniFB28 5061 def +/uniFB29 5062 def +/uniFB2A 5063 def +/uniFB2B 5064 def +/uniFB2C 5065 def +/uniFB2D 5066 def +/uniFB2E 5067 def +/uniFB2F 5068 def +/uniFB30 5069 def +/uniFB31 5070 def +/uniFB32 5071 def +/uniFB33 5072 def +/uniFB34 5073 def +/uniFB35 5074 def +/uniFB36 5075 def +/uniFB38 5076 def +/uniFB39 5077 def +/uniFB3A 5078 def +/uniFB3B 5079 def +/uniFB3C 5080 def +/uniFB3E 5081 def +/uniFB40 5082 def +/uniFB41 5083 def +/uniFB43 5084 def +/uniFB44 5085 def +/uniFB46 5086 def +/uniFB47 5087 def +/uniFB48 5088 def +/uniFB49 5089 def +/uniFB4A 5090 def +/uniFB4B 5091 def +/uniFB4C 5092 def +/uniFB4D 5093 def +/uniFB4E 5094 def +/uniFB4F 5095 def +/uniFB52 5096 def +/uniFB53 5097 def +/uniFB54 5098 def +/uniFB55 5099 def +/uniFB56 5100 def +/uniFB57 5101 def +/uniFB58 5102 def +/uniFB59 5103 def +/uniFB5A 5104 def +/uniFB5B 5105 def +/uniFB5C 5106 def +/uniFB5D 5107 def +/uniFB5E 5108 def +/uniFB5F 5109 def +/uniFB60 5110 def +/uniFB61 5111 def +/uniFB62 5112 def +/uniFB63 5113 def +/uniFB64 5114 def +/uniFB65 5115 def +/uniFB66 5116 def +/uniFB67 5117 def +/uniFB68 5118 def +/uniFB69 5119 def +/uniFB6A 5120 def +/uniFB6B 5121 def +/uniFB6C 5122 def +/uniFB6D 5123 def +/uniFB6E 5124 def +/uniFB6F 5125 def +/uniFB70 5126 def +/uniFB71 5127 def +/uniFB72 5128 def +/uniFB73 5129 def +/uniFB74 5130 def +/uniFB75 5131 def +/uniFB76 5132 def +/uniFB77 5133 def +/uniFB78 5134 def +/uniFB79 5135 def +/uniFB7A 5136 def +/uniFB7B 5137 def +/uniFB7C 5138 def +/uniFB7D 5139 def +/uniFB7E 5140 def +/uniFB7F 5141 def +/uniFB80 5142 def +/uniFB81 5143 def +/uniFB82 5144 def +/uniFB83 5145 def +/uniFB84 5146 def +/uniFB85 5147 def +/uniFB86 5148 def +/uniFB87 5149 def +/uniFB88 5150 def +/uniFB89 5151 def +/uniFB8A 5152 def +/uniFB8B 5153 def +/uniFB8C 5154 def +/uniFB8D 5155 def +/uniFB8E 5156 def +/uniFB8F 5157 def +/uniFB90 5158 def +/uniFB91 5159 def +/uniFB92 5160 def +/uniFB93 5161 def +/uniFB94 5162 def +/uniFB95 5163 def +/uniFB96 5164 def +/uniFB97 5165 def +/uniFB98 5166 def +/uniFB99 5167 def +/uniFB9A 5168 def +/uniFB9B 5169 def +/uniFB9C 5170 def +/uniFB9D 5171 def +/uniFB9E 5172 def +/uniFB9F 5173 def +/uniFBA0 5174 def +/uniFBA1 5175 def +/uniFBA2 5176 def +/uniFBA3 5177 def +/uniFBAA 5178 def +/uniFBAB 5179 def +/uniFBAC 5180 def +/uniFBAD 5181 def +/uniFBD3 5182 def +/uniFBD4 5183 def +/uniFBD5 5184 def +/uniFBD6 5185 def +/uniFBD7 5186 def +/uniFBD8 5187 def +/uniFBD9 5188 def +/uniFBDA 5189 def +/uniFBDB 5190 def +/uniFBDC 5191 def +/uniFBDE 5192 def +/uniFBDF 5193 def +/uniFBE4 5194 def +/uniFBE5 5195 def +/uniFBE6 5196 def +/uniFBE7 5197 def +/uniFBE8 5198 def +/uniFBE9 5199 def +/uniFBFC 5200 def +/uniFBFD 5201 def +/uniFBFE 5202 def +/uniFBFF 5203 def +/uniFE00 5204 def +/uniFE01 5205 def +/uniFE02 5206 def +/uniFE03 5207 def +/uniFE04 5208 def +/uniFE05 5209 def +/uniFE06 5210 def +/uniFE07 5211 def +/uniFE08 5212 def +/uniFE09 5213 def +/uniFE0A 5214 def +/uniFE0B 5215 def +/uniFE0C 5216 def +/uniFE0D 5217 def +/uniFE0E 5218 def +/uniFE0F 5219 def +/uniFE20 5220 def +/uniFE21 5221 def +/uniFE22 5222 def +/uniFE23 5223 def +/uniFE70 5224 def +/uniFE71 5225 def +/uniFE72 5226 def +/uniFE73 5227 def +/uniFE74 5228 def +/uniFE76 5229 def +/uniFE77 5230 def +/uniFE78 5231 def +/uniFE79 5232 def +/uniFE7A 5233 def +/uniFE7B 5234 def +/uniFE7C 5235 def +/uniFE7D 5236 def +/uniFE7E 5237 def +/uniFE7F 5238 def +/uniFE80 5239 def +/uniFE81 5240 def +/uniFE82 5241 def +/uniFE83 5242 def +/uniFE84 5243 def +/uniFE85 5244 def +/uniFE86 5245 def +/uniFE87 5246 def +/uniFE88 5247 def +/uniFE89 5248 def +/uniFE8A 5249 def +/uniFE8B 5250 def +/uniFE8C 5251 def +/uniFE8D 5252 def +/uniFE8E 5253 def +/uniFE8F 5254 def +/uniFE90 5255 def +/uniFE91 5256 def +/uniFE92 5257 def +/uniFE93 5258 def +/uniFE94 5259 def +/uniFE95 5260 def +/uniFE96 5261 def +/uniFE97 5262 def +/uniFE98 5263 def +/uniFE99 5264 def +/uniFE9A 5265 def +/uniFE9B 5266 def +/uniFE9C 5267 def +/uniFE9D 5268 def +/uniFE9E 5269 def +/uniFE9F 5270 def +/uniFEA0 5271 def +/uniFEA1 5272 def +/uniFEA2 5273 def +/uniFEA3 5274 def +/uniFEA4 5275 def +/uniFEA5 5276 def +/uniFEA6 5277 def +/uniFEA7 5278 def +/uniFEA8 5279 def +/uniFEA9 5280 def +/uniFEAA 5281 def +/uniFEAB 5282 def +/uniFEAC 5283 def +/uniFEAD 5284 def +/uniFEAE 5285 def +/uniFEAF 5286 def +/uniFEB0 5287 def +/uniFEB1 5288 def +/uniFEB2 5289 def +/uniFEB3 5290 def +/uniFEB4 5291 def +/uniFEB5 5292 def +/uniFEB6 5293 def +/uniFEB7 5294 def +/uniFEB8 5295 def +/uniFEB9 5296 def +/uniFEBA 5297 def +/uniFEBB 5298 def +/uniFEBC 5299 def +/uniFEBD 5300 def +/uniFEBE 5301 def +/uniFEBF 5302 def +/uniFEC0 5303 def +/uniFEC1 5304 def +/uniFEC2 5305 def +/uniFEC3 5306 def +/uniFEC4 5307 def +/uniFEC5 5308 def +/uniFEC6 5309 def +/uniFEC7 5310 def +/uniFEC8 5311 def +/uniFEC9 5312 def +/uniFECA 5313 def +/uniFECB 5314 def +/uniFECC 5315 def +/uniFECD 5316 def +/uniFECE 5317 def +/uniFECF 5318 def +/uniFED0 5319 def +/uniFED1 5320 def +/uniFED2 5321 def +/uniFED3 5322 def +/uniFED4 5323 def +/uniFED5 5324 def +/uniFED6 5325 def +/uniFED7 5326 def +/uniFED8 5327 def +/uniFED9 5328 def +/uniFEDA 5329 def +/uniFEDB 5330 def +/uniFEDC 5331 def +/uniFEDD 5332 def +/uniFEDE 5333 def +/uniFEDF 5334 def +/uniFEE0 5335 def +/uniFEE1 5336 def +/uniFEE2 5337 def +/uniFEE3 5338 def +/uniFEE4 5339 def +/uniFEE5 5340 def +/uniFEE6 5341 def +/uniFEE7 5342 def +/uniFEE8 5343 def +/uniFEE9 5344 def +/uniFEEA 5345 def +/uniFEEB 5346 def +/uniFEEC 5347 def +/uniFEED 5348 def +/uniFEEE 5349 def +/uniFEEF 5350 def +/uniFEF0 5351 def +/uniFEF1 5352 def +/uniFEF2 5353 def +/uniFEF3 5354 def +/uniFEF4 5355 def +/uniFEF5 5356 def +/uniFEF6 5357 def +/uniFEF7 5358 def +/uniFEF8 5359 def +/uniFEF9 5360 def +/uniFEFA 5361 def +/uniFEFB 5362 def +/uniFEFC 5363 def +/uniFEFF 5364 def +/uniFFF9 5365 def +/uniFFFA 5366 def +/uniFFFB 5367 def +/uniFFFC 5368 def +/uniFFFD 5369 def +/u10300 5370 def +/u10301 5371 def +/u10302 5372 def +/u10303 5373 def +/u10304 5374 def +/u10305 5375 def +/u10306 5376 def +/u10307 5377 def +/u10308 5378 def +/u10309 5379 def +/u1030A 5380 def +/u1030B 5381 def +/u1030C 5382 def +/u1030D 5383 def +/u1030E 5384 def +/u1030F 5385 def +/u10310 5386 def +/u10311 5387 def +/u10312 5388 def +/u10313 5389 def +/u10314 5390 def +/u10315 5391 def +/u10316 5392 def +/u10317 5393 def +/u10318 5394 def +/u10319 5395 def +/u1031A 5396 def +/u1031B 5397 def +/u1031C 5398 def +/u1031D 5399 def +/u1031E 5400 def +/u10320 5401 def +/u10321 5402 def +/u10322 5403 def +/u10323 5404 def +/u1D300 5405 def +/u1D301 5406 def +/u1D302 5407 def +/u1D303 5408 def +/u1D304 5409 def +/u1D305 5410 def +/u1D306 5411 def +/u1D307 5412 def +/u1D308 5413 def +/u1D309 5414 def +/u1D30A 5415 def +/u1D30B 5416 def +/u1D30C 5417 def +/u1D30D 5418 def +/u1D30E 5419 def +/u1D30F 5420 def +/u1D310 5421 def +/u1D311 5422 def +/u1D312 5423 def +/u1D313 5424 def +/u1D314 5425 def +/u1D315 5426 def +/u1D316 5427 def +/u1D317 5428 def +/u1D318 5429 def +/u1D319 5430 def +/u1D31A 5431 def +/u1D31B 5432 def +/u1D31C 5433 def +/u1D31D 5434 def +/u1D31E 5435 def +/u1D31F 5436 def +/u1D320 5437 def +/u1D321 5438 def +/u1D322 5439 def +/u1D323 5440 def +/u1D324 5441 def +/u1D325 5442 def +/u1D326 5443 def +/u1D327 5444 def +/u1D328 5445 def +/u1D329 5446 def +/u1D32A 5447 def +/u1D32B 5448 def +/u1D32C 5449 def +/u1D32D 5450 def +/u1D32E 5451 def +/u1D32F 5452 def +/u1D330 5453 def +/u1D331 5454 def +/u1D332 5455 def +/u1D333 5456 def +/u1D334 5457 def +/u1D335 5458 def +/u1D336 5459 def +/u1D337 5460 def +/u1D338 5461 def +/u1D339 5462 def +/u1D33A 5463 def +/u1D33B 5464 def +/u1D33C 5465 def +/u1D33D 5466 def +/u1D33E 5467 def +/u1D33F 5468 def +/u1D340 5469 def +/u1D341 5470 def +/u1D342 5471 def +/u1D343 5472 def +/u1D344 5473 def +/u1D345 5474 def +/u1D346 5475 def +/u1D347 5476 def +/u1D348 5477 def +/u1D349 5478 def +/u1D34A 5479 def +/u1D34B 5480 def +/u1D34C 5481 def +/u1D34D 5482 def +/u1D34E 5483 def +/u1D34F 5484 def +/u1D350 5485 def +/u1D351 5486 def +/u1D352 5487 def +/u1D353 5488 def +/u1D354 5489 def +/u1D355 5490 def +/u1D356 5491 def +/u1D538 5492 def +/u1D539 5493 def +/u1D53B 5494 def +/u1D53C 5495 def +/u1D53D 5496 def +/u1D53E 5497 def +/u1D540 5498 def +/u1D541 5499 def +/u1D542 5500 def +/u1D543 5501 def +/u1D544 5502 def +/u1D546 5503 def +/u1D54A 5504 def +/u1D54B 5505 def +/u1D54C 5506 def +/u1D54D 5507 def +/u1D54E 5508 def +/u1D54F 5509 def +/u1D550 5510 def +/u1D552 5511 def +/u1D553 5512 def +/u1D554 5513 def +/u1D555 5514 def +/u1D556 5515 def +/u1D557 5516 def +/u1D558 5517 def +/u1D559 5518 def +/u1D55A 5519 def +/u1D55B 5520 def +/u1D55C 5521 def +/u1D55D 5522 def +/u1D55E 5523 def +/u1D55F 5524 def +/u1D560 5525 def +/u1D561 5526 def +/u1D562 5527 def +/u1D563 5528 def +/u1D564 5529 def +/u1D565 5530 def +/u1D566 5531 def +/u1D567 5532 def +/u1D568 5533 def +/u1D569 5534 def +/u1D56A 5535 def +/u1D56B 5536 def +/u1D5A0 5537 def +/u1D5A1 5538 def +/u1D5A2 5539 def +/u1D5A3 5540 def +/u1D5A4 5541 def +/u1D5A5 5542 def +/u1D5A6 5543 def +/u1D5A7 5544 def +/u1D5A8 5545 def +/u1D5A9 5546 def +/u1D5AA 5547 def +/u1D5AB 5548 def +/u1D5AC 5549 def +/u1D5AD 5550 def +/u1D5AE 5551 def +/u1D5AF 5552 def +/u1D5B0 5553 def +/u1D5B1 5554 def +/u1D5B2 5555 def +/u1D5B3 5556 def +/u1D5B4 5557 def +/u1D5B5 5558 def +/u1D5B6 5559 def +/u1D5B7 5560 def +/u1D5B8 5561 def +/u1D5B9 5562 def +/u1D5BA 5563 def +/u1D5BB 5564 def +/u1D5BC 5565 def +/u1D5BD 5566 def +/u1D5BE 5567 def +/u1D5BF 5568 def +/u1D5C0 5569 def +/u1D5C1 5570 def +/u1D5C2 5571 def +/u1D5C3 5572 def +/u1D5C4 5573 def +/u1D5C5 5574 def +/u1D5C6 5575 def +/u1D5C7 5576 def +/u1D5C8 5577 def +/u1D5C9 5578 def +/u1D5CA 5579 def +/u1D5CB 5580 def +/u1D5CC 5581 def +/u1D5CD 5582 def +/u1D5CE 5583 def +/u1D5CF 5584 def +/u1D5D0 5585 def +/u1D5D1 5586 def +/u1D5D2 5587 def +/u1D5D3 5588 def +/u1D7D8 5589 def +/u1D7D9 5590 def +/u1D7DA 5591 def +/u1D7DB 5592 def +/u1D7DC 5593 def +/u1D7DD 5594 def +/u1D7DE 5595 def +/u1D7DF 5596 def +/u1D7E0 5597 def +/u1D7E1 5598 def +/u1D7E2 5599 def +/u1D7E3 5600 def +/u1D7E4 5601 def +/u1D7E5 5602 def +/u1D7E6 5603 def +/u1D7E7 5604 def +/u1D7E8 5605 def +/u1D7E9 5606 def +/u1D7EA 5607 def +/u1D7EB 5608 def +/u1EE00 5609 def +/u1EE01 5610 def +/u1EE02 5611 def +/u1EE03 5612 def +/u1EE05 5613 def +/u1EE06 5614 def +/u1EE07 5615 def +/u1EE08 5616 def +/u1EE09 5617 def +/u1EE0A 5618 def +/u1EE0B 5619 def +/u1EE0C 5620 def +/u1EE0D 5621 def +/u1EE0E 5622 def +/u1EE0F 5623 def +/u1EE10 5624 def +/u1EE11 5625 def +/u1EE12 5626 def +/u1EE13 5627 def +/u1EE14 5628 def +/u1EE15 5629 def +/u1EE16 5630 def +/u1EE17 5631 def +/u1EE18 5632 def +/u1EE19 5633 def +/u1EE1A 5634 def +/u1EE1B 5635 def +/u1EE1C 5636 def +/u1EE1D 5637 def +/u1EE1E 5638 def +/u1EE1F 5639 def +/u1EE21 5640 def +/u1EE22 5641 def +/u1EE24 5642 def +/u1EE27 5643 def +/u1EE29 5644 def +/u1EE2A 5645 def +/u1EE2B 5646 def +/u1EE2C 5647 def +/u1EE2D 5648 def +/u1EE2E 5649 def +/u1EE2F 5650 def +/u1EE30 5651 def +/u1EE31 5652 def +/u1EE32 5653 def +/u1EE34 5654 def +/u1EE35 5655 def +/u1EE36 5656 def +/u1EE37 5657 def +/u1EE39 5658 def +/u1EE3B 5659 def +/u1EE61 5660 def +/u1EE62 5661 def +/u1EE64 5662 def +/u1EE67 5663 def +/u1EE68 5664 def +/u1EE69 5665 def +/u1EE6A 5666 def +/u1EE6C 5667 def +/u1EE6D 5668 def +/u1EE6E 5669 def +/u1EE6F 5670 def +/u1EE70 5671 def +/u1EE71 5672 def +/u1EE72 5673 def +/u1EE74 5674 def +/u1EE75 5675 def +/u1EE76 5676 def +/u1EE77 5677 def +/u1EE79 5678 def +/u1EE7A 5679 def +/u1EE7B 5680 def +/u1EE7C 5681 def +/u1EE7E 5682 def +/u1F030 5683 def +/u1F031 5684 def +/u1F032 5685 def +/u1F033 5686 def +/u1F034 5687 def +/u1F035 5688 def +/u1F036 5689 def +/u1F037 5690 def +/u1F038 5691 def +/u1F039 5692 def +/u1F03A 5693 def +/u1F03B 5694 def +/u1F03C 5695 def +/u1F03D 5696 def +/u1F03E 5697 def +/u1F03F 5698 def +/u1F040 5699 def +/u1F041 5700 def +/u1F042 5701 def +/u1F043 5702 def +/u1F044 5703 def +/u1F045 5704 def +/u1F046 5705 def +/u1F047 5706 def +/u1F048 5707 def +/u1F049 5708 def +/u1F04A 5709 def +/u1F04B 5710 def +/u1F04C 5711 def +/u1F04D 5712 def +/u1F04E 5713 def +/u1F04F 5714 def +/u1F050 5715 def +/u1F051 5716 def +/u1F052 5717 def +/u1F053 5718 def +/u1F054 5719 def +/u1F055 5720 def +/u1F056 5721 def +/u1F057 5722 def +/u1F058 5723 def +/u1F059 5724 def +/u1F05A 5725 def +/u1F05B 5726 def +/u1F05C 5727 def +/u1F05D 5728 def +/u1F05E 5729 def +/u1F05F 5730 def +/u1F060 5731 def +/u1F061 5732 def +/u1F062 5733 def +/u1F063 5734 def +/u1F064 5735 def +/u1F065 5736 def +/u1F066 5737 def +/u1F067 5738 def +/u1F068 5739 def +/u1F069 5740 def +/u1F06A 5741 def +/u1F06B 5742 def +/u1F06C 5743 def +/u1F06D 5744 def +/u1F06E 5745 def +/u1F06F 5746 def +/u1F070 5747 def +/u1F071 5748 def +/u1F072 5749 def +/u1F073 5750 def +/u1F074 5751 def +/u1F075 5752 def +/u1F076 5753 def +/u1F077 5754 def +/u1F078 5755 def +/u1F079 5756 def +/u1F07A 5757 def +/u1F07B 5758 def +/u1F07C 5759 def +/u1F07D 5760 def +/u1F07E 5761 def +/u1F07F 5762 def +/u1F080 5763 def +/u1F081 5764 def +/u1F082 5765 def +/u1F083 5766 def +/u1F084 5767 def +/u1F085 5768 def +/u1F086 5769 def +/u1F087 5770 def +/u1F088 5771 def +/u1F089 5772 def +/u1F08A 5773 def +/u1F08B 5774 def +/u1F08C 5775 def +/u1F08D 5776 def +/u1F08E 5777 def +/u1F08F 5778 def +/u1F090 5779 def +/u1F091 5780 def +/u1F092 5781 def +/u1F093 5782 def +/u1F0A0 5783 def +/u1F0A1 5784 def +/u1F0A2 5785 def +/u1F0A3 5786 def +/u1F0A4 5787 def +/u1F0A5 5788 def +/u1F0A6 5789 def +/u1F0A7 5790 def +/u1F0A8 5791 def +/u1F0A9 5792 def +/u1F0AA 5793 def +/u1F0AB 5794 def +/u1F0AC 5795 def +/u1F0AD 5796 def +/u1F0AE 5797 def +/u1F0B1 5798 def +/u1F0B2 5799 def +/u1F0B3 5800 def +/u1F0B4 5801 def +/u1F0B5 5802 def +/u1F0B6 5803 def +/u1F0B7 5804 def +/u1F0B8 5805 def +/u1F0B9 5806 def +/u1F0BA 5807 def +/u1F0BB 5808 def +/u1F0BC 5809 def +/u1F0BD 5810 def +/u1F0BE 5811 def +/u1F0C1 5812 def +/u1F0C2 5813 def +/u1F0C3 5814 def +/u1F0C4 5815 def +/u1F0C5 5816 def +/u1F0C6 5817 def +/u1F0C7 5818 def +/u1F0C8 5819 def +/u1F0C9 5820 def +/u1F0CA 5821 def +/u1F0CB 5822 def +/u1F0CC 5823 def +/u1F0CD 5824 def +/u1F0CE 5825 def +/u1F0CF 5826 def +/u1F0D1 5827 def +/u1F0D2 5828 def +/u1F0D3 5829 def +/u1F0D4 5830 def +/u1F0D5 5831 def +/u1F0D6 5832 def +/u1F0D7 5833 def +/u1F0D8 5834 def +/u1F0D9 5835 def +/u1F0DA 5836 def +/u1F0DB 5837 def +/u1F0DC 5838 def +/u1F0DD 5839 def +/u1F0DE 5840 def +/u1F0DF 5841 def +/u1F42D 5842 def +/u1F42E 5843 def +/u1F431 5844 def +/u1F435 5845 def +/u1F600 5846 def +/u1F601 5847 def +/u1F602 5848 def +/u1F603 5849 def +/u1F604 5850 def +/u1F605 5851 def +/u1F606 5852 def +/u1F607 5853 def +/u1F608 5854 def +/u1F609 5855 def +/u1F60A 5856 def +/u1F60B 5857 def +/u1F60C 5858 def +/u1F60D 5859 def +/u1F60E 5860 def +/u1F60F 5861 def +/u1F610 5862 def +/u1F611 5863 def +/u1F612 5864 def +/u1F613 5865 def +/u1F614 5866 def +/u1F615 5867 def +/u1F616 5868 def +/u1F617 5869 def +/u1F618 5870 def +/u1F619 5871 def +/u1F61A 5872 def +/u1F61B 5873 def +/u1F61C 5874 def +/u1F61D 5875 def +/u1F61E 5876 def +/u1F61F 5877 def +/u1F620 5878 def +/u1F621 5879 def +/u1F622 5880 def +/u1F623 5881 def +/u1F625 5882 def +/u1F626 5883 def +/u1F627 5884 def +/u1F628 5885 def +/u1F629 5886 def +/u1F62A 5887 def +/u1F62B 5888 def +/u1F62D 5889 def +/u1F62E 5890 def +/u1F62F 5891 def +/u1F630 5892 def +/u1F631 5893 def +/u1F632 5894 def +/u1F633 5895 def +/u1F634 5896 def +/u1F635 5897 def +/u1F636 5898 def +/u1F637 5899 def +/u1F638 5900 def +/u1F639 5901 def +/u1F63A 5902 def +/u1F63B 5903 def +/u1F63C 5904 def +/u1F63D 5905 def +/u1F63E 5906 def +/u1F63F 5907 def +/u1F640 5908 def +/dlLtcaron 5909 def +/Dieresis 5910 def +/Acute 5911 def +/Tilde 5912 def +/Grave 5913 def +/Circumflex 5914 def +/Caron 5915 def +/uni0311.case 5916 def +/Breve 5917 def +/Dotaccent 5918 def +/Hungarumlaut 5919 def +/Doublegrave 5920 def +/arabic_dot 5921 def +/arabic_2dots 5922 def +/arabic_3dots 5923 def +/arabic_3dots_a 5924 def +/arabic_2dots_a 5925 def +/arabic_4dots 5926 def +/uni066E.fina 5927 def +/uni066E.init 5928 def +/uni066E.medi 5929 def +/uni06A1.fina 5930 def +/uni06A1.init 5931 def +/uni06A1.medi 5932 def +/uni066F.fina 5933 def +/uni066F.init 5934 def +/uni066F.medi 5935 def +/uni06BA.init 5936 def +/uni06BA.medi 5937 def +/arabic_ring 5938 def +/uni067C.fina 5939 def +/uni067C.init 5940 def +/uni067C.medi 5941 def +/uni067D.fina 5942 def +/uni067D.init 5943 def +/uni067D.medi 5944 def +/uni0681.fina 5945 def +/uni0681.init 5946 def +/uni0681.medi 5947 def +/uni0682.fina 5948 def +/uni0682.init 5949 def +/uni0682.medi 5950 def +/uni0685.fina 5951 def +/uni0685.init 5952 def +/uni0685.medi 5953 def +/uni06BF.fina 5954 def +/uni06BF.init 5955 def +/uni06BF.medi 5956 def +/arabic_gaf_bar 5957 def +/Eng.alt 5958 def +/uni0268.dotless 5959 def +/uni029D.dotless 5960 def +/uni03080304 5961 def +/uni03040308 5962 def +/uni03070304 5963 def +/uni03080301 5964 def +/uni03080300 5965 def +/uni03040301 5966 def +/uni03040300 5967 def +/uni03030304 5968 def +/uni0308030C 5969 def end readonly def + /sfnts[<00010000001201000004002047444546196b17d40000012c0000002247504f53b6adcb8e0000015000000ec44753 +5542871f76f900001014000000e84d415448093f3384000010fc000000f64f532f326aab715a000011f400000056636d6170 +009e06b10000124c000000586376742000691d39000012a4000001fe6670676d7134766a000014a4000000ab676173700007 +0007000015500000000c676c79669ab7aa750000155c0000a37c68656164085dc2860000b8d800000036686865610d9f1ebe +0000b91000000024686d74781390a0b70000b93400005d466c6f6361900268200001167c00002ea66d6178701bbf06710001 +4524000000206e616d6527ed3dbc00014544000001d4706f73745e83c6f9000147180000e1b3707265703b07f100000228cc +0000056800010000000c00000000000000020003000300030001008702110001174617460001000000010000000a002e003c +000244464c54000e6c61746e0018000400000000ffff0000000400000000ffff0001000000016b65726e0008000000010000 +00010004000200000001000800020ace000400000b380c0e0019003700000000000000000000000000000000ff9000000000 +00000000000000000000000000000000000000000000000000000000000000000000ffdc0000000000000000000000000000 +00000000000000000000000000000000000000000000ff9000000000000000000000ff900000000000000000000000000000 +ffb70000ff9a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000ffb70000fee6ff9afef0000000000000ffdc00000000ffdc000000000000ffdcff44000000000000ffdc0000 +ffdcffdc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000ff90000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000ff9a0000000000000000ff6b0000ff7d0000ffd30000ffa400000000ffa4 +000000000000ffa4ff900000ff9affd3ffa40000ffa4ffa40000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000ffdc000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +ff9000000000ff9000000000000000000000fee60000fef000000000fef0000000000000ff1500000000ff90fee6fef00000 +fef0ff1500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000ffd3ffd3ffdcffdcffd3ffdc0000000000000000000000000000ffd30000 +ffd3000000000000ffd300000000000000480000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffdc00000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000ffdc00000000ffdc0000ff610000ff6100000000ffdcffdc00000000ffdc +00000000ffdc0000ff75000000000000ffdc0000ffdc0000003900000000ffdc0000ffdcffdcffdcffdc000000000000ffdc +ffdcff6100000000ff90ffadff61ff75000000000000ffdc000000000000ffdc00000000ffdc0000ff610000ff6100000000 +ffdcffdc00000000ffdc00000000ffdc00000000000000000000ffdc0000ffdc0000003900000000ffdc0000ffdcffdcffdc +ffdc000000000000ffdc0000ff6100000000ff90ffadff610000000000000000ffdc00000000000000000000000000000000 +00000000ff900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000ffd3ffd3ffdcffdcffd3ffdc0000000000000000 +000000000000ffd30000ffd3000000000000ffd3000000000000ffdc00000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000ff880000000000000000ffdc000000000000feadfea4fea400000000fea4 +fed3fead0000fec9fec10000ff88feadfea40000fea4fec900000000fea40000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000001003300880089009200940095009600970098009b +009c009d009e009f00a000a100aa00ab00ac00ad00b200b300b400b500b600b700b800bf00c100c200c400c600c800ce00d0 +00d200dd00e000fb00ff0102010a01160117011a011b0120012201260130013a013f0002002300880088000e00890089000f +009400980003009b009e0006009f009f000700a000a0001000a100a1001100aa00ad000900b200b2001200b300b3000a00b4 +00b8000b00bf00bf000d00c100c1000d00c200c2001300c400c4001300c600c6001400c800c8000f00ce00ce000f00d200d2 +001500dd00dd000900e000e0000100fb00fb000200ff00ff0002010201020016010a010a000a01160116000401170117000c +011a011a0004011b011b0017012001200005012201220005012601260018013001300006013a013a0007013f013f00080002 +0067008800880015008900890016009400980004009b009e0007009f009f000800a000a1000300a200a2001700a300a3000a +00a400a7001700a900a9000b00aa00aa001800ab00ab000c00ac00ad001800b200b2001900b300b3000e00b400b4001a00b5 +00b5000f00b600b8001a00bb00bb001b00bc00bc001300bd00be001b00bf00bf001400c100c1001400c200c2001c00c300c3 +001d00c400c4001c00c500c5001d00c600c6001c00c700c7001d00c800c8000100c900c9001e00ca00ca001f00cb00cb0020 +00cc00cc001f00cd00cd002100ce00ce000100cf00cf001e00d000d0000200d100d1002200d300d3002300d500d5002400d7 +00d7002400d900d9002400db00db002400dd00dd000c00de00de001f00e000e0002500e100e1000d00e200e2001f00e400e4 +002600f100f1002700f500f5002700fb00fb000300ff00ff0003010a010a000e010e010e001f010f010f002801100110001f +011101110028011201120029011301130028011601160003011701170010011901190027011a011a0003011b011b0010011c +011c0005011e011e000501200120000501210121001101220122000501230123001101240124002a01250125002101260126 +000601270127001201280128002b012b012b002c012d012d002c012f012f002c01300130000701310131001301330133002c +01350135002c01360136002d01370137002e01380138002f013901390030013a013a0008013f013f000901880188002001ac +01ac003101ad01ad003201ae01ae003101af01af003201da01da000501dc01dc003301dd01dd002001f001f0001f01f101f1 +003401f301f3002001f401f4003501f501f5003600010000000a00c200d0001444464c54007a6172616200b461726d6e00b4 +6272616900b463616e7300b46368657200b46379726c00b467656f7200b46772656b00b468616e6900b46865627200b46b61 +6e6100b46c616f2000b46c61746e00846d61746800b46e6b6f2000b46f67616d00b472756e7200b474666e6700b474686169 +00b4000400000000ffff00000000000649534d2000284b534d2000284c534d2000284e534d200028534b5320002853534d20 +00280000ffff000100000000000000016c6f636c000800000001000000010004000100000001000800010006163a00010001 +010c00010000000a00e000e80050003c0c0007dd00000000028200000460000005d500000000000004600000000000000000 +0000000000000460000000000000016800000460000000550000000000000000000000000000000000000000000000000000 +0000000000000000010e0000027600000000000000000000000000000000000000000000000000000000000000000000005a +0000010e0000005a0000005a0000010e00000000000000000000010e0000005a0000005a0000010e0000005a0000005a0000 +005a000001720000005a0000005a000002380000fb8f0000003c00000000000000000028000a000a00000000000100000000 +0001040e019000050000053305990000011e05330599000003d7006602120000020b06030308040202040000000e00000000 +000000000000000050664564004000c5024f0614fe14019a076d01e30000000100000000000000000003000000030000001c +0000000a0000003c000300010000001c0004002000000004000400010000024fffff000000c5ffffffc2000100000000000c +00000000001c0000000000000001000000c50000024f00000087013500b800cb00cb00c100aa009c01a600b8006600000071 +00cb00a002b20085007500b800c301cb0189022d00cb00a600f000d300aa008700cb03aa0400014a003300cb000000d90502 +00f4015400b4009c01390114013907060400044e04b4045204b804e704cd0037047304cd04600473013303a2055605a60556 +053903c5021200c9001f00b801df007300ba03e9033303bc0444040e00df03cd03aa00e503aa0404000000cb008f00a4007b +00b80014016f007f027b0252008f00c705cd009a009a006f00cb00cd019e01d300f000ba018300d5009803040248009e01d5 +00c100cb00f600830354027f00000333026600d300c700a400cd008f009a0073040005d5010a00fe022b00a400b4009c0000 +0062009c0000001d032d05d505d505d505f0007f007b005400a406b80614072301d300b800cb00a601c301ec069300a000d3 +035c037103db0185042304a80448008f0139011401390360008f05d5019a0614072306660179046004600460047b009c0000 +0277046001aa00e904600762007b00c5007f027b000000b4025205cd006600bc00660077061000cd013b01850389008f007b +0000001d00cd074a042f009c009c0000077d006f0000006f0335006a006f007b00ae00b2002d0396008f027b00f600830354 +063705f6008f009c04e10266008f018d02f600cd03440029006604ee00730000140000960000b707060504030201002c2010 +b002254964b040515820c859212d2cb002254964b040515820c859212d2c20100720b00050b00d7920b8ffff5058041b0559 +b0051cb0032508b0042523e120b00050b00d7920b8ffff5058041b0559b0051cb0032508e12d2c4b505820b0fd454459212d +2cb002254560442d2c4b5358b00225b0022545445921212d2c45442d2cb00225b0022549b00525b005254960b0206368208a +108a233a8a10653a2d000000000200080002ffff0003000201350000020005d5000300090035400f07008304810208070501 +030400000a10fc4bb00b5458b90000ffc038593cec32393931002fe4fccc3001b6000b200b500b035d253315231133110323 +030135cbcbcb14a215fefe05d5fd71fe9b016500000200100000056805d50002000a00c24041001101000405040211050504 +01110a030a0011020003030a0711050406110505040911030a08110a030a4200030795010381090509080706040302010009 +050a0b10d4c4173931002f3ce4d4ec1239304b5358071005ed0705ed071005ed0705ed071008ed071005ed071005ed071008 +ed5922b2200c01015d40420f010f020f070f080f005800760070008c000907010802060309041601190256015802500c6701 +6802780176027c0372047707780887018802800c980299039604175d005d090121013301230321032302bcfeee0225fe7be5 +0239d288fd5f88d5050efd1903aefa2b017ffe8100010073ffe3052705f000190036401a0da10eae0a951101a100ae049517 +91118c1a07190d003014101a10fcec32ec310010e4f4ecf4ec10eef6ee30b40f1b1f1b02015d01152e012320001110002132 +3637150e01232000111000213216052766e782ff00fef00110010082e7666aed84feadfe7a0186015386ed0562d55f5efec7 +fed8fed9fec75e5fd34848019f01670168019f47000200c9000005b005d500080011002e4015009509810195100802100a00 +05190d32001c09041210fcecf4ec113939393931002fecf4ec30b2601301015d011133200011100021252120001110002901 +0193f40135011ffee1fecbfe42019f01b20196fe68fe50fe61052ffb770118012e012c0117a6fe97fe80fe7efe96000100c9 +0000048b05d5000b002e401506950402950081089504ad0a05010907031c00040c10fcec32d4c4c431002fececf4ec10ee30 +b21f0d01015d132115211121152111211521c903b0fd1a02c7fd3902f8fc3e05d5aafe46aafde3aa00010073ffe3058b05f0 +001d0039402000051b0195031b950812a111ae15950e91088c1e02001c1134043318190b101e10fcecfce4fcc4310010e4f4 +ecf4ec10fed4ee11393930251121352111060423200011100021320417152e0123200011100021323604c3feb6021275fee6 +a0fea2fe75018b015e9201076f70fc8bfeeefeed011301126ba8d50191a6fd7f53550199016d016e01994846d75f60fecefe +d1fed2fece25000100c90000053b05d5000b002c4014089502ad0400810a0607031c053809011c00040c10fcec32fcec3231 +002f3ce432fcec30b2500d01015d133311211133112311211123c9ca02decacafd22ca05d5fd9c0264fa2b02c7fd39000001 +00c90000019305d50003002eb700af02011c00040410fc4bb0105458b9000000403859ec31002fec3001400d300540055005 +60058f059f05065d13331123c9caca05d5fa2b000001ff96fe66019305d5000b004240130b0200079505b000810c05080639 +011c00040c10fc4bb0105458b9000000403859ece43939310010e4fcec1139393001400d300d400d500d600d8f0d9f0d065d +13331110062b013533323635c9cacde34d3f866e05d5fa93fef2f4aa96c2000100c90000056a05d5000a00ef402808110506 +0507110606050311040504021105050442080502030300af09060501040608011c00040b10fcec32d4c4113931002f3cec32 +1739304b5358071004ed071005ed071005ed071004ed5922b2080301015d4092140201040209081602280528083702360534 +084702460543085502670276027705830288058f0894029b08e702150603090509061b031907050a030a07180328052b062a +073604360536063507300c41034004450540064007400c62036004680567077705700c8b038b058e068f078f0c9a039d069d +07b603b507c503c507d703d607e803e904e805ea06f703f805f9062c5d71005d711333110121090121011123c9ca029e0104 +fd1b031afef6fd33ca05d5fd890277fd48fce302cffd3100000100c90000046a05d500050025400c0295008104011c033a00 +040610fcecec31002fe4ec304009300750078003800404015d133311211521c9ca02d7fc5f05d5fad5aa000100c900000533 +05d500090079401e071101020102110607064207020300af0805060107021c0436071c00040a10fcecfcec11393931002f3c +ec323939304b5358071004ed071004ed5922b21f0b01015d40303602380748024707690266078002070601090615011a0646 +0149065701580665016906790685018a0695019a069f0b105d005d13210111331121011123c901100296c4fef0fd6ac405d5 +fb1f04e1fa2b04e1fb1f00020073ffe305d905f0000b00170023401306951200950c91128c1809190f33031915101810fcec +fcec310010e4f4ec10ee300122001110003332001110002720001110002120001110000327dcfefd0103dcdc0101feffdc01 +3a0178fe88fec6fec5fe870179054cfeb8fee5fee6feb80148011a011b0148a4fe5bfe9efe9ffe5b01a40162016201a50002 +00c90000055405d50013001c00b14035090807030a061103040305110404034206040015030415950914950d810b04050603 +1109001c160e050a191904113f140a1c0c041d10fcec32fcc4ec1117391139393931002f3cf4ecd4ec123912391239304b53 +58071005ed071005ed1117395922b2401e01015d40427a130105000501050206030704150015011402160317042500250125 +0226032706260726082609201e3601360246014602680575047505771388068807980698071f5d005d011e01171323032e01 +2b01112311212016151406011133323635342623038d417b3ecdd9bf4a8b78dcca01c80100fc83fd89fe9295959202bc1690 +7efe68017f9662fd8905d5d6d88dba024ffdee878383850000010087ffe304a205f00027007e403c0d0c020e0b021e1f1e08 +0902070a021f1f1e420a0b1e1f0415010015a11494189511049500942591118c281e0a0b1f1b0700221b190e2d0719142228 +10dcc4ecfcece4111239393939310010e4f4e4ec10eef6ee10c6111739304b535807100eed11173907100eed1117395922b2 +0f2901015db61f292f294f29035d01152e012322061514161f011e0115140421222627351e013332363534262f012e013534 +24333216044873cc5fa5b377a67ae2d7feddfee76aef807bec72adbc879a7be2ca0117f569da05a4c53736807663651f192b +d9b6d9e0302fd04546887e6e7c1f182dc0abc6e426000001fffa000004e905d50007004a400e0602950081040140031c0040 +050810d4e4fce431002ff4ec3230014bb00a5458bd00080040000100080008ffc03811373859401300091f00100110021f07 +1009400970099f09095d03211521112311210604effdeecbfdee05d5aafad5052b00000100b2ffe3052905d5001100404016 +0802110b0005950e8c09008112081c0a38011c00411210fc4bb0105458b90000ffc03859ecfcec310010e432f4ec11393939 +393001b61f138f139f13035d133311141633323635113311100021200011b2cbaec3c2aecbfedffee6fee5fedf05d5fc75f0 +d3d3f0038bfc5cfedcfed6012a01240000010044000007a605d5000c017b4049051a0605090a09041a0a09031a0a0b0a021a +01020b0b0a061107080705110405080807021103020c000c011100000c420a050203060300af0b080c0b0a09080605040302 +010b07000d10d4cc173931002f3cec32321739304b5358071005ed071008ed071008ed071005ed071008ed071005ed0705ed +071008ed5922b2000e01015d40f206020605020a000a000a120a2805240a200a3e023e05340a300a4c024d05420a400a5902 +6a026b05670a600a7b027f027c057f05800a960295051d070009020803000406050005000601070408000807090009040a0a +0c000e1a0315041508190c100e200421052006200720082309240a250b200e200e3c023a033504330530083609390b3f0c30 +0e460046014a0240044505400542064207420840084009440a4d0c400e400e58025608590c500e6602670361046205600660 +0760086409640a640b770076017b027803770474057906790777087008780c7f0c7f0e860287038804890585098a0b8f0e97 +049f0eaf0e5b5d005d1333090133090133012309012344cc013a0139e3013a0139cdfe89fefec5fec2fe05d5fb1204eefb12 +04eefa2b0510faf00001fffc000004e705d50008009440280311040504021101020505040211030208000801110000084202 +0300af0602070440051c0040070910d4e4fce4123931002fec3239304b5358071005ed071008ed071008ed071005ed5922b2 +000a01015d403c05021402350230023005300846024002400540085102510551086502840293021016011a031f0a26012903 +37013803400a670168037803700a9f0a0d5d005d03330901330111231104d9019e019bd9fdf0cb05d5fd9a0266fcf2fd3902 +c7000001005c0000051f05d500090090401b03110708070811020302420895008103950508030001420400060a10dc4bb009 +544bb00a545b58b90006ffc03859c4d4e411393931002fecf4ec304b5358071005ed071005ed592201404005020a07180729 +02260738074802470748080905030b08000b16031a08100b2f0b350339083f0b47034a084f0b55035908660369086f0b7703 +78087f0b9f0b165d005d13211501211521350121730495fc5003c7fb3d03b0fc6705d59afb6faa9a0491000100aa04f00289 +066600030031400901b400b3040344010410dcec310010f4ec30004bb009544bb00e545b58bd0004ffc00001000400040040 +381137385909012301016f011a99feba0666fe8a01760002007bffe3042d047b000a002500bc4027191f0b17090e00a91706 +b90e1120861fba1cb923b8118c170c001703180d09080b1f030814452610fcecccd4ec323211393931002fc4e4f4fcf4ec10 +c6ee10ee11391139123930406e301d301e301f3020302130223f27401d401e401f402040214022501d501e501f5020502150 +2250277027851d871e871f8720872185229027a027f0271e301e301f30203021401e401f40204021501e501f50205021601e +601f60206021701e701f70207021801e801f80208021185d015d0122061514163332363d01371123350e0123222635343633 +2135342623220607353e0133321602bedfac816f99b9b8b83fbc88accbfdfb0102a79760b65465be5af3f00233667b6273d9 +b4294cfd81aa6661c1a2bdc0127f8b2e2eaa2727fc0000010071ffe303e7047b0019003f401b00860188040e860d880ab911 +04b917b8118c1a07120d004814451a10fce432ec310010e4f4ec10fef4ee10f5ee30400b0f1b101b801b901ba01b05015d01 +152e0123220615141633323637150e0123220011100021321603e74e9d50b3c6c6b3509d4e4da55dfdfed6012d010655a204 +35ac2b2be3cdcde32b2baa2424013e010e0112013a2300020071ffe3045a06140010001c003840191ab9000e14b905088c0e +b801970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e801ea01e03015d011133112335 +0e0123220211100033321601141633323635342623220603a2b8b83ab17ccbff00ffcb7cb1fdc7a79292a8a89292a703b602 +5ef9eca86461014401080108014461fe15cbe7e7cbcbe7e700020071ffe3047f047b0014001b007040240015010986088805 +15a90105b90c01bb18b912b80c8c1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee10ee10f4 +ee1112393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f092c0a6f006f016f026f156f1b095d71 +015d0115211e0133323637150e01232000111000333200072e0123220607047ffcb20ccdb76ac76263d06bfef4fec70129fc +e20107b802a5889ab90e025e5abec73434ae2a2c0138010a01130143feddc497b4ae9e0000020071fe56045a047b000b0028 +004a4023190c1d0912861316b90f03b92623b827bc09b90fbd1a1d261900080c4706121220452910fcc4ecf4ec323231002f +c4e4ece4f4c4ec10fed5ee1112393930b6602a802aa02a03015d01342623220615141633323617100221222627351e013332 +363d010e0123220211101233321617353303a2a59594a5a59495a5b8fefefa61ac51519e52b5b439b27ccefcfcce7cb239b8 +023dc8dcdcc8c7dcdcebfee2fee91d1eb32c2abdbf5b6362013a01030104013a6263aa00000100ba00000464061400130034 +4019030900030e0106870e11b80c970a010208004e0d09080b461410fcec32f4ec31002f3cecf4c4ec1112173930b2601501 +015d0111231134262322061511231133113e013332160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870614 +fd9e6564ef00000200c100000179061400030007002b400e06be04b100bc020501080400460810fc3cec3231002fe4fcec30 +400b1009400950096009700905015d1333112311331523c1b8b8b8b80460fba00614e9000002ffdbfe5601790614000b000f +0044401c0b0207000ebe0c078705bd00bc0cb110081005064f0d01080c00461010fc3cec32e4391239310010ece4f4ec10ee +1112393930400b1011401150116011701105015d13331114062b01353332363511331523c1b8a3b54631694cb8b80460fb8c +d6c09c61990628e9000100ba0000049c0614000a00bc40290811050605071106060503110405040211050504420805020303 +bc009709060501040608010800460b10fcec32d4c4113931002f3cece41739304b5358071004ed071005ed071005ed071004 +ed5922b2100c01015d405f04020a081602270229052b0856026602670873027705820289058e08930296059708a302120905 +0906020b030a072803270428052b062b07400c6803600c8903850489058d068f079a039707aa03a705b607c507d607f703f0 +03f704f0041a5d71005d1333110133090123011123bab90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd0001 +00c100000179061400030022b7009702010800460410fcec31002fec30400d10054005500560057005f00506015d13331123 +c1b8b80614f9ec00000100ba00000464047b001300364019030900030e0106870e11b80cbc0a010208004e0d09080b461410 +fcec32f4ec31002f3ce4f4c4ec1112173930b46015cf1502015d0111231134262322061511231133153e013332160464b87c +7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870460ae6564ef00020071ffe30475047b000b0017004a401306b91200 +b90cb8128c1809120f51031215451810fcecf4ec310010e4f4ec10ee3040233f197b007b067f077f087f097f0a7f0b7b0c7f +0d7f0e7f0f7f107f117b12a019f01911015d012206151416333236353426273200111000232200111000027394acab9593ac +ac93f00112feeef0f1feef011103dfe7c9c9e7e8c8c7e99cfec8feecfeedfec70139011301140138000100ba0000034a047b +001100304014060b0700110b03870eb809bc070a06080008461210fcc4ec3231002fe4f4ecc4d4cc11123930b450139f1302 +015d012e012322061511231133153e0133321617034a1f492c9ca7b9b93aba85132e1c03b41211cbbefdb20460ae66630505 +0001006fffe303c7047b002700e7403c0d0c020e0b531f1e080902070a531f1f1e420a0b1e1f041500860189041486158918 +b91104b925b8118c281e0a0b1f1b0700521b080e07081422452810fcc4ecd4ece4111239393939310010e4f4ec10fef5ee10 +f5ee121739304b535807100eed111739070eed1117395922b2002701015d406d1c0a1c0b1c0c2e092c0a2c0b2c0c3b093b0a +3b0b3b0c0b200020012402280a280b2a132f142f152a16281e281f292029212427860a860b860c860d12000000010202060a +060b030c030d030e030f03100319031a031b031c041d09272f293f295f297f2980299029a029f029185d005d7101152e0123 +22061514161f011e0115140623222627351e013332363534262f012e01353436333216038b4ea85a898962943fc4a5f7d85a +c36c66c661828c65ab40ab98e0ce66b4043fae282854544049210e2a99899cb62323be353559514b50250f2495829eac1e00 +00010037000002f2059e0013003840190e05080f03a9001101bc08870a0b08090204000810120e461410fc3cc4fc3cc43239 +3931002fecf43cc4ec3211393930b2af1501015d01112115211114163b01152322263511233533110177017bfe854b73bdbd +d5a28787059efec28ffda0894e9a9fd202608f013e00000200aeffe30458047b00130014003b401c030900030e0106870e11 +8c0a01bc14b80c0d0908140b4e020800461510fcecf439ec3231002fe4e432f4c4ec1112173930b46f15c01502015d131133 +1114163332363511331123350e0123222601aeb87c7c95adb8b843b175c1c801cf01ba02a6fd619f9fbea4027bfba0ac6663 +f003a80000010056000006350460000c01eb404905550605090a0904550a0903550a0b0a025501020b0b0a06110708070511 +0405080807021103020c000c011100000c420a050203060300bf0b080c0b0a09080605040302010b07000d10d44bb00a544b +b011545b4bb012545b4bb013545b4bb00b545b58b9000000403859014bb00c544bb00d545b4bb010545b58b90000ffc03859 +cc173931002f3cec32321739304b5358071005ed071008ed071008ed071005ed071008ed071005ed0705ed071008ed592201 +40ff050216021605220a350a49024905460a400a5b025b05550a500a6e026e05660a79027f0279057f05870299029805940a +bc02bc05ce02c703cf051d0502090306040b050a080b09040b050c1502190316041a051b081b09140b150c25002501230227 +03210425052206220725082709240a210b230c390336043608390c300e460248034604400442054006400740084409440a44 +0b400e400e560056015602500451055206520750085309540a550b6300640165026a0365046a056a066a076e09610b670c6f +0e7500750179027d0378047d057a067f067a077f07780879097f097b0a760b7d0c870288058f0e97009701940293039c049b +05980698079908402f960c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb502b103bd04bb05b809bf0ec402c3 +03cc04ca05795d005d13331b01331b013301230b012356b8e6e5d9e6e5b8fedbd9f1f2d90460fc96036afc96036afba00396 +fc6a0001003dfe56047f0460000f018b40430708020911000f0a110b0a00000f0e110f000f0d110c0d00000f0d110e0d0a0b +0a0c110b0b0a420d0b0910000b058703bd0e0bbc100e0d0c0a09060300080f040f0b1010d44bb00a544bb008545b58b9000b +004038594bb0145458b9000bffc03859c4c4111739310010e432f4ec113911391239304b5358071005ed071008ed071008ed +071005ed071008ed0705ed173259220140f0060005080609030d160a170d100d230d350d490a4f0a4e0d5a095a0a6a0a870d +800d930d120a000a09060b050c0b0e0b0f1701150210041005170a140b140c1a0e1a0f270024012402200420052908280925 +0a240b240c270d2a0e2a0f201137003501350230043005380a360b360c380d390e390f301141004001400240034004400540 +06400740084209450a470d490e490f40115400510151025503500450055606550756085709570a550b550c590e590f501166 +016602680a690e690f60117b08780e780f89008a09850b850c890d890e890f9909950b950c9a0e9a0fa40ba40cab0eab0fb0 +11cf11df11ff11655d005d050e012b01353332363f01013309013302934e947c936c4c543321fe3bc3015e015ec368c87a9a +488654044efc94036c0000010058000003db04600009009d401a081102030203110708074208a900bc03a905080301000401 +060a10dc4bb00b544bb00c545b58b90006ffc038594bb0135458b9000600403859c432c411393931002fecf4ec304b535807 +1005ed071005ed592201404205021602260247024907050b080f0b18031b082b08200b36033908300b400140024503400440 +054308570359085f0b6001600266036004600562087f0b800baf0b1b5d005d1321150121152135012171036afd4c02b4fc7d +02b4fd650460a8fcdb93a8032500000200d7054603290610000300070092400e0602ce0400cd080164000564040810dcfcd4 +ec310010fc3cec3230004bb00a544bb00d545b58bd00080040000100080008ffc03811373859014bb00c544bb00d545b4bb0 +0e545b4bb017545b58bd0008ffc000010008000800403811373859014bb00f544bb019545b58bd00080040000100080008ff +c03811373859401160016002600560067001700270057006085d0133152325331523025ecbcbfe79cbcb0610cacaca000001 +00d50562032b05f60003002fb702ef00ee0401000410d4cc310010fcec30004bb009544bb00e545b58bd0004ffc000010004 +00040040381137385913211521d50256fdaa05f694000001017304ee0352066600030031400902b400b3040344010410d4ec +310010f4ec30004bb009544bb00e545b58bd0004ffc00001000400040040381137385901330123028bc7feba990666fe8800 +000100db024801ae034600030012b7028300040119000410d4ec310010d4ec3013331523dbd3d30346fe00010123fe7502c1 +00000013001f400e09060a0df306001300102703091410dcd4ecd4cc31002fd4fcc4123930211e0115140623222627351e01 +333236353426270254373678762e572b224a2f3b3c2b2d3e6930595b0c0c83110f302e1e573d0003001000000568076d000b +000e002100cb40540c110d0c1b1c1b0e111c1b1e111c1b1d111c1c1b0d11210f210c110e0c0f0f2120110f211f11210f2142 +0c1b0f0d0903c115091e950d098e201c1e1d1c18201f210d12060e180c061b0056181c0f0656121c212210d4c4d4ec3210d4 +ee32113911391112391139391112393931002f3ce6d6ee10d4ee1112393939304b5358071005ed0705ed071008ed071005ed +071005ed0705ed0705ed071008ed5922b2202301015d40201a0c730c9b0c03070f081b5023660d690e750d7b0e791c791d76 +20762180230c5d005d013426232206151416333236030121012e01353436333216151406070123032103230354593f405758 +3f3f5998fef00221fe583d3e9f7372a13f3c0214d288fd5f88d5065a3f5957413f5858fef3fd19034e29734973a0a1724676 +29fa8b017ffe8100000200080000074805d5000f00130087403911110e0f0e10110f0f0e0d110f0e0c110e0f0e420595030b +951101951095008111079503ad0d0911100f0d0c050e0a00040806021c120a0e1410d4d43cec32d4c4c41112173931002f3c +ececc4f4ecec10ee10ee304b5358071005ed0705ed071005ed071005ed5922b2801501015d4013671177107711860c851096 +119015a015bf15095d01152111211521112115211121032301170121110735fd1b02c7fd3902f8fc3dfdf0a0cd02718bfeb6 +01cb05d5aafe46aafde3aa017ffe8105d59efcf00310ffff0073fe75052705f01226002600001007007a012d0000ffff00c9 +0000048b076b12260028000010071719049e0175ffff00c90000048b076b12260028000010071717049e0175ffff00c90000 +048b076d1226002800001107171a049e017500074003400c015d3100ffff00c90000048b074e12260028000011071716049e +017500094005400c4010025d3100ffff003b000001ba076b1226002c000010071719032f0175ffff00a20000021f076b1226 +002c000010071717032f0175fffffffe00000260076d1226002c00001107171a032f01750008b401060a00072b31ffff0006 +00000258074e1226002c000011071716032f01750008b4000a0701072b310002000a000005ba05d5000c0019006740201009 +a90b0d95008112950e0b0707011913040f0d161904320a110d1c0800791a10f43cec32c4f4ec10c4173931002fc632eef6ee +10ee32304028201b7f1bb01b039f099f0a9f0b9f0c9f0e9f0f9f109f11bf09bf0abf0bbf0cbf0ebf0fbf10bf11105d015d13 +21200011100029011123353313112115211133200011100021d301a001b10196fe69fe50fe60c9c9cb0150feb0f30135011f +fee1fecb05d5fe97fe80fe7efe9602bc9001e3fe1d90fdea0118012e012c0117ffff00c900000533075e1226003100001107 +171804fe01750014b400132204072b400930133f2210131f22045d31ffff0073ffe305d9076b122600320000100717190527 +0175ffff0073ffe305d9076b1226003200001007171705270175ffff0073ffe305d9076d1226003200001107171a05270175 +0010b40f1a1e15072b40051f1a101e025d31ffff0073ffe305d9075e12260032000011071718052701750018b40321300907 +2b400d30213f3020212f3010211f30065d31ffff0073ffe305d9074e12260032000011071716052701750014b4031f1a0907 +2b4009401f4f1a101f1f1a045d3100010119003f059c04c5000b0085404d0a9c0b0a070807099c080807049c030407070605 +9c060706049c0504010201039c0202010b9c0001000a9c090a010100420a080706040201000805030b090c0b0a0907050403 +0108020008060c10d43ccc321739310010d43ccc321739304b5358071008ed071005ed071005ed071008ed071005ed071008 +ed071005ed071008ed59220902070901270901370901059cfe3701c977fe35fe357601c8fe387601cb01cb044cfe35fe3779 +01cbfe357901c901cb79fe3501cb00030066ffba05e5061700090013002b009e403c1d1f1a0d2b2c130a0100040d29262014 +0d042a261e1a0495260d951a91268c2c2b2c2a141710201e23130a0100041d2910071f07192333101917102c10fcecfcecc0 +111239391739123939111239391139310010e4f4ec10ee10c010c011123939123912173912391112393930402a57005a1557 +1955216a1565217b15761c7521094613590056136a006413641c6a287c007313761c7a280b5d015d09011e01333200113426 +272e012322001114161707260235100021321617371707161215100021222627072704b6fd333ea15fdc010127793da15fdc +fefd2727864e4f0179013b82dd57a266aa4e50fe88fec680dd5ba2670458fcb240430148011a70b8b84043feb8fee570bc44 +9e660108a0016201a54d4bbf59c667fef69efe9ffe5b4b4bbf58ffff00b2ffe30529076b1226003800001007171904ee0175 +ffff00b2ffe30529076b1226003800001007171704ee0175ffff00b2ffe30529076d1226003800001107171a04ee01750014 +b40a141800072b40092f1420181f141018045d31ffff00b2ffe30529074e1226003800001107171604ee0175001cb4011914 +09072b401150195f1440194f1420192f1410191f14085d31fffffffc000004e7076b1226003c000010071717047301750002 +00c90000048d05d5000c0015003d401b0e95090d9502f600810b150f090304011219063f0d0a011c00041610fcec3232fcec +11173931002ff4fcecd4ec3040090f171f173f175f1704015d1333113332041514042b011123131133323635342623c9cafe +fb0101fefffbfecacafe8d9a998e05d5fef8e1dcdce2feae0427fdd192868691000100baffe304ac0614002f009a40302d27 +210c04060d2000042a1686171ab9132ab90397138c2e0c090d1d2021270908242708061d082410162d081000463010fcc4fc +cc10c6eed4ee10ee1139391239123931002fe4feee10fed5ee12173917393040400f050f060f070f270f288a0c8a0d070a06 +0a070a0b0a0c0a0d0a1f0d200a210c220426190d191f19203a203a214d1f4d20492149226a1f6a20a506a507a620185d015d +133436333216170e011514161f011e0115140623222627351e013332363534262f012e01353436372e01232206151123baef +dad0db0397a83a4139a660e1d3408849508c4174783b655c6057a7970883718288bb0471c8dbe8e00873602f512a256a8e64 +acb71918a41e1d5f5b3f543e373b875b7fac1d67708b83fb9300ffff007bffe3042d0666122600440000110600435200000b +40073f262f261f26035d3100ffff007bffe3042d0666122600440000110600765200000b40073f262f261f26035d3100ffff +007bffe3042d06661226004400001106028852000008b40b282c14072b31ffff007bffe3042d06371226004400001106029e +52000014b4142e3c0b072b4009202e2f3c102e1f3c045d31ffff007bffe3042d06101226004400001106006a52000020b414 +2d280b072b40157f286f28502d5f28402d4f28302d3f28002d0f280a5d31ffff007bffe3042d07061226004400001106029c +52000025400e262c142c260b0732381438320b072b10c42b10c4310040093f353f2f0f350f2f045d30000003007bffe3076f +047b00060033003e01034043272d253d0e0d0034a925168615881200a90e3a12b91c192e862dba2a03b90ebb07310ab81f19 +8c253f343726060f0025371c07260f1500080d3d26080f2d370822453f10fcecccd4fc3cd4ecc41112393911391112391112 +39310010c4e432f43cc4e4fc3cf4ec10c4ee3210ee10f4ee10ee11391139111239304081302b302c302d302e302f3030402b +402c402d402e402f4030502b502c502d502e502f5030852b853080409040a040b040c040d040e040e040f0401d3f003f063f +0d3f0e3f0f05302c302d302e302f402c402d402e402f502c502d502e502f6f006f066f0d6f0e6f0f602c602d602e602f702c +702d702e702f802c802d802e802f1d5d71015d012e0123220607033e013332001d01211e0133323637150e01232226270e01 +232226353436332135342623220607353e013332160322061514163332363d0106b601a58999b90e444ad484e20108fcb20c +ccb768c86464d06aa7f84d49d88fbdd2fdfb0102a79760b65465be5a8ed5efdfac816f99b9029497b4ae9e01305a5efeddfa +5abfc83535ae2a2c79777878bba8bdc0127f8b2e2eaa272760fe18667b6273d9b429ffff0071fe7503e7047b122600460000 +1007007a008f0000ffff0071ffe3047f066612260048000010070043008b0000ffff0071ffe3047f06661226004800001007 +0076008b0000ffff0071ffe3047f066612260048000011070288008b00000008b4151e221b072b31ffff0071ffe3047f0610 +1226004800001107006a008b0000000740034020015d3100ffffffc7000001a6066610270043ff1d0000120600f30000ffff +00900000026f066610270076ff1d0000120600f30000ffffffde0000025c0666122600f3000011070288ff1d00000008b401 +070b00072b31fffffff4000002460610122600f300001107006aff1d00000008b4000b0801072b3100020071ffe304750614 +000e00280127405e257b26251e231e247b23231e0f7b231e287b27281e231e262728272524252828272223221f201f212020 +1f42282726252221201f08231e030f2303b91b09b9158c1b23b1292627120c212018282523221f051e0f060c121251061218 +452910fcecf4ec113939173912393911123939310010ecc4f4ec10ee12391239121739304b535807100ec9071008c9071008 +c907100ec9071008ed070eed071005ed071008ed5922b23f2a01015d407616252b1f28222f232f2429252d262d272a283625 +462558205821602060216622752075217522132523252426262627272836243625462445255a205a21622062217f007f017f +027a037b097f0a7f0b7f0c7f0d7f0e7f0f7f107f117f127f137f147b157a1b7a1c7f1d7f1e762076217822a02af02a275d00 +5d012e0123220615141633323635342613161215140023220011340033321617270527252733172517050346325829a7b9ae +9291ae36097e72fee4e6e7fee50114dd12342a9ffec1210119b5e47f014d21fed903931110d8c3bcdedebc7abc01268ffee0 +adfffec9013700fffa01370505b46b635ccc916f6162ffff00ba0000046406371226005100001007029e00980000ffff0071 +ffe304750666122600520000100600437300ffff0071ffe304750666122600520000100600767300ffff0071ffe304750666 +1226005200001106028873000008b40f1a1e15072b31ffff0071ffe3047506371226005200001106029e73000014b415202e +0f072b400920202f2e10201f2e045d31ffff0071ffe3047506101226005200001106006a73000014b4031f1a09072b400940 +1f4f1a301f3f1a045d31000300d9009605db046f00030007000b0029401400ea0206ea0402089c040a0c090501720400080c +10dcd43cfc3cc4310010d4c4fcc410ee10ee3001331523113315230121152102dff6f6f6f6fdfa0502fafe046ff6fe12f502 +41aa00030048ffa2049c04bc00090013002b00e4403c2b2c261f1d1a130a0100040d292620140d042a261e1a04b9260db91a +b8268c2c2b2c2a141710201e23130a01000410071f1d0712235129101217452c10fcec32f4ec32c011121739123939111239 +391139310010e4f4ec10ee10c010c011123939123912173911393911123930407028013f2d5914561c551d56206a1566217f +007b047f057f067f077f087f097f0a7f0b7f0c7b0d7a157b1a7f1b7f1c7f1d7f1e7f1f7f207b217f227f237f247f257b269b +199525a819a02df02d2659005613551d5a2869006613651c6a287a007413761c7a28891e95189a24a218ad24115d015d0901 +1e01333236353426272e0123220615141617072e01351000333216173717071e011510002322262707270389fe1929674193 +ac145c2a673e97a913147d36360111f15d9f438b5f923536feeef060a13f8b600321fdb02a28e8c84f759a2929ebd3486e2e +974dc577011401383334a84fb34dc678feedfec73433a84effff00aeffe304580666122600580000100600437b00ffff00ae +ffe304580666122600580000100600767b00ffff00aeffe304580666122600580000110602887b000008b40b171b01072b31 +ffff00aeffe3045806101226005800001106006a7b000018b4021b180a072b400d401b4f18301b3f18001b0f18065d31ffff +003dfe56047f06661226005c0000100600765e00000200bafe5604a406140010001c003e401b14b905081ab9000e8c08b801 +bd03971d11120b471704000802461d10fcec3232f4ec310010ece4e4f4c4ec10c6ee304009601e801ea01ee01e04015d2511 +231133113e013332001110022322260134262322061514163332360173b9b93ab17bcc00ffffcc7bb10238a79292a7a79292 +a7a8fdae07befda26461febcfef8fef8febc6101ebcbe7e7cbcbe7e7ffff003dfe56047f06101226005c00001106006a5e00 +0016b418171219072b400b30173f1220172f121f12055d31ffff00100000056807311027007100bc013b1306002400000010 +b40e030209072b400540034f02025d31ffff007bffe3042d05f6102600714a001306004400000010b41803020f072b40056f +027f03025d31ffff00100000056807921027029a00ce014a1306002400000012b418000813072b310040056f006f08025d30 +ffff007bffe3042d061f1026029a4fd71306004400000008b422000819072b31ffff0010fe7505a505d51226002400001007 +029d02e40000ffff007bfe750480047b1226004400001007029d01bf0000ffff0073ffe30527076b12260026000010071717 +052d0175ffff0071ffe303e706661226004600001007007600890000ffff0073ffe30527076d1027171a054c017513060026 +00000009b204041e103c3d2f3100ffff0071ffe303e706661226004600001007028800a40000ffff0073ffe3052707501027 +171e054c0175120600260000ffff0071ffe303e70614102702b804a40000120600460000ffff0073ffe30527076d12260026 +00001107171b052d0175000740031f1d015d3100ffff0071ffe303e706661226004600001007028900890000ffff00c90000 +05b0076d1027171b04ec0175120600270000ffff0071ffe305db06141226004700001107171505140000000b40075f1d3f1d +1f1d035d3100ffff000a000005ba05d510060092000000020071ffe304f4061400180024004a40240703d30901f922b90016 +1cb90d108c16b805970b021f0c04030008080a0647191213452510fcecf43cc4fc173cc431002fece4f4c4ec10c4eefd3cee +3230b660268026a02603015d01112135213533153315231123350e0123220211100033321601141633323635342623220603 +a2feba0146b89a9ab83ab17ccbff00ffcb7cb1fdc7a79292a8a89292a703b6014e7d93937dfafca864610144010801080144 +61fe15cbe7e7cbcbe7e7ffff00c90000048b07331226002800001007007100a1013dffff0071ffe3047f05f6102700710096 +0000130600480000000740037000015d3100ffff00c90000048b076d1027171d04a10175130600280000000740034000015d +3100ffff0071ffe3047f06481027029a00960000130600480000000740037000015d3100ffff00c90000048b07501027171e +049e0175120600280000ffff0071ffe3047f0614102702b804960000120600480000ffff00c9fe75048d05d5122600280000 +1007029d01cc0000ffff0071fe75047f047b1226004800001007029d01780000ffff00c90000048b07671226002800001107 +171b04a6016f00074003400c015d3100ffff0071ffe3047f0661122600480000110702890094fffb0010b400211d0f072b40 +050f21001d025d31ffff0073ffe3058b076d1027171a055c01751306002a00000009b2040415103c3d2f3100ffff0071fe56 +045a06661026028868001306004a00000009b204040a103c3d2f3100ffff0073ffe3058b076d1226002a00001007171d051b +0175ffff0071fe56045a06481226004a00001007029a008b0000ffff0073ffe3058b07501027171e055c01751306002a0000 +00080040033f00015d30ffff0071fe56045a0614102702b8046a00001206004a0000ffff0073fe01058b05f0102702d7055e +ffed1206002a0000ffff0071fe56045a0634102702c303e0010c1206004a0000ffff00c90000053b076d1027171a05020175 +1306002b00000014b40c020607072b40092f0220061f021006045d31ffffffe500000464076d1027171a031601751306004b +0000002ab414020613072b31004bb00e5158bb0014ffc00013ffc0383859400d901490138014801340144013065d000200c9 +0000068b05d500130017003a401e060212950914110c9515ad0400810e0a070c17041c0538120d14011c001810dcec3232cc +fcec3232cc31002f3ce432fcecdc3232ec3232300133152135331533152311231121112311233533171521350171ca02deca +a8a8cafd22caa8a8ca02de05d5e0e0e0a4fbaf02c7fd390451a4a4e0e000000100780000049f0614001b003e402103090003 +16010e12870d1506871619b810970a010208004e130e11150908100b1c10dc32ec3232ccccf4ec31002f3cecf4c4ecdc32ec +32111217393001112311342623220615112311233533353315211521113e01333216049fb87c7c95acb97d7db90160fea042 +b375c1c602a4fd5c029e9f9ebea4fd8704f6a47a7aa4febc6564ef00ffffffe400000278075e10271718032e01751306002c +00000008b41e09181f072b31ffffffd30000026706371027029eff1d0000130600f300000008b41c08161d072b31ffff0003 +00000259073110270071ff2e013b1306002c00000008b404030205072b31fffffff20000024805f510270071ff1dffff1306 +00f300000008b404030205072b31fffffff500000267076d1027171d032e01751306002c00000008b40e00080f072b31ffff +ffe40000025606481027029aff1d0000130600f300000008b40e00080f072b31ffff00b0fe75022505d51027029dff640000 +1206002c0000ffff0096fe75020b06141027029dff4a00001206004c0000ffff00c90000019507501226002c00001107171e +032f01750013b306010700103c103c3100b43f073f06025d3000000200c100000179047b00030004002c400b04b800bf0204 +010800460510fcec3931002fece43040110404340444041006400650066006700608015d1333112313c1b8b85c0460fba004 +7b00ffff00c9fe6603ef05d51027002d025c00001106002c00000008400311040110ec31ffff00c1fe5603b106141027004d +023800001106004c00000008400319460110ec31ffffff96fe66025f076d1027171a032e01751306002d00000008b4080206 +07072b31ffffffdbfe56025c066610270288ff1d0000130601f900000008b408020607072b31ffff00c9fe1e056a05d51027 +02d7051b000a1206002e0000ffff00bafe1e049c0614102702d704ac000a1206004e0000000100ba0000049c0460000a00bb +4028081105060507110606050311040504021105050442080502030300bc09060501040608010800460b10fcec32d4c41139 +31002f3cec321739304b5358071004ed071005ed071005ed071004ed5922b2100c01015d405f04020a081602270229052b08 +56026602670873027705820289058e08930296059708a3021209050906020b030a072803270428052b062b07400c6803600c +8903850489058d068f079a039707aa03a705b607c507d607f703f003f704f0041a5d71005d1333110133090123011123bab9 +0225ebfdae026bf0fdc7b90460fe1b01e5fdf2fdae0221fddf00ffff00c90000046a076c10271717036e01761206002f0000 +ffff00c10000024a076c10271717035a01761306004f0000001eb10304103c31004bb00e5158b900000040385940079f008f +004f00035d30ffff00c9fe1e046a05d5102702d7049b000a1206002f0000ffff0088fe1e01ad0614102702d7031e000a1306 +004f0000000740034000015d3100ffff00c90000046a05d510271715029fffc31206002f0000ffff00c10000030006141027 +1715023900021106004f0000000940058f001f00025d3100ffff00c90000046a05d510270079023100771206002f0000ffff +00c10000028406141027007900d600731106004f000000174bb00d514bb011534bb018515a5b58b900000040385931000001 +fff20000047505d5000d003f401e0c0b0a040302060006950081080304010b0e000405011c0c073a0900790e10f43cecc4fc +3cc411123911123931002fe4ec11173930b4300f500f02015d1333112517011121152111072737d3cb013950fe7702d7fc5e +944de105d5fd98db6ffeeefde3aa023b6a6e9e0000010002000002480614000b005e401a0a09080403020600970603040109 +0a00047a0501080a7a07000c10d43ce4fc3ce411123911123931002fec173930014bb0105458bd000c00400001000c000cff +c038113738594013100d400d500d600d73047a0a700de00df00d095d133311371707112311072737c7b87d4cc9b87b4ac506 +14fda65a6a8dfce3029a586a8d00ffff00c900000533076c1027171704c50176130600310000000740034f00015d3100ffff +00ba00000464066d102600764207130600510000000940053f004f00025d3100ffff00c9fe1e053305d5102702d70500000a +120600310000ffff00bafe1e0464047b102702d70490000a120600510000ffff00c900000533075f1226003100001107171b +04f501670014b4040f0b00072b40092f0f200b1f0f100b045d31ffff00ba00000464066612260051000011070289008d0000 +0010b40019150c072b40050f190015025d31ffff00cd000005b905d510270051015500001006027e1b00000100c9fe560519 +05f0001c003b400d191612181c1c120a051c07411d10fc4bb0105458b90007ffc03859ec32d4fccc113100400c199516b007 +02950e910881072fe4f4ec10f4ec30011021220615112311331536373633321219011407062b0135333236350450fecdb3d7 +caca4e696a99e3e95152b55731664f037f01acffdefcb205d5f1864343fec1feccfc6fd561609c5aa000000100bafe560464 +047b001f003b401c0d13000318150787061087181cb816bc15070d08004e13170816462010fcec32f4ecc431002fe4f4c4ec +d4ec1112173930b46021cf2102015d01111407062b0135333237363511342623220615112311331536373633321716046452 +51b5fee96926267c7c95acb9b942595a75c1636302a4fd48d660609c30319902b29f9ebea4fd870460ae653232777800ffff +0073ffe305d90731102700710127013b1306003200000010b40d020307072b40051f021003025d31ffff0071ffe3047505f5 +1026007173ff1306005200000008b413020319072b31ffff0073ffe305d9076d1027171d052701751306003200000010b411 +000817072b400510001f08025d31ffff0071ffe3047506481026029a73001306005200000008b41d080023072b31ffff0073 +ffe305d9076b1027171f05270175120600320000ffff0071ffe3047506661027029f00a00000120600520000000200730000 +080c05d500100019003b401f059503110195008118079503ad091812100a1506021c1100040815190d101a10fcecd4c4c4d4 +ec32123939393931002fecec32f4ec3210ee30011521112115211121152120001110002117232000111000213307fafd1a02 +c7fd3902f8fbd7fe4ffe4101bf01b16781febffec0014001418105d5aafe46aafde3aa017c0170016d017caafee1fee0fedf +fedf00030071ffe307c3047b0006002700330084403107080010860f880c00a9082e0cb916132803b908bb22251fb819138c +340600162231090f0008074b311209512b121c453410fcecf4fcf4ecc4111239391239310010e432f43cc4e4ec3210c4ee32 +10ee10f4ee1112393040253f355f3570359f35cf35d035f035073f003f063f073f083f09056f006f066f076f086f09055d71 +015d012e01232206070515211e0133323637150e01232226270e01232200111000333216173e013332002522061514163332 +36353426070a02a48999b90e0348fcb20cccb76ac86264d06aa0f25147d18cf1feef0111f18cd3424ee88fe20108fab094ac +ab9593acac029498b3ae9e355abec73434ae2a2c6e6d6e6d01390113011401386f6c6b70fedd87e7c9c9e7e8c8c7e900ffff +00c900000554076c1027171704950176120600350000ffff00ba00000394066d102600764207120600550000ffff00c9fe1e +055405d5102702d70510000a120600350000ffff0082fe1e034a047b102702d70318000a120600550000ffff00c900000554 +075f1226003500001107171b047d016700080040035f1d015d30ffff00ba0000035a0666122600550000110602891b000010 +b411171309072b40050f170013025d31ffff0087ffe304a2076c1027171704950176120600360000ffff006fffe303c7066d +102600764207120600560000ffff0087ffe304a2076d1027171a04930175130600360000000bb404201529291049633a3100 +ffff006fffe303c70666102602882500130600560000000bb404201529291049633a3100ffff0087fe7504a205f012260036 +00001007007a008b0000ffff006ffe7503c7047b1226005600001006007a1700ffff0087ffe304a2076d1226003600001107 +171b048b0175000bb42b200e22221049633a3100ffff006fffe303c70666122600560000110702bd04270000000bb42b200e +22221049633a3100fffffffafe7504e905d51026007a5000120600370000ffff0037fe7502f2059e1026007ae10012060057 +0000fffffffa000004e9075f1226003700001107171b047301670010b4010d0900072b310040035f08015d30ffff00370000 +02fe06821226005700001107171502370070000740038f14015d31000001fffa000004e905d5000f00464018070b95040c09 +030f9500810905014007031c0c00400a0e1010d43ce4ccfc3ce4cc31002ff4ec3210d43cec323001401300111f0010011002 +1f0f1011401170119f11095d032115211121152111231121352111210604effdee0109fef7cbfef70109fdee05d5aafdc0aa +fdbf0241aa02400000010037000002f2059e001d0043401f0816a90517041aa900011bbc0d8710100d0e020608040008171b +15191d461e10fc3c3cc432fc3c3cc4c432393931002fecf43cc4fc3cdc3cec3230b2af1f01015d0111211521152115211514 +17163b0115232227263d0123353335233533110177017bfe85017bfe85252673bdbdd5515187878787059efec28fe98ee989 +27279a504fd2e98ee98f013effff00b2ffe30529075e1027171804ee01751306003800000010b41f091827072b400510091f +18025d31ffff00aeffe3045806371027029e008300001306005800000008b41e081626072b31ffff00b2ffe3052907311027 +007100ee013b1306003800000014b40503020d072b40092f0220031f021003045d31ffff00aeffe3045805f5102700710083 +ffff1306005800000008b40603020e072b31ffff00b2ffe30529076d1027171d04ee01751306003800000010b40f00081707 +2b400510001f08025d31ffff00aeffe3045806481027029a008300001306005800000008b410000818072b31ffff00b2ffe3 +0529076f1226003800001007029c00f00069ffff00aeffe3045806ca1226005800001106029c7cc40009400540154021025d +3100ffff00b2ffe30529076b1027171f04ee0175120600380000ffff00aeffe3045e06661027029f00b00000120600580000 +ffff00b2fe75052905d51226003800001007029d00fa0000ffff00aefe7504e8047b1226005800001007029d02270000ffff +0044000007a607741027171a05f5017c1306003a00000008b415020614072b31ffff005600000635066d1027028801450007 +1306005a00000008b415020614072b31fffffffc000004e707741027171a0472017c1306003c00000008b40b020607072b31 +ffff003dfe56047f066d102602885e071306005c00000008b418020617072b31fffffffc000004e7074e1226003c00001107 +1716047301750008b400100b04072b31ffff005c0000051f076c10271717049501761206003d0000ffff0058000003db066d +1026007642071206005d0000ffff005c0000051f07501027171e04be01751206003d0000ffff0058000003db0614102702b8 +041700001306005d0000000e0140094f0a5f0aaf0adf0a045d31ffff005c0000051f076d1226003d00001007171b04be0175 +ffff0058000003db06661226005d0000110602891b000010b4010f0b00072b40050f0f000b025d310001002f000002f80614 +0010002340120b870a970102a905bc010a10080406024c1110fc3cccfccc31002ff4ec10f4ec302123112335333534363b01 +1523220706150198b9b0b0aebdaeb063272603d18f4ebbab9928296700020020ffe304a40614000f002c0044402504b91014 +0cb9201c8c14b8222925a92c242797222e45001218472a20062c2808252327462d10fc3cccec323232ccf4ecec31002ff4dc +3cec3210e4f4c4ec10c6ee300134272623220706151417163332373601363736333217161110070623222726271523112335 +3335331521152103e5535492925453535492925453fd8e3a59587bcc7f80807fcc7b58593ab99a9ab90145febb022fcb7473 +7374cbcb747373740252643031a2a2fef8fef8a2a2313064a805047d93937d000003ff970000055005d50008001100290043 +40231900950a0995128101950aad1f110b080213191f05000e1c1605191c2e09001c12042a10fcec32fcecd4ec1117393939 +31002fececf4ec10ee3930b20f2201015d01112132363534262301112132363534262325213216151406071e011514042321 +1122061d012335343601f70144a39d9da3febc012b94919194fe0b0204e7fa807c95a5fef0fbfde884769cc002c9fddd878b +8c850266fe3e6f727170a6c0b189a21420cb98c8da05305f693146b5a300ffff00c9000004ec05d5120603a50000000200ba +ffe304a40614001600260038401f1bb9000423b9100c8c04b81216a913971228451417120847101f160813462710fcec3232 +f4ecc4ec31002ff4ec10e4f4c4ec10c6ee300136373633321716111007062322272627152311211525013427262322070615 +1417163332373601733a59587bcc7f80807fcc7b58593ab9034efd6b027253549292545353549292545303b6643031a2a2fe +f8fef8a2a2313064a80614a601fcc0cb74737374cbcb7473737400020000000004ec05d5000a00170033400c170b19001910 +2e050b1c15162fdcec32fcecc410cc31400905950cad0b81069514002fece4f4ecb315150b141112392f3001342726232111 +213237360111213204151404232111230104174f4ea3febc0144a34e4ffd7c014efb0110fef0fbfde8c9013801b78b4443fd +dd444304a8fd9adadeddda044401910000020000ffe304a406150012001e003e400d111220131206470d1912080f102fdcec +3232f4ecc410cc31400e0016b903b80e0c1cb9098c11970e002fe4f4ecc410f4ecc4b30f0f110e1112392f30013e01333200 +1110022322262715231123013301342623220615141633323601733ab17bcc00ffffcc7bb13ab9ba0122510272a79292a7a7 +9292a703b66461febcfef8fef8febc6164a8044401d1fc1acbe7e7cbcbe7e70000010073ffe3052705f000190030401b1986 +0088169503911a0d860c881095098c1a1b10131906300d001a10dc3cf4ecec310010f4ecf4ec10f4ecf4ec30133e01332000 +11100021222627351e01332000111000212206077368ed8601530186fe7afead84ed6a66e78201000110fef0ff0082e76605 +624747fe61fe98fe99fe614848d35f5e01390127012801395e5f00010073ffe3065a0764002400444022219520250da10eae +0a951101a100ae04951791118c25200719141b110d003014102510fcfc32ec10ecc4310010e4f4ecf4ec10eef6ee10dcec30 +b40f261f2602015d01152e0123200011100021323637150e0123200011100021321716173637363b0115232206052766e782 +ff00fef00110010082e7666aed84feadfe7a01860153609c0d0c105366e34d3f866e0562d55f5efec7fed8fed9fec75e5fd3 +4848019f01670168019f240304c3627aaa9600010071ffe304cc06140022004e402400860188040e860d880ab91104b917b8 +118c2301871e972307121419081e0d004814452310fcf432ccec10ec310010f4ec10e4f4ec10fef4ee10f5ee30400b0f2410 +2480249024a02405015d01152e0123220615141633323637150e012322001110002132173534363b011523220603e74e9d50 +b3c6c6b3509d4e4da55dfdfed6012d01064746a1b54530694c047ef52b2be3cdcde32b2baa2424013e010e0112013a0c0fd6 +c09c6100ffff000a000005ba05d51006009200000002ff970000061405d50008001a002e4015009509810195100802100a00 +05190d32001c09041b10fcecf4ec113939393931002fecf4ec30b2601301015d011133200011100021252120001110002901 +1122061d012335343601f7f40135011ffee1fecbfe42019f01b20196fe68fe50fe6184769cc0052ffb770118012e012c0117 +a6fe97fe80fe7efe9605305f693146b5a300000200c9000004ec05d500070014002e400c160804131c0a2e00190e101510fc +ecf4ec32c4c431400c139509810a049512ad03950a002fecf4ec10f4ec300110290111212206112111212224353424332111 +21019e01400144febca39d034efde8fbfef00110fb014efd7c01b7feef0223870393fa2bdadeddda01c000020071ffe3045a +06140012001e003f401d1cb9110e16b905088c0eb80312870197031904110802470013120b451f10fcecc4f4ec323231002f +fcec10e4f4c4ec10c4ee30b660208020a02003015d0135211123350e01232202111000333216171101141633323635342623 +2206010d034db83ab17ccbff00ffcb7cb13afd8da79292a8a89292a7056ea6f9eca864610144010801080144616401b9fcc0 +cbe7e7cbcbe7e70000020071fe560474046300190027005440140d0c0b202945170b12021a12175106201211452810fcecc4 +f4b27f17015decd4ec10ec1112393900400e0d0c1d09060709b9041db914b62810f4ecd4fcd4cc1112393940060025530c0d +0c070e10ec39313025161510212227351633323534252627261110003332000314020336262322061514161716173e01036b +9dfe47dd7866f6f6fef8d0758e0112eff00113019b2701ab9494acbc7e4033636e424f8dfef0469946755c30257087010f01 +0f0139fec7feed9cfefc01a0cbe5e8c3c2c70b060e2adc00000100830000044505d5000b002b40090d05091c000b07020c10 +dcc4c4d4ec32c431400c0a950b8102069507ad039502002fecf4ec10f4ec300111213521112135211121350445fc3e02f8fd +3902c7fd1a05d5fa2baa021daa01baaa00020075ffe305d905f00013001a0044402601140008a107ae040095141795110095 +14ad04950b91118c1b01141a1a190f3314190700101b10fcc4ecf4ec111239310010e4f4ecf4e410ee10ee10f4ee11123930 +13211000212206073536243320001110002120003716003332003775048ffeedfeee8bfc706f010792015e018bfe88fec6fe +b7fe97dc0d00ffcaca00ff0d030c010c0132605fd74648fe67fe92fe9ffe5b01b7ccc3fee4011cc3000100a4ffe3047b05f0 +0028004040240a8609880d9506912900169513ad291f8620881c95238c292a14091f101903191926102910fcecd4ecd4c4c4 +cc310010f4ecf4ec10f4ec3910f4ecf4ec30012e0135342433321617152e012322061514163b011523220615141633323637 +150e0123202435343601d8838e010ce659c97372be5398a39e95b6aea5b9c7be6dc8546ac75efee8fed0a3032521ab7cb2d1 +2020b426247b737077a695848f963231c32525f2dd90c4000001ff96fe66042305d500110041401f1108120d950cb0120695 +040295008104ad12110800070c050107031c00041210fcec32d4c4c411123939310010ecf4ec10ee10f4ec10393930b20f0b +01015d13211521112115211110062b013533323635c9035afd700250fdb0cde34d3f866e05d5aafe48aafd9ffef2f4aa96c2 +0001ff7ffe5602f80614001b00654023130a0f870dbd1d0518011408a906018700971606bc1c021b0700070905081517134c +1c10fc4bb00a5458b90013004038594bb0165458b90013ffc038593cc4fc3cc4c4123939310010e432fcec10ee3212393910 +f4ec39393001b6401d501da01d035d01152322061d012115211114062b013533323635112335333534363302f8b0634d012f +fed1aebdaeb0634db0b0aebd0614995068638ffbebbbab995068042a8f4ebbab00010073ffe3069707640026004940101502 +001c04111c1a34043321190b462710fcecfcf4ec10fcc4c4314018169515270005240195032495081ba11aae1e950e91088c +270010e4f4ecf4ec10fed4ee11393910dcec3025112135211106042320001110002132161734363b01152322061d012e0123 +200011100021323604c3feb6021275fee6a0fea2fe75018b015e5ba344c9e34d3f866e70fc8bfeeefeed011301126ba8d501 +91a6fd7f53550199016d016e01991919bceaaa96c2d75f60fecefed1fed2fece250000020008fe52057605d5000f00250095 +400d27501201120419170c191f242610d4d4ecd4ecd45dc4b510080003040c1112173931400a00951bbd1125122481260010 +e4323232f4ecb31f17081b1112393930400c131111121208232511242408070510ec3c0710ec3cb613110812082408070810 +ecb623110824081208070810ecb410251311230f40101615140317132408222120031f231208040711121739071112173901 +3237363534272627060706151417161301330116171615140706232227263534373637013302bf362c1c1f332c2c331f1c2c +3601d9defdba68432e4b649b9b644b2e4368fdbadefefd2014423949795c5c794939421420037a035efbcfc8ae77428b4157 +57418b4277aec8043100000100ba000007470614002a004f40112c0d120408112a1508264e1f1b081d462b10fcec32f4ecc4 +c4ccd4ec3931004019088709271426008711151b260320111887200923b81e97111c2f3cecf43cc4ec1112173910ec123939 +10ec30253237363534272627351617161114002b012226351134262322061511231133113e013332161511141633054c9554 +574a3e79e06d6ffee0dd46bb9d7c7c95acb9b942b375c1c64c699c62659bde705f21941d8f91feecf5fee6c8ce01089f9ebe +a4fd870614fd9e6564efe8fef2936700000100c9000002c605d5000b002e40100b02000695008107050806011c00040c10fc +4bb0105458b9000000403859ecc4393931002fe4ec113939300113331114163b011523222611c9ca6e863f4de3cd05d5fc2d +c296aaf4010e0001000a0000025205d5000b00454011020b95050800af060305011c0a0800040c10fc3cc44bb0105458bb00 +08004000000040383859ec32c431002fecdc3cf4323001400d300d400d500d600d8f0d9f0d065d1333113315231123112335 +33c9cabfbfcabfbf05d5fd16aafdbf0241aa000100c9000005f705f000170066400e001c0107080f07090b0f1c0e041810fc +ec32d4c4113910d4ec00310040250b110809080a1109090811110708071011080807420b0810030e0c1702059513910eaf0c +092f3cecf4ec393911121739304b5358071004ed071005ed071005ed071004ed592201233534262322070901210111231133 +110136333217161505f7aa49264625fddd031afef6fd33caca026c5571885555044879365023fdf9fce302cffd3105d5fd89 +02434f5c5b6e000100b90000049c0614001200cb400b040d090c0e10090800461310fcec32d4c41139c43100400f42100d0a +030b11069503970bbc110e2f3ce4fce411121739304b5358401410110d0e0d0f110e0e0d0b110c0d0c0a110d0d0c071004ed +071005ed071005ed071004ed59b2101401015d40350b0b0a0f280b270c280d2b0e2b0f4014680b6014890b850c890d8d0e8f +0f9a0b970faa0ba70db60fc50fd60ff70bf00bf70cf00c1a5db4090d090e0271004025040a0a10160a270a290d2b10560a66 +0a6710730a770d820a890d8e10930a960d9710a30a125d1334363b011523220615110133090123011123b9a3b5bfa8694c02 +25ebfdae026bf0fdc7b9047ed6c09c6199fdff01e3fdf4fdac0223fddd000001000a0000022a0614000b0032400705010808 +00460c10fc3cec3231004008020ba905080097062fecd43cec3230400d100d400d500d600d700df00d06015d133311331523 +112311233533c1b8b1b1b8b7b70614fd3890fd4402bc90000001003d0000047f0614000f00a0401308020b05010e070d080c +06090406110c06001010d4c4b28006015dd4c410c4cc11121739b410094009025d3100400f08020b05010e06060004090697 +0d002f3cf4c4c4111217393040320a03a902a90ba90508040c0709040f11000e11010d060100051102110e110f0e01110001 +0d110c070c0b11081107110d060d070510ececec071005ec08ec08ec05ecec070810ec0510ec0708103c3cecec0efc3c3301 +27052725273317251705012309013d01eb47fed42101294bc834013a21fec901edc3fec6fe7e0432bc656363c58a686168fa +d7033cfcc400000100b2ffe3072705d50027004a4012001214201d1c291f50121c14500a1c08042810fcecfcfcfcccfc3c11 +12393100401607140a1c11000621080e18952103248c28121d0881202ff43c3c10f43cc4ec32111217393039250e01232227 +263511331114171633323635113311141716333237363511331123350e012322272603a645c082af5f5fcb2739758fa6cb39 +39777b5353cbcb3fb0797a5655d57c767b7ae2041bfbefba354ebea403ecfbefa24e4d5f60a303ecfa29ae67623e3e000001 +ff96fe66053305d50011008c402907110102010211060706420811000d950cb01207020300af05060107021c04360b0e0c39 +071c00041210fcece43939fcec11393931002fec32393910fcec113939304b5358071004ed071004ed5922b21f0b01015d40 +303602380748024707690266078002070601090615011a06460149065701580665016906790685018a0695019a069f13105d +005d13210111331121011110062b013533323635c901100296c4fef0fd6acde3473f866e05d5fb1f04e1fa2b04e1fb87fef2 +f4aa96c2ffff00bafe560464047b1006034b000000030073ffe305d905f0000b001200190031400b19101906330f13190010 +1a10fcec32f4ec323100400f16950913950fad1a0c950391098c1a10e4f4ec10f4ec10ec3013100021200011100021200001 +220007212602011a0133321213730179013a013b0178fe88fec5fec6fe8702b5caff000c03ac0efefd5608fbdcdcf80802e9 +016201a5fe5bfe9ffe9efe5b01a403c5fee4c3c3011cfd7afefffec2013d0102ffff0067ffe3061d061410260032f4001007 +02cc05a20134ffff0076ffe304d304eb102702cc0458000b10060052050000020073ffe306cf05f00014001f0033401c0495 +10af0015950d91001b95078c0021131c001e1c100418190a102010fcecd43cecdcecc431002ff4ec10f4ec10f4ec30211134 +262311062120001110002132172132161901012200111000333237112606056e7abcfec5fec6fe870179013b70610127e3cd +fc58dcfefd0103dcaf808a03d3c296fb8bd301a40162016201a51bf4fef2fc2d054cfeb8fee6fee5feb86704184600020071 +fe560559047b00160021003a4020058711bc2217b90eb8221db9088c16bd22110105231508011f08051a120b452210fcecd4 +ecdcecc4111239310010e4f4ec10f4ec10f4ec30011134272623110623220011100033321733321716151101220615141633 +3237112604a126266989f0f1feef0111f16452d8b55251fd1a94acab95814054fe560474993130fcbc9d0139011301140138 +1b6060d6fb8c0589e7c9c9e73a02f0360002ff97000004f105d50008001c003a40180195100095098112100a080204000519 +0d3f11001c09041d10fcec32fcec11173931002ff4ecd4ec30400b0f151f153f155f15af1505015d01113332363534262325 +2132041514042b0111231122061d012335343601f7fe8d9a9a8dfe3801c8fb0101fefffbfeca84769cc0052ffdcf92878692 +a6e3dbdde2fda805305f693146b5a300000200b9fe5604a4061400180024004f402423b900171db90e11b8178c01bd25030c +09a90697251a12144706090307200c000802462510fcec3232cc113939f4ec310010f4ec393910e4e4f4c4ec10c4ee304009 +60268026a026e02604015d2511231134363b01152322061d013e013332001110022322260134262322061514163332360173 +baa3b5fee7694c3ab17bcc00ffffcc7bb10238a79292a7a79292a7a8fdae0628d6c09c6199c86461febcfef8fef8febc6101 +ebcbe7e7cbcbe7e7000200c9fef8055405d50015001d005640170506031300091d1810050a1a1904133f0e160a120c041e10 +fcec3232fcc4ec1117391139393931004010001706030417950916950f81040d810b2fecdcf4ecd4ec123939123930014009 +201f401f75047c05025d011e01171323032e012b0111231133113320161514060111333236102623038d417b3ecdd9bf4a8b +78dccacafe0100fc83fd89fe8d9a998e01b416907efe68017f9662fe9105d5fef8d6d88dba024ffdd192010c910000010072 +ffe3048d05f0002100644011071819061d0a0f1d19042d00220a19152210dcece4fcecc41112393939393100401942191807 +06040e21000ea10f940c9511209500940291118c2210e4f4e4ec10eef6ee10ce111739304b5358400a180207060719020606 +0707100eed07100eed591336200410060f010e0114163332371504232027263534363f013637363427262007cce401c60117 +cae27b9a87bcade1f8fefdd6fee79291d7e27aa63c3b595afea1e405a44ce4fe8fc02d181f7cec888bd05f7070d9b6d92b19 +1f3233d940406d0000010064ffe303bc047b002700cf40110a1e1d090d21142108060d0800521a452810fce4ecd4ecc41112 +393939393140191e1d0a09041300862789241486138910b91724b903b8178c280010e4f4ec10fef5ee10f5ee121739304012 +1b1c021a1d53090a201f02211e530a0a09424b535807100eed111739070eed1117395922b2000101015d40112f293f295f29 +7f2980299029a029f029085d4025200020272426281e281d2a152f142f132a12280a2809290829072401861e861d861c861b +12005d40171c1e1c1d1c1c2e1f2c1e2c1d2c1c3b1f3b1e3b1d3b1c0b71133e013332161514060f010e011514163332363715 +0e012322263534363f013e0135342623220607a04cb466cee098ab40ab658c8261c6666cc35ad8f7a5c43f946289895aa84e +043f1e1eac9e8295240f25504b51593535be2323b69c89992a0e2149405454282800ffff00c90000048b05d5100603370000 +0002fef2fe5602d706140016001f0036400c1d0e0a1506140108170a4f2010fc32fc32cccc10d4cc3100400f141f87000b1b +87109720048706bd2010fcec10f4ecd43cec3230011114163b01152322263511232035342132171617331525262726232207 +063301774d63b0aebdaebefef2012fb5523512bffe860811216e7c030377046afb3d685099abbb04aed2d860406f9b9a2c18 +3041330000010037fe5602f2059e001d003f400e0e14080802090400081a1c18461e10fc3cc4fc3cdc3239fccc3100401218 +05081903a9001b01bc08871510870ebd152ffcec10ecf43cccec321139393001112115211114163b011514062b0135333237 +363d0122263511233533110177017bfe854b73bda4b446306a2626d5a78787059efec28ffda0894eaed6c09c303199149fd2 +02608f013e0000010018000004e905d5000f005840150d0a0c06029500810400070140031c050b1c0d051010d4d4ec10fce4 +393931002ff4ec32c4393930014bb00a5458bd00100040000100100010ffc03811373859401300111f00100110021f071011 +401170119f11095d012115211123112322061d012335343601ae033bfdeecb5e84769cc005d5aafad5052b5a693146b5a300 +00010037000002f20614001b0049401019160b080417090204000810130e461c10fc3cc4fc3cc43232173931004013130019 +8716970a0e05080f03a91101bc08870a2fecf43cec3211393910f4ec393930b2af1501015d01152115211114163b01152322 +2635112335333534363b01152322060177017bfe854b73bdbdd5a28787aebdaeb0634d04c3638ffda0894e9a9fd202608f4e +bbab99510001fffafe6604e905d5000f0054401407950abd100e0295008110080140031c00400d1010d4e4fce4c4310010f4 +ec3210f4ec30014bb00a5458bd00100040000100100010ffc03811373859401300111f00100110021f0f1011401170119f11 +095d032115211114163b01152322261901210604effdee6e863f4ee3cdfdee05d5aafb3dc296aaf4010e04c3ffff00adfff7 +065f061410260038fb14100702cc05e40134ffff00b0ffe3056904eb102702cc04ee000b1006005802000001004effe305cf +05ca001f003a40101d1a1921100004330a1114190d0a102010fcc4fcc410f4c4ecfcc43100400e0d11011d951e1081201795 +078c2010f4ec10fc3cec32323230012116121510002120001134123721352115060215140033320035340227352105cffec0 +a18efe7ffed1fecffe81919efec10258b2c70109d8d80108c6b1025805188dfed8c2fecbfe77018a013eb8012a8bb2b261fe +b4caeffedd0122f0ca014c61b200000100c9ffe1057605d5001b002d400d10150c070803190c181c15041c10fcecd4ec2f3c +111239310040090816811c0095108c1c10f4ec10ecc430253200353427262735171612151007062127262726190133111416 +3302c6d8010863416eb3a18ec0bffecf4de86167ca6e868d0122f0caa66d5744018dfed8c2fecbc5c40206747a010e03f0fc +10c296000001fffc000005f005f000170064400f131c140c040b070040051c0940071810d4e4fce41239c4392fec3100400b +12151400950e910b09af062fec39f4eccc39393040190c110405040b110a0b0505040b110c0b0809080a11090908424b5358 +071005ed071008ed071008ed071005ed59220122070607011123110133090136333217161d012335342604d739152511fe84 +cbfdf0d9019e014e5aa3885555aa4905470e1819fdbffd3902c7030efd9a01f9885c5b6e837936500001003dfe5605d8047b +001f016a4017120e151b1f1808151f0e0d0c0a09060300081f041f0b2010d44bb00a544bb008545b58b9000b004038594bb0 +145458b9000bffc03859c4c411173910d4ec11391112393100403a0708020911001f0a110b0a00001f0e111d001f0d110c0d +00001f0d110e0d0a0b0a0c110b0b0a420d0b0920000b058703bd201bb912b80bbc172010c4e4f4ec10f4ec11391139123930 +4b5358071005ed071008ed071008ed071005ed071008ed0705ed1732592201408d0a000a09060b050c170115021004100517 +0a140b140c2700240124022004200529082809250a240b240c270d37003501350230043005380a360b360c380d4100400140 +024003400440054006400740084209450a470d5400510151025503500450055606550756085709570a550b550c6601660268 +0a7b0889008a09850b850c890d9909950b950ca40ba40c465d004025060005080609030d160a170d100d230d350d490a4f0a +4e0d5a095a0a6a0a870d800d930d125d050e012b01353332363f01013309013637363332161d012335342623220706070293 +4e947c936c4c543321fe3bc3015e011a1530588783b9b251393929140a68c87a9a488654044efc9402c0343360bf8672723a +542a14190001005c0000051f05d5001100c0403506030207020c0f100b1007110b100b101102070242050d95040e12109500 +810795090c06030f040e04080e00100700014208000a1210dc4bb009544bb00a545b58b9000affc03859c4d4e411393910c4 +10c411173931002fecf4ec10d43cec32304b5358071005ed071005ed0710053c3c0710053c3c592201404005020a0b180b29 +02260b380b4802470b48100905070b10001316071a1010132f13350739103f1347074a104f1355075911660769106f137707 +78107f139f13165d005d132115012115210121152135012135210121730495fe700119fe73fe5403c7fb3d01b9fed5019f01 +83fc6705d59afe1190fdeeaa9a02229001df00010058000003db0460001100c540310c0f100b100603020702101102070207 +110b100b4210a900bc09050da9040e07a90910070f03060c0601000e0408010a1210dc4bb00b544bb00c545b58b9000affc0 +38594bb0135458b9000a00403859c432c4c4c411173931002fecd43cec3210f4ec304b5358071005ed071005ed0710053c3c +0710053c3c59220140420502160226024702490b050b100f1318071b102b1020133607391030134001400245074008400943 +10570759105f136001600266076008600962107f138013af131b5d005d13211503331521012115213501233521012171036a +fbc2fec2fec302b4fc7d012bd40150010dfd650460a8fedc90fe8f93a8015c900139000100a0ffc104f805d500220070400e +0b0e0d080a04190e10160a0d1e2310dcc4c4d439c4ec1239b43f0e4f0e025d111239310040130a0995100f0b950d81231fa1 +1eae00951a8c2310f4ecf4ec10f4ec39d4ec3930400a10110a0b0a0b110f100f071005ec071005ec400e090a370f0205100b +0b15103b0b04015d005d25323736353427262b013501213521150132171617161514070621222726273516171602a8c06364 +5c5da5ae0181fcfc0400fe656a806256519898fee8777d7e866a7f7e6b4b4b8f86494a9801eaaa9afe16382a6d688adc7a79 +131225c3311919000001005cffc104b405d50022005e400f1816151b1f130d1916051f19150d2310dcc4b430154015025dec +d4c4c41139113911123931004013191b951314189516812304a105ae0095098c2310f4ecf4ec10f4ec39d4ec3930400a1311 +1918191811141314071005ec071005ec25323736371506070623202726353437363736330135211521011523220706151417 +1602ac897e7f6a867e7d77fee89898515662806afe650400fcfc0181aea55d5c64636b191931c3251213797adc8a686d2a38 +01ea9aaafe16984a49868f4b4b0000010068fe4c043f0460002000a3400b0006020c121b130306022110dcccc4c4d4ec1112 +393100401a0c1b0018064200a90707032104a9031386149310b918bd03bc2110e4fcecf4ec10ec1112392fecec1112393930 +40080611000511010702070510ec0410ec401b03050500140516002305250037003405460043055b0054057e000d015d401b +040604011406140125062401350137064501460654015c067f060d005d4009061507161a151a12045d090135211521011523 +220706151417163332363715060706232024353437363736025bfe65036afd6501aeaea55d5c6463be6dc8546a64635efee8 +fed05156628001dc01dca893fe0da64a4b848f4b4b3231c3251312f2dd8a686d2a3800010071fe5603e80460002000000132 +37363715060706232011342524353423302101213521150120151005061514027f544d4f5157505661fe200196011cebfede +01e5fd65036afe9e016ffe30e2feee15152cb3200d0e0119ee3525627c023893a8fe64e5feec3118618b000100960000044a +05f00024000025211521350137213521363736353427262322070607353e01333204151407060733152307018902c1fc4c01 +3a73fea701e25f25275354865f696a787ad458e80114221f4a68ec30aaaaaa014075906d484c49774b4b212143cc3132e8c2 +5c52496090310001005dffc104f905d500190035400e1b0308110a0b080700081907461a10fcd4ec10ecd4d4eccc3100400d +169501001a06950d0b9509811a10f4ecd4ec10ccd4ec300110201134262321112115211125241716100f0106070620243501 +26030ab9a5fdf703a1fd2901730100a2513b1c142d98fdc4fed00190fedb01258693032caafe250101d068fee056291d2479 +f2dd00010068fe4c043f0460001a0033400b1c0408120a0c081a08461b10fcc4ecd4d4eccc3100400f0287001a18bd1b0787 +0e0c870abc1b10f4ecd4ec10fccc32ec30171633201134262321112115211133321e01100f0106070621222768aace0196b9 +a5fe9f0319fd9fdd69e4a63b1c142d98fee8bbd4a76301258693032caafe2663d4fee056291d24794a0000010058ffe303a5 +059e0024000001071617161514070621222726273516171633323736373427262b01132335331133113315022102aa706c6e +89feed5551514c49544e50b36339013a56c03e02e5e5cae703e67d1e7773aaba7d9d121123ac281816724185624c72010fa4 +0114feeca400000200bafe5604a4047b000e00170040400b1911080d0417000802461810fcec3232d4eccc3100400c421587 +05098c03bc0001bd1810ecc4f4f4ccec304b5358b617050f8700000e070410ed0010cc590511231133153637363332171615 +100100353427262322070173b9b9348751d2b84d4efccf0272393878dcad7afed0060aaa425231707199fe57fee40190f985 +4241ef00000100c9fe56019305d500030026400a009702bd04010800460410fcec310010ecec30400d100540055005600570 +05f00506015d13331123c9caca05d5f88100ffff00c9fe56032705d5102701820194000010060182000000010014fe56039c +05d50013003a401d0c09a90f061302a91005050a00970abd14070309050108120d0c10001410d43c3ccc32fc3c3ccc323100 +10ecec11392f3cec32dc3cec323001331121152115211521112311213521352135210173ca015ffea1015ffea1cafea1015f +fea1015f05d5fd97a8f0aafd2c02d4aaf0a8ffff00c90000019405d5100600049400ffff00c900000ad0076d1027013f05b1 +0000100600270000ffff00c9000009b006661027014005d50000100600270000ffff0071ffe3089106661027014004b60000 +100600470000ffff00c9fe66062405d51027002d049100001006002f0000ffff00c9fe5605de06141027004d046500001006 +002f0000ffff00c1fe5602ef06141027004d017600001006004f0000ffff00c9fe6606f205d51027002d055f000010060031 +0000ffff00c9fe5606b706141027004d053e0000100600310000ffff00bafe5605de06141027004d04650000100600510000 +ffff001000000568076d1226002400001107171b04be01750006b10e00103c31ffff007bffe3042d06661226004400001106 +02895a000008b40b2b2714072b31fffffffe00000260076d1226002c00001107171b032f0175000bb407200100001049633a +3100ffffffe00000025e0666122600f3000011070289ff1f0000000bb408200100001049633a3100ffff0073ffe305d9076d +1226003200001007171b05270175ffff0071ffe3047506661226005200001106028976000006b11b0c103c31ffff00b2ffe3 +0529076d1226003800001107171b04f601750006b11505103c31ffff00aeffe304580666122600580000110602897600000b +b418200b01011049633a3100ffff00b2ffe305290833102617493000120600380000ffff00aeffe30458073110270071007b +013b120600be0000ffff00b2ffe30529085a1226003800001006174c3600ffff00aeffe3045807221226005800001007174c +ffbefec8ffff00b2ffe30529085a122600380000100617513000ffff00aeffe30458072212260058000010071751ffc4fec8 +ffff00b2ffe3052908601226003800001006174d3006ffff00aeffe3045807221226005800001007174dffbefec8ffff0071 +ffe3047f047b1206021b0000ffff0010000005680833122600240000100617490000ffff007bffe3042d0731122600a60000 +100700710052013bffff00100000056808331226002400001006174b0000ffff007bffe3042d06f41226004400001007174b +ff93fec1ffff00080000074807341027007102d7013e120600880000ffff007bffe3076f05f21027007101e8fffc120600a8 +000000010073ffe3060405f00025005440102124221e1c11340200043318190b102610fcecfc3ccce4fcc4c431004018041f +012200051b2395251b950812a111ae15950e91088c2610e4f4ecf4ec10fed4ee113939dcb00b4b5458b1224038593ccc3230 +011133152315060423200011100021320417152e012320001110002132363735233533352135058b797975fee6a0fea2fe75 +018b015e9201076f70fc8bfeeefeed011301126ba843fdfdfeb6030cfed658ff53550199016d016e01994846d75f60fecefe +d1fed2fece2527b55884a60000020071fe5604fa047b000b00340058400e0f22322500080c470612182c453510fcc4ecf4ec +3232c4c43100401b20110e23250c29091886191cb91503b9322fb833bc09b915bd26292fc4e4ece4f4c4ec10fed5ee111239 +39d43ccc3230b660368036a03603015d01342623220615141633323617140733152306070621222627351e01333237363721 +3521363d010e0123220211101233321617353303a2a59594a5a59495a5b813b3c61f3a7ffefa61ac51519e52b55a1511fd84 +029a1639b27ccefcfcce7cb239b8023dc8dcdcc8c7dcdceb6e58465d408c1d1eb32c2a5f171c45475e5b6362013a01030104 +013a6263aa00ffff0073ffe3058b076d1226002a00001107171b054a01750010b1210e103c4007942154212421035d31ffff +0071fe56045a0663102602894afd1206004a0000ffff00c90000056a076d1027171b04a201751206002e0000ffffffe90000 +049c076d1226004e00001107171b031a0175002ab401100c00072b31004bb00e5158bb0001ffc00000ffc0383859400d9001 +90008001800040014000065dffff0073fe7505d905f01027029d01340000120600320000ffff0071fe750475047b1027029d +00800000120600520000ffff0073fe7505d90731102700710127013b120601ac0000ffff0071fe75047505f51026007173ff +120601ad0000ffff00a0ffc104f8076d1027171b04be0175120601790000ffff0058fe4c042f0666102602891b0010060254 +0000ffffffdbfe560264066610270289ff250000110601f90000000bb403200807071049633a3100ffff00c900000ad005d5 +1027003d05b10000100600270000ffff00c9000009b005d51027005d05d50000100600270000ffff0071ffe3089106141027 +005d04b60000100600470000ffff0073ffe3058b076c10271717051b01761206002a0000ffff0071fe56045a06631226004a +0000100600761bfd000100c9ffe3082d05d5001d0035400e0e1c1119031c06381b011c00041e10fcec32fcec32d4ec310040 +0e0f1a9502ad0400811c0a95158c1c2fe4ec10e432fcecc43013331121113311141716173237363511331114070621202726 +3511211123c9ca02deca3e3d9994423eca6460fee6feed6764fd22ca05d5fd9c0264fbec9f504e014f4ba4029ffd5adf8078 +7876e9010dfd3900000200c9fe56050205f0000e00170040400b19111c0d0417001c02041810fcec3232d4eccc3100400c42 +159505098c03810001bd1810ecc4f4f4ccec304b5358b617050f8700000e070410ed0010cc59251123113315363736333217 +1615100100113427262322030193caca389157e2c65354fc9102a13d3c81edba9cfdba077fb9485735787aa4fe37fece01ae +010c8f4746feff00ffff00c900000533076b10271719051e0175120600310000ffff00ba0000046406641226005100001007 +00430118fffeffff001000000568077312260087000010071717065c017dffff007bffe304dc0773122600a7000010071717 +05ec017dffff000800000748076c10271717065c0176120600880000ffff007bffe3076f0663122600a80000100700760165 +fffdffff0066ffba05e5076c1027171704fe01761206009a0000ffff0048ffa2049c0663122600ba0000100600761cfdffff +00100000056807701226002400001007172004e5017affff007bffe3042d0664102702c00498fffe120600440000ffff0010 +0000056807361226002400001007171c04bc013effff007bffe3042d0648102702c204650000120600440000ffff00c90000 +048b07701226002800001007172004a5017affff0071ffe3047f0663102702c004bafffd120600480000ffff00c90000048b +07361226002800001007171c04a6013effff0071ffe3047f0648102702c204a90000120600480000ffffffa7000002730770 +1226002c0000100717200359017affffffc3000002810663102702c00366fffd120600f30000ffff00050000027707361226 +002c00001007171c033e013effffffe3000002550648102702c203240000120600f30000ffff0073ffe305d9077012260032 +0000100717200541017affff0071ffe304750664102702c0049ffffe120600520000ffff0073ffe305d90736122600320000 +1007171c051c013effff0071ffe304750648102702c204980000120600520000ffff00c70000055407701226003500001007 +17200479017affff00820000034a0663102702c00425fffd120600550000ffff00c90000055407361226003500001007171c +0480013effff00ba0000035e0648102702c2042d0000120600550000ffff00b2ffe305290770122600380000100717200515 +017affff00aeffe304580664102702c004d4fffe120600580000ffff00b2ffe3052907361226003800001007171c04ec013e +ffff00aeffe304580648102702c204ab0000120600580000ffff0087fe1404a205f0102702d704760000120600360000ffff +006ffe1403c7047b102702d7042c0000120600560000fffffffafe1404e905d5102702d704530000120600370000ffff0037 +fe1402f2059e102702d7040000001206005700000001009cfe52047305f0002e0000010411140e010c01073536243e013534 +2623220f0135373e0335342e03232207353633321e0115140e02033f01346fb9ff00feea99c80131b95c7d705f73a3f83c66 +683d23374b4826b8f3efce83cb7c173a6e02a243fedb70cea0886022a0378c999d4f65843348ab6a1a41638b52375633220c +b8bea456b6803c66717400010047fe4f03bc047b00340000011e0315140e0507353e0435342623220f0135373e0435342e03 +23220607352433321e0115140602a746703e21426c989db3954aa2f59e6328765d3b3fd8df2241573f2d1f3143412345a893 +010a8670b8746701cd08445a58254b8a6c61463d270f822e605b625b33587019568b550d203c4566392c462a1b0a3b5a9a85 +4792616e9900ffff00c90000053b076d1027171b050401751206002b0000fffffff000000464076d1027171b032101751306 +004b0000002ab414050113072b31004bb00e5158bb0014ffc00013ffc0383859400d901490138014801340144013065d0001 +00c9fe56051905f00013002e401203950e91098112b008131c120b061c08411410fc4bb0105458b90008ffc03859ec32d4fc +31002fece4f4ec300134262322061511231133153e0117321219012304509a99b3d7caca51cc9de3e9c9037fd7d5ffdefcb2 +05d5f1878601fec1feccfad900030071ff700644061400070028003400002516333235342722073633321510212227060723 +36372635060706232227261037363332171617113300101716203736102726200704b61125a03434ca6e88f4feaa49352218 +c41d43303a58597ccb807f7f80cb7c59583ab8fcd55354012454545454fedc548205af2d0120b8cefebf0f483a45933c2464 +3031a2a20210a2a2313064025efce6fe6a74737374019674737300020071ffe3052505f0000c003b0057401c240014330418 +103d450a1c28421d181c21383b101c3742041c2f453c10fcecf4ecccb2203b015df4ecccf4ecec1112173931004012243300 +9514ad3c0d3b1c1d913c07082c8c3c10f4ec10f4ccd4cc10f4ec39393001220706101716203736353426030e011514171633 +32373635342726273532171615140607161716151407062027263534373637262726353437362102cbb86a6b6b6a01706b6b +d4f482aa5f3bcca85f604c6d82e4968baa98ac5f609c9bfdba9b9c6061abab43558274010102c54d4dfef24d4d4d4e86879a +0227037c4f45482d4141889e2b4d08646861ba80b2202263638fd974747474d98f6363221f46595882534a0000020071ffe3 +0471050f000d00340043401636450a0818420e3432081028292b08264204081f453510fcecf4eccc32d4eccc32f4ecec3100 +400e3429142200b92ead3507b91c8c3510f4ec10f4ec3939cc32300122070610171620373635342726131615140706071617 +16151407062027263534363726272635343733061417163332373635342702719053525253012053535352fe3a3448829252 +518584fe128485a492903b343fa12b49488382494a2c02c54d4dfef24d4d4d4e86874d4d024a4062994059202263638fd974 +747474d98fc62223564b8e594941e84141414174773e0001005cfe56051f05d50015009f400c0f141112420b081506110d16 +10dc4bb009544bb00a545b58b9000dffc03859c4c4d4ece41139393100400c420795050c0f95118114950c2fecf4ec10dcec +304b5358400a14110e0f0e0f11131413071005ed071005ed5901404005130a0e180e2913260e380e4813470e480f0905140b +0f001716141a0f10172f173514390f3f1747144a0f4f175514590f6614690f6f177714780f7f179f17165d005d051007062b +0135333237363d01213501213521150121051f9e4872fee9692626fbf503b0fc670495fc5003c714fedf50259c303199149a +0491aa9afb6f00010058fe5603db0460001500ac400c0b08150d0f14121112060d1610dc4bb00b544bb00c545b58b9000dff +c038594bb0135458b9000d00403859c4c4b440126012025dc411393910d4b440156015025dec3100400c4207a9050c0fa911 +bc14a90c2fecf4ec10dcec304b5358400a0f1113141314110e0f0e071005ed071005ed590140320513161326134713490e05 +0b0f0f1718141b0f2b0f20173614390f30174514490f5714590f5f176614680f7f178017af17135d005d051007062b013533 +3237363d0121350121352115012103db9e4872fee9692626fd3502b4fd65036afd4c02b414fedf50259c30319914a8032593 +a8fcdb00ffff00100000056807501027171e04bc0175120600240000ffff007bffe3042d0614102702b8044a000012060044 +0000ffff00c9fe75048b05d51226002800001007007a00a20000ffff0071fe75047f047b1226004800001006007a7b00ffff +0073ffe305d90833122600320000100617496200ffff0071ffe304750731122600b80000100700710073013bffff0073ffe3 +05d90833122600320000100617506900ffff0071ffe3047506e912260052000010071750ffb5feb6ffff0073ffe305d90750 +1027171e05270175120600320000ffff0071ffe304750614102702b804730000120600520000ffff0073ffe305d908331226 +003200001006174b6a00ffff0071ffe304750731122601f10000100700710073013bfffffffc000004e70731102700710072 +013b1206003c0000ffff003dfe56047f05f5102600715eff1206005c00000002008aff70035c060e00070019000025163332 +3534272207363332151021222706072336372637113301ce1125a03434ca6e88f4feaa49352218c41d433101b88205af2d01 +20b8cefebf0f483a45933c5a0530000200baff70064e047b0007002b00002516333235342722073633321510212227060723 +36372637113426232206151123113315363736333217161504c01125a03434ca6e88f4feaa49352218c41d4331017c7c95ac +b9b942595a75c163638205af2d0120b8cefebf0f483a45933c5a01c09f9ebea4fd870460ae6532327778e80000020037ff70 +0361059e0007002100002516333235342722073633321510212227060723363726351123353311331121152101d31125a034 +34ca6e88f4feaa49362118c41d43318787b9017bfe858205af2d0120b8cefebf0f483a45933c5a02f38f013efec28f000001 +ffdbfe5601790460000b003840150b020700078705bd00bc0c080c05064f010800460c10fcece4391239310010e4f4ec1112 +393930400b100d400d500d600d700d05015d13331114062b013533323635c1b8a3b54631694c0460fb8cd6c09c6199000003 +0071ffe3078c061400090023002f00414013314525121447051b0d082b180e47011221453010fcecf43c3cfc3c3cf4ecec31 +0040102808b90a2e04b9161d8c110ab80d97192fece432f432ec3210ec323000101716203610262007133217113311363332 +0010022322271523350623222726103736001027262007061017162037012f53540124a8a8fedc54b9f572b972f4cc00ffff +ccf472b972f5cb807f7f80055d5354fedc5453535401245402fafe6a7473e70196e773010dc5025efda2c5febcfdf0febcc5 +a8a8c5a2a20210a2a2fce9019674737374fe6a74737300030071fe56078c047b000b0025002f004440133145011224472b11 +1d12070e1e47271217453010fcecf43c3cfc3c3cf4ecec310040120a2ab913042eb9211ab80c138c0fbd1dbc3010e4e4e432 +f43cec3210ec3230001027262007061017162037032227112311062322272610373633321735331536333200100200101716 +20361026200706cd5354fedc54535354012454b9f472b972f5cb807f7f80cbf572b972f4cc00fffffaa253540124a8a8fedc +540164019674737374fe6a747373fef3c5fdae0252c5a2a20210a2a2c5aaaac5febcfdf0febc0317fe6a7473e70196e77300 +0003fffdffba057c06170012001600190000013313011709012303210f012307272337273709013301032103024ae5860161 +66fe70017cd288fdd6cd32463b520201142f0290feee16016fbd015d6a05d5fea101a159fe27fc1b017ff18e464601113804 +c4fd1901b1fe4f011f000002000cffba058a06170022002c0000172713261110373621321716173717071526270116171621 +3237363715060706232027130123262320070611147266dc75c3c3015386763d3a6566632e31fcf4090b880100827473666a +777684feb4c23902d8017482ff00888846580105bb01170168cfd024121b785976bb2b21fc660d0c9d2f2f5fd3482424c701 +15035c2f9c9dfed8ad0000020009ffa2045d04bc0022002b0000172737263510373621321716173717071526270116171633 +32373637150607062322271301262322070615146960bd559796010655512e2d595f761918fdd3070663b3504e4f4e4d5253 +5df0933701ee4747b363635e4ee68dcc01129d9d110a106c4f8f550e0bfd5e08087115162baa2412129001050256117172cd +67000001000a0000046a05d5000d003b40160c050a95020c06950081080305011c073a0c0a00040e10fc3cccecfc3ccc3100 +2fe4ecd43cec3230400d300f500f800780087f047f0306015d1333113315231121152111233533c9cabfbf02d7fc5fbfbf05 +d5fd7790fdeeaa02bc900002ffb2ffba05310617000f001200000115230111231101270111213521371709012104e934fe22 +cbfe0d67025afdee04993866fda6012cfed405693efdccfd090207fdb35802c70252aa4259fe0b0162000001006ffe100419 +047b003d0000013427262f0126272635343633321617152e0123220706151417161f0116171615140706071f011633152322 +27262f012627262726273516171633323736030a3233ab40ab4c4ce0ce66b44c4ea85a8944453131943fc650537b57849f93 +2a4c2754724759ed1e241011616c6663636182464601274b2828250f244a4b829eac1e1eae28282a2a54402524210e2c4b4c +899c5b40139f7e249a3d265bf31e1003021223be351a1b2d2c0000010058fe1004330460001800001321150116170117163b +0115232227262f01262b013d01012171036afd4e5c310108932a4c6c9354724759ed3d5a5e02b4fd650460a8fcdd1031fef8 +7e249a3d265bf33f9c0c0325000100500000048d05d500180036401112130c0b040f000501081916011c040f1910d4d4ecd4 +ec1139391117393100400b0095050f95100b951281022ff4ecd4ecd4ec3001231123113332363534262b0122060735363b01 +3204151404029127caf18d9a9a8dfe45af4f98abfef40108fef7025afda603009187888f2a2cb646dce1d7e7000100500000 +038f047b0018003740100a08060f040c01000412131608000c1910d4d4ecd4ec12391217393100400d16b901170c860d8808 +b90fb8172ff4ecf4ee10d4ec3001333236353427262322070607353633321716151406231123012f648d9a4c55864956564e +98abfb7d84d4c2ca01a691878d414815152bb6466e74dbd5e5fefc000003000a000004ec05d5000c00150028005c401a150f +0c06171d230500121c1a0919202e02040d001c262516042910fc3cccec3232ccfcecd4ec1117393939310040152801952504 +0400051d00950e0d95168105950ead232fececf4ec10ee391112392f3cec3230b20f2a01015d011521152115213236353426 +2301112132363534262325213216151406071e011514042321112335330193015bfea50144a39d9da3febc012b94919194fe +0b0204e7fa807c95a5fef0fbfde8bfbf02c9c990ca878b8c850266fe3e6f727170a6c0b189a21420cb98c8da017090000002 +000cffe305ce05d50014001d005f400f15031c0709053816011c131100411e10fc4bb0105458b90000ffc038593cccec32fc +3cccec32310040161d17100a000714039511091616001a950d8c0400811e10e432f4ec11392f3c3cec323211393939393001 +b61f1f8f1f9f1f035d133311211133113315231510002120001135233533052115141633323635b2cb02e1cba5a5fedffee6 +fee5fedfa6a603acfd1faec3c2ae05d5fd96026afd96a496fedcfed6012a012496a4a47df0d3d3f0ffff00100000056805d5 +100603300000000300c9ff42048b069300130017001b00000133073315230321152103211521072337231121011323110113 +211103b8aa41589297010afebcb9022efd9841aa41b002aefe3cb9d9011397fe560693beaafe46aafde3aabebe05d5fad502 +1dfde302c701bafe460000040071ff42047f051e00050026002d00310000012627262703051521031633323637150e012322 +27072313262726111000333217373307161716051326232206071b01231603c702530e106f019afe2b944a616ac76263d06b +7b6350aa6d211c9d0129fc383147aa5c392f83fdbc8714169ab90e5a6fcf0b0294975a100dfef2365afe971c3434ae2a2c21 +c20109171d9c010a0113014309ace0223292c5014a02ae9efe63010eac000001ff96fe66025205d500130059401f0b02070c +010c95120f14079505b010811400110d0508063901111c0c10041410fc4bb0105458b90010004038593cec32e43939c410c4 +310010e4fcec10d43cec32111239393001400d30154015501560158f159f15065d01231110062b0135333236351123353311 +3311330252bfcde34d3f866ebfbfcabf0277fdf1fef2f4aa96c2020fa602b8fd48000002ffdbfe56021c0614001300170053 +402417be14b1180f060b000b8709bd180213a9051000bc180c18090a4f15050108141000461810fc3c3cec3232e439123931 +0010e4dc3ce43210f4ec1112393910f4ec30400b1019401950196019701905015d1333113315231114062b01353332363511 +23353311331523c1b8a3a3a3b54631694cb5b5b8b80460fe08a4fe28d6c09c619901d8a403ace90000020073fe6606b005f1 +0018002400434024030c0d069509b025229500161c950d108c169101af25090608021f0d001c02191913102510fcecd4ec32 +3210cc3939310010ece4f4c4ec10c4ee10e4ec113939300135331114163b011523222611350e012320001110002132160110 +1233321211100223220204b3c46e86454de3cd4deca5fef2feac0154010ea5ecfcdfeacccdebebcdccea04ede8fa93c296aa +f4010e7f848001ab015c015c01ab80fd78fee3febb0145011d011d0145febb0000020071fe560540047b0018002400484022 +188700bd2522b9110e1cb905088c0eb812bc25011718131f041108134719120b452510fcecf4ec323210cc3939310010ece4 +f4c4ec10c4ee10f4ec30b660268026a02603015d012322263d010e012322021110003332161735331114163b010114163332 +36353426232206054046b5a33ab17ccbff00ffcb7cb13ab84c6931fbefa79292a8a89292a7fe56c0d6bc6461014401080108 +01446164aafb8c9961033dcbe7e7cbcbe7e70002000a0000055405d50017002000bb4018050603150900201a12050a1d1904 +153f180a1c0e110c042110fc3cccec32fcc4ec1117391139393931004021090807030a061103040305110404034206040019 +03041019950d09189511810b042f3cf4ecd432ec32123912391239304b5358071005ed071005ed1117395922b2402201015d +40427a1701050005010502060307041500150114021603170425002501250226032706260726082609202236013602460146 +02680575047505771788068807980698071f5d005d011e01171323032e012b01112311233533112120161514060111333236 +35342623038d417b3ecdd9bf4a8b78dccabfbf01c80100fc83fd89fe9295959202bc16907efe68017f9662fd890277a602b8 +d6d88dba024ffdee878383850001000e0000034a047b0018003d400a0a18030806120804461910fc3cc4c4fc3c3c31004010 +12110b15870eb8030818a9050209bc032fe4d43cec3210f4ecc4d4cc30b4501a9f1a02015d0115231123112335331133153e +013332161f012e0123220615021eabb9acacb93aba85132e1c011f492c9ca70268a4fe3c01c4a401f8ae66630505bd1211ce +a1000002fff6000004ec05d500110014000003331721373307331521011123110121353305211704d997020c96d9979cfef5 +fef6cbfef6fef49d0277fed19805d5e0e0e0a4fe76fd3902c7018aa4a4e20002000bfe5604b504600018001b0000050e012b +01353332363f0103213533033313211333033315212b011302934e947c936c4c543321cdfed6f0bec3b8014cb8c3b9effed7 +c1da6d68c87a9a48865401f28f01cdfe3301cdfe338ffef000020071ffe3047f047b0014001b004140240015010986088805 +01a91518b91215bb05b90cb8128c1c02151b1b080f4b15120801451c10fcc4ecf4ec111239310010e4f4ece410ee10ee10f4 +ee111239301335212e0123220607353e01332000111000232200371e013332363771034e0ccdb76ac76263d06b010c0139fe +d7fce2fef9b802a5889ab90e02005abec73434ae2a2cfec8fef6feedfebd0123c497b4ae9e0000010058fe4c042f04600020 +00a9400a1b1f151222061e1f0e2110dcd4c4d4c4ec10ccb2001f1b1112393140161b4200a91a1a1e211da91e0e860d9311b9 +09bd1ebc210010e4fcecf4ec10ec1112392fececb315060009111239393040081b11001c11201a1f070510ec0410ec401b0c +1c0a001b1c19002a1c2a0038003b1c49004c1c54005b1c71000d015d401b041b0420141b1420251b24203520371b4520461b +54205c1b7f1b0d005d4009070b060c1a0c1a0f045d0132171617161514042122272627351e0133323736353427262b013501 +21352115023c6a80625651fed0fee85e63646a54c86dbe63645c5da5ae01aefd65036a01dc382a6d688addf2121325c33132 +4b4b8f844b4aa601f393a800ffff00b203fe01d705d510060afb0000000100c104ee033f066600060037400c040502b400b3 +07040275060710dcec39310010f4ec323930004bb009544bb00e545b58bd0007ffc000010007000700403811373859013313 +2327072301b694f58bb4b48b0666fe88f5f5000100c104ee033f066600060037400c0300b40401b307030575010710dcec39 +310010f43cec3930004bb009544bb00e545b58bd0007ffc0000100070007004038113738590103331737330301b6f58bb4b4 +8bf504ee0178f5f5fe88000100c7052903390648000d0057400e0bf0040700b30e0756080156000e10dcecd4ec310010f43c +d4ec30004bb0095458bd000effc00001000e000e00403811373859004bb00f544bb010545b4bb011545b58bd000e00400001 +000e000effc0381137385913331e0133323637330e01232226c7760b615756600d760a9e91919e06484b4b4a4c8f90900002 +00ee04e103120706000b00170020401103c115f209c10ff11800560c780656121810d4ecf4ec310010f4ecf4ec3001342623 +2206151416333236371406232226353436333216029858404157574140587a9f73739f9f73739f05f43f5857404157584073 +a0a073739f9f0001014cfe7502c1000000130020400f0b0e0a07f30ef40001000a0427111410d4ecc4d4cc31002ffcfcc412 +393021330e0115141633323637150e0123222635343601b8772d2b3736203e1f26441e7a73353d581f2e2e0f0f850a0a575d +3069000100b6051d034a0637001b006340240012070e0b040112070f0b0412c3190704c3150bed1c0f010e00071556167707 +5608761c10f4ecfcec1139393939310010fc3cfcd43cec11123911123911123911123930004bb009544bb00c545b58bd001c +ffc00001001c001c0040381137385901272e0123220607233e013332161f011e0133323637330e0123222601fc3916210d26 +24027d02665b2640253916210d2624027d02665b2640055a371413495287931c21371413495287931c00000200f004ee03ae +066600030007004240110602b40400b3080407030005010305070810d4dcd4cc1139111239310010f43cec3230004bb00954 +4bb00e545b58bd0008ffc000010008000800403811373859013303230333032302fcb2f88781aadf890666fe880178fe8800 +0002fda2047bfe5a0614000300040025400c02be00b104b805040108000510d4ec39310010e4fcec30000140070404340444 +04035d0133152317fda2b8b85e0614e9b0000002fcc5047bff43066600060007003c400f0300b40401b307b8080703057501 +0810dcec3939310010e4f43cec3930004bb009544bb00e545b58bd0007ffc000010007000700403811373859010333173733 +0307fdbaf58bb4b48bf54e04ee0178f5f5fe88730002fc5d04eeff1b066600030007004240110602b40400b3080405010007 +030107050810d4dcd4cc1139111239310010f43cec3230004bb009544bb00e545b58bd0008ffc00001000800080040381137 +38590113230321132303fd0fcd87f80200be89df0666fe880178fe8801780001fcbf0529ff310648000c0018b50756080156 +002fecd4ec3100b40af00400072f3cdcec3003232e0123220607233e012016cf760b615756600d760a9e01229e05294b4b4a +4c8f90900001fe1f03e9ff4405280003000a40030201040010d4cc3001231333fef2d3a48103e9013f000001fef0036b007b +04e000130031400607560e0411002f4bb00c544bb00d545b4bb00e545b58b9000000403859dc32dcec310040050a04c10011 +2fc4fccc3001351e0133323635342627331e01151406232226fef03d581f2e2e0f0f850a0a575d306903d7772d2b3736203e +1f26441e7a7335000001fd6afe14fe8fff540003000a40030300040010d4cc3005330323fdbcd3a481acfec0000100100000 +056805d50006003c400b420695028105010804010710d4c4c431002f3cf4ec304b5358401206110302010511040403061102 +0011010102050710ec10ec0710ec0810ec5933230133012301e5d5023ae50239d2fe2605d5fa2b050e00000100c90000048b +05d5000b00464011420a06950781000495030d01080407040c10fc3cd43ccc31002fec32f4ec32304b535840120b11050504 +0a110606050b11050011040504050710ec10ec0710ec0810ec5925211521350901352115210101b102dafc3e01dffe2103b0 +fd3801dfaaaaaa02700211aaaafdf300000100bafe560464047b00150031401606870e12b80cbc02bd0b17460308004e090d +080c461610fcec32f4ecec31002fece4f4c4ec304005a017801702015d011123113426232206151123113315363736333217 +160464b87c7c95acb9b942595a75c1636302a4fbb204489f9ebea4fd870460ae653232777800000200c9000004ec05d50008 +0015002e400c17090019102e040b1c15041610fcec32f4ecc4cc3100400c0b9515811404950cad0595142fecf4ec10f4ec30 +0134262321112132361315211121320415140429011104179da3febc0144a39d6cfd10014efb0110fef9fefcfde801b78b87 +fddd8704a8a6fe40dadeddda05d5000100b203fe01d705d500050018400b039e00810603040119000610dcecd4cc310010f4 +ec300133150323130104d3a4815205d598fec1013f000001ffb9049a00c706120003000a40030003040010d4cc3011330323 +c775990612fe88000002fcd7050eff2905d90003000700a5400d0400ce0602080164000564040810d4fcdcec310010d43cec +3230004bb00e544bb011545b58bd00080040000100080008ffc03811373859014bb00e544bb00d545b4bb017545b58bd0008 +ffc000010008000800403811373859014bb011544bb019545b58bd00080040000100080008ffc03811373859004bb0185458 +bd0008ffc00001000800080040381137385940116001600260056006700170027005700608015d0133152325331523fe5ecb +cbfe79cbcb05d9cbcbcb0001fd7304eefef005f60003007f40110203000301000003420002fa040103030410c410c0310010 +f4cc304b5358071005c9071005c95922004bb00c5458bd0004ffc000010004000400403811373859004bb00e5458bd000400 +40000100040004ffc03811373859402006021502250125023602460256026a016702090f000f011f001f012f002f01065d01 +5d01330323fe37b9e49905f6fef80001fcb6050eff4a05e9001d0075402116100f03130c0701000308170cc30413c31b08fa +1e10010f00071656180756091e10d4ecd4ec1139393939310010f43cecd4ec321217391112173930004bb00c5458bd001eff +c00001001e001e00403811373859004bb00e5458bd001e00400001001e001effc03811373859b4100b1f1a025d01272e0123 +22061d012334363332161f011e013332363d01330e01232226fdfc39191f0c24287d6756243d303917220f20287d02675422 +3b0539210e0b322d066576101b1e0d0c3329066477100001fd0c04eefe8b05f60003008940110102030200030302420001fa +040103030410c410c0310010f4cc304b5358071005c9071005c95922004bb00c5458bd0004ffc00001000400040040381137 +3859004bb00e5458bd00040040000100040004ffc03811373859402a06000601160012012400240135014301550055019f00 +9f01af00af010e0f000f031f001f032f002f03065d015d01132303fdc7c499e605f6fef801080001fccf04eeff3105f80006 +0077400a04000502fa070402060710d4c439310010f43cc43930004bb00c5458bd0007ffc000010007000700403811373859 +004bb00e5458bd00070040000100070007ffc03811373859014bb00e5458bd0007ffc0000100070007004038113738594013 +0f000f010c041f001f011d042f002f012d0409005d01331323270723fda2bcd38ba6a68b05f8fef6b2b20001fccf04eeff31 +05f800060086400a03040100fa070305010710d4c439310010f4c4323930004bb00c544bb009545b4bb00a545b4bb00b545b +58bd0007ffc000010007000700403811373859004bb00e5458bd00070040000100070007ffc03811373859014bb00e5458bd +0007ffc000010007000700403811373859401300000303000610001203100620002203200609005d01033317373303fda2d3 +8ba6a68bd304ee010ab2b2fef6000001fcc70506ff3905f8000d000003232e0123220607233e01333216c7760d6353526110 +760aa08f909f050636393738777b7a000001fcc70506ff3905f8000d006a400e070004c30bfa0e0756080156000e10d4ecd4 +ec310010f4fccc3230004bb00c5458bd000effc00001000e000e00403811373859004bb00e5458bd000e00400001000e000e +ffc03811373859014bb00e544bb00f545b58bd000effc00001000e000e0040381137385901331e0133323637330e01232226 +fcc7760d6353526110760aa08f909f05f836393738777b7a0001fd9a050efe6605db00030047b700ce02040164000410d4ec +310010d4ec30004bb00e544bb011545b58bd00040040000100040004ffc03811373859004bb0185458bd0004ffc000010004 +00040040381137385901331523fd9acccc05dbcd0002fce604eeffb205f600030007001340070004030708000410cc310010 +d43ccc32300133032303330323fef9b9e4998bb9e49905f6fef80108fef80002fc4e04eeff1a05f600030007000001132303 +21132303fd07c499e40208c499e405f6fef80108fef80108000100d5fe56052705d50013004a402111110102010211101110 +420b950a11020300af10130b100111021c0436111c001410dcecfcec113939cc31002f3cec323939dcec304b5358071004ed +071004ed5922b21f1501015d1333011133111407062b01353332373635011123d5b802e2b85251b5fee9692626fd1eb805d5 +fb83047dfa17d660609c3031ad047dfb8300ffff0192066303e808331027007100bd023d1007171604bc0155ffff0192065e +03e808331027171e04bc01501007007100bd023dffff0193066303e5085a1027171704f002641007171604bc0155ffff0193 +066303e5085a10271719048c02641007171604bc0155ffff0176066a040a08331027171804c0015c1007007100bd023dffff +018b066303ed085a1027171b04bc02621007171604bc0155000100000002599902d562eb5f0f3cf5001f080000000000d17e +0ee400000000d17e0ee4f7d6fc4c0e5909dc00000008000000000000000000010000076dfe1d00000efef7d6fa510e590001 +0000000000000000000000000000175104cd00660000000002aa0000028b0000033501350000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000057900100000000005960073062900c9050e00c90000000006330073060400c9025c00c9025cff96 +053f00c9047500c90000000005fc00c9064c00730000000000000000058f00c90514008704e3fffa05db00b20000000007e9 +00440000000004e3fffc057b005c0000000000000000000000000000000000000000040000aa04e7007b0000000004660071 +0514007104ec00710000000005140071051200ba023900c10239ffdb04a200ba023900c100000000051200ba04e500710000 +000000000000034a00ba042b006f03230037051200ae00000000068b00560000000004bc003d043300580000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000040000d7000000000000 +000000000000000000000000000000000000040000d500000000000000000000000000000000040001730000000000000000 +028b00db04000123000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000579001007cb000805960073050e00c9050e00c9050e00c9050e00c9025c003b025c00a2025cfffe025c0006 +0633000a05fc00c9064c0073064c0073064c0073064c0073064c007306b40119064c006605db00b205db00b205db00b205db +00b204e3fffc04d700c9050a00ba04e7007b04e7007b04e7007b04e7007b04e7007b04e7007b07db007b0466007104ec0071 +04ec007104ec007104ec00710239ffc7023900900239ffde0239fff404e50071051200ba04e5007104e5007104e5007104e5 +007104e5007106b400d904e50048051200ae051200ae051200ae051200ae04bc003d051400ba04bc003d0579001004e7007b +0579001004e7007b0579001004e7007b05960073046600710596007304660071059600730466007105960073046600710629 +00c9051400710633000a05140071050e00c904ec0071050e00c904ec0071050e00c904ec0071050e00c904ec0071050e00c9 +04ec00710633007305140071063300730514007106330073051400710633007305140071060400c90512ffe5075400c9058f +0078025cffe40239ffd3025c00030239fff2025cfff50239ffe4025c00b002390096025c00c9023900c104b800c9047200c1 +025cff960239ffdb053f00c904a200ba04a200ba047500c9023900c1047500c902390088047500c9030000c1047500c902bc +00c1047ffff20246000205fc00c9051200ba05fc00c9051200ba05fc00c9051200ba068200cd05fc00c9051200ba064c0073 +04e50071064c007304e50071064c007304e50071088f0073082f0071058f00c9034a00ba058f00c9034a0082058f00c9034a +00ba05140087042b006f05140087042b006f05140087042b006f05140087042b006f04e3fffa0323003704e3fffa03230037 +04e3fffa0323003705db00b2051200ae05db00b2051200ae05db00b2051200ae05db00b2051200ae05db00b2051200ae05db +00b2051200ae07e90044068b005604e3fffc04bc003d04e3fffc057b005c04330058057b005c04330058057b005c04330058 +02d1002f0514002005e1ff97057d00c9051400ba057d00000514000005a0007305960073046600710633000a068dff97057d +00c90514007104e50071050e0083064c007504ea00a4049aff9602d1ff7f06330073057e000807df00ba02d400c9025c000a +05f700c904a200b90239000a04bc003d07cb00b205fcff96051200ba064c0073074e006704e5007607970073061300710537 +ff97051400b9058f00c905140072042b0064050e00c902b0fef20323003704e300180323003704e3fffa06dd00ad051200b0 +061d004e05c400c905f3fffc05d8003d057b005c04330058055400a00554005c049f006804330071051700960554005d049f +006804150058051400ba025c00c903f000c903ac0014025d00c90b6000c90a6400c9093c007106af00c9064b00c903a700c1 +077300c9076400c9066100ba0579001004e7007b025cfffe0239ffe0064c007304e5007105db00b2051200ae05db00b20512 +00ae05db00b2051200ae05db00b2051200ae05db00b2051200ae04ec00710579001004e7007b0579001004e7007b07cb0008 +07db007b06330073051400710633007305140071053f00c904a2ffe9064c007304e50071064c007304e50071055400a0049f +00580239ffdb0b6000c90a6400c9093c0071063300730514007108e700c9057500c905fc00c9051200ba0579001004e7007b +07cb000807db007b064c006604e500480579001004e7007b0579001004e7007b050e00c904ec0071050e00c904ec0071025c +ffa70239ffc3025c00050239ffe3064c007304e50071064c007304e50071058f00c7034a0082058f00c9034a00ba05db00b2 +051200ae05db00b2051200ae05140087042b006f04e3fffa032300370504009c042c0047060400c90512fff005e200c906b4 +00710596007104e20071057b005c043300580579001004e7007b050e00c904ec0071064c007304e50071064c007304e50071 +064c007304e50071064c007304e5007104e3fffc04bc003d03cc008a06be00ba03d100370239ffdb07fc007107fc00710579 +fffd0596000c046600090475000a04e3ffb2042b006f0433005804d3005003d50050057d000a05db000c05790010050e00c9 +04ec0071025cff960239ffdb0640007305140071058f000a034a000e04e3fff604bc000b0000000000000000000000000000 +0000000000000000000000000000000000000000000004ec0071000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +049f005800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000028b00b200000000000000000000000000000000000000000000000000000000 +0000000000000000040000c1040000c100000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000040000c700000000040000ee0400014c040000b6 +040000f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000fda2000000000000000000000000000000000000fcc500000000000000000000fc5d000000000000fcbf0000fe1f0000 +0000000000000000000000000000000000000000000000000000000000000000fef000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000fd6a00000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000579001000000000000000000000000000000000 +0000000000000000050e00c90000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000051200ba0000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057d +00c9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000028b00b2000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000><0000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000ffb90000fcd70000fd730000fcb60000fd0c0000fccf0000fccf0000fcc70000fcc70000fd9a0000fce60000 +fc4e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +05fc00d500000000000000000578019200000000057801920578019305780193000000000000000005780176018b00000000 +0000000000000000003100310031003100310031003100310031003100310031003100310031003100310031003100310031 +0031003100310031003100310031003100310031003100ae00ae00f901380167016701ba01e8020c024302d602f802f8034c +039103910391041b049704cf0511051105ee05ee064f06ae06ae06ae06ae06ae06ae06d6076c076c07b70803086d086d08d1 +090d0935097209ea0a080a080a440a950a950a950acc0b7b0bb80bfa0bfa0d0c0d0c0df10e570e570e570e570e570e570e57 +0e570e570e570e570e570e570eb30eb30eb30eb30eb30eb30eb30ed80ed80ed80ed80ed80eff0eff0eff0f140f440f440f44 +0f440f440f440f440f440f440f440f440f440f440fe4104f105b106710731084109610a210ae10bf10d01135114c11581164 +1179119211a9120d12a912b512c112d812f312ff134213d513e713f91409141f143b145a15371543154f155b156c157d1589 +159515a615b7168f169b16a616b116c116d716ed171b17d517e017eb17fb1813181e186d1884189918ad18c318d318df18eb +18f7190319151921192d1939194a195619621975197d19db19e719f81a091a1a1a261a321a3e1a4a1a5b1a701a821a931a9f +1aab1abc1ac81ad41ae01af71b191b5c1ba61bb71bc81bd91bea1bfb1c0c1c181c241c3b1c611c721c831c941ca51cb11cbd +1d351d411d5d1d691d7a1d861d981da41dbd1dfa1e421e531e641e701e7c1e931ea81eb41eff1f4d1f621f721f871f971fa3 +1faf1ffe2092209e20a920b520c120d220e620f220fd21102122212e2139214c215f216a2175218a219b21dc2229223e224f +22662277228c229d22a922ba22c622d222de22ea22fb230c231d232d233e234a2355236123752381239523c12427248a2492 +24ec2532258525cd262d268a269226dc271a276d27d92807285e28ba28f9295429b92a432aaa2ad72b0f2b6d2bf62c252c99 +2cf92d602d682db92dc52dd12e242e792ec42f242f822fec308f309730e43130317831c5320b32173223327932bf331c3404 +3488350d357d35e4366b36a136da3723376937a237ec380c38183857385f386b38773883388f389b38a738b338bf38cb38db +38eb38fe3911391d392c393c394e395939653970397c39873993399e39aa39b239bd39c939d439e039ec39f83a613adb3af0 +3afb3b073b293b353b413b4d3b583b643b6f3b823b8e3b9a3ba63bb23bbd3c083c533c5f3c6b3c773c833c8f3c9b3ca73cb2 +3cbe3cca3cd63ce23cee3cfa3d063d123d1e3d2a3d363d423d4e3d5a3d663d723d7e3d8a3d963da23dae3dba3dc63dd23dde +3dea3df63e023e483e913e9d3ebf3ef83f4a3fd0404240b74133413f414b41574162416d417941844190419c41a841b341bf +41cb41d642004241427542a74317438943c0440b4452448844b1450d4538457a45bd462b468b469346c7471c476947b74817 +48744907494d497449a349a349a349a349a349a349a349a349a349a349f549f549f549f549f549f549f549f549f549f549f5 +49f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f5 +49f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f549f54a7e4a7e4a7e4a7e +4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e +4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a7e4a864a864a864a864a864a864a864a864a864a864ab34ae1 +4ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14ae14b264b264b5c4b8c4beb4c214c214c214c21 +4c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c214c434c434c434c43 +4c434c764c764c764cad4cad4cd24ce54ce54ce54ce54ce54ce54ce54ce54ce54d1f4d1f4d1f4d1f4d1f4d1f4d1f4d1f4d1f +4d1f4d1f4d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d31 +4d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d31 +4d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d31 +4d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d314d624d624d624d624d624d624d624da04da0 +4da04da04da04da04da04da04da04da04da04da04da04da04da04da04da04da04da04da04ddd4ddd4ddd4ddd4ddd4ddd4ddd +4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd +4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd +4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd +4ddd4ddd4ddd4ddd4ddd4ddd4ddd4ddd4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c +4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e1c4e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e394e39 +4e394e394e394e394e394e394e394e394e394e4b4eb04efd4f654fb85005505b507550c450f4511251285128512851285128 +5128512851285128512851285128512851285128512851285128512851285128512851285128512851285128512851285128 +51285128512851285128512851285128517051705170517d517d518a519751a451a451a451b151be00000001000017520354 +002b0068000c00020010009900080000041502160008000400000007005a0003000104090000013000000003000104090001 +0016013000030001040900020008014600030001040900030016013000030001040900040016013000030001040900050018 +014e0003000104090006001401660043006f0070007900720069006700680074002000280063002900200032003000300033 +002000620079002000420069007400730074007200650061006d002c00200049006e0063002e00200041006c006c00200052 +00690067006800740073002000520065007300650072007600650064002e000a0043006f0070007900720069006700680074 +0020002800630029002000320030003000360020006200790020005400610076006d006a006f006e00670020004200610068 +002e00200041006c006c0020005200690067006800740073002000520065007300650072007600650064002e000a00440065 +006a0061005600750020006300680061006e006700650073002000610072006500200069006e0020007000750062006c0069 +006300200064006f006d00610069006e000a00440065006a006100560075002000530061006e00730042006f006f006b0056 +0065007200730069006f006e00200032002e0033003500440065006a00610056007500530061006e00730002000000000000 +ff7e005a000000000000000000000000000000000000000017520000000100020003000400050006000700080009000a000b +000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f00200021002200230024 +00250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d +003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056 +005700580059005a005b005c005d005e005f0060006100ac00a30084008500bd009600e80086008e008b009d00a900a40102 +008a00da0083009300f200f3008d0097008800c300de00f1009e00aa00f500f400f600a200ad00c900c700ae006200630090 +006400cb006500c800ca00cf00cc00cd00ce00e9006600d300d000d100af006700f0009100d600d400d5006800eb00ed0089 +006a0069006b006d006c006e00a0006f0071007000720073007500740076007700ea0078007a0079007b007d007c00b800a1 +007f007e0080008100ec00ee00ba01030104010501060107010800fd00fe0109010a010b010c00ff0100010d010e010f0101 +0110011101120113011401150116011701180119011a011b00f800f9011c011d011e011f0120012101220123012401250126 +012701280129012a012b00fa00d7012c012d012e012f0130013101320133013401350136013701380139013a00e200e3013b +013c013d013e013f014001410142014301440145014601470148014900b000b1014a014b014c014d014e014f015001510152 +015300fb00fc00e400e5015401550156015701580159015a015b015c015d015e015f01600161016201630164016501660167 +0168016900bb016a016b016c016d00e600e7016e016f0170017101720173017401750176017701780179017a017b017c017d +017e017f018000a6018101820183018401850186018701880189018a018b018c018d018e018f019001910192019301940195 +0196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae +01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c7 +01c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e0 +01e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f9 +01fa01fb01fc01fd01fe01ff0200020102020203020402050206020702080209020a020b020c020d020e020f021002110212 +0213021402150216021702180219021a021b021c021d021e021f0220022102220223022402250226022702280229022a022b +022c022d022e022f0230023102320233023402350236023702380239023a023b023c023d023e023f02400241024202430244 +02450246024702480249024a024b024c024d024e024f0250025102520253025402550256025702580259025a025b025c025d +025e025f0260026102620263026402650266026702680269026a026b026c026d026e026f0270027102720273027402750276 +027702780279027a027b027c027d027e027f0280028102820283028402850286028702880289028a028b028c028d028e028f +0290029102920293029402950296029702980299029a029b029c029d029e029f02a002a102a202a302a402a502a602a702a8 +02a902aa02ab02ac02ad02ae02af02b002b102b202b300d800e102b402b502b602b702b802b902ba02bb02bc02bd02be02bf +02c002c102c202c300db00dc00dd00e000d900df02c402c502c602c702c802c902ca02cb02cc02cd02ce02cf02d002d102d2 +02d302d402d502d602d702d802d902da02db02dc02dd02de02df02e002e102e202e302e402e502e602e702e802e902ea02eb +02ec02ed02ee02ef02f002f102f202f302f402f502f602f702f802f902fa02fb02fc02fd02fe02ff03000301030203030304 +03050306030703080309030a030b030c030d030e030f0310031103120313031403150316031703180319031a031b031c031d +031e031f0320032103220323032403250326032703280329032a032b032c032d032e032f0330033103320333033403350336 +033703380339033a033b033c033d033e033f0340034103420343034403450346034703480349034a034b034c034d034e034f +0350035103520353035403550356035703580359035a035b035c035d035e035f0360009f0361036203630364036503660367 +03680369036a036b036c036d036e036f0370037103720373037403750376009b037703780379037a037b037c037d037e037f +0380038103820383038403850386038703880389038a038b038c038d038e038f039003910392039303940395039603970398 +0399039a039b039c039d039e039f03a003a103a203a303a403a503a603a703a803a903aa03ab03ac03ad03ae03af03b003b1 +03b203b303b403b503b603b703b803b903ba03bb03bc03bd03be03bf03c003c103c203c303c403c503c603c703c803c903ca +03cb03cc03cd03ce03cf03d003d103d203d303d403d503d603d703d803d903da03db03dc03dd03de03df03e003e103e203e3 +03e403e503e603e703e803e903ea03eb03ec03ed03ee03ef03f003f103f203f303f403f503f603f703f803f903fa03fb03fc +03fd03fe03ff0400040104020403040404050406040704080409040a040b040c040d040e040f041004110412041304140415 +0416041704180419041a041b041c041d041e041f0420042104220423042404250426042704280429042a042b042c042d042e +042f0430043104320433043404350436043704380439043a043b043c043d043e043f04400441044204430444044504460447 +04480449044a044b044c044d044e044f0450045104520453045404550456045704580459045a045b045c045d045e045f0460 +046104620463046404650466046704680469046a046b046c046d046e046f0470047104720473047404750476047704780479 +047a047b047c047d047e047f0480048104820483048404850486048704880489048a048b048c048d048e048f049004910492 +0493049404950496049704980499049a049b049c049d049e049f04a004a104a204a304a404a504a604a704a804a904aa04ab +04ac04ad04ae04af04b004b104b204b304b404b504b604b704b804b904ba04bb04bc04bd04be04bf04c004c104c204c304c4 +04c504c604c704c804c904ca04cb04cc04cd04ce04cf04d004d104d204d304d404d504d604d704d804d904da04db04dc04dd +04de04df04e004e104e204e304e404e504e604e704e804e904ea04eb04ec04ed04ee04ef04f004f104f204f304f404f504f6 +04f704f804f904fa04fb04fc04fd04fe04ff0500050105020503050405050506050705080509050a050b050c050d050e050f +0510051105120513051405150516051705180519051a051b051c051d051e051f052005210522052305240525052605270528 +0529052a052b052c052d052e052f0530053105320533053405350536053705380539053a053b053c053d053e053f05400541 +05420543054405450546054705480549054a054b054c054d054e054f0550055105520553055405550556055705580559055a +055b055c055d055e055f0560056105620563056405650566056705680569056a056b056c056d056e056f0570057105720573 +057405750576057705780579057a057b057c057d057e057f0580058105820583058405850586058705880589058a058b058c +058d058e058f0590059105920593059405950596059705980599059a059b059c059d059e059f05a005a105a205a305a405a5 +05a605a705a805a905aa05ab05ac05ad05ae05af05b005b105b205b305b405b505b605b705b805b905ba05bb05bc05bd05be +05bf05c005c105c205c305c405c505c605c705c805c905ca05cb05cc05cd05ce05cf05d005d105d205d305d405d505d605d7 +05d805d905da05db05dc05dd05de05df05e005e105e205e305e405e505e605e705e805e905ea05eb05ec05ed05ee05ef05f0 +05f105f205f305f405f505f605f705f805f905fa05fb05fc05fd05fe05ff0600060106020603060406050606060706080609 +060a060b060c060d060e060f0610061106120613061406150616061706180619061a061b061c061d061e061f062006210622 +0623062406250626062706280629062a062b062c062d062e062f0630063106320633063406350636063706380639063a063b +063c063d063e063f0640064106420643064406450646064706480649064a064b064c064d064e064f06500651065206530654 +06550656065706580659065a065b065c065d065e065f0660066106620663066406650666066706680669066a066b066c066d +066e066f0670067106720673067406750676067706780679067a067b067c067d067e067f0680068106820683068406850686 +068706880689068a068b068c068d068e068f0690069106920693069406950696069706980699069a069b069c069d069e069f +06a006a106a206a306a406a506a606a706a806a906aa06ab06ac06ad06ae06af06b006b106b206b306b406b506b606b706b8 +06b906ba06bb06bc06bd06be06bf06c006c106c206c306c406c506c606c706c806c906ca06cb06cc06cd06ce06cf06d006d1 +06d206d306d406d506d606d706d806d906da06db06dc06dd06de06df06e006e106e206e306e406e506e606e706e806e906ea +06eb06ec06ed06ee06ef06f006f106f206f306f406f506f606f706f806f906fa06fb06fc06fd06fe06ff0700070107020703 +070407050706070707080709070a070b070c070d070e070f0710071107120713071407150716071707180719071a071b071c +071d071e071f0720072107220723072407250726072707280729072a072b072c072d072e072f073007310732073307340735 +0736073707380739073a073b073c073d073e073f0740074107420743074407450746074707480749074a074b074c074d074e +074f0750075107520753075407550756075707580759075a075b075c075d075e075f07600761076207630764076507660767 +07680769076a076b076c076d076e076f0770077107720773077407750776077707780779077a077b077c077d077e077f0780 +078107820783078407850786078707880789078a078b078c078d078e078f0790079107920793079407950796079707980799 +079a079b079c079d079e079f07a007a107a207a307a407a507a607a707a807a907aa07ab07ac07ad07ae07af07b007b107b2 +07b307b407b507b607b707b807b907ba07bb07bc07bd07be07bf07c007c107c207c307c407c507c607c707c807c907ca07cb +07cc07cd07ce07cf07d007d107d207d307d407d507d607d707d807d907da07db07dc07dd07de07df07e007e107e207e307e4 +07e507e607e707e807e907ea07eb07ec07ed07ee07ef07f007f107f207f307f407f507f607f707f807f907fa07fb07fc07fd +07fe07ff0800080108020803080408050806080708080809080a080b080c080d080e080f0810081108120813081408150816 +081708180819081a081b081c081d081e081f0820082108220823082408250826082708280829082a082b082c082d082e082f +0830083108320833083408350836083708380839083a083b083c083d083e083f084008410842084308440845084608470848 +0849084a084b084c084d084e084f0850085108520853085408550856085708580859085a085b085c085d085e085f08600861 +08620863086408650866086708680869086a086b086c086d086e086f0870087108720873087408750876087708780879087a +087b087c087d087e087f0880088108820883088408850886088708880889088a088b088c088d088e088f0890089108920893 +089408950896089708980899089a089b089c089d089e089f08a008a108a208a308a408a508a608a708a808a908aa08ab08ac +08ad08ae08af08b008b108b208b308b408b508b608b708b808b908ba08bb08bc08bd08be08bf08c008c108c208c308c408c5 +08c608c708c808c908ca08cb08cc08cd08ce08cf08d008d108d208d308d408d508d608d708d808d908da08db08dc08dd08de +08df08e008e108e208e308e408e508e608e708e808e908ea08eb08ec08ed08ee08ef08f008f108f208f308f408f508f608f7 +08f808f908fa08fb08fc08fd08fe08ff0900090109020903090409050906090709080909090a090b090c090d090e090f0910 +091109120913091409150916091709180919091a091b091c091d091e091f0920092109220923092409250926092709280929 +092a092b092c092d092e092f0930093109320933093409350936093709380939093a093b093c093d093e093f094009410942 +0943094409450946094709480949094a094b094c094d094e094f0950095109520953095409550956095709580959095a095b +095c095d095e095f0960096109620963096409650966096709680969096a096b096c096d096e096f09700971097209730974 +09750976097709780979097a097b097c097d097e097f0980098109820983098409850986098709880989098a098b098c098d +098e098f0990099109920993099409950996099709980999099a099b099c099d099e099f09a009a109a209a309a409a509a6 +09a709a809a909aa09ab09ac09ad09ae09af09b009b109b209b309b409b509b609b709b809b909ba09bb09bc09bd09be09bf +09c009c109c209c309c409c509c609c709c809c909ca09cb09cc09cd09ce09cf09d009d109d209d309d409d509d609d709d8 +09d909da09db09dc09dd09de09df09e009e109e209e309e409e509e609e709e809e909ea09eb09ec09ed09ee09ef09f009f1 +09f209f309f409f509f609f709f809f909fa09fb09fc09fd09fe09ff0a000a010a020a030a040a050a060a070a080a090a0a +0a0b0a0c0a0d0a0e0a0f0a100a110a120a130a140a150a160a170a180a190a1a0a1b0a1c0a1d0a1e0a1f0a200a210a220a23 +0a240a250a260a270a280a290a2a0a2b0a2c0a2d0a2e0a2f0a300a310a320a330a340a350a360a370a380a390a3a0a3b0a3c +0a3d0a3e0a3f0a400a410a420a430a440a450a460a470a480a490a4a0a4b0a4c0a4d0a4e0a4f0a500a510a520a530a540a55 +0a560a570a580a590a5a0a5b0a5c0a5d0a5e0a5f0a600a610a620a630a640a650a660a670a680a690a6a0a6b0a6c0a6d0a6e +0a6f0a700a710a720a730a740a750a760a770a780a790a7a0a7b0a7c0a7d0a7e0a7f0a800a810a820a830a840a850a860a87 +0a880a890a8a0a8b0a8c0a8d0a8e0a8f0a900a910a920a930a940a950a960a970a980a990a9a0a9b0a9c0a9d0a9e0a9f0aa0 +0aa10aa20aa30aa40aa50aa60aa70aa80aa90aaa0aab0aac0aad0aae0aaf0ab00ab10ab20ab30ab40ab50ab60ab70ab80ab9 +0aba0abb0abc0abd0abe0abf0ac00ac10ac20ac30ac40ac50ac60ac70ac80ac90aca0acb0acc0acd0ace0acf0ad00ad10ad2 +0ad30ad40ad50ad60ad70ad80ad90ada0adb0adc0add0ade0adf0ae00ae10ae20ae30ae40ae50ae60ae70ae80ae90aea0aeb +0aec0aed0aee0aef0af00af10af20af30af40af50af60af70af80af90afa0afb0afc0afd0afe0aff0b000b010b020b030b04 +0b050b060b070b080b090b0a0b0b0b0c0b0d0b0e0b0f0b100b110b120b130b140b150b1600b200b30b170b180b1900b600b7 +00c40b1a00b400b500c50b1b008200c200870b1c0b1d0b1e00ab0b1f0b200b210b220b230b240b250b260b2700c60b280b29 +0b2a0b2b0b2c0b2d0b2e0b2f00be00bf0b300b310b320b330b340b350b360b370b3800bc0b390b3a0b3b0b3c0b3d0b3e0b3f +0b400b410b420b430b440b450b460b470b480b490b4a0b4b0b4c0b4d0b4e0b4f0b500b510b520b530b540b550b560b570b58 +0b590b5a0b5b0b5c0b5d0b5e0b5f0b600b610b620b630b640b650b660b670b680b690b6a0b6b0b6c0b6d0b6e0b6f0b700b71 +0b720b730b740b750b760b770b780b790b7a0b7b0b7c0b7d0b7e0b7f0b800b810b820b830b840b850b860b870b880b890b8a +0b8b00f70b8c0b8d0b8e0b8f0b900b910b920b930b940b950b960b970b980b990b9a0b9b0b9c0b9d0b9e0b9f0ba00ba10ba2 +0ba30ba40ba50ba60ba70ba80ba90baa0bab0bac0bad0bae0baf0bb00bb10bb20bb30bb40bb50bb60bb70bb80bb90bba0bbb +0bbc0bbd0bbe0bbf0bc00bc10bc20bc30bc40bc50bc60bc70bc80bc9008c0bca0bcb0bcc0bcd0bce0bcf0bd00bd10bd20bd3 +0bd40bd50bd60bd70bd80bd90bda0bdb0bdc0bdd0bde0bdf0be00be10be20be30be40be50be60be70be80be90bea0beb0bec +0bed0bee0bef0bf00bf10bf20bf30bf40bf50bf60bf70bf80bf90bfa0bfb0bfc0bfd0bfe0bff0c000c010c020c030c040c05 +0c060c070c080c090c0a0c0b0c0c0c0d0c0e0c0f0c100c110c120c130c140c150c160c170c180c190c1a0c1b0c1c0c1d0c1e +0c1f0c200c210c220c230c240c250c260c270c280c290c2a0c2b0c2c0c2d0c2e0c2f0c300c310c320c330c340c350c360c37 +0c380c390c3a0c3b0c3c0c3d0c3e0c3f0c400c410c420c430c440c450c460c470c480c490c4a0c4b0c4c0c4d0c4e0c4f0c50 +0c510c520c530c540c550c560c570c580c590c5a0c5b0c5c0c5d0c5e0c5f0c600c610c620c630c640c650c660c670c680c69 +0c6a0c6b0c6c0c6d0c6e0c6f0c700c710c720c730c740c750c760c770c780c790c7a0c7b0c7c0c7d0c7e0c7f0c800c810c82 +0c830c840c850c860c870c880c890c8a0c8b0c8c0c8d0c8e0c8f0c900c910c920c930c940c950c960c970c980c990c9a0c9b +00980c9c0c9d0c9e00a80c9f0ca00ca10ca20ca30ca40ca50ca6009a0ca7009900ef0ca80ca90caa0cab0cac0cad0cae00a5 +0caf0cb00cb100920cb20cb30cb40cb50cb60cb70cb80cb90cba0cbb0cbc0cbd009c0cbe0cbf0cc00cc10cc20cc30cc40cc5 +0cc60cc70cc80cc90cca0ccb0ccc0ccd0cce0ccf0cd00cd10cd20cd30cd40cd50cd60cd70cd80cd900a70cda0cdb0cdc0cdd +0cde0cdf0ce00ce10ce20ce30ce40ce50ce60ce70ce80ce90cea0ceb0cec0ced0cee0cef0cf0008f0cf10cf20cf300940095 +0cf40cf50cf60cf70cf80cf90cfa0cfb0cfc0cfd0cfe0cff0d000d010d020d030d040d050d060d070d080d090d0a0d0b0d0c +0d0d0d0e0d0f0d100d110d120d130d140d150d160d170d180d190d1a0d1b0d1c0d1d0d1e0d1f0d200d210d220d230d240d25 +0d260d270d280d290d2a0d2b0d2c0d2d0d2e0d2f0d300d310d320d330d340d350d360d370d380d390d3a0d3b0d3c0d3d0d3e +0d3f0d400d410d420d430d440d450d460d470d480d490d4a0d4b0d4c0d4d0d4e0d4f0d500d510d520d530d540d550d560d57 +0d580d590d5a0d5b0d5c0d5d0d5e0d5f0d600d610d620d630d640d650d660d670d680d690d6a0d6b0d6c0d6d0d6e0d6f0d70 +0d710d720d730d740d750d760d770d780d790d7a0d7b0d7c0d7d0d7e0d7f0d800d810d820d830d840d850d860d870d880d89 +0d8a0d8b0d8c0d8d0d8e0d8f0d900d910d920d930d940d950d960d970d980d990d9a0d9b0d9c0d9d0d9e0d9f0da00da10da2 +0da30da40da50da60da70da80da90daa0dab0dac0dad0dae0daf0db00db10db20db30db40db50db60db70db80db90dba0dbb +0dbc0dbd0dbe0dbf0dc00dc10dc20dc30dc40dc50dc60dc70dc80dc90dca0dcb0dcc0dcd0dce0dcf0dd00dd10dd20dd30dd4 +0dd50dd60dd70dd80dd90dda0ddb0ddc0ddd0dde0ddf0de00de10de20de30de40de50de60de70de80de90dea0deb0dec0ded +0dee0def0df00df10df20df30df40df50df60df70df80df90dfa0dfb0dfc0dfd0dfe0dff0e000e010e020e030e040e050e06 +0e070e080e090e0a0e0b0e0c0e0d0e0e0e0f0e100e110e120e130e140e150e160e170e180e190e1a0e1b0e1c0e1d0e1e0e1f +0e200e210e220e230e240e250e260e270e280e290e2a0e2b0e2c0e2d0e2e0e2f0e300e310e320e330e340e350e360e370e38 +0e390e3a0e3b0e3c0e3d0e3e0e3f0e400e410e420e430e440e450e460e470e480e490e4a0e4b0e4c0e4d0e4e0e4f0e500e51 +0e520e530e540e550e560e570e580e590e5a0e5b0e5c0e5d0e5e0e5f0e600e610e620e630e640e650e660e670e680e690e6a +0e6b0e6c0e6d0e6e0e6f0e700e710e720e730e740e750e760e770e780e790e7a0e7b0e7c0e7d0e7e0e7f0e800e810e820e83 +0e840e850e860e870e880e890e8a0e8b0e8c0e8d0e8e0e8f0e900e910e920e930e940e950e960e970e980e990e9a0e9b0e9c +0e9d0e9e0e9f0ea00ea10ea20ea30ea400b90ea50ea60ea70ea80ea90eaa0eab0eac0ead0eae0eaf0eb00eb10eb20eb30eb4 +0eb50eb60eb70eb80eb90eba0ebb0ebc0ebd0ebe0ebf0ec00ec10ec20ec30ec40ec50ec60ec70ec80ec90eca0ecb0ecc0ecd +0ece0ecf0ed00ed10ed20ed30ed40ed50ed60ed70ed80ed90eda0edb0edc0edd0ede0edf0ee00ee10ee20ee30ee40ee50ee6 +0ee70ee80ee90eea0eeb0eec0eed0eee0eef0ef00ef10ef20ef30ef40ef50ef60ef70ef80ef90efa0efb0efc0efd0efe0eff +0f000f010f020f030f040f050f060f070f080f090f0a0f0b0f0c0f0d0f0e0f0f0f100f110f120f130f140f150f160f170f18 +0f190f1a0f1b0f1c0f1d0f1e0f1f0f200f210f220f230f240f250f260f270f280f290f2a0f2b0f2c0f2d0f2e0f2f0f300f31 +0f320f330f340f350f360f370f380f390f3a0f3b0f3c0f3d0f3e0f3f0f400f410f420f430f440f450f460f470f480f490f4a +0f4b0f4c0f4d0f4e0f4f0f500f510f520f530f540f550f560f570f580f590f5a0f5b0f5c0f5d0f5e0f5f0f600f610f620f63 +0f640f650f660f670f680f690f6a0f6b0f6c0f6d0f6e0f6f0f700f710f720f730f740f750f760f770f780f790f7a0f7b0f7c +0f7d0f7e0f7f0f800f810f820f830f840f850f860f870f880f890f8a0f8b0f8c0f8d0f8e0f8f0f900f910f920f930f940f95 +0f960f970f980f990f9a0f9b0f9c0f9d0f9e0f9f0fa00fa10fa20fa30fa40fa50fa60fa70fa80fa90faa0fab0fac0fad0fae +0faf0fb00fb10fb20fb30fb40fb50fb60fb70fb80fb90fba0fbb0fbc0fbd0fbe0fbf0fc00fc10fc20fc30fc40fc50fc60fc7 +0fc80fc90fca0fcb0fcc0fcd0fce0fcf0fd00fd10fd20fd30fd40fd50fd60fd70fd80fd90fda0fdb0fdc0fdd0fde0fdf0fe0 +0fe10fe20fe30fe40fe50fe60fe70fe80fe90fea0feb0fec0fed0fee0fef0ff00ff10ff20ff30ff40ff50ff60ff70ff80ff9 +0ffa0ffb0ffc0ffd0ffe0fff1000100110021003100410051006100710081009100a100b100c100d100e100f101010111012 +1013101410151016101710181019101a101b101c101d101e101f1020102110221023102410251026102710281029102a102b +102c102d102e102f1030103110321033103410351036103710381039103a103b103c103d103e103f10401041104210431044 +10451046104710481049104a104b104c104d104e104f1050105110521053105410551056105710581059105a105b105c105d +105e105f1060106110621063106410651066106710681069106a106b106c106d106e106f1070107110721073107410751076 +107710781079107a107b107c107d107e107f1080108110821083108410851086108710881089108a108b108c108d108e108f +1090109110921093109410951096109710981099109a109b109c109d109e109f10a010a110a210a310a410a510a610a710a8 +10a910aa10ab10ac10ad10ae10af10b010b110b210b310b410b510b610b710b810b910ba10bb10bc10bd10be10bf10c010c1 +10c210c310c410c510c610c710c810c910ca10cb10cc10cd10ce10cf10d010d110d210d310d410d510d610d710d810d910da +10db10dc10dd10de10df10e010e110e210e310e410e510e610e710e810e910ea10eb10ec10ed10ee10ef10f010f110f210f3 +10f410f510f610f710f810f910fa10fb10fc10fd10fe10ff1100110111021103110411051106110711081109110a110b110c +110d110e110f1110111111121113111411151116111711181119111a111b111c111d111e111f112011211122112311241125 +1126112711281129112a112b112c112d112e112f1130113111321133113411351136113711381139113a113b113c113d113e +113f1140114111421143114411451146114711481149114a114b114c114d114e114f11501151115211531154115511561157 +11581159115a115b115c115d115e115f1160116111621163116411651166116711681169116a116b116c116d116e116f1170 +117111721173117411751176117711781179117a117b117c117d117e117f1180118111821183118411851186118711881189 +118a118b118c118d118e118f1190119111921193119411951196119711981199119a119b119c119d119e119f11a011a111a2 +11a311a411a511a611a711a811a911aa11ab11ac11ad11ae11af11b011b111b211b311b411b511b611b711b811b911ba11bb +11bc11bd11be11bf11c011c111c211c311c411c511c611c711c811c911ca11cb11cc11cd11ce11cf11d011d111d211d311d4 +11d511d611d711d811d911da11db11dc11dd11de11df11e011e111e211e311e411e511e611e711e811e911ea11eb11ec11ed +11ee11ef11f011f111f211f311f411f511f611f711f811f911fa11fb11fc11fd11fe11ff1200120112021203120412051206 +120712081209120a120b120c120d120e120f1210121112121213121412151216121712181219121a121b121c121d121e121f +1220122112221223122412251226122712281229122a122b122c122d122e122f123012311232123312341235123612371238 +1239123a123b123c123d123e123f1240124112421243124412451246124712481249124a124b124c124d124e124f12501251 +12521253125412551256125712581259125a125b125c125d125e125f1260126112621263126412651266126712681269126a +126b126c126d126e126f1270127112721273127412751276127712781279127a127b127c127d127e127f1280128112821283 +128412851286128712881289128a128b128c128d128e128f1290129112921293129412951296129712981299129a129b129c +129d129e129f12a012a112a212a312a412a512a612a712a812a912aa12ab12ac12ad12ae12af12b012b112b212b312b412b5 +12b612b712b812b912ba12bb12bc12bd12be12bf12c012c112c212c312c412c512c612c712c812c912ca12cb12cc12cd12ce +12cf12d012d112d212d312d412d512d612d712d812d912da12db12dc12dd12de12df12e012e112e212e312e412e512e612e7 +12e812e912ea12eb12ec12ed12ee12ef12f012f112f212f312f412f512f612f712f812f912fa12fb12fc12fd12fe12ff1300 +130113021303130413051306130713081309130a130b130c130d130e130f1310131113121313131413151316131713181319 +131a131b131c131d131e131f1320132113221323132413251326132713281329132a132b132c132d132e132f133013311332 +1333133413351336133713381339133a133b133c133d133e133f1340134113421343134413451346134713481349134a134b +134c134d134e134f1350135113521353135413551356135713581359135a135b135c135d135e135f13601361136213631364 +13651366136713681369136a136b136c136d136e136f1370137113721373137413751376137713781379137a137b137c137d +137e137f1380138113821383138413851386138713881389138a138b138c138d138e138f1390139113921393139413951396 +139713981399139a139b139c139d139e139f13a013a113a213a313a413a513a613a713a813a913aa13ab13ac13ad13ae13af +13b013b100c000c113b213b313b413b513b613b713b813b913ba13bb13bc13bd13be13bf13c013c113c213c313c413c513c6 +13c713c813c913ca13cb13cc13cd13ce13cf13d013d113d213d313d413d513d613d713d813d913da13db13dc13dd13de13df +13e013e113e213e313e413e513e613e713e813e913ea13eb13ec13ed13ee13ef13f013f113f213f313f413f513f613f713f8 +13f913fa13fb13fc13fd13fe13ff1400140114021403140414051406140714081409140a140b140c140d140e140f14101411 +14121413141414151416141714181419141a141b141c141d141e141f1420142114221423142414251426142714281429142a +142b142c142d142e142f1430143114321433143414351436143714381439143a143b143c143d143e143f1440144114421443 +144414451446144714481449144a144b144c144d144e144f1450145114521453145414551456145714581459145a145b145c +145d145e145f1460146114621463146414651466146714681469146a146b146c146d146e146f147014711472147314741475 +1476147714781479147a147b147c147d147e147f1480148114821483148414851486148714881489148a148b148c148d148e +148f1490149114921493149414951496149714981499149a149b149c149d149e149f14a014a114a214a314a414a514a614a7 +14a814a914aa14ab14ac14ad14ae14af14b014b114b214b314b414b514b614b714b814b914ba14bb14bc14bd14be14bf14c0 +14c114c214c314c414c514c614c714c814c914ca14cb14cc14cd14ce14cf14d014d114d214d314d414d514d614d714d814d9 +14da14db14dc14dd14de14df14e014e114e214e314e414e514e614e714e814e914ea14eb14ec14ed14ee14ef14f014f114f2 +14f314f414f514f614f714f814f914fa14fb14fc14fd14fe14ff1500150115021503150415051506150715081509150a150b +150c150d150e150f1510151115121513151415151516151715181519151a151b151c151d151e151f15201521152215231524 +15251526152715281529152a152b152c152d152e152f1530153115321533153415351536153715381539153a153b153c153d +153e153f1540154115421543154415451546154715481549154a154b154c154d154e154f1550155115521553155415551556 +155715581559155a155b155c155d155e155f1560156115621563156415651566156715681569156a156b156c156d156e156f +1570157115721573157415751576157715781579157a157b157c157d157e157f158015811582158315841585158615871588 +1589158a158b158c158d158e158f1590159115921593159415951596159715981599159a159b159c159d159e159f15a015a1 +15a215a315a415a515a615a715a815a915aa15ab15ac15ad15ae15af15b015b115b215b315b415b515b615b715b815b915ba +15bb15bc15bd15be15bf15c015c115c215c315c415c515c615c715c815c915ca15cb15cc15cd15ce15cf15d015d115d215d3 +15d415d515d615d715d815d915da15db15dc15dd15de15df15e015e115e215e315e415e515e615e715e815e915ea15eb15ec +15ed15ee15ef15f015f115f215f315f415f515f615f715f815f915fa15fb15fc15fd15fe15ff160016011602160316041605 +1606160716081609160a160b160c160d160e160f1610161116121613161416151616161716181619161a161b161c161d161e +161f1620162116221623162416251626162716281629162a162b162c162d162e162f16301631163216331634163516361637 +16381639163a163b163c163d163e163f1640164116421643164416451646164716481649164a164b164c164d164e164f1650 +165116521653165416551656165716581659165a165b165c165d165e165f1660166116621663166416651666166716681669 +166a166b166c166d166e166f1670167116721673167416751676167716781679167a167b167c167d167e167f168016811682 +1683168416851686168716881689168a168b168c168d168e168f1690169116921693169416951696169716981699169a169b +169c169d169e169f16a016a116a216a316a416a516a616a716a816a916aa16ab16ac16ad16ae16af16b016b116b216b316b4 +16b516b616b716b816b916ba16bb16bc16bd16be16bf16c016c116c216c316c416c516c616c716c816c916ca16cb16cc16cd +16ce16cf16d016d116d216d316d416d516d616d716d816d916da16db16dc16dd16de16df16e016e116e216e316e416e516e6 +16e716e816e916ea16eb16ec16ed16ee16ef16f016f116f216f316f416f516f616f716f816f916fa16fb16fc16fd16fe16ff +1700170117021703170417051706170717081709170a170b170c170d170e170f171017111712171317141715171617171718 +1719171a171b171c171d171e171f1720172117221723172417251726172717281729172a172b172c172d172e172f17301731 +17321733173417351736173717381739173a173b173c173d173e173f1740174117421743174417451746174717481749174a +174b174c174d174e174f1750175117520973667468797068656e07416d6163726f6e07616d6163726f6e0641627265766506 +61627265766507416f676f6e656b07616f676f6e656b0b4363697263756d666c65780b6363697263756d666c65780a43646f +74616363656e740a63646f74616363656e7406446361726f6e06646361726f6e064463726f617407456d6163726f6e07656d +6163726f6e06456272657665066562726576650a45646f74616363656e740a65646f74616363656e7407456f676f6e656b07 +656f676f6e656b06456361726f6e06656361726f6e0b4763697263756d666c65780b6763697263756d666c65780a47646f74 +616363656e740a67646f74616363656e740c47636f6d6d61616363656e740c67636f6d6d61616363656e740b486369726375 +6d666c65780b6863697263756d666c657804486261720468626172064974696c6465066974696c646507496d6163726f6e07 +696d6163726f6e064962726576650669627265766507496f676f6e656b07696f676f6e656b02494a02696a0b4a6369726375 +6d666c65780b6a63697263756d666c65780c4b636f6d6d61616363656e740c6b636f6d6d61616363656e740c6b677265656e +6c616e646963064c6163757465066c61637574650c4c636f6d6d61616363656e740c6c636f6d6d61616363656e74064c6361 +726f6e066c6361726f6e044c646f74046c646f74064e6163757465066e61637574650c4e636f6d6d61616363656e740c6e63 +6f6d6d61616363656e74064e6361726f6e066e6361726f6e0b6e61706f7374726f70686503456e6703656e67074f6d616372 +6f6e076f6d6163726f6e064f6272657665066f62726576650d4f68756e676172756d6c6175740d6f68756e676172756d6c61 +757406526163757465067261637574650c52636f6d6d61616363656e740c72636f6d6d61616363656e7406526361726f6e06 +726361726f6e06536163757465067361637574650b5363697263756d666c65780b7363697263756d666c65780c54636f6d6d +61616363656e740c74636f6d6d61616363656e7406546361726f6e06746361726f6e04546261720474626172065574696c64 +65067574696c646507556d6163726f6e07756d6163726f6e0655627265766506756272657665055572696e67057572696e67 +0d5568756e676172756d6c6175740d7568756e676172756d6c61757407556f676f6e656b07756f676f6e656b0b5763697263 +756d666c65780b7763697263756d666c65780b5963697263756d666c65780b7963697263756d666c6578065a616375746506 +7a61637574650a5a646f74616363656e740a7a646f74616363656e74056c6f6e677307756e693031383007756e6930313831 +07756e693031383207756e693031383307756e693031383407756e693031383507756e693031383607756e69303138370775 +6e693031383807756e693031383907756e693031384107756e693031384207756e693031384307756e693031384407756e69 +3031384507756e693031384607756e693031393007756e693031393107756e693031393307756e693031393407756e693031 +393507756e693031393607756e693031393707756e693031393807756e693031393907756e693031394107756e6930313942 +07756e693031394307756e693031394407756e693031394507756e6930313946054f686f726e056f686f726e07756e693031 +413207756e693031413307756e693031413407756e693031413507756e693031413607756e693031413707756e6930314138 +07756e693031413907756e693031414107756e693031414207756e693031414307756e693031414407756e69303141450555 +686f726e0575686f726e07756e693031423107756e693031423207756e693031423307756e693031423407756e6930314235 +07756e693031423607756e693031423707756e693031423807756e693031423907756e693031424107756e69303142420775 +6e693031424307756e693031424407756e693031424507756e693031424607756e693031433007756e693031433107756e69 +3031433207756e693031433307756e693031433407756e693031433507756e693031433607756e693031433707756e693031 +433807756e693031433907756e693031434107756e693031434207756e693031434307756e693031434407756e6930314345 +07756e693031434607756e693031443007756e693031443107756e693031443207756e693031443307756e69303144340775 +6e693031443507756e693031443607756e693031443707756e693031443807756e693031443907756e693031444107756e69 +3031444207756e693031444307756e693031444407756e693031444507756e693031444607756e693031453007756e693031 +453107756e693031453207756e693031453307756e693031453407756e693031453506476361726f6e06676361726f6e0775 +6e693031453807756e693031453907756e693031454107756e693031454207756e693031454307756e693031454407756e69 +3031454507756e693031454607756e693031463007756e693031463107756e693031463207756e693031463307756e693031 +463407756e693031463507756e693031463607756e693031463707756e693031463807756e69303146390a4172696e676163 +7574650a6172696e676163757465074145616375746507616561637574650b4f736c61736861637574650b6f736c61736861 +6375746507756e693032303007756e693032303107756e693032303207756e693032303307756e693032303407756e693032 +303507756e693032303607756e693032303707756e693032303807756e693032303907756e693032304107756e6930323042 +07756e693032304307756e693032304407756e693032304507756e693032304607756e693032313007756e69303231310775 +6e693032313207756e693032313307756e693032313407756e693032313507756e693032313607756e69303231370c53636f +6d6d61616363656e740c73636f6d6d61616363656e7407756e693032314107756e693032314207756e693032314307756e69 +3032314407756e693032314507756e693032314607756e693032323007756e693032323107756e693032323207756e693032 +323307756e693032323407756e693032323507756e693032323607756e693032323707756e693032323807756e6930323239 +07756e693032324107756e693032324207756e693032324307756e693032324407756e693032324507756e69303232460775 +6e693032333007756e693032333107756e693032333207756e693032333307756e693032333407756e693032333507756e69 +3032333608646f746c6573736a07756e693032333807756e693032333907756e693032334107756e693032334207756e6930 +32334307756e693032334407756e693032334507756e693032334607756e693032343007756e693032343107756e69303234 +3207756e693032343307756e693032343407756e693032343507756e693032343607756e693032343707756e693032343807 +756e693032343907756e693032344107756e693032344207756e693032344307756e693032344407756e693032344507756e +693032344607756e693032353007756e693032353107756e693032353207756e693032353307756e693032353407756e6930 +32353507756e693032353607756e693032353707756e693032353807756e693032353907756e693032354107756e69303235 +4207756e693032354307756e693032354407756e693032354507756e693032354607756e693032363007756e693032363107 +756e693032363207756e693032363307756e693032363407756e693032363507756e693032363607756e693032363707756e +693032363807756e693032363907756e693032364107756e693032364207756e693032364307756e693032364407756e6930 +32364507756e693032364607756e693032373007756e693032373107756e693032373207756e693032373307756e69303237 +3407756e693032373507756e693032373607756e693032373707756e693032373807756e693032373907756e693032374107 +756e693032374207756e693032374307756e693032374407756e693032374507756e693032374607756e693032383007756e +693032383107756e693032383207756e693032383307756e693032383407756e693032383507756e693032383607756e6930 +32383707756e693032383807756e693032383907756e693032384107756e693032384207756e693032384307756e69303238 +4407756e693032384507756e693032384607756e693032393007756e693032393107756e693032393207756e693032393307 +756e693032393407756e693032393507756e693032393607756e693032393707756e693032393807756e693032393907756e +693032394107756e693032394207756e693032394307756e693032394407756e693032394507756e693032394607756e6930 +32413007756e693032413107756e693032413207756e693032413307756e693032413407756e693032413507756e69303241 +3607756e693032413707756e693032413807756e693032413907756e693032414107756e693032414207756e693032414307 +756e693032414407756e693032414507756e693032414607756e693032423007756e693032423107756e693032423207756e +693032423307756e693032423407756e693032423507756e693032423607756e693032423707756e693032423807756e6930 +32423907756e693032424107756e693032424207756e693032424307756e693032424407756e693032424507756e69303242 +4607756e693032433007756e693032433107756e693032433207756e693032433307756e693032433407756e693032433507 +756e693032433807756e693032433907756e693032434107756e693032434207756e693032434307756e693032434407756e +693032434507756e693032434607756e693032443007756e693032443107756e693032443207756e693032443307756e6930 +32443407756e693032443507756e693032443607756e693032443707756e693032444507756e693032444607756e69303245 +3007756e693032453107756e693032453207756e693032453307756e693032453407756e693032453507756e693032453607 +756e693032453707756e693032453807756e693032453907756e693032454307756e693032454407756e693032454507756e +693032463307756e6930324637096772617665636f6d62096163757465636f6d6207756e69303330320974696c6465636f6d +6207756e693033303407756e693033303507756e693033303607756e693033303707756e69303330380d686f6f6b61626f76 +65636f6d6207756e693033304107756e693033304207756e693033304307756e693033304407756e693033304507756e6930 +33304607756e693033313007756e693033313107756e693033313207756e693033313307756e693033313407756e69303331 +3507756e693033313607756e693033313707756e693033313807756e693033313907756e693033314107756e693033314207 +756e693033314307756e693033314407756e693033314507756e693033314607756e693033323007756e693033323107756e +69303332320c646f7462656c6f77636f6d6207756e693033323407756e693033323507756e693033323607756e6930333237 +07756e693033323807756e693033323907756e693033324107756e693033324207756e693033324307756e69303332440775 +6e693033324507756e693033324607756e693033333007756e693033333107756e693033333207756e693033333307756e69 +3033333407756e693033333507756e693033333607756e693033333707756e693033333807756e693033333907756e693033 +334107756e693033334207756e693033334307756e693033334407756e693033334507756e693033334607756e6930333430 +07756e693033343107756e693033343207756e693033343307756e693033343407756e693033343507756e69303334360775 +6e693033343707756e693033343807756e693033343907756e693033344107756e693033344207756e693033344307756e69 +3033344407756e693033344507756e693033344607756e693033353107756e693033353207756e693033353307756e693033 +353707756e693033353807756e693033354107756e693033354307756e693033354407756e693033354507756e6930333546 +07756e693033363007756e693033363107756e693033363207756e693033373007756e693033373107756e69303337320775 +6e693033373307756e693033373407756e693033373507756e693033373607756e693033373707756e693033374107756e69 +3033374207756e693033374307756e693033374407756e693033374505746f6e6f730d6469657265736973746f6e6f730a41 +6c706861746f6e6f7309616e6f74656c6569610c457073696c6f6e746f6e6f7308457461746f6e6f7309496f7461746f6e6f +730c4f6d6963726f6e746f6e6f730c557073696c6f6e746f6e6f730a4f6d656761746f6e6f7311696f746164696572657369 +73746f6e6f7305416c70686104426574610547616d6d6107756e693033393407457073696c6f6e045a657461034574610554 +6865746104496f7461054b61707061064c616d626461024d75024e75025869074f6d6963726f6e0250690352686f05536967 +6d610354617507557073696c6f6e0350686903436869035073690c496f746164696572657369730f557073696c6f6e646965 +72657369730a616c706861746f6e6f730c657073696c6f6e746f6e6f7308657461746f6e6f7309696f7461746f6e6f731475 +7073696c6f6e6469657265736973746f6e6f7305616c70686104626574610567616d6d610564656c746107657073696c6f6e +047a6574610365746105746865746104696f7461056b61707061066c616d62646107756e6930334243026e75027869076f6d +6963726f6e0372686f067369676d6131057369676d610374617507757073696c6f6e037068690363686903707369056f6d65 +67610c696f746164696572657369730f757073696c6f6e64696572657369730c6f6d6963726f6e746f6e6f730c757073696c +6f6e746f6e6f730a6f6d656761746f6e6f7307756e693033434607756e69303344300674686574613108557073696c6f6e31 +07756e693033443307756e69303344340470686931066f6d6567613107756e693033443707756e693033443807756e693033 +443907756e693033444107756e693033444207756e693033444307756e693033444407756e693033444507756e6930334446 +07756e693033453007756e693033453107756e693033453207756e693033453307756e693033453407756e69303345350775 +6e693033453607756e693033453707756e693033453807756e693033453907756e693033454107756e693033454207756e69 +3033454307756e693033454407756e693033454507756e693033454607756e693033463007756e693033463107756e693033 +463207756e693033463307756e693033463407756e693033463507756e693033463607756e693033463707756e6930334638 +07756e693033463907756e693033464107756e693033464207756e693033464307756e693033464407756e69303346450775 +6e693033464607756e693034303007756e693034303107756e693034303207756e693034303307756e693034303407756e69 +3034303507756e693034303607756e693034303707756e693034303807756e693034303907756e693034304107756e693034 +304207756e693034304307756e693034304407756e693034304507756e693034304607756e693034313007756e6930343131 +07756e693034313207756e693034313307756e693034313407756e693034313507756e693034313607756e69303431370775 +6e693034313807756e693034313907756e693034314107756e693034314207756e693034314307756e693034314407756e69 +3034314507756e693034314607756e693034323007756e693034323107756e693034323207756e693034323307756e693034 +323407756e693034323507756e693034323607756e693034323707756e693034323807756e693034323907756e6930343241 +07756e693034324207756e693034324307756e693034324407756e693034324507756e693034324607756e69303433300775 +6e693034333107756e693034333207756e693034333307756e693034333407756e693034333507756e693034333607756e69 +3034333707756e693034333807756e693034333907756e693034334107756e693034334207756e693034334307756e693034 +334407756e693034334507756e693034334607756e693034343007756e693034343107756e693034343207756e6930343433 +07756e693034343407756e693034343507756e693034343607756e693034343707756e693034343807756e69303434390775 +6e693034344107756e693034344207756e693034344307756e693034344407756e693034344507756e693034344607756e69 +3034353007756e693034353107756e693034353207756e693034353307756e693034353407756e693034353507756e693034 +353607756e693034353707756e693034353807756e693034353907756e693034354107756e693034354207756e6930343543 +07756e693034354407756e693034354507756e693034354607756e693034363007756e693034363107756e69303436320775 +6e693034363307756e693034363407756e693034363507756e693034363607756e693034363707756e693034363807756e69 +3034363907756e693034364107756e693034364207756e693034364307756e693034364407756e693034364507756e693034 +364607756e693034373007756e693034373107756e693034373207756e693034373307756e693034373407756e6930343735 +07756e693034373607756e693034373707756e693034373807756e693034373907756e693034374107756e69303437420775 +6e693034374307756e693034374407756e693034374507756e693034374607756e693034383007756e693034383107756e69 +3034383207756e693034383307756e693034383407756e693034383507756e693034383607756e693034383707756e693034 +383807756e693034383907756e693034384107756e693034384207756e693034384307756e693034384407756e6930343845 +07756e693034384607756e693034393007756e693034393107756e693034393207756e693034393307756e69303439340775 +6e693034393507756e693034393607756e693034393707756e693034393807756e693034393907756e693034394107756e69 +3034394207756e693034394307756e693034394407756e693034394507756e693034394607756e693034413007756e693034 +413107756e693034413207756e693034413307756e693034413407756e693034413507756e693034413607756e6930344137 +07756e693034413807756e693034413907756e693034414107756e693034414207756e693034414307756e69303441440775 +6e693034414507756e693034414607756e693034423007756e693034423107756e693034423207756e693034423307756e69 +3034423407756e693034423507756e693034423607756e693034423707756e693034423807756e693034423907756e693034 +424107756e693034424207756e693034424307756e693034424407756e693034424507756e693034424607756e6930344330 +07756e693034433107756e693034433207756e693034433307756e693034433407756e693034433507756e69303443360775 +6e693034433707756e693034433807756e693034433907756e693034434107756e693034434207756e693034434307756e69 +3034434407756e693034434507756e693034434607756e693034443007756e693034443107756e693034443207756e693034 +443307756e693034443407756e693034443507756e693034443607756e693034443707756e693034443807756e6930344439 +07756e693034444107756e693034444207756e693034444307756e693034444407756e693034444507756e69303444460775 +6e693034453007756e693034453107756e693034453207756e693034453307756e693034453407756e693034453507756e69 +3034453607756e693034453707756e693034453807756e693034453907756e693034454107756e693034454207756e693034 +454307756e693034454407756e693034454507756e693034454607756e693034463007756e693034463107756e6930344632 +07756e693034463307756e693034463407756e693034463507756e693034463607756e693034463707756e69303446380775 +6e693034463907756e693034464107756e693034464207756e693034464307756e693034464407756e693034464507756e69 +3034464607756e693035303007756e693035303107756e693035303207756e693035303307756e693035303407756e693035 +303507756e693035303607756e693035303707756e693035303807756e693035303907756e693035304107756e6930353042 +07756e693035304307756e693035304407756e693035304507756e693035304607756e693035313007756e69303531310775 +6e693035313207756e693035313307756e693035313407756e693035313507756e693035313607756e693035313707756e69 +3035313807756e693035313907756e693035314107756e693035314207756e693035314307756e693035314407756e693035 +314507756e693035314607756e693035323007756e693035323107756e693035323207756e693035323307756e6930353234 +07756e693035323507756e693035333107756e693035333207756e693035333307756e693035333407756e69303533350775 +6e693035333607756e693035333707756e693035333807756e693035333907756e693035334107756e693035334207756e69 +3035334307756e693035334407756e693035334507756e693035334607756e693035343007756e693035343107756e693035 +343207756e693035343307756e693035343407756e693035343507756e693035343607756e693035343707756e6930353438 +07756e693035343907756e693035344107756e693035344207756e693035344307756e693035344407756e69303534450775 +6e693035344607756e693035353007756e693035353107756e693035353207756e693035353307756e693035353407756e69 +3035353507756e693035353607756e693035353907756e693035354107756e693035354207756e693035354307756e693035 +354407756e693035354507756e693035354607756e693035363107756e693035363207756e693035363307756e6930353634 +07756e693035363507756e693035363607756e693035363707756e693035363807756e693035363907756e69303536410775 +6e693035364207756e693035364307756e693035364407756e693035364507756e693035364607756e693035373007756e69 +3035373107756e693035373207756e693035373307756e693035373407756e693035373507756e693035373607756e693035 +373707756e693035373807756e693035373907756e693035374107756e693035374207756e693035374307756e6930353744 +07756e693035374507756e693035374607756e693035383007756e693035383107756e693035383207756e69303538330775 +6e693035383407756e693035383507756e693035383607756e693035383707756e693035383907756e693035384107756e69 +3035423007756e693035423107756e693035423207756e693035423307756e693035423407756e693035423507756e693035 +423607756e693035423707756e693035423807756e693035423907756e693035424107756e693035424207756e6930354243 +07756e693035424407756e693035424507756e693035424607756e693035433007756e693035433107756e69303543320775 +6e693035433307756e693035433607756e693035433707756e693035443007756e693035443107756e693035443207756e69 +3035443307756e693035443407756e693035443507756e693035443607756e693035443707756e693035443807756e693035 +443907756e693035444107756e693035444207756e693035444307756e693035444407756e693035444507756e6930354446 +07756e693035453007756e693035453107756e693035453207756e693035453307756e693035453407756e69303545350775 +6e693035453607756e693035453707756e693035453807756e693035453907756e693035454107756e693035463007756e69 +3035463107756e693035463207756e693035463307756e693035463407756e693036303607756e693036303707756e693036 +303907756e693036304107756e693036304307756e693036313507756e693036314207756e693036314607756e6930363231 +07756e693036323207756e693036323307756e693036323407756e693036323507756e693036323607756e69303632370775 +6e693036323807756e693036323907756e693036324107756e693036324207756e693036324307756e693036324407756e69 +3036324507756e693036324607756e693036333007756e693036333107756e693036333207756e693036333307756e693036 +333407756e693036333507756e693036333607756e693036333707756e693036333807756e693036333907756e6930363341 +07756e693036343007756e693036343107756e693036343207756e693036343307756e693036343407756e69303634350775 +6e693036343607756e693036343707756e693036343807756e693036343907756e693036344107756e693036344207756e69 +3036344307756e693036344407756e693036344507756e693036344607756e693036353007756e693036353107756e693036 +353207756e693036353307756e693036353407756e693036353507756e693036353707756e693036354107756e6930363630 +07756e693036363107756e693036363207756e693036363307756e693036363407756e693036363507756e69303636360775 +6e693036363707756e693036363807756e693036363907756e693036364107756e693036364207756e693036364307756e69 +3036364407756e693036364507756e693036364607756e693036373007756e693036373407756e693036373907756e693036 +374107756e693036374207756e693036374307756e693036374407756e693036374507756e693036374607756e6930363830 +07756e693036383107756e693036383207756e693036383307756e693036383407756e693036383507756e69303638360775 +6e693036383707756e693036383807756e693036383907756e693036384107756e693036384207756e693036384307756e69 +3036384407756e693036384507756e693036384607756e693036393007756e693036393107756e693036393207756e693036 +393307756e693036393407756e693036393507756e693036393607756e693036393707756e693036393807756e6930363939 +07756e693036394107756e693036394207756e693036394307756e693036394407756e693036394507756e69303639460775 +6e693036413007756e693036413107756e693036413207756e693036413307756e693036413407756e693036413507756e69 +3036413607756e693036413707756e693036413807756e693036413907756e693036414107756e693036414207756e693036 +414307756e693036414407756e693036414507756e693036414607756e693036423007756e693036423107756e6930364232 +07756e693036423307756e693036423407756e693036423507756e693036423607756e693036423707756e69303642380775 +6e693036423907756e693036424107756e693036424207756e693036424307756e693036424407756e693036424507756e69 +3036424607756e693036433607756e693036433707756e693036433807756e693036434207756e693036434307756e693036 +434507756e693036443007756e693036443507756e693036463007756e693036463107756e693036463207756e6930364633 +07756e693036463407756e693036463507756e693036463607756e693036463707756e693036463807756e69303646390775 +6e693037433007756e693037433107756e693037433207756e693037433307756e693037433407756e693037433507756e69 +3037433607756e693037433707756e693037433807756e693037433907756e693037434107756e693037434207756e693037 +434307756e693037434407756e693037434507756e693037434607756e693037443007756e693037443107756e6930374432 +07756e693037443307756e693037443407756e693037443507756e693037443607756e693037443707756e69303744380775 +6e693037443907756e693037444107756e693037444207756e693037444307756e693037444407756e693037444507756e69 +3037444607756e693037453007756e693037453107756e693037453207756e693037453307756e693037453407756e693037 +453507756e693037453607756e693037453707756e693037454207756e693037454307756e693037454407756e6930374545 +07756e693037454607756e693037463007756e693037463107756e693037463207756e693037463307756e69303746340775 +6e693037463507756e693037463807756e693037463907756e693037464107756e693045334607756e693045383107756e69 +3045383207756e693045383407756e693045383707756e693045383807756e693045384107756e693045384407756e693045 +393407756e693045393507756e693045393607756e693045393707756e693045393907756e693045394107756e6930453942 +07756e693045394307756e693045394407756e693045394507756e693045394607756e693045413107756e69304541320775 +6e693045413307756e693045413507756e693045413707756e693045414107756e693045414207756e693045414407756e69 +3045414507756e693045414607756e693045423007756e693045423107756e693045423207756e693045423307756e693045 +423407756e693045423507756e693045423607756e693045423707756e693045423807756e693045423907756e6930454242 +07756e693045424307756e693045424407756e693045433007756e693045433107756e693045433207756e69304543330775 +6e693045433407756e693045433607756e693045433807756e693045433907756e693045434107756e693045434207756e69 +3045434307756e693045434407756e693045443007756e693045443107756e693045443207756e693045443307756e693045 +443407756e693045443507756e693045443607756e693045443707756e693045443807756e693045443907756e6930454443 +07756e693045444407756e693130413007756e693130413107756e693130413207756e693130413307756e69313041340775 +6e693130413507756e693130413607756e693130413707756e693130413807756e693130413907756e693130414107756e69 +3130414207756e693130414307756e693130414407756e693130414507756e693130414607756e693130423007756e693130 +423107756e693130423207756e693130423307756e693130423407756e693130423507756e693130423607756e6931304237 +07756e693130423807756e693130423907756e693130424107756e693130424207756e693130424307756e69313042440775 +6e693130424507756e693130424607756e693130433007756e693130433107756e693130433207756e693130433307756e69 +3130433407756e693130433507756e693130443007756e693130443107756e693130443207756e693130443307756e693130 +443407756e693130443507756e693130443607756e693130443707756e693130443807756e693130443907756e6931304441 +07756e693130444207756e693130444307756e693130444407756e693130444507756e693130444607756e69313045300775 +6e693130453107756e693130453207756e693130453307756e693130453407756e693130453507756e693130453607756e69 +3130453707756e693130453807756e693130453907756e693130454107756e693130454207756e693130454307756e693130 +454407756e693130454507756e693130454607756e693130463007756e693130463107756e693130463207756e6931304633 +07756e693130463407756e693130463507756e693130463607756e693130463707756e693130463807756e69313046390775 +6e693130464107756e693130464207756e693130464307756e693134303107756e693134303207756e693134303307756e69 +3134303407756e693134303507756e693134303607756e693134303707756e693134303907756e693134304107756e693134 +304207756e693134304307756e693134304407756e693134304507756e693134304607756e693134313007756e6931343131 +07756e693134313207756e693134313307756e693134313407756e693134313507756e693134313607756e69313431370775 +6e693134313807756e693134313907756e693134314107756e693134314207756e693134314407756e693134314507756e69 +3134314607756e693134323007756e693134323107756e693134323207756e693134323307756e693134323407756e693134 +323507756e693134323607756e693134323707756e693134323807756e693134323907756e693134324107756e6931343242 +07756e693134324307756e693134324407756e693134324507756e693134324607756e693134333007756e69313433310775 +6e693134333207756e693134333307756e693134333407756e693134333507756e693134333707756e693134333807756e69 +3134333907756e693134334107756e693134334207756e693134334307756e693134334407756e693134334507756e693134 +334607756e693134343007756e693134343107756e693134343207756e693134343307756e693134343407756e6931343435 +07756e693134343607756e693134343707756e693134343807756e693134343907756e693134344107756e69313434430775 +6e693134344407756e693134344507756e693134344607756e693134353007756e693134353107756e693134353207756e69 +3134353407756e693134353507756e693134353607756e693134353707756e693134353807756e693134353907756e693134 +354107756e693134354207756e693134354307756e693134354407756e693134354507756e693134354607756e6931343630 +07756e693134363107756e693134363207756e693134363307756e693134363407756e693134363507756e69313436360775 +6e693134363707756e693134363807756e693134363907756e693134364107756e693134364207756e693134364307756e69 +3134364407756e693134364507756e693134364607756e693134373007756e693134373107756e693134373207756e693134 +373307756e693134373407756e693134373507756e693134373607756e693134373707756e693134373807756e6931343739 +07756e693134374107756e693134374207756e693134374307756e693134374407756e693134374507756e69313437460775 +6e693134383007756e693134383107756e693134383207756e693134383307756e693134383407756e693134383507756e69 +3134383607756e693134383707756e693134383807756e693134383907756e693134384107756e693134384207756e693134 +384307756e693134384407756e693134384507756e693134384607756e693134393007756e693134393107756e6931343932 +07756e693134393307756e693134393407756e693134393507756e693134393607756e693134393707756e69313439380775 +6e693134393907756e693134394107756e693134394207756e693134394307756e693134394407756e693134394507756e69 +3134394607756e693134413007756e693134413107756e693134413207756e693134413307756e693134413407756e693134 +413507756e693134413607756e693134413707756e693134413807756e693134413907756e693134414107756e6931344142 +07756e693134414307756e693134414407756e693134414507756e693134414607756e693134423007756e69313442310775 +6e693134423207756e693134423307756e693134423407756e693134423507756e693134423607756e693134423707756e69 +3134423807756e693134423907756e693134424107756e693134424207756e693134424307756e693134424407756e693134 +433007756e693134433107756e693134433207756e693134433307756e693134433407756e693134433507756e6931344336 +07756e693134433707756e693134433807756e693134433907756e693134434107756e693134434207756e69313443430775 +6e693134434407756e693134434507756e693134434607756e693134443007756e693134443107756e693134443207756e69 +3134443307756e693134443407756e693134443507756e693134443607756e693134443707756e693134443807756e693134 +443907756e693134444107756e693134444207756e693134444307756e693134444407756e693134444507756e6931344446 +07756e693134453007756e693134453107756e693134453207756e693134453307756e693134453407756e69313445350775 +6e693134453607756e693134453707756e693134453807756e693134453907756e693134454107756e693134454307756e69 +3134454407756e693134454507756e693134454607756e693134463007756e693134463107756e693134463207756e693134 +463307756e693134463407756e693134463507756e693134463607756e693134463707756e693134463807756e6931344639 +07756e693134464107756e693134464207756e693134464307756e693134464407756e693134464507756e69313446460775 +6e693135303007756e693135303107756e693135303207756e693135303307756e693135303407756e693135303507756e69 +3135303607756e693135303707756e693135313007756e693135313107756e693135313207756e693135313307756e693135 +313407756e693135313507756e693135313607756e693135313707756e693135313807756e693135313907756e6931353141 +07756e693135314207756e693135314307756e693135314407756e693135314507756e693135314607756e69313532300775 +6e693135323107756e693135323207756e693135323307756e693135323407756e693135323507756e693135323607756e69 +3135323707756e693135323807756e693135323907756e693135324107756e693135324207756e693135324307756e693135 +324407756e693135324507756e693135324607756e693135333007756e693135333107756e693135333207756e6931353333 +07756e693135333407756e693135333507756e693135333607756e693135333707756e693135333807756e69313533390775 +6e693135334107756e693135334207756e693135334307756e693135334407756e693135334507756e693135343007756e69 +3135343107756e693135343207756e693135343307756e693135343407756e693135343507756e693135343607756e693135 +343707756e693135343807756e693135343907756e693135344107756e693135344207756e693135344307756e6931353444 +07756e693135344507756e693135344607756e693135353007756e693135353207756e693135353307756e69313535340775 +6e693135353507756e693135353607756e693135353707756e693135353807756e693135353907756e693135354107756e69 +3135354207756e693135354307756e693135354407756e693135354507756e693135354607756e693135363007756e693135 +363107756e693135363207756e693135363307756e693135363407756e693135363507756e693135363607756e6931353637 +07756e693135363807756e693135363907756e693135364107756e693135373407756e693135373507756e69313537360775 +6e693135373707756e693135373807756e693135373907756e693135374107756e693135374207756e693135374307756e69 +3135374407756e693135374507756e693135374607756e693135383007756e693135383107756e693135383207756e693135 +383307756e693135383407756e693135383507756e693135384107756e693135384207756e693135384307756e6931353844 +07756e693135384507756e693135384607756e693135393007756e693135393107756e693135393207756e69313539330775 +6e693135393407756e693135393507756e693135393607756e693135413007756e693135413107756e693135413207756e69 +3135413307756e693135413407756e693135413507756e693135413607756e693135413707756e693135413807756e693135 +413907756e693135414107756e693135414207756e693135414307756e693135414407756e693135414507756e6931354146 +07756e693135444507756e693135453107756e693136343607756e693136343707756e693136364507756e69313636460775 +6e693136373007756e693136373107756e693136373207756e693136373307756e693136373407756e693136373507756e69 +3136373607756e693136383007756e693136383107756e693136383207756e693136383307756e693136383407756e693136 +383507756e693136383607756e693136383707756e693136383807756e693136383907756e693136384107756e6931363842 +07756e693136384307756e693136384407756e693136384507756e693136384607756e693136393007756e69313639310775 +6e693136393207756e693136393307756e693136393407756e693136393507756e693136393607756e693136393707756e69 +3136393807756e693136393907756e693136394107756e693136394207756e693136394307756e693144303007756e693144 +303107756e693144303207756e693144303307756e693144303407756e693144303507756e693144303607756e6931443037 +07756e693144303807756e693144303907756e693144304107756e693144304207756e693144304307756e69314430440775 +6e693144304507756e693144304607756e693144313007756e693144313107756e693144313207756e693144313307756e69 +3144313407756e693144313607756e693144313707756e693144313807756e693144313907756e693144314107756e693144 +314207756e693144314307756e693144314407756e693144314507756e693144314607756e693144323007756e6931443231 +07756e693144323207756e693144323307756e693144323607756e693144323707756e693144323807756e69314432390775 +6e693144324107756e693144324207756e693144324307756e693144324407756e693144324507756e693144333007756e69 +3144333107756e693144333207756e693144333307756e693144333407756e693144333507756e693144333607756e693144 +333707756e693144333807756e693144333907756e693144334107756e693144334207756e693144334307756e6931443344 +07756e693144334507756e693144334607756e693144343007756e693144343107756e693144343207756e69314434330775 +6e693144343407756e693144343507756e693144343607756e693144343707756e693144343807756e693144343907756e69 +3144344107756e693144344207756e693144344307756e693144344407756e693144344507756e693144344607756e693144 +353007756e693144353107756e693144353207756e693144353307756e693144353407756e693144353507756e6931443536 +07756e693144353707756e693144353807756e693144353907756e693144354107756e693144354207756e69314435440775 +6e693144354507756e693144354607756e693144363007756e693144363107756e693144363207756e693144363307756e69 +3144363407756e693144363507756e693144363607756e693144363707756e693144363807756e693144363907756e693144 +364107756e693144373707756e693144373807756e693144374207756e693144374407756e693144383507756e6931443942 +07756e693144394307756e693144394407756e693144394507756e693144394607756e693144413007756e69314441310775 +6e693144413207756e693144413307756e693144413407756e693144413507756e693144413607756e693144413707756e69 +3144413807756e693144413907756e693144414107756e693144414207756e693144414307756e693144414407756e693144 +414507756e693144414607756e693144423007756e693144423107756e693144423207756e693144423307756e6931444234 +07756e693144423507756e693144423607756e693144423707756e693144423807756e693144423907756e69314442410775 +6e693144424207756e693144424307756e693144424407756e693144424507756e693144424607756e693144433407756e69 +3144433507756e693144433607756e693144433707756e693144433807756e693144433907756e693145303007756e693145 +303107756e693145303207756e693145303307756e693145303407756e693145303507756e693145303607756e6931453037 +07756e693145303807756e693145303907756e693145304107756e693145304207756e693145304307756e69314530440775 +6e693145304507756e693145304607756e693145313007756e693145313107756e693145313207756e693145313307756e69 +3145313407756e693145313507756e693145313607756e693145313707756e693145313807756e693145313907756e693145 +314107756e693145314207756e693145314307756e693145314407756e693145314507756e693145314607756e6931453230 +07756e693145323107756e693145323207756e693145323307756e693145323407756e693145323507756e69314532360775 +6e693145323707756e693145323807756e693145323907756e693145324107756e693145324207756e693145324307756e69 +3145324407756e693145324507756e693145324607756e693145333007756e693145333107756e693145333207756e693145 +333307756e693145333407756e693145333507756e693145333607756e693145333707756e693145333807756e6931453339 +07756e693145334107756e693145334207756e693145334307756e693145334407756e693145334507756e69314533460775 +6e693145343007756e693145343107756e693145343207756e693145343307756e693145343407756e693145343507756e69 +3145343607756e693145343707756e693145343807756e693145343907756e693145344107756e693145344207756e693145 +344307756e693145344407756e693145344507756e693145344607756e693145353007756e693145353107756e6931453532 +07756e693145353307756e693145353407756e693145353507756e693145353607756e693145353707756e69314535380775 +6e693145353907756e693145354107756e693145354207756e693145354307756e693145354407756e693145354507756e69 +3145354607756e693145363007756e693145363107756e693145363207756e693145363307756e693145363407756e693145 +363507756e693145363607756e693145363707756e693145363807756e693145363907756e693145364107756e6931453642 +07756e693145364307756e693145364407756e693145364507756e693145364607756e693145373007756e69314537310775 +6e693145373207756e693145373307756e693145373407756e693145373507756e693145373607756e693145373707756e69 +3145373807756e693145373907756e693145374107756e693145374207756e693145374307756e693145374407756e693145 +374507756e693145374606576772617665067767726176650657616375746506776163757465095764696572657369730977 +646965726573697307756e693145383607756e693145383707756e693145383807756e693145383907756e69314538410775 +6e693145384207756e693145384307756e693145384407756e693145384507756e693145384607756e693145393007756e69 +3145393107756e693145393207756e693145393307756e693145393407756e693145393507756e693145393607756e693145 +393707756e693145393807756e693145393907756e693145394107756e693145394207756e693145394307756e6931453944 +07756e693145394507756e693145394607756e693145413007756e693145413107756e693145413207756e69314541330775 +6e693145413407756e693145413507756e693145413607756e693145413707756e693145413807756e693145413907756e69 +3145414107756e693145414207756e693145414307756e693145414407756e693145414507756e693145414607756e693145 +423007756e693145423107756e693145423207756e693145423307756e693145423407756e693145423507756e6931454236 +07756e693145423707756e693145423807756e693145423907756e693145424107756e693145424207756e69314542430775 +6e693145424407756e693145424507756e693145424607756e693145433007756e693145433107756e693145433207756e69 +3145433307756e693145433407756e693145433507756e693145433607756e693145433707756e693145433807756e693145 +433907756e693145434107756e693145434207756e693145434307756e693145434407756e693145434507756e6931454346 +07756e693145443007756e693145443107756e693145443207756e693145443307756e693145443407756e69314544350775 +6e693145443607756e693145443707756e693145443807756e693145443907756e693145444107756e693145444207756e69 +3145444307756e693145444407756e693145444507756e693145444607756e693145453007756e693145453107756e693145 +453207756e693145453307756e693145453407756e693145453507756e693145453607756e693145453707756e6931454538 +07756e693145453907756e693145454107756e693145454207756e693145454307756e693145454407756e69314545450775 +6e693145454607756e693145463007756e6931454631065967726176650679677261766507756e693145463407756e693145 +463507756e693145463607756e693145463707756e693145463807756e693145463907756e693145464107756e6931454642 +07756e693146303007756e693146303107756e693146303207756e693146303307756e693146303407756e69314630350775 +6e693146303607756e693146303707756e693146303807756e693146303907756e693146304107756e693146304207756e69 +3146304307756e693146304407756e693146304507756e693146304607756e693146313007756e693146313107756e693146 +313207756e693146313307756e693146313407756e693146313507756e693146313807756e693146313907756e6931463141 +07756e693146314207756e693146314307756e693146314407756e693146323007756e693146323107756e69314632320775 +6e693146323307756e693146323407756e693146323507756e693146323607756e693146323707756e693146323807756e69 +3146323907756e693146324107756e693146324207756e693146324307756e693146324407756e693146324507756e693146 +324607756e693146333007756e693146333107756e693146333207756e693146333307756e693146333407756e6931463335 +07756e693146333607756e693146333707756e693146333807756e693146333907756e693146334107756e69314633420775 +6e693146334307756e693146334407756e693146334507756e693146334607756e693146343007756e693146343107756e69 +3146343207756e693146343307756e693146343407756e693146343507756e693146343807756e693146343907756e693146 +344107756e693146344207756e693146344307756e693146344407756e693146353007756e693146353107756e6931463532 +07756e693146353307756e693146353407756e693146353507756e693146353607756e693146353707756e69314635390775 +6e693146354207756e693146354407756e693146354607756e693146363007756e693146363107756e693146363207756e69 +3146363307756e693146363407756e693146363507756e693146363607756e693146363707756e693146363807756e693146 +363907756e693146364107756e693146364207756e693146364307756e693146364407756e693146364507756e6931463646 +07756e693146373007756e693146373107756e693146373207756e693146373307756e693146373407756e69314637350775 +6e693146373607756e693146373707756e693146373807756e693146373907756e693146374107756e693146374207756e69 +3146374307756e693146374407756e693146383007756e693146383107756e693146383207756e693146383307756e693146 +383407756e693146383507756e693146383607756e693146383707756e693146383807756e693146383907756e6931463841 +07756e693146384207756e693146384307756e693146384407756e693146384507756e693146384607756e69314639300775 +6e693146393107756e693146393207756e693146393307756e693146393407756e693146393507756e693146393607756e69 +3146393707756e693146393807756e693146393907756e693146394107756e693146394207756e693146394307756e693146 +394407756e693146394507756e693146394607756e693146413007756e693146413107756e693146413207756e6931464133 +07756e693146413407756e693146413507756e693146413607756e693146413707756e693146413807756e69314641390775 +6e693146414107756e693146414207756e693146414307756e693146414407756e693146414507756e693146414607756e69 +3146423007756e693146423107756e693146423207756e693146423307756e693146423407756e693146423607756e693146 +423707756e693146423807756e693146423907756e693146424107756e693146424207756e693146424307756e6931464244 +07756e693146424507756e693146424607756e693146433007756e693146433107756e693146433207756e69314643330775 +6e693146433407756e693146433607756e693146433707756e693146433807756e693146433907756e693146434107756e69 +3146434207756e693146434307756e693146434407756e693146434507756e693146434607756e693146443007756e693146 +443107756e693146443207756e693146443307756e693146443607756e693146443707756e693146443807756e6931464439 +07756e693146444107756e693146444207756e693146444407756e693146444507756e693146444607756e69314645300775 +6e693146453107756e693146453207756e693146453307756e693146453407756e693146453507756e693146453607756e69 +3146453707756e693146453807756e693146453907756e693146454107756e693146454207756e693146454307756e693146 +454407756e693146454507756e693146454607756e693146463207756e693146463307756e693146463407756e6931464636 +07756e693146463707756e693146463807756e693146463907756e693146464107756e693146464207756e69314646430775 +6e693146464407756e693146464507756e693230303007756e693230303107756e693230303207756e693230303307756e69 +3230303407756e693230303507756e693230303607756e693230303707756e693230303807756e693230303907756e693230 +304107756e693230304207756e693230304307756e693230304407756e693230304507756e693230304607756e6932303130 +07756e69323031310a6669677572656461736807756e693230313507756e69323031360d756e64657273636f726564626c0d +71756f7465726576657273656407756e693230314607756e69323032330e6f6e65646f74656e6c65616465720e74776f646f +74656e6c656164657207756e693230323707756e693230323807756e693230323907756e693230324107756e693230324207 +756e693230324307756e693230324407756e693230324507756e693230324607756e6932303331066d696e75746506736563 +6f6e6407756e693230333407756e693230333507756e693230333607756e693230333707756e693230333807756e69323033 +42096578636c616d64626c07756e693230334407756e693230334507756e693230334607756e693230343007756e69323034 +3107756e693230343207756e693230343307756e693230343507756e693230343607756e693230343707756e693230343807 +756e693230343907756e693230344107756e693230344207756e693230344307756e693230344407756e693230344507756e +693230344607756e693230353007756e693230353107756e693230353207756e693230353307756e693230353407756e6932 +30353507756e693230353607756e693230353707756e693230353807756e693230353907756e693230354107756e69323035 +4207756e693230354307756e693230354407756e693230354507756e693230354607756e693230363007756e693230363107 +756e693230363207756e693230363307756e693230363407756e693230364107756e693230364207756e693230364307756e +693230364407756e693230364507756e693230364607756e693230373007756e693230373107756e693230373407756e6932 +30373507756e693230373607756e693230373707756e693230373807756e693230373907756e693230374107756e69323037 +4207756e693230374307756e693230374407756e693230374507756e693230374607756e693230383007756e693230383107 +756e693230383207756e693230383307756e693230383407756e693230383507756e693230383607756e693230383707756e +693230383807756e693230383907756e693230384107756e693230384207756e693230384307756e693230384407756e6932 +30384507756e693230393007756e693230393107756e693230393207756e693230393307756e693230393407756e69323039 +3507756e693230393607756e693230393707756e693230393807756e693230393907756e693230394107756e693230394207 +756e693230394307756e69323041300d636f6c6f6e6d6f6e657461727907756e6932304132046c69726107756e6932304135 +07756e69323041360670657365746107756e693230413807756e693230413907756e693230414104646f6e67044575726f07 +756e693230414407756e693230414507756e693230414607756e693230423007756e693230423107756e693230423207756e +693230423307756e693230423407756e693230423507756e693230423807756e693230423907756e693230424107756e6932 +30424407756e693230443007756e693230443107756e693230443607756e693230443707756e693230444207756e69323044 +4307756e693230453107756e693231303007756e693231303107756e693231303207756e693231303307756e693231303407 +756e693231303507756e693231303607756e693231303707756e693231303807756e693231303907756e693231304207756e +693231304307756e693231304407756e693231304507756e693231304607756e693231313008496672616b74757207756e69 +3231313207756e693231313307756e693231313407756e693231313507756e693231313607756e69323131370b7765696572 +73747261737307756e693231313907756e693231314107756e693231314208526672616b74757207756e69323131440c7072 +65736372697074696f6e07756e693231314607756e693231323007756e693231323107756e693231323307756e6932313234 +07756e693231323507756e693231323607756e693231323707756e693231323807756e693231323907756e69323132410775 +6e693231324207756e693231324307756e693231324409657374696d6174656407756e693231324607756e69323133300775 +6e693231333107756e693231333207756e693231333307756e693231333405616c65706807756e693231333607756e693231 +333707756e693231333807756e693231333907756e693231334107756e693231334207756e693231334307756e6932313344 +07756e693231334507756e693231334607756e693231343007756e693231343107756e693231343207756e69323134330775 +6e693231343407756e693231343507756e693231343607756e693231343707756e693231343807756e693231343907756e69 +3231344207756e693231344507756e693231353007756e693231353107756e6932313532086f6e6574686972640974776f74 +686972647307756e693231353507756e693231353607756e693231353707756e693231353807756e693231353907756e6932 +313541096f6e656569676874680c7468726565656967687468730b66697665656967687468730c736576656e656967687468 +7307756e693231354607756e693231363007756e693231363107756e693231363207756e693231363307756e693231363407 +756e693231363507756e693231363607756e693231363707756e693231363807756e693231363907756e693231364107756e +693231364207756e693231364307756e693231364407756e693231364507756e693231364607756e693231373007756e6932 +31373107756e693231373207756e693231373307756e693231373407756e693231373507756e693231373607756e69323137 +3707756e693231373807756e693231373907756e693231374107756e693231374207756e693231374307756e693231374407 +756e693231374507756e693231374607756e693231383007756e693231383107756e693231383207756e693231383307756e +693231383407756e693231383507756e6932313839096172726f776c656674076172726f7775700a6172726f777269676874 +096172726f77646f776e096172726f77626f7468096172726f777570646e07756e693231393607756e693231393707756e69 +3231393807756e693231393907756e693231394107756e693231394207756e693231394307756e693231394407756e693231 +394507756e693231394607756e693231413007756e693231413107756e693231413207756e693231413307756e6932314134 +07756e693231413507756e693231413607756e69323141370c6172726f777570646e62736507756e693231413907756e6932 +31414107756e693231414207756e693231414307756e693231414407756e693231414507756e693231414607756e69323142 +3007756e693231423107756e693231423207756e693231423307756e69323142340e636172726961676572657475726e0775 +6e693231423607756e693231423707756e693231423807756e693231423907756e693231424107756e693231424207756e69 +3231424307756e693231424407756e693231424507756e693231424607756e693231433007756e693231433107756e693231 +433207756e693231433307756e693231433407756e693231433507756e693231433607756e693231433707756e6932314338 +07756e693231433907756e693231434107756e693231434207756e693231434307756e693231434407756e69323143450775 +6e69323143460c6172726f7764626c6c6566740a6172726f7764626c75700d6172726f7764626c72696768740c6172726f77 +64626c646f776e0c6172726f7764626c626f746807756e693231443507756e693231443607756e693231443707756e693231 +443807756e693231443907756e693231444107756e693231444207756e693231444307756e693231444407756e6932314445 +07756e693231444607756e693231453007756e693231453107756e693231453207756e693231453307756e69323145340775 +6e693231453507756e693231453607756e693231453707756e693231453807756e693231453907756e693231454107756e69 +3231454207756e693231454307756e693231454407756e693231454507756e693231454607756e693231463007756e693231 +463107756e693231463207756e693231463307756e693231463407756e693231463507756e693231463607756e6932314637 +07756e693231463807756e693231463907756e693231464107756e693231464207756e693231464307756e69323146440775 +6e693231464507756e693231464609756e6976657273616c07756e69323230310b6578697374656e7469616c07756e693232 +303408656d707479736574086772616469656e7407656c656d656e740a6e6f74656c656d656e7407756e6932323041087375 +63687468617407756e693232304307756e693232304407756e693232304507756e693232313007756e693232313307756e69 +3232313407756e693232313507756e69323231360c617374657269736b6d61746807756e693232313807756e693232313907 +756e693232314207756e69323231430c70726f706f7274696f6e616c0a6f7274686f676f6e616c05616e676c6507756e6932 +32323107756e693232323207756e693232323307756e693232323407756e693232323507756e69323232360a6c6f67696361 +6c616e64096c6f676963616c6f720c696e74657273656374696f6e05756e696f6e07756e693232324307756e693232324407 +756e693232324507756e693232324607756e693232333007756e693232333107756e693232333207756e6932323333097468 +657265666f726507756e693232333507756e693232333607756e693232333707756e693232333807756e693232333907756e +693232334107756e69323233420773696d696c617207756e693232334407756e693232334507756e693232334607756e6932 +32343007756e693232343107756e693232343207756e693232343307756e693232343409636f6e677275656e7407756e6932 +32343607756e693232343707756e693232343907756e693232344107756e693232344207756e693232344307756e69323234 +4407756e693232344507756e693232344607756e693232353007756e693232353107756e693232353207756e693232353307 +756e693232353407756e693232353507756e693232353607756e693232353707756e693232353807756e693232353907756e +693232354107756e693232354207756e693232354307756e693232354407756e693232354507756e69323235460b65717569 +76616c656e636507756e693232363207756e693232363307756e693232363607756e693232363707756e693232363807756e +693232363907756e693232364107756e693232364207756e693232364307756e693232364407756e693232364507756e6932 +32364607756e693232373007756e693232373107756e693232373207756e693232373307756e693232373407756e69323237 +3507756e693232373607756e693232373707756e693232373807756e693232373907756e693232374107756e693232374207 +756e693232374307756e693232374407756e693232374507756e693232374607756e693232383007756e69323238310c7072 +6f7065727375627365740e70726f7065727375706572736574096e6f7473756273657407756e69323238350c7265666c6578 +7375627365740e7265666c6578737570657273657407756e693232383807756e693232383907756e693232384107756e6932 +32384207756e693232384307756e693232384407756e693232384507756e693232384607756e693232393007756e69323239 +3107756e693232393207756e693232393307756e69323239340a636972636c65706c757307756e69323239360e636972636c +656d756c7469706c7907756e693232393807756e693232393907756e693232394107756e693232394207756e693232394307 +756e693232394407756e693232394507756e693232394607756e693232413007756e693232413107756e693232413207756e +693232413307756e69323241340d70657270656e646963756c617207756e693232413607756e693232413707756e69323241 +3807756e693232413907756e693232414107756e693232414207756e693232414307756e693232414407756e693232414507 +756e693232414607756e693232423007756e693232423107756e693232423207756e693232423307756e693232423407756e +693232423507756e693232423607756e693232423707756e693232423807756e693232423907756e693232424107756e6932 +32424207756e693232424307756e693232424407756e693232424507756e693232424607756e693232433007756e69323243 +3107756e693232433207756e693232433307756e693232433407646f746d61746807756e693232433607756e693232433707 +756e693232433807756e693232433907756e693232434107756e693232434207756e693232434307756e693232434407756e +693232434507756e693232434607756e693232443007756e693232443107756e693232443207756e693232443307756e6932 +32443407756e693232443507756e693232443607756e693232443707756e693232443807756e693232443907756e69323244 +4107756e693232444207756e693232444307756e693232444407756e693232444507756e693232444607756e693232453007 +756e693232453107756e693232453207756e693232453307756e693232453407756e693232453507756e693232453607756e +693232453707756e693232453807756e693232453907756e693232454107756e693232454207756e693232454307756e6932 +32454407756e693232454507756e693232454607756e693232463007756e693232463107756e693232463207756e69323246 +3307756e693232463407756e693232463507756e693232463607756e693232463707756e693232463807756e693232463907 +756e693232464107756e693232464207756e693232464307756e693232464407756e693232464507756e693232464607756e +693233303007756e693233303105686f75736507756e693233303307756e693233303407756e693233303507756e69323330 +3607756e693233303707756e693233303807756e693233303907756e693233304107756e693233304207756e693233304307 +756e693233304407756e693233304507756e69323330460d7265766c6f676963616c6e6f7407756e693233313107756e6932 +33313807756e693233313907756e693233314307756e693233314407756e693233314507756e69323331460a696e74656772 +616c74700a696e74656772616c627407756e693233323407756e693233323507756e693233323607756e693233323707756e +693233323807756e693233324207756e693233324307756e693233373307756e693233373407756e693233373507756e6932 +33374107756e693233374407756e693233383707756e693233393407756e693233394207756e693233394307756e69323339 +4407756e693233394507756e693233394607756e693233413007756e693233413107756e693233413207756e693233413307 +756e693233413407756e693233413507756e693233413607756e693233413707756e693233413807756e693233413907756e +693233414107756e693233414207756e693233414307756e693233414407756e693233414507756e693233434507756e6932 +33434607756e693233453307756e693233453507756e693233453807756e693234323207756e693234323307756e69323436 +3007756e693234363107756e693234363207756e693234363307756e693234363407756e693234363507756e693234363607 +756e693234363707756e693234363807756e693234363908534631303030303007756e693235303108534631313030303007 +756e693235303307756e693235303407756e693235303507756e693235303607756e693235303707756e693235303807756e +693235303907756e693235304107756e693235304208534630313030303007756e693235304407756e693235304507756e69 +3235304608534630333030303007756e693235313107756e693235313207756e693235313308534630323030303007756e69 +3235313507756e693235313607756e693235313708534630343030303007756e693235313907756e693235314107756e6932 +35314208534630383030303007756e693235314407756e693235314507756e693235314607756e693235323007756e693235 +323107756e693235323207756e693235323308534630393030303007756e693235323507756e693235323607756e69323532 +3707756e693235323807756e693235323907756e693235324107756e693235324208534630363030303007756e6932353244 +07756e693235324507756e693235324607756e693235333007756e693235333107756e693235333207756e69323533330853 +4630373030303007756e693235333507756e693235333607756e693235333707756e693235333807756e693235333907756e +693235334107756e693235334208534630353030303007756e693235334407756e693235334507756e693235334607756e69 +3235343007756e693235343107756e693235343207756e693235343307756e693235343407756e693235343507756e693235 +343607756e693235343707756e693235343807756e693235343907756e693235344107756e693235344207756e6932353443 +07756e693235344407756e693235344507756e69323534460853463433303030300853463234303030300853463531303030 +3008534635323030303008534633393030303008534632323030303008534632313030303008534632353030303008534635 +3030303030085346343930303030085346333830303030085346323830303030085346323730303030085346323630303030 +0853463336303030300853463337303030300853463432303030300853463139303030300853463230303030300853463233 +3030303008534634373030303008534634383030303008534634313030303008534634353030303008534634363030303008 +534634303030303008534635343030303008534635333030303008534634343030303007756e693235364407756e69323536 +4507756e693235364607756e693235373007756e693235373107756e693235373207756e693235373307756e693235373407 +756e693235373507756e693235373607756e693235373707756e693235373807756e693235373907756e693235374107756e +693235374207756e693235374307756e693235374407756e693235374507756e6932353746077570626c6f636b07756e6932 +35383107756e693235383207756e693235383307646e626c6f636b07756e693235383507756e693235383607756e69323538 +3705626c6f636b07756e693235383907756e693235384107756e6932353842076c66626c6f636b07756e693235384407756e +693235384507756e6932353846077274626c6f636b076c74736861646505736861646507646b736861646507756e69323539 +3407756e693235393507756e693235393607756e693235393707756e693235393807756e693235393907756e693235394107 +756e693235394207756e693235394307756e693235394407756e693235394507756e69323539460966696c6c6564626f7806 +48323230373307756e693235413207756e693235413307756e693235413407756e693235413507756e693235413607756e69 +3235413707756e693235413807756e693235413906483138353433064831383535310a66696c6c65647265637407756e6932 +35414407756e693235414507756e693235414607756e693235423007756e6932354231077472696167757007756e69323542 +3307756e693235423407756e693235423507756e693235423607756e693235423707756e693235423807756e693235423907 +7472696167727407756e6932354242077472696167646e07756e693235424407756e693235424507756e693235424607756e +693235433007756e693235433107756e693235433207756e69323543330774726961676c6607756e693235433507756e6932 +35433607756e693235433707756e693235433807756e693235433906636972636c6507756e693235434307756e6932354344 +07756e69323543450648313835333307756e693235443007756e693235443107756e693235443207756e693235443307756e +693235443407756e693235443507756e693235443607756e693235443709696e7662756c6c657409696e76636972636c6507 +756e693235444107756e693235444207756e693235444307756e693235444407756e693235444507756e693235444607756e +693235453007756e693235453107756e693235453207756e693235453307756e693235453407756e69323545350a6f70656e +62756c6c657407756e693235453707756e693235453807756e693235453907756e693235454107756e693235454207756e69 +3235454307756e693235454407756e693235454507756e693235454607756e693235463007756e693235463107756e693235 +463207756e693235463307756e693235463407756e693235463507756e693235463607756e693235463707756e6932354638 +07756e693235463907756e693235464107756e693235464207756e693235464307756e693235464407756e69323546450775 +6e693235464607756e693236303007756e693236303107756e693236303207756e693236303307756e693236303407756e69 +3236303507756e693236303607756e693236303707756e693236303807756e693236303907756e693236304107756e693236 +304207756e693236304307756e693236304407756e693236304507756e693236304607756e693236313007756e6932363131 +07756e693236313207756e693236313307756e693236313407756e693236313507756e693236313607756e69323631370775 +6e693236313807756e693236313907756e693236314107756e693236314207756e693236314307756e693236314407756e69 +3236314507756e693236314607756e693236323007756e693236323107756e693236323207756e693236323307756e693236 +323407756e693236323507756e693236323607756e693236323707756e693236323807756e693236323907756e6932363241 +07756e693236324207756e693236324307756e693236324407756e693236324507756e693236324607756e69323633300775 +6e693236333107756e693236333207756e693236333307756e693236333407756e693236333507756e693236333607756e69 +3236333707756e693236333807756e693236333909736d696c65666163650c696e76736d696c65666163650373756e07756e +693236334407756e693236334507756e69323633460666656d616c6507756e6932363431046d616c6507756e693236343307 +756e693236343407756e693236343507756e693236343607756e693236343707756e693236343807756e693236343907756e +693236344107756e693236344207756e693236344307756e693236344407756e693236344507756e693236344607756e6932 +36353007756e693236353107756e693236353207756e693236353307756e693236353407756e693236353507756e69323635 +3607756e693236353707756e693236353807756e693236353907756e693236354107756e693236354207756e693236354307 +756e693236354407756e693236354507756e693236354605737061646507756e693236363107756e693236363204636c7562 +07756e6932363634056865617274076469616d6f6e6407756e693236363707756e693236363807756e69323636390b6d7573 +6963616c6e6f74650e6d75736963616c6e6f746564626c07756e693236364307756e693236364407756e693236364507756e +693236364607756e693236373007756e693236373107756e693236373207756e693236373307756e693236373407756e6932 +36373507756e693236373607756e693236373707756e693236373807756e693236373907756e693236374107756e69323637 +4207756e693236374307756e693236374407756e693236374507756e693236374607756e693236383007756e693236383107 +756e693236383207756e693236383307756e693236383407756e693236383507756e693236383607756e693236383707756e +693236383807756e693236383907756e693236384107756e693236384207756e693236384307756e693236384407756e6932 +36384507756e693236384607756e693236393007756e693236393107756e693236393207756e693236393307756e69323639 +3407756e693236393507756e693236393607756e693236393707756e693236393807756e693236393907756e693236394107 +756e693236394207756e693236394307756e693236394507756e693236394607756e693236413007756e693236413107756e +693236413207756e693236413307756e693236413407756e693236413507756e693236413607756e693236413707756e6932 +36413807756e693236413907756e693236414107756e693236414207756e693236414307756e693236414407756e69323641 +4507756e693236414607756e693236423007756e693236423107756e693236423207756e693236423307756e693236423407 +756e693236423507756e693236423607756e693236423707756e693236423807756e693236433007756e693236433107756e +693236433207756e693236433307756e693236453207756e693237303107756e693237303207756e693237303307756e6932 +37303407756e693237303607756e693237303707756e693237303807756e693237303907756e693237304307756e69323730 +4407756e693237304507756e693237304607756e693237313007756e693237313107756e693237313207756e693237313307 +756e693237313407756e693237313507756e693237313607756e693237313707756e693237313807756e693237313907756e +693237314107756e693237314207756e693237314307756e693237314407756e693237314507756e693237314607756e6932 +37323007756e693237323107756e693237323207756e693237323307756e693237323407756e693237323507756e69323732 +3607756e693237323707756e693237323907756e693237324107756e693237324207756e693237324307756e693237324407 +756e693237324507756e693237324607756e693237333007756e693237333107756e693237333207756e693237333307756e +693237333407756e693237333507756e693237333607756e693237333707756e693237333807756e693237333907756e6932 +37334107756e693237334207756e693237334307756e693237334407756e693237334507756e693237334607756e69323734 +3007756e693237343107756e693237343207756e693237343307756e693237343407756e693237343507756e693237343607 +756e693237343707756e693237343807756e693237343907756e693237344107756e693237344207756e693237344407756e +693237344607756e693237353007756e693237353107756e693237353207756e693237353607756e693237353807756e6932 +37353907756e693237354107756e693237354207756e693237354307756e693237354407756e693237354507756e69323736 +3107756e693237363207756e693237363307756e693237363407756e693237363507756e693237363607756e693237363707 +756e693237363807756e693237363907756e693237364107756e693237364207756e693237364307756e693237364407756e +693237364507756e693237364607756e693237373007756e693237373107756e693237373207756e693237373307756e6932 +37373407756e693237373507756e693237373607756e693237373707756e693237373807756e693237373907756e69323737 +4107756e693237374207756e693237374307756e693237374407756e693237374507756e693237374607756e693237383007 +756e693237383107756e693237383207756e693237383307756e693237383407756e693237383507756e693237383607756e +693237383707756e693237383807756e693237383907756e693237384107756e693237384207756e693237384307756e6932 +37384407756e693237384507756e693237384607756e693237393007756e693237393107756e693237393207756e69323739 +3307756e693237393407756e693237393807756e693237393907756e693237394107756e693237394207756e693237394307 +756e693237394407756e693237394507756e693237394607756e693237413007756e693237413107756e693237413207756e +693237413307756e693237413407756e693237413507756e693237413607756e693237413707756e693237413807756e6932 +37413907756e693237414107756e693237414207756e693237414307756e693237414407756e693237414507756e69323741 +4607756e693237423107756e693237423207756e693237423307756e693237423407756e693237423507756e693237423607 +756e693237423707756e693237423807756e693237423907756e693237424107756e693237424207756e693237424307756e +693237424407756e693237424507756e693237433507756e693237433607756e693237453007756e693237453607756e6932 +37453707756e693237453807756e693237453907756e693237454107756e693237454207756e693237463007756e69323746 +3107756e693237463207756e693237463307756e693237463407756e693237463507756e693237463607756e693237463707 +756e693237463807756e693237463907756e693237464107756e693237464207756e693237464307756e693237464407756e +693237464507756e693237464607756e693238303007756e693238303107756e693238303207756e693238303307756e6932 +38303407756e693238303507756e693238303607756e693238303707756e693238303807756e693238303907756e69323830 +4107756e693238304207756e693238304307756e693238304407756e693238304507756e693238304607756e693238313007 +756e693238313107756e693238313207756e693238313307756e693238313407756e693238313507756e693238313607756e +693238313707756e693238313807756e693238313907756e693238314107756e693238314207756e693238314307756e6932 +38314407756e693238314507756e693238314607756e693238323007756e693238323107756e693238323207756e69323832 +3307756e693238323407756e693238323507756e693238323607756e693238323707756e693238323807756e693238323907 +756e693238324107756e693238324207756e693238324307756e693238324407756e693238324507756e693238324607756e +693238333007756e693238333107756e693238333207756e693238333307756e693238333407756e693238333507756e6932 +38333607756e693238333707756e693238333807756e693238333907756e693238334107756e693238334207756e69323833 +4307756e693238334407756e693238334507756e693238334607756e693238343007756e693238343107756e693238343207 +756e693238343307756e693238343407756e693238343507756e693238343607756e693238343707756e693238343807756e +693238343907756e693238344107756e693238344207756e693238344307756e693238344407756e693238344507756e6932 +38344607756e693238353007756e693238353107756e693238353207756e693238353307756e693238353407756e69323835 +3507756e693238353607756e693238353707756e693238353807756e693238353907756e693238354107756e693238354207 +756e693238354307756e693238354407756e693238354507756e693238354607756e693238363007756e693238363107756e +693238363207756e693238363307756e693238363407756e693238363507756e693238363607756e693238363707756e6932 +38363807756e693238363907756e693238364107756e693238364207756e693238364307756e693238364407756e69323836 +4507756e693238364607756e693238373007756e693238373107756e693238373207756e693238373307756e693238373407 +756e693238373507756e693238373607756e693238373707756e693238373807756e693238373907756e693238374107756e +693238374207756e693238374307756e693238374407756e693238374507756e693238374607756e693238383007756e6932 +38383107756e693238383207756e693238383307756e693238383407756e693238383507756e693238383607756e69323838 +3707756e693238383807756e693238383907756e693238384107756e693238384207756e693238384307756e693238384407 +756e693238384507756e693238384607756e693238393007756e693238393107756e693238393207756e693238393307756e +693238393407756e693238393507756e693238393607756e693238393707756e693238393807756e693238393907756e6932 +38394107756e693238394207756e693238394307756e693238394407756e693238394507756e693238394607756e69323841 +3007756e693238413107756e693238413207756e693238413307756e693238413407756e693238413507756e693238413607 +756e693238413707756e693238413807756e693238413907756e693238414107756e693238414207756e693238414307756e +693238414407756e693238414507756e693238414607756e693238423007756e693238423107756e693238423207756e6932 +38423307756e693238423407756e693238423507756e693238423607756e693238423707756e693238423807756e69323842 +3907756e693238424107756e693238424207756e693238424307756e693238424407756e693238424507756e693238424607 +756e693238433007756e693238433107756e693238433207756e693238433307756e693238433407756e693238433507756e +693238433607756e693238433707756e693238433807756e693238433907756e693238434107756e693238434207756e6932 +38434307756e693238434407756e693238434507756e693238434607756e693238443007756e693238443107756e69323844 +3207756e693238443307756e693238443407756e693238443507756e693238443607756e693238443707756e693238443807 +756e693238443907756e693238444107756e693238444207756e693238444307756e693238444407756e693238444507756e +693238444607756e693238453007756e693238453107756e693238453207756e693238453307756e693238453407756e6932 +38453507756e693238453607756e693238453707756e693238453807756e693238453907756e693238454107756e69323845 +4207756e693238454307756e693238454407756e693238454507756e693238454607756e693238463007756e693238463107 +756e693238463207756e693238463307756e693238463407756e693238463507756e693238463607756e693238463707756e +693238463807756e693238463907756e693238464107756e693238464207756e693238464307756e693238464407756e6932 +38464507756e693238464607756e693239303607756e693239303707756e693239304107756e693239304207756e69323934 +3007756e693239343107756e693239383307756e693239383407756e693239434507756e693239434607756e693239443007 +756e693239443107756e693239443207756e693239443307756e693239443407756e693239443507756e693239454207756e +693239464107756e693239464207756e693241303007756e693241303107756e693241303207756e693241304307756e6932 +41304407756e693241304507756e693241304607756e693241313007756e693241313107756e693241313207756e69324131 +3307756e693241313407756e693241313507756e693241313607756e693241313707756e693241313807756e693241313907 +756e693241314107756e693241314207756e693241314307756e693241324607756e693241364107756e693241364207756e +693241374407756e693241374507756e693241374607756e693241383007756e693241383107756e693241383207756e6932 +41383307756e693241383407756e693241383507756e693241383607756e693241383707756e693241383807756e69324138 +3907756e693241384107756e693241384207756e693241384307756e693241384407756e693241384507756e693241384607 +756e693241393007756e693241393107756e693241393207756e693241393307756e693241393407756e693241393507756e +693241393607756e693241393707756e693241393807756e693241393907756e693241394107756e693241394207756e6932 +41394307756e693241394407756e693241394507756e693241394607756e693241413007756e693241414507756e69324141 +4607756e693241423007756e693241423107756e693241423207756e693241423307756e693241423407756e693241423507 +756e693241423607756e693241423707756e693241423807756e693241423907756e693241424107756e693241463907756e +693241464107756e693242303007756e693242303107756e693242303207756e693242303307756e693242303407756e6932 +42303507756e693242303607756e693242303707756e693242303807756e693242303907756e693242304107756e69324230 +4207756e693242304307756e693242304407756e693242304507756e693242304607756e693242313007756e693242313107 +756e693242313207756e693242313307756e693242313407756e693242313507756e693242313607756e693242313707756e +693242313807756e693242313907756e693242314107756e693242314607756e693242323007756e693242323107756e6932 +42323207756e693242323307756e693242323407756e693242353307756e693242353407756e693243363007756e69324336 +3107756e693243363207756e693243363307756e693243363407756e693243363507756e693243363607756e693243363707 +756e693243363807756e693243363907756e693243364107756e693243364207756e693243364307756e693243364407756e +693243364507756e693243364607756e693243373007756e693243373107756e693243373207756e693243373307756e6932 +43373407756e693243373507756e693243373607756e693243373707756e693243373907756e693243374107756e69324337 +4207756e693243374307756e693243374407756e693243374507756e693243374607756e693244303007756e693244303107 +756e693244303207756e693244303307756e693244303407756e693244303507756e693244303607756e693244303707756e +693244303807756e693244303907756e693244304107756e693244304207756e693244304307756e693244304407756e6932 +44304507756e693244304607756e693244313007756e693244313107756e693244313207756e693244313307756e69324431 +3407756e693200><44313507756e693244313607756e693244313707756e693244313807756e693244313907756e69324431 +4107756e693244314207756e693244314307756e693244314407756e693244314507756e693244314607756e693244323007 +756e693244323107756e693244323207756e693244323307756e693244323407756e693244323507756e693244333007756e +693244333107756e693244333207756e693244333307756e693244333407756e693244333507756e693244333607756e6932 +44333707756e693244333807756e693244333907756e693244334107756e693244334207756e693244334307756e69324433 +4407756e693244334507756e693244334607756e693244343007756e693244343107756e693244343207756e693244343307 +756e693244343407756e693244343507756e693244343607756e693244343707756e693244343807756e693244343907756e +693244344107756e693244344207756e693244344307756e693244344407756e693244344507756e693244344607756e6932 +44353007756e693244353107756e693244353207756e693244353307756e693244353407756e693244353507756e69324435 +3607756e693244353707756e693244353807756e693244353907756e693244354107756e693244354207756e693244354307 +756e693244354407756e693244354507756e693244354607756e693244363007756e693244363107756e693244363207756e +693244363307756e693244363407756e693244363507756e693244364607756e693245313807756e693245314607756e6932 +45323207756e693245323307756e693245323407756e693245323507756e693245324507756e693444433007756e69344443 +3107756e693444433207756e693444433307756e693444433407756e693444433507756e693444433607756e693444433707 +756e693444433807756e693444433907756e693444434107756e693444434207756e693444434307756e693444434407756e +693444434507756e693444434607756e693444443007756e693444443107756e693444443207756e693444443307756e6934 +44443407756e693444443507756e693444443607756e693444443707756e693444443807756e693444443907756e69344444 +4107756e693444444207756e693444444307756e693444444407756e693444444507756e693444444607756e693444453007 +756e693444453107756e693444453207756e693444453307756e693444453407756e693444453507756e693444453607756e +693444453707756e693444453807756e693444453907756e693444454107756e693444454207756e693444454307756e6934 +44454407756e693444454507756e693444454607756e693444463007756e693444463107756e693444463207756e69344446 +3307756e693444463407756e693444463507756e693444463607756e693444463707756e693444463807756e693444463907 +756e693444464107756e693444464207756e693444464307756e693444464407756e693444464507756e693444464607756e +694134443007756e694134443107756e694134443207756e694134443307756e694134443407756e694134443507756e6941 +34443607756e694134443707756e694134443807756e694134443907756e694134444107756e694134444207756e69413444 +4307756e694134444407756e694134444507756e694134444607756e694134453007756e694134453107756e694134453207 +756e694134453307756e694134453407756e694134453507756e694134453607756e694134453707756e694134453807756e +694134453907756e694134454107756e694134454207756e694134454307756e694134454407756e694134454507756e6941 +34454607756e694134463007756e694134463107756e694134463207756e694134463307756e694134463407756e69413446 +3507756e694134463607756e694134463707756e694134463807756e694134463907756e694134464107756e694134464207 +756e694134464307756e694134464407756e694134464507756e694134464607756e694136343407756e694136343507756e +694136343607756e694136343707756e694136344307756e694136344407756e694136353007756e694136353107756e6941 +36353407756e694136353507756e694136353607756e694136353707756e694136363207756e694136363307756e69413636 +3407756e694136363507756e694136363607756e694136363707756e694136363807756e694136363907756e694136364107 +756e694136364207756e694136364307756e694136364407756e694136364507756e694136384107756e694136384207756e +694136384307756e694136384407756e694136393407756e694136393507756e694137303807756e694137303907756e6941 +37304107756e694137304207756e694137304307756e694137304407756e694137304507756e694137304607756e69413731 +3007756e694137313107756e694137313207756e694137313307756e694137313407756e694137313507756e694137313607 +756e694137314207756e694137314307756e694137314407756e694137314507756e694137314607756e694137323207756e +694137323307756e694137323407756e694137323507756e694137323607756e694137323707756e694137323807756e6941 +37323907756e694137324107756e694137324207756e694137333007756e694137333107756e694137333207756e69413733 +3307756e694137333407756e694137333507756e694137333607756e694137333707756e694137333807756e694137333907 +756e694137334107756e694137334207756e694137334307756e694137334407756e694137334507756e694137334607756e +694137343007756e694137343107756e694137343607756e694137343707756e694137343807756e694137343907756e6941 +37344107756e694137344207756e694137344507756e694137344607756e694137353007756e694137353107756e69413735 +3207756e694137353307756e694137353607756e694137353707756e694137363407756e694137363507756e694137363607 +756e694137363707756e694137383007756e694137383107756e694137383207756e694137383307756e694137383907756e +694137384107756e694137384207756e694137384307756e694137384407756e694137384507756e694137393007756e6941 +37393107756e694137413007756e694137413107756e694137413207756e694137413307756e694137413407756e69413741 +3507756e694137413607756e694137413707756e694137413807756e694137413907756e694137414107756e694137463807 +756e694137463907756e694137464107756e694137464207756e694137464307756e694137464407756e694137464507756e +694137464609756e69303245352e3509756e69303245362e3509756e69303245372e3509756e69303245382e3509756e6930 +3245392e3509756e69303245352e3409756e69303245362e3409756e69303245372e3409756e69303245382e3409756e6930 +3245392e3409756e69303245352e3309756e69303245362e3309756e69303245372e3309756e69303245382e3309756e6930 +3245392e3309756e69303245352e3209756e69303245362e3209756e69303245372e3209756e69303245382e3209756e6930 +3245392e3209756e69303245352e3109756e69303245362e3109756e69303245372e3109756e69303245382e3109756e6930 +3245392e31047374656d07756e694630303007756e694630303107756e694630303207756e694630303307756e6946343030 +07756e694634303107756e694634303207756e694634303307756e694634303407756e694634303507756e69463430360775 +6e694634303707756e694634303807756e694634303907756e694634304107756e694634304207756e694634304307756e69 +4634304407756e694634304507756e694634304607756e694634313007756e694634313107756e694634313207756e694634 +313307756e694634313407756e694634313507756e694634313607756e694634313707756e694634313807756e6946343139 +07756e694634314107756e694634314207756e694634314307756e694634314407756e694634314507756e69463431460775 +6e694634323007756e694634323107756e694634323207756e694634323307756e694634323407756e694634323507756e69 +4634323607756e694634323807756e694634323907756e694634324107756e694634324207756e694634324307756e694634 +324407756e694634324507756e694634324607756e694634333007756e694634333107756e694634333207756e6946343333 +07756e694634333407756e694634333507756e694634333607756e694634333707756e694634333807756e69463433390775 +6e694634334107756e694634334207756e694634334307756e694634334407756e694634334507756e694634334607756e69 +4634343007756e694634343107756e694636433507756e694642303007756e694642303307756e694642303407756e694642 +303507756e694642303607756e694642313307756e694642313407756e694642313507756e694642313607756e6946423137 +07756e694642314407756e694642314507756e694642314607756e694642323007756e694642323107756e69464232320775 +6e694642323307756e694642323407756e694642323507756e694642323607756e694642323707756e694642323807756e69 +4642323907756e694642324107756e694642324207756e694642324307756e694642324407756e694642324507756e694642 +324607756e694642333007756e694642333107756e694642333207756e694642333307756e694642333407756e6946423335 +07756e694642333607756e694642333807756e694642333907756e694642334107756e694642334207756e69464233430775 +6e694642334507756e694642343007756e694642343107756e694642343307756e694642343407756e694642343607756e69 +4642343707756e694642343807756e694642343907756e694642344107756e694642344207756e694642344307756e694642 +344407756e694642344507756e694642344607756e694642353207756e694642353307756e694642353407756e6946423535 +07756e694642353607756e694642353707756e694642353807756e694642353907756e694642354107756e69464235420775 +6e694642354307756e694642354407756e694642354507756e694642354607756e694642363007756e694642363107756e69 +4642363207756e694642363307756e694642363407756e694642363507756e694642363607756e694642363707756e694642 +363807756e694642363907756e694642364107756e694642364207756e694642364307756e694642364407756e6946423645 +07756e694642364607756e694642373007756e694642373107756e694642373207756e694642373307756e69464237340775 +6e694642373507756e694642373607756e694642373707756e694642373807756e694642373907756e694642374107756e69 +4642374207756e694642374307756e694642374407756e694642374507756e694642374607756e694642383007756e694642 +383107756e694642383207756e694642383307756e694642383407756e694642383507756e694642383607756e6946423837 +07756e694642383807756e694642383907756e694642384107756e694642384207756e694642384307756e69464238440775 +6e694642384507756e694642384607756e694642393007756e694642393107756e694642393207756e694642393307756e69 +4642393407756e694642393507756e694642393607756e694642393707756e694642393807756e694642393907756e694642 +394107756e694642394207756e694642394307756e694642394407756e694642394507756e694642394607756e6946424130 +07756e694642413107756e694642413207756e694642413307756e694642414107756e694642414207756e69464241430775 +6e694642414407756e694642443307756e694642443407756e694642443507756e694642443607756e694642443707756e69 +4642443807756e694642443907756e694642444107756e694642444207756e694642444307756e694642444507756e694642 +444607756e694642453407756e694642453507756e694642453607756e694642453707756e694642453807756e6946424539 +07756e694642464307756e694642464407756e694642464507756e694642464607756e694645303007756e69464530310775 +6e694645303207756e694645303307756e694645303407756e694645303507756e694645303607756e694645303707756e69 +4645303807756e694645303907756e694645304107756e694645304207756e694645304307756e694645304407756e694645 +304507756e694645304607756e694645323007756e694645323107756e694645323207756e694645323307756e6946453730 +07756e694645373107756e694645373207756e694645373307756e694645373407756e694645373607756e69464537370775 +6e694645373807756e694645373907756e694645374107756e694645374207756e694645374307756e694645374407756e69 +4645374507756e694645374607756e694645383007756e694645383107756e694645383207756e694645383307756e694645 +383407756e694645383507756e694645383607756e694645383707756e694645383807756e694645383907756e6946453841 +07756e694645384207756e694645384307756e694645384407756e694645384507756e694645384607756e69464539300775 +6e694645393107756e694645393207756e694645393307756e694645393407756e694645393507756e694645393607756e69 +4645393707756e694645393807756e694645393907756e694645394107756e694645394207756e694645394307756e694645 +394407756e694645394507756e694645394607756e694645413007756e694645413107756e694645413207756e6946454133 +07756e694645413407756e694645413507756e694645413607756e694645413707756e694645413807756e69464541390775 +6e694645414107756e694645414207756e694645414307756e694645414407756e694645414507756e694645414607756e69 +4645423007756e694645423107756e694645423207756e694645423307756e694645423407756e694645423507756e694645 +423607756e694645423707756e694645423807756e694645423907756e694645424107756e694645424207756e6946454243 +07756e694645424407756e694645424507756e694645424607756e694645433007756e694645433107756e69464543320775 +6e694645433307756e694645433407756e694645433507756e694645433607756e694645433707756e694645433807756e69 +4645433907756e694645434107756e694645434207756e694645434307756e694645434407756e694645434507756e694645 +434607756e694645443007756e694645443107756e694645443207756e694645443307756e694645443407756e6946454435 +07756e694645443607756e694645443707756e694645443807756e694645443907756e694645444107756e69464544420775 +6e694645444307756e694645444407756e694645444507756e694645444607756e694645453007756e694645453107756e69 +4645453207756e694645453307756e694645453407756e694645453507756e694645453607756e694645453707756e694645 +453807756e694645453907756e694645454107756e694645454207756e694645454307756e694645454407756e6946454545 +07756e694645454607756e694645463007756e694645463107756e694645463207756e694645463307756e69464546340775 +6e694645463507756e694645463607756e694645463707756e694645463807756e694645463907756e694645464107756e69 +4645464207756e694645464307756e694645464607756e694646463907756e694646464107756e694646464207756e694646 +464307756e694646464406753130333030067531303330310675313033303206753130333033067531303330340675313033 +3035067531303330360675313033303706753130333038067531303330390675313033304106753130333042067531303330 +4306753130333044067531303330450675313033304606753130333130067531303331310675313033313206753130333133 +0675313033313406753130333135067531303331360675313033313706753130333138067531303331390675313033314106 +7531303331420675313033314306753130333144067531303331450675313033323006753130333231067531303332320675 +3130333233067531443330300675314433303106753144333032067531443330330675314433303406753144333035067531 +4433303606753144333037067531443330380675314433303906753144333041067531443330420675314433304306753144 +3330440675314433304506753144333046067531443331300675314433313106753144333132067531443331330675314433 +3134067531443331350675314433313606753144333137067531443331380675314433313906753144333141067531443331 +4206753144333143067531443331440675314433314506753144333146067531443332300675314433323106753144333232 +0675314433323306753144333234067531443332350675314433323606753144333237067531443332380675314433323906 +7531443332410675314433324206753144333243067531443332440675314433324506753144333246067531443333300675 +3144333331067531443333320675314433333306753144333334067531443333350675314433333606753144333337067531 +4433333806753144333339067531443333410675314433334206753144333343067531443333440675314433334506753144 +3333460675314433343006753144333431067531443334320675314433343306753144333434067531443334350675314433 +3436067531443334370675314433343806753144333439067531443334410675314433344206753144333443067531443334 +4406753144333445067531443334460675314433353006753144333531067531443335320675314433353306753144333534 +0675314433353506753144333536067531443533380675314435333906753144353342067531443533430675314435334406 +7531443533450675314435343006753144353431067531443534320675314435343306753144353434067531443534360675 +3144353441067531443534420675314435344306753144353444067531443534450675314435344606753144353530067531 +4435353206753144353533067531443535340675314435353506753144353536067531443535370675314435353806753144 +3535390675314435354106753144353542067531443535430675314435354406753144353545067531443535460675314435 +3630067531443536310675314435363206753144353633067531443536340675314435363506753144353636067531443536 +3706753144353638067531443536390675314435364106753144353642067531443541300675314435413106753144354132 +0675314435413306753144354134067531443541350675314435413606753144354137067531443541380675314435413906 +7531443541410675314435414206753144354143067531443541440675314435414506753144354146067531443542300675 +3144354231067531443542320675314435423306753144354234067531443542350675314435423606753144354237067531 +4435423806753144354239067531443542410675314435424206753144354243067531443542440675314435424506753144 +3542460675314435433006753144354331067531443543320675314435433306753144354334067531443543350675314435 +4336067531443543370675314435433806753144354339067531443543410675314435434206753144354343067531443543 +4406753144354345067531443543460675314435443006753144354431067531443544320675314435443306753144374438 +0675314437443906753144374441067531443744420675314437444306753144374444067531443744450675314437444606 +7531443745300675314437453106753144374532067531443745330675314437453406753144374535067531443745360675 +3144374537067531443745380675314437453906753144374541067531443745420675314545303006753145453031067531 +4545303206753145453033067531454530350675314545303606753145453037067531454530380675314545303906753145 +4530410675314545304206753145453043067531454530440675314545304506753145453046067531454531300675314545 +3131067531454531320675314545313306753145453134067531454531350675314545313606753145453137067531454531 +3806753145453139067531454531410675314545314206753145453143067531454531440675314545314506753145453146 +0675314545323106753145453232067531454532340675314545323706753145453239067531454532410675314545324206 +7531454532430675314545324406753145453245067531454532460675314545333006753145453331067531454533320675 +3145453334067531454533350675314545333606753145453337067531454533390675314545334206753145453631067531 +4545363206753145453634067531454536370675314545363806753145453639067531454536410675314545364306753145 +4536440675314545364506753145453646067531454537300675314545373106753145453732067531454537340675314545 +3735067531454537360675314545373706753145453739067531454537410675314545374206753145453743067531454537 +4506753146303330067531463033310675314630333206753146303333067531463033340675314630333506753146303336 +0675314630333706753146303338067531463033390675314630334106753146303342067531463033430675314630334406 +7531463033450675314630334606753146303430067531463034310675314630343206753146303433067531463034340675 +3146303435067531463034360675314630343706753146303438067531463034390675314630344106753146303442067531 +4630344306753146303444067531463034450675314630344606753146303530067531463035310675314630353206753146 +3035330675314630353406753146303535067531463035360675314630353706753146303538067531463035390675314630 +3541067531463035420675314630354306753146303544067531463035450675314630354606753146303630067531463036 +3106753146303632067531463036330675314630363406753146303635067531463036360675314630363706753146303638 +0675314630363906753146303641067531463036420675314630364306753146303644067531463036450675314630364606 +7531463037300675314630373106753146303732067531463037330675314630373406753146303735067531463037360675 +3146303737067531463037380675314630373906753146303741067531463037420675314630374306753146303744067531 +4630374506753146303746067531463038300675314630383106753146303832067531463038330675314630383406753146 +3038350675314630383606753146303837067531463038380675314630383906753146303841067531463038420675314630 +3843067531463038440675314630384506753146303846067531463039300675314630393106753146303932067531463039 +3306753146304130067531463041310675314630413206753146304133067531463041340675314630413506753146304136 +0675314630413706753146304138067531463041390675314630414106753146304142067531463041430675314630414406 +7531463041450675314630423106753146304232067531463042330675314630423406753146304235067531463042360675 +3146304237067531463042380675314630423906753146304241067531463042420675314630424306753146304244067531 +4630424506753146304331067531463043320675314630433306753146304334067531463043350675314630433606753146 +3043370675314630433806753146304339067531463043410675314630434206753146304343067531463043440675314630 +4345067531463043460675314630443106753146304432067531463044330675314630443406753146304435067531463044 +3606753146304437067531463044380675314630443906753146304441067531463044420675314630444306753146304444 +0675314630444506753146304446067531463432440675314634324506753146343331067531463433350675314636303006 +7531463630310675314636303206753146363033067531463630340675314636303506753146363036067531463630370675 +3146363038067531463630390675314636304106753146363042067531463630430675314636304406753146363045067531 +4636304606753146363130067531463631310675314636313206753146363133067531463631340675314636313506753146 +3631360675314636313706753146363138067531463631390675314636314106753146363142067531463631430675314636 +3144067531463631450675314636314606753146363230067531463632310675314636323206753146363233067531463632 +3506753146363236067531463632370675314636323806753146363239067531463632410675314636324206753146363244 +0675314636324506753146363246067531463633300675314636333106753146363332067531463633330675314636333406 +7531463633350675314636333606753146363337067531463633380675314636333906753146363341067531463633420675 +31463633430675314636334406753146363345067531463633460675314636343009646c4c746361726f6e08446965726573 +69730541637574650554696c64650547726176650a43697263756d666c6578054361726f6e0c756e69303331312e63617365 +05427265766509446f74616363656e740c48756e676172756d6c6175740b446f75626c6567726176650a6172616269635f64 +6f740c6172616269635f32646f74730c6172616269635f33646f74730e6172616269635f33646f74735f610e617261626963 +5f32646f74735f610c6172616269635f34646f74730c756e69303636452e66696e610c756e69303636452e696e69740c756e +69303636452e6d6564690c756e69303641312e66696e610c756e69303641312e696e69740c756e69303641312e6d6564690c +756e69303636462e66696e610c756e69303636462e696e69740c756e69303636462e6d6564690c756e69303642412e696e69 +740c756e69303642412e6d6564690b6172616269635f72696e670c756e69303637432e66696e610c756e69303637432e696e +69740c756e69303637432e6d6564690c756e69303637442e66696e610c756e69303637442e696e69740c756e69303637442e +6d6564690c756e69303638312e66696e610c756e69303638312e696e69740c756e69303638312e6d6564690c756e69303638 +322e66696e610c756e69303638322e696e69740c756e69303638322e6d6564690c756e69303638352e66696e610c756e6930 +3638352e696e69740c756e69303638352e6d6564690c756e69303642462e66696e610c756e69303642462e696e69740c756e +69303642462e6d6564690e6172616269635f6761665f62617207456e672e616c740f756e69303236382e646f746c6573730f +756e69303239442e646f746c6573730b756e6930333038303330340b756e6930333034303330380b756e6930333037303330 +340b756e6930333038303330310b756e6930333038303330300b756e6930333034303330310b756e6930333034303330300b +756e6930333033303330340b756e69303330383033304300b8028040fffbfe03fa1403f92503f83203f79603f60e03f5fe03 +f4fe03f32503f20e03f19603f02503ef8a4105effe03ee9603ed9603ecfa03ebfa03eafe03e93a03e84203e7fe03e63203e5 +e45305e59603e48a4105e45303e3e22f05e3fa03e22f03e1fe03e0fe03df3203de1403dd9603dcfe03db1203da7d03d9bb03 +d8fe03d68a4105d67d03d5d44705d57d03d44703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e05cc +fe03cb1e03ca3203c9fe03c6851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe03bcfe03bbfe +03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05b7bb03b78004b6b52505b65d40ff03b64004b52503b4fe03b39603b2 +fe03b1fe03b0fe03affe03ae6403ad0e03acab2505ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03 +a51203a4fe03a3a20e05a33203a20e03a16403a08a4105a096039ffe039e9d0c059efe039d0c039c9b19059c64039b9a1005 +9b19039a1003990a0398fe0397960d0597fe03960d03958a410595960394930e05942803930e0392fa039190bb0591fe0390 +8f5d0590bb039080048f8e25058f5d038f40048e25038dfe038c8b2e058cfe038b2e038a8625058a410389880b0589140388 +0b03878625058764038685110586250385110384fe038382110583fe0382110381fe0380fe037ffe0340ff7e7d7d057efe03 +7d7d037c64037b5415057b25037afe0379fe03780e03770c03760a0375fe0374fa0373fa0372fa0371fa0370fe036ffe036e +fe036c21036bfe036a1142056a530369fe03687d036711420566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035d +fe035bfe035afe0359580a0559fa03580a035716190557320356fe035554150555420354150353011005531803521403514a +130551fe03500b034ffe034e4d10054efe034d10034cfe034b4a13054bfe034a4910054a1303491d0d05491003480d0347fe +0346960345960344fe0343022d0543fa0342bb03414b0340fe033ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff +0f033b0d033afe0339fe033837140538fa033736100537140336350b05361003350b03341e03330d0332310b0532fe03310b +03302f0b05300d032f0b032e2d09052e10032d09032c32032b2a25052b64032a2912052a2503291203282725052841032725 +0326250b05260f03250b0324fe0323fe03220f03210110052112032064031ffa031e1d0d051e64031d0d031c1142051cfe03 +1bfa031a42031911420519fe031864031716190517fe031601100516190315fe0314fe0313fe031211420512fe0311022d05 +114203107d030f64030efe030d0c16050dfe030c0110050c16030bfe030a100309fe0308022d0508fe030714030664030401 +100504fe03401503022d0503fe0302011005022d0301100300fe0301b80164858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b00 +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b1d00>]def + FontName currentdict end definefont pop -systemdict/resourcestatus known - {42 /FontType resourcestatus - {pop pop false}{true}ifelse} - {true}ifelse -{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse -/FontType 3 def - /TrueState 271 string def - TrueDict begin sfnts save - 72 0 matrix defaultmatrix dtransform dup - mul exch dup mul add sqrt cvi 0 72 matrix - defaultmatrix dtransform dup mul exch dup - mul add sqrt cvi 3 -1 roll restore - TrueState initer end - /BuildGlyph{exch begin - CharStrings dup 2 index known - {exch}{exch pop /.notdef}ifelse - get dup xcheck - {currentdict systemdict begin begin exec end end} - {TrueDict begin /bander load cvlit exch TrueState render end} - ifelse - end}bind def - /BuildChar{ - 1 index /Encoding get exch get - 1 index /BuildGlyph get exec - }bind def -}if - -FontName currentdict end definefont pop -%!PS-TrueTypeFont-1.0-0.58982 -%%Title: unknown -%%Creator: Converted from TrueType to type 42 by PPR -15 dict begin -/FontName /WenQuanYiZenHei def -/PaintType 0 def -/FontMatrix[1 0 0 1 0 0]def -/FontBBox[-126 -297 1051 963]def -/FontType 42 def -/Encoding StandardEncoding def -/FontInfo 10 dict dup begin -/FamilyName (unknown) def -/FullName (unknown) def -/Weight (unknown) def -/Version (unknown) def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -230 def -/UnderlineThickness 51 def -end readonly def -/sfnts[<00010000000700400002003063767420002202880000007C00000004676C7966 -AC20EA39000000800000024A68656164F2831BDF000002CC000000366868656107EC01A3 -0000030400000024686D747805A7004E000003280000001A6C6F636101A0011100000344 -000000126D617870008A02690000035800000020002202880002000DFF8503D50341000D -0024000005260736351134271637061511140106072E0127020726273E01371617371716 -17071617160223292A04042A290301B4250C80D74AC4F7122795F54C171806050B0B0958 -74627B04044D4C014C4D4D04044D4DFEB44C01D91A2B27C278FEE18B2C194DEFA3100C03 -0806050E9B59460000010021FF8203EC033300230000011617070607062B01222E011037 -23130607060726273E013503211114163B013637363703A712331E020C070AD6303F0503 -DF0104584E781630809C01017715119C1204040101012503F3150D053D5F02652CFE9FD0 -8176422D0D33F1AB01A7FD05141D01101D1D0002001FFF7C03D60369002A003700000106 -17262321151407062736271E01363D012122073627163321353721220736271633211706 -0F011521320123350527371707211523352103D603033A3BFECA1E2B720C24215A10FEB1 -3A3A03033A3A014FA5FEB53A3B04043B3A01BC081F189F01363BFCE74801994E2D6B2001 -9F49FD2F011D1F2003FE261C2501322604010C1BEA03201F03499703201F034108159229 -0118BF0137414A2EBE8500050010FF8803DF03350016001B00230027002D000025260322 -0736271633210207161706072627060726273613161736370126273E011317031307273F -0126273716170264721E201F03033C3D01591E916EB82915A46F8AD01B25E0441A5E7815 -FD7B2A3E2C5E5929741F40953FA845523C564AD3CF011902212103FEADDBA66510265EA8 -AE5123184D02A4F9B4BBF2FCCE180251D0010115FE850193318832204C42344650000000 -000100000000E666EDAC36235F0F3CF5003F040000000000C7BE78E900000000C7BE78E9 -FF7FFED0043403DA0000000800020000000000000001000003DAFED0005C0455FF7FFE78 -043400010000000000000000000000000000000501760022000000000000000000000000 -0400000D0021001F00100000000000000000000000000040007B00D10125000000010000 -00080165002800D10012000200000001000100000040002E0006000200>]def -/CharStrings 5 dict dup begin + %%!PS-TrueTypeFont-1.0-2.3499908 + 10 dict begin + /FontType 42 def + /FontMatrix [1 0 0 1 0 0] def + /FontName /DejaVuSans-1 def + /FontInfo 7 dict dup begin + /FullName (DejaVu Sans) def + /FamilyName (DejaVu Sans) def + /Version (Version 2.35) def + /ItalicAngle 0.0 def + /isFixedPitch false def + /UnderlinePosition -130 def + /UnderlineThickness 90 def + end readonly def + /Encoding StandardEncoding def + /FontBBox [-2090 -948 3673 2524] def + /PaintType 0 def + /CIDMap 0 def + /CharStrings 5862 dict dup begin /.notdef 0 def -/uni51E0 5 def -/uni6C49 7 def -/uni4E2A 4 def -/uni5B57 6 def +/.null 1 def +/nonmarkingreturn 2 def +/space 3 def +/exclam 4 def +/quotedbl 5 def +/numbersign 6 def +/dollar 7 def +/percent 8 def +/ampersand 9 def +/quotesingle 10 def +/parenleft 11 def +/parenright 12 def +/asterisk 13 def +/plus 14 def +/comma 15 def +/hyphen 16 def +/period 17 def +/slash 18 def +/zero 19 def +/one 20 def +/two 21 def +/three 22 def +/four 23 def +/five 24 def +/six 25 def +/seven 26 def +/eight 27 def +/nine 28 def +/colon 29 def +/semicolon 30 def +/less 31 def +/equal 32 def +/greater 33 def +/question 34 def +/at 35 def +/A 36 def +/B 37 def +/C 38 def +/D 39 def +/E 40 def +/F 41 def +/G 42 def +/H 43 def +/I 44 def +/J 45 def +/K 46 def +/L 47 def +/M 48 def +/N 49 def +/O 50 def +/P 51 def +/Q 52 def +/R 53 def +/S 54 def +/T 55 def +/U 56 def +/V 57 def +/W 58 def +/X 59 def +/Y 60 def +/Z 61 def +/bracketleft 62 def +/backslash 63 def +/bracketright 64 def +/asciicircum 65 def +/underscore 66 def +/grave 67 def +/a 68 def +/b 69 def +/c 70 def +/d 71 def +/e 72 def +/f 73 def +/g 74 def +/h 75 def +/i 76 def +/j 77 def +/k 78 def +/l 79 def +/m 80 def +/n 81 def +/o 82 def +/p 83 def +/q 84 def +/r 85 def +/s 86 def +/t 87 def +/u 88 def +/v 89 def +/w 90 def +/x 91 def +/y 92 def +/z 93 def +/braceleft 94 def +/bar 95 def +/braceright 96 def +/asciitilde 97 def +/nonbreakingspace 98 def +/exclamdown 99 def +/cent 100 def +/sterling 101 def +/currency 102 def +/yen 103 def +/brokenbar 104 def +/section 105 def +/dieresis 106 def +/copyright 107 def +/ordfeminine 108 def +/guillemotleft 109 def +/logicalnot 110 def +/sfthyphen 111 def +/registered 112 def +/macron 113 def +/degree 114 def +/plusminus 115 def +/twosuperior 116 def +/threesuperior 117 def +/acute 118 def +/mu 119 def +/paragraph 120 def +/periodcentered 121 def +/cedilla 122 def +/onesuperior 123 def +/ordmasculine 124 def +/guillemotright 125 def +/onequarter 126 def +/onehalf 127 def +/threequarters 128 def +/questiondown 129 def +/Agrave 130 def +/Aacute 131 def +/Acircumflex 132 def +/Atilde 133 def +/Adieresis 134 def +/Aring 135 def +/AE 136 def +/Ccedilla 137 def +/Egrave 138 def +/Eacute 139 def +/Ecircumflex 140 def +/Edieresis 141 def +/Igrave 142 def +/Iacute 143 def +/Icircumflex 144 def +/Idieresis 145 def +/Eth 146 def +/Ntilde 147 def +/Ograve 148 def +/Oacute 149 def +/Ocircumflex 150 def +/Otilde 151 def +/Odieresis 152 def +/multiply 153 def +/Oslash 154 def +/Ugrave 155 def +/Uacute 156 def +/Ucircumflex 157 def +/Udieresis 158 def +/Yacute 159 def +/Thorn 160 def +/germandbls 161 def +/agrave 162 def +/aacute 163 def +/acircumflex 164 def +/atilde 165 def +/adieresis 166 def +/aring 167 def +/ae 168 def +/ccedilla 169 def +/egrave 170 def +/eacute 171 def +/ecircumflex 172 def +/edieresis 173 def +/igrave 174 def +/iacute 175 def +/icircumflex 176 def +/idieresis 177 def +/eth 178 def +/ntilde 179 def +/ograve 180 def +/oacute 181 def +/ocircumflex 182 def +/otilde 183 def +/odieresis 184 def +/divide 185 def +/oslash 186 def +/ugrave 187 def +/uacute 188 def +/ucircumflex 189 def +/udieresis 190 def +/yacute 191 def +/thorn 192 def +/ydieresis 193 def +/Amacron 194 def +/amacron 195 def +/Abreve 196 def +/abreve 197 def +/Aogonek 198 def +/aogonek 199 def +/Cacute 200 def +/cacute 201 def +/Ccircumflex 202 def +/ccircumflex 203 def +/Cdotaccent 204 def +/cdotaccent 205 def +/Ccaron 206 def +/ccaron 207 def +/Dcaron 208 def +/dcaron 209 def +/Dcroat 210 def +/dcroat 211 def +/Emacron 212 def +/emacron 213 def +/Ebreve 214 def +/ebreve 215 def +/Edotaccent 216 def +/edotaccent 217 def +/Eogonek 218 def +/eogonek 219 def +/Ecaron 220 def +/ecaron 221 def +/Gcircumflex 222 def +/gcircumflex 223 def +/Gbreve 224 def +/gbreve 225 def +/Gdotaccent 226 def +/gdotaccent 227 def +/Gcommaaccent 228 def +/gcommaaccent 229 def +/Hcircumflex 230 def +/hcircumflex 231 def +/Hbar 232 def +/hbar 233 def +/Itilde 234 def +/itilde 235 def +/Imacron 236 def +/imacron 237 def +/Ibreve 238 def +/ibreve 239 def +/Iogonek 240 def +/iogonek 241 def +/Idotaccent 242 def +/dotlessi 243 def +/IJ 244 def +/ij 245 def +/Jcircumflex 246 def +/jcircumflex 247 def +/Kcommaaccent 248 def +/kcommaaccent 249 def +/kgreenlandic 250 def +/Lacute 251 def +/lacute 252 def +/Lcommaaccent 253 def +/lcommaaccent 254 def +/Lcaron 255 def +/lcaron 256 def +/Ldot 257 def +/ldot 258 def +/Lslash 259 def +/lslash 260 def +/Nacute 261 def +/nacute 262 def +/Ncommaaccent 263 def +/ncommaaccent 264 def +/Ncaron 265 def +/ncaron 266 def +/napostrophe 267 def +/Eng 268 def +/eng 269 def +/Omacron 270 def +/omacron 271 def +/Obreve 272 def +/obreve 273 def +/Ohungarumlaut 274 def +/ohungarumlaut 275 def +/OE 276 def +/oe 277 def +/Racute 278 def +/racute 279 def +/Rcommaaccent 280 def +/rcommaaccent 281 def +/Rcaron 282 def +/rcaron 283 def +/Sacute 284 def +/sacute 285 def +/Scircumflex 286 def +/scircumflex 287 def +/Scedilla 288 def +/scedilla 289 def +/Scaron 290 def +/scaron 291 def +/Tcommaaccent 292 def +/tcommaaccent 293 def +/Tcaron 294 def +/tcaron 295 def +/Tbar 296 def +/tbar 297 def +/Utilde 298 def +/utilde 299 def +/Umacron 300 def +/umacron 301 def +/Ubreve 302 def +/ubreve 303 def +/Uring 304 def +/uring 305 def +/Uhungarumlaut 306 def +/uhungarumlaut 307 def +/Uogonek 308 def +/uogonek 309 def +/Wcircumflex 310 def +/wcircumflex 311 def +/Ycircumflex 312 def +/ycircumflex 313 def +/Ydieresis 314 def +/Zacute 315 def +/zacute 316 def +/Zdotaccent 317 def +/zdotaccent 318 def +/Zcaron 319 def +/zcaron 320 def +/longs 321 def +/uni0180 322 def +/uni0181 323 def +/uni0182 324 def +/uni0183 325 def +/uni0184 326 def +/uni0185 327 def +/uni0186 328 def +/uni0187 329 def +/uni0188 330 def +/uni0189 331 def +/uni018A 332 def +/uni018B 333 def +/uni018C 334 def +/uni018D 335 def +/uni018E 336 def +/uni018F 337 def +/uni0190 338 def +/uni0191 339 def +/florin 340 def +/uni0193 341 def +/uni0194 342 def +/uni0195 343 def +/uni0196 344 def +/uni0197 345 def +/uni0198 346 def +/uni0199 347 def +/uni019A 348 def +/uni019B 349 def +/uni019C 350 def +/uni019D 351 def +/uni019E 352 def +/uni019F 353 def +/Ohorn 354 def +/ohorn 355 def +/uni01A2 356 def +/uni01A3 357 def +/uni01A4 358 def +/uni01A5 359 def +/uni01A6 360 def +/uni01A7 361 def +/uni01A8 362 def +/uni01A9 363 def +/uni01AA 364 def +/uni01AB 365 def +/uni01AC 366 def +/uni01AD 367 def +/uni01AE 368 def +/Uhorn 369 def +/uhorn 370 def +/uni01B1 371 def +/uni01B2 372 def +/uni01B3 373 def +/uni01B4 374 def +/uni01B5 375 def +/uni01B6 376 def +/uni01B7 377 def +/uni01B8 378 def +/uni01B9 379 def +/uni01BA 380 def +/uni01BB 381 def +/uni01BC 382 def +/uni01BD 383 def +/uni01BE 384 def +/uni01BF 385 def +/uni01C0 386 def +/uni01C1 387 def +/uni01C2 388 def +/uni01C3 389 def +/uni01C4 390 def +/uni01C5 391 def +/uni01C6 392 def +/uni01C7 393 def +/uni01C8 394 def +/uni01C9 395 def +/uni01CA 396 def +/uni01CB 397 def +/uni01CC 398 def +/uni01CD 399 def +/uni01CE 400 def +/uni01CF 401 def +/uni01D0 402 def +/uni01D1 403 def +/uni01D2 404 def +/uni01D3 405 def +/uni01D4 406 def +/uni01D5 407 def +/uni01D6 408 def +/uni01D7 409 def +/uni01D8 410 def +/uni01D9 411 def +/uni01DA 412 def +/uni01DB 413 def +/uni01DC 414 def +/uni01DD 415 def +/uni01DE 416 def +/uni01DF 417 def +/uni01E0 418 def +/uni01E1 419 def +/uni01E2 420 def +/uni01E3 421 def +/uni01E4 422 def +/uni01E5 423 def +/Gcaron 424 def +/gcaron 425 def +/uni01E8 426 def +/uni01E9 427 def +/uni01EA 428 def +/uni01EB 429 def +/uni01EC 430 def +/uni01ED 431 def +/uni01EE 432 def +/uni01EF 433 def +/uni01F0 434 def +/uni01F1 435 def +/uni01F2 436 def +/uni01F3 437 def +/uni01F4 438 def +/uni01F5 439 def +/uni01F6 440 def +/uni01F7 441 def +/uni01F8 442 def +/uni01F9 443 def +/Aringacute 444 def +/aringacute 445 def +/AEacute 446 def +/aeacute 447 def +/Oslashacute 448 def +/oslashacute 449 def +/uni0200 450 def +/uni0201 451 def +/uni0202 452 def +/uni0203 453 def +/uni0204 454 def +/uni0205 455 def +/uni0206 456 def +/uni0207 457 def +/uni0208 458 def +/uni0209 459 def +/uni020A 460 def +/uni020B 461 def +/uni020C 462 def +/uni020D 463 def +/uni020E 464 def +/uni020F 465 def +/uni0210 466 def +/uni0211 467 def +/uni0212 468 def +/uni0213 469 def +/uni0214 470 def +/uni0215 471 def +/uni0216 472 def +/uni0217 473 def +/Scommaaccent 474 def +/scommaaccent 475 def +/uni021A 476 def +/uni021B 477 def +/uni021C 478 def +/uni021D 479 def +/uni021E 480 def +/uni021F 481 def +/uni0220 482 def +/uni0221 483 def +/uni0222 484 def +/uni0223 485 def +/uni0224 486 def +/uni0225 487 def +/uni0226 488 def +/uni0227 489 def +/uni0228 490 def +/uni0229 491 def +/uni022A 492 def +/uni022B 493 def +/uni022C 494 def +/uni022D 495 def +/uni022E 496 def +/uni022F 497 def +/uni0230 498 def +/uni0231 499 def +/uni0232 500 def +/uni0233 501 def +/uni0234 502 def +/uni0235 503 def +/uni0236 504 def +/dotlessj 505 def +/uni0238 506 def +/uni0239 507 def +/uni023A 508 def +/uni023B 509 def +/uni023C 510 def +/uni023D 511 def +/uni023E 512 def +/uni023F 513 def +/uni0240 514 def +/uni0241 515 def +/uni0242 516 def +/uni0243 517 def +/uni0244 518 def +/uni0245 519 def +/uni0246 520 def +/uni0247 521 def +/uni0248 522 def +/uni0249 523 def +/uni024A 524 def +/uni024B 525 def +/uni024C 526 def +/uni024D 527 def +/uni024E 528 def +/uni024F 529 def +/uni0250 530 def +/uni0251 531 def +/uni0252 532 def +/uni0253 533 def +/uni0254 534 def +/uni0255 535 def +/uni0256 536 def +/uni0257 537 def +/uni0258 538 def +/uni0259 539 def +/uni025A 540 def +/uni025B 541 def +/uni025C 542 def +/uni025D 543 def +/uni025E 544 def +/uni025F 545 def +/uni0260 546 def +/uni0261 547 def +/uni0262 548 def +/uni0263 549 def +/uni0264 550 def +/uni0265 551 def +/uni0266 552 def +/uni0267 553 def +/uni0268 554 def +/uni0269 555 def +/uni026A 556 def +/uni026B 557 def +/uni026C 558 def +/uni026D 559 def +/uni026E 560 def +/uni026F 561 def +/uni0270 562 def +/uni0271 563 def +/uni0272 564 def +/uni0273 565 def +/uni0274 566 def +/uni0275 567 def +/uni0276 568 def +/uni0277 569 def +/uni0278 570 def +/uni0279 571 def +/uni027A 572 def +/uni027B 573 def +/uni027C 574 def +/uni027D 575 def +/uni027E 576 def +/uni027F 577 def +/uni0280 578 def +/uni0281 579 def +/uni0282 580 def +/uni0283 581 def +/uni0284 582 def +/uni0285 583 def +/uni0286 584 def +/uni0287 585 def +/uni0288 586 def +/uni0289 587 def +/uni028A 588 def +/uni028B 589 def +/uni028C 590 def +/uni028D 591 def +/uni028E 592 def +/uni028F 593 def +/uni0290 594 def +/uni0291 595 def +/uni0292 596 def +/uni0293 597 def +/uni0294 598 def +/uni0295 599 def +/uni0296 600 def +/uni0297 601 def +/uni0298 602 def +/uni0299 603 def +/uni029A 604 def +/uni029B 605 def +/uni029C 606 def +/uni029D 607 def +/uni029E 608 def +/uni029F 609 def +/uni02A0 610 def +/uni02A1 611 def +/uni02A2 612 def +/uni02A3 613 def +/uni02A4 614 def +/uni02A5 615 def +/uni02A6 616 def +/uni02A7 617 def +/uni02A8 618 def +/uni02A9 619 def +/uni02AA 620 def +/uni02AB 621 def +/uni02AC 622 def +/uni02AD 623 def +/uni02AE 624 def +/uni02AF 625 def +/uni02B0 626 def +/uni02B1 627 def +/uni02B2 628 def +/uni02B3 629 def +/uni02B4 630 def +/uni02B5 631 def +/uni02B6 632 def +/uni02B7 633 def +/uni02B8 634 def +/uni02B9 635 def +/uni02BA 636 def +/uni02BB 637 def +/uni02BC 638 def +/uni02BD 639 def +/uni02BE 640 def +/uni02BF 641 def +/uni02C0 642 def +/uni02C1 643 def +/uni02C2 644 def +/uni02C3 645 def +/uni02C4 646 def +/uni02C5 647 def +/circumflex 648 def +/caron 649 def +/uni02C8 650 def +/uni02C9 651 def +/uni02CA 652 def +/uni02CB 653 def +/uni02CC 654 def +/uni02CD 655 def +/uni02CE 656 def +/uni02CF 657 def +/uni02D0 658 def +/uni02D1 659 def +/uni02D2 660 def +/uni02D3 661 def +/uni02D4 662 def +/uni02D5 663 def +/uni02D6 664 def +/uni02D7 665 def +/breve 666 def +/dotaccent 667 def +/ring 668 def +/ogonek 669 def +/tilde 670 def +/hungarumlaut 671 def +/uni02DE 672 def +/uni02DF 673 def +/uni02E0 674 def +/uni02E1 675 def +/uni02E2 676 def +/uni02E3 677 def +/uni02E4 678 def +/uni02E5 679 def +/uni02E6 680 def +/uni02E7 681 def +/uni02E8 682 def +/uni02E9 683 def +/uni02EC 684 def +/uni02ED 685 def +/uni02EE 686 def +/uni02F3 687 def +/uni02F7 688 def +/gravecomb 689 def +/acutecomb 690 def +/uni0302 691 def +/tildecomb 692 def +/uni0304 693 def +/uni0305 694 def +/uni0306 695 def +/uni0307 696 def +/uni0308 697 def +/hookabovecomb 698 def +/uni030A 699 def +/uni030B 700 def +/uni030C 701 def +/uni030D 702 def +/uni030E 703 def +/uni030F 704 def +/uni0310 705 def +/uni0311 706 def +/uni0312 707 def +/uni0313 708 def +/uni0314 709 def +/uni0315 710 def +/uni0316 711 def +/uni0317 712 def +/uni0318 713 def +/uni0319 714 def +/uni031A 715 def +/uni031B 716 def +/uni031C 717 def +/uni031D 718 def +/uni031E 719 def +/uni031F 720 def +/uni0320 721 def +/uni0321 722 def +/uni0322 723 def +/dotbelowcomb 724 def +/uni0324 725 def +/uni0325 726 def +/uni0326 727 def +/uni0327 728 def +/uni0328 729 def +/uni0329 730 def +/uni032A 731 def +/uni032B 732 def +/uni032C 733 def +/uni032D 734 def +/uni032E 735 def +/uni032F 736 def +/uni0330 737 def +/uni0331 738 def +/uni0332 739 def +/uni0333 740 def +/uni0334 741 def +/uni0335 742 def +/uni0336 743 def +/uni0337 744 def +/uni0338 745 def +/uni0339 746 def +/uni033A 747 def +/uni033B 748 def +/uni033C 749 def +/uni033D 750 def +/uni033E 751 def +/uni033F 752 def +/uni0340 753 def +/uni0341 754 def +/uni0342 755 def +/uni0343 756 def +/uni0344 757 def +/uni0345 758 def +/uni0346 759 def +/uni0347 760 def +/uni0348 761 def +/uni0349 762 def +/uni034A 763 def +/uni034B 764 def +/uni034C 765 def +/uni034D 766 def +/uni034E 767 def +/uni034F 768 def +/uni0351 769 def +/uni0352 770 def +/uni0353 771 def +/uni0357 772 def +/uni0358 773 def +/uni035A 774 def +/uni035C 775 def +/uni035D 776 def +/uni035E 777 def +/uni035F 778 def +/uni0360 779 def +/uni0361 780 def +/uni0362 781 def +/uni0370 782 def +/uni0371 783 def +/uni0372 784 def +/uni0373 785 def +/uni0374 786 def +/uni0375 787 def +/uni0376 788 def +/uni0377 789 def +/uni037A 790 def +/uni037B 791 def +/uni037C 792 def +/uni037D 793 def +/uni037E 794 def +/tonos 795 def +/dieresistonos 796 def +/Alphatonos 797 def +/anoteleia 798 def +/Epsilontonos 799 def +/Etatonos 800 def +/Iotatonos 801 def +/Omicrontonos 802 def +/Upsilontonos 803 def +/Omegatonos 804 def +/iotadieresistonos 805 def +/Alpha 806 def +/Beta 807 def +/Gamma 808 def +/uni0394 809 def +/Epsilon 810 def +/Zeta 811 def +/Eta 812 def +/Theta 813 def +/Iota 814 def +/Kappa 815 def +/Lambda 816 def +/Mu 817 def +/Nu 818 def +/Xi 819 def +/Omicron 820 def +/Pi 821 def +/Rho 822 def +/Sigma 823 def +/Tau 824 def +/Upsilon 825 def +/Phi 826 def +/Chi 827 def +/Psi 828 def +/Omega 829 def +/Iotadieresis 830 def +/Upsilondieresis 831 def +/alphatonos 832 def +/epsilontonos 833 def +/etatonos 834 def +/iotatonos 835 def +/upsilondieresistonos 836 def +/alpha 837 def +/beta 838 def +/gamma 839 def +/delta 840 def +/epsilon 841 def +/zeta 842 def +/eta 843 def +/theta 844 def +/iota 845 def +/kappa 846 def +/lambda 847 def +/uni03BC 848 def +/nu 849 def +/xi 850 def +/omicron 851 def +/pi 852 def +/rho 853 def +/sigma1 854 def +/sigma 855 def +/tau 856 def +/upsilon 857 def +/phi 858 def +/chi 859 def +/psi 860 def +/omega 861 def +/iotadieresis 862 def +/upsilondieresis 863 def +/omicrontonos 864 def +/upsilontonos 865 def +/omegatonos 866 def +/uni03CF 867 def +/uni03D0 868 def +/theta1 869 def +/Upsilon1 870 def +/uni03D3 871 def +/uni03D4 872 def +/phi1 873 def +/omega1 874 def +/uni03D7 875 def +/uni03D8 876 def +/uni03D9 877 def +/uni03DA 878 def +/uni03DB 879 def +/uni03DC 880 def +/uni03DD 881 def +/uni03DE 882 def +/uni03DF 883 def +/uni03E0 884 def +/uni03E1 885 def +/uni03E2 886 def +/uni03E3 887 def +/uni03E4 888 def +/uni03E5 889 def +/uni03E6 890 def +/uni03E7 891 def +/uni03E8 892 def +/uni03E9 893 def +/uni03EA 894 def +/uni03EB 895 def +/uni03EC 896 def +/uni03ED 897 def +/uni03EE 898 def +/uni03EF 899 def +/uni03F0 900 def +/uni03F1 901 def +/uni03F2 902 def +/uni03F3 903 def +/uni03F4 904 def +/uni03F5 905 def +/uni03F6 906 def +/uni03F7 907 def +/uni03F8 908 def +/uni03F9 909 def +/uni03FA 910 def +/uni03FB 911 def +/uni03FC 912 def +/uni03FD 913 def +/uni03FE 914 def +/uni03FF 915 def +/uni0400 916 def +/uni0401 917 def +/uni0402 918 def +/uni0403 919 def +/uni0404 920 def +/uni0405 921 def +/uni0406 922 def +/uni0407 923 def +/uni0408 924 def +/uni0409 925 def +/uni040A 926 def +/uni040B 927 def +/uni040C 928 def +/uni040D 929 def +/uni040E 930 def +/uni040F 931 def +/uni0410 932 def +/uni0411 933 def +/uni0412 934 def +/uni0413 935 def +/uni0414 936 def +/uni0415 937 def +/uni0416 938 def +/uni0417 939 def +/uni0418 940 def +/uni0419 941 def +/uni041A 942 def +/uni041B 943 def +/uni041C 944 def +/uni041D 945 def +/uni041E 946 def +/uni041F 947 def +/uni0420 948 def +/uni0421 949 def +/uni0422 950 def +/uni0423 951 def +/uni0424 952 def +/uni0425 953 def +/uni0426 954 def +/uni0427 955 def +/uni0428 956 def +/uni0429 957 def +/uni042A 958 def +/uni042B 959 def +/uni042C 960 def +/uni042D 961 def +/uni042E 962 def +/uni042F 963 def +/uni0430 964 def +/uni0431 965 def +/uni0432 966 def +/uni0433 967 def +/uni0434 968 def +/uni0435 969 def +/uni0436 970 def +/uni0437 971 def +/uni0438 972 def +/uni0439 973 def +/uni043A 974 def +/uni043B 975 def +/uni043C 976 def +/uni043D 977 def +/uni043E 978 def +/uni043F 979 def +/uni0440 980 def +/uni0441 981 def +/uni0442 982 def +/uni0443 983 def +/uni0444 984 def +/uni0445 985 def +/uni0446 986 def +/uni0447 987 def +/uni0448 988 def +/uni0449 989 def +/uni044A 990 def +/uni044B 991 def +/uni044C 992 def +/uni044D 993 def +/uni044E 994 def +/uni044F 995 def +/uni0450 996 def +/uni0451 997 def +/uni0452 998 def +/uni0453 999 def +/uni0454 1000 def +/uni0455 1001 def +/uni0456 1002 def +/uni0457 1003 def +/uni0458 1004 def +/uni0459 1005 def +/uni045A 1006 def +/uni045B 1007 def +/uni045C 1008 def +/uni045D 1009 def +/uni045E 1010 def +/uni045F 1011 def +/uni0460 1012 def +/uni0461 1013 def +/uni0462 1014 def +/uni0463 1015 def +/uni0464 1016 def +/uni0465 1017 def +/uni0466 1018 def +/uni0467 1019 def +/uni0468 1020 def +/uni0469 1021 def +/uni046A 1022 def +/uni046B 1023 def +/uni046C 1024 def +/uni046D 1025 def +/uni046E 1026 def +/uni046F 1027 def +/uni0470 1028 def +/uni0471 1029 def +/uni0472 1030 def +/uni0473 1031 def +/uni0474 1032 def +/uni0475 1033 def +/uni0476 1034 def +/uni0477 1035 def +/uni0478 1036 def +/uni0479 1037 def +/uni047A 1038 def +/uni047B 1039 def +/uni047C 1040 def +/uni047D 1041 def +/uni047E 1042 def +/uni047F 1043 def +/uni0480 1044 def +/uni0481 1045 def +/uni0482 1046 def +/uni0483 1047 def +/uni0484 1048 def +/uni0485 1049 def +/uni0486 1050 def +/uni0487 1051 def +/uni0488 1052 def +/uni0489 1053 def +/uni048A 1054 def +/uni048B 1055 def +/uni048C 1056 def +/uni048D 1057 def +/uni048E 1058 def +/uni048F 1059 def +/uni0490 1060 def +/uni0491 1061 def +/uni0492 1062 def +/uni0493 1063 def +/uni0494 1064 def +/uni0495 1065 def +/uni0496 1066 def +/uni0497 1067 def +/uni0498 1068 def +/uni0499 1069 def +/uni049A 1070 def +/uni049B 1071 def +/uni049C 1072 def +/uni049D 1073 def +/uni049E 1074 def +/uni049F 1075 def +/uni04A0 1076 def +/uni04A1 1077 def +/uni04A2 1078 def +/uni04A3 1079 def +/uni04A4 1080 def +/uni04A5 1081 def +/uni04A6 1082 def +/uni04A7 1083 def +/uni04A8 1084 def +/uni04A9 1085 def +/uni04AA 1086 def +/uni04AB 1087 def +/uni04AC 1088 def +/uni04AD 1089 def +/uni04AE 1090 def +/uni04AF 1091 def +/uni04B0 1092 def +/uni04B1 1093 def +/uni04B2 1094 def +/uni04B3 1095 def +/uni04B4 1096 def +/uni04B5 1097 def +/uni04B6 1098 def +/uni04B7 1099 def +/uni04B8 1100 def +/uni04B9 1101 def +/uni04BA 1102 def +/uni04BB 1103 def +/uni04BC 1104 def +/uni04BD 1105 def +/uni04BE 1106 def +/uni04BF 1107 def +/uni04C0 1108 def +/uni04C1 1109 def +/uni04C2 1110 def +/uni04C3 1111 def +/uni04C4 1112 def +/uni04C5 1113 def +/uni04C6 1114 def +/uni04C7 1115 def +/uni04C8 1116 def +/uni04C9 1117 def +/uni04CA 1118 def +/uni04CB 1119 def +/uni04CC 1120 def +/uni04CD 1121 def +/uni04CE 1122 def +/uni04CF 1123 def +/uni04D0 1124 def +/uni04D1 1125 def +/uni04D2 1126 def +/uni04D3 1127 def +/uni04D4 1128 def +/uni04D5 1129 def +/uni04D6 1130 def +/uni04D7 1131 def +/uni04D8 1132 def +/uni04D9 1133 def +/uni04DA 1134 def +/uni04DB 1135 def +/uni04DC 1136 def +/uni04DD 1137 def +/uni04DE 1138 def +/uni04DF 1139 def +/uni04E0 1140 def +/uni04E1 1141 def +/uni04E2 1142 def +/uni04E3 1143 def +/uni04E4 1144 def +/uni04E5 1145 def +/uni04E6 1146 def +/uni04E7 1147 def +/uni04E8 1148 def +/uni04E9 1149 def +/uni04EA 1150 def +/uni04EB 1151 def +/uni04EC 1152 def +/uni04ED 1153 def +/uni04EE 1154 def +/uni04EF 1155 def +/uni04F0 1156 def +/uni04F1 1157 def +/uni04F2 1158 def +/uni04F3 1159 def +/uni04F4 1160 def +/uni04F5 1161 def +/uni04F6 1162 def +/uni04F7 1163 def +/uni04F8 1164 def +/uni04F9 1165 def +/uni04FA 1166 def +/uni04FB 1167 def +/uni04FC 1168 def +/uni04FD 1169 def +/uni04FE 1170 def +/uni04FF 1171 def +/uni0500 1172 def +/uni0501 1173 def +/uni0502 1174 def +/uni0503 1175 def +/uni0504 1176 def +/uni0505 1177 def +/uni0506 1178 def +/uni0507 1179 def +/uni0508 1180 def +/uni0509 1181 def +/uni050A 1182 def +/uni050B 1183 def +/uni050C 1184 def +/uni050D 1185 def +/uni050E 1186 def +/uni050F 1187 def +/uni0510 1188 def +/uni0511 1189 def +/uni0512 1190 def +/uni0513 1191 def +/uni0514 1192 def +/uni0515 1193 def +/uni0516 1194 def +/uni0517 1195 def +/uni0518 1196 def +/uni0519 1197 def +/uni051A 1198 def +/uni051B 1199 def +/uni051C 1200 def +/uni051D 1201 def +/uni051E 1202 def +/uni051F 1203 def +/uni0520 1204 def +/uni0521 1205 def +/uni0522 1206 def +/uni0523 1207 def +/uni0524 1208 def +/uni0525 1209 def +/uni0531 1210 def +/uni0532 1211 def +/uni0533 1212 def +/uni0534 1213 def +/uni0535 1214 def +/uni0536 1215 def +/uni0537 1216 def +/uni0538 1217 def +/uni0539 1218 def +/uni053A 1219 def +/uni053B 1220 def +/uni053C 1221 def +/uni053D 1222 def +/uni053E 1223 def +/uni053F 1224 def +/uni0540 1225 def +/uni0541 1226 def +/uni0542 1227 def +/uni0543 1228 def +/uni0544 1229 def +/uni0545 1230 def +/uni0546 1231 def +/uni0547 1232 def +/uni0548 1233 def +/uni0549 1234 def +/uni054A 1235 def +/uni054B 1236 def +/uni054C 1237 def +/uni054D 1238 def +/uni054E 1239 def +/uni054F 1240 def +/uni0550 1241 def +/uni0551 1242 def +/uni0552 1243 def +/uni0553 1244 def +/uni0554 1245 def +/uni0555 1246 def +/uni0556 1247 def +/uni0559 1248 def +/uni055A 1249 def +/uni055B 1250 def +/uni055C 1251 def +/uni055D 1252 def +/uni055E 1253 def +/uni055F 1254 def +/uni0561 1255 def +/uni0562 1256 def +/uni0563 1257 def +/uni0564 1258 def +/uni0565 1259 def +/uni0566 1260 def +/uni0567 1261 def +/uni0568 1262 def +/uni0569 1263 def +/uni056A 1264 def +/uni056B 1265 def +/uni056C 1266 def +/uni056D 1267 def +/uni056E 1268 def +/uni056F 1269 def +/uni0570 1270 def +/uni0571 1271 def +/uni0572 1272 def +/uni0573 1273 def +/uni0574 1274 def +/uni0575 1275 def +/uni0576 1276 def +/uni0577 1277 def +/uni0578 1278 def +/uni0579 1279 def +/uni057A 1280 def +/uni057B 1281 def +/uni057C 1282 def +/uni057D 1283 def +/uni057E 1284 def +/uni057F 1285 def +/uni0580 1286 def +/uni0581 1287 def +/uni0582 1288 def +/uni0583 1289 def +/uni0584 1290 def +/uni0585 1291 def +/uni0586 1292 def +/uni0587 1293 def +/uni0589 1294 def +/uni058A 1295 def +/uni05B0 1296 def +/uni05B1 1297 def +/uni05B2 1298 def +/uni05B3 1299 def +/uni05B4 1300 def +/uni05B5 1301 def +/uni05B6 1302 def +/uni05B7 1303 def +/uni05B8 1304 def +/uni05B9 1305 def +/uni05BA 1306 def +/uni05BB 1307 def +/uni05BC 1308 def +/uni05BD 1309 def +/uni05BE 1310 def +/uni05BF 1311 def +/uni05C0 1312 def +/uni05C1 1313 def +/uni05C2 1314 def +/uni05C3 1315 def +/uni05C6 1316 def +/uni05C7 1317 def +/uni05D0 1318 def +/uni05D1 1319 def +/uni05D2 1320 def +/uni05D3 1321 def +/uni05D4 1322 def +/uni05D5 1323 def +/uni05D6 1324 def +/uni05D7 1325 def +/uni05D8 1326 def +/uni05D9 1327 def +/uni05DA 1328 def +/uni05DB 1329 def +/uni05DC 1330 def +/uni05DD 1331 def +/uni05DE 1332 def +/uni05DF 1333 def +/uni05E0 1334 def +/uni05E1 1335 def +/uni05E2 1336 def +/uni05E3 1337 def +/uni05E4 1338 def +/uni05E5 1339 def +/uni05E6 1340 def +/uni05E7 1341 def +/uni05E8 1342 def +/uni05E9 1343 def +/uni05EA 1344 def +/uni05F0 1345 def +/uni05F1 1346 def +/uni05F2 1347 def +/uni05F3 1348 def +/uni05F4 1349 def +/uni0606 1350 def +/uni0607 1351 def +/uni0609 1352 def +/uni060A 1353 def +/uni060C 1354 def +/uni0615 1355 def +/uni061B 1356 def +/uni061F 1357 def +/uni0621 1358 def +/uni0622 1359 def +/uni0623 1360 def +/uni0624 1361 def +/uni0625 1362 def +/uni0626 1363 def +/uni0627 1364 def +/uni0628 1365 def +/uni0629 1366 def +/uni062A 1367 def +/uni062B 1368 def +/uni062C 1369 def +/uni062D 1370 def +/uni062E 1371 def +/uni062F 1372 def +/uni0630 1373 def +/uni0631 1374 def +/uni0632 1375 def +/uni0633 1376 def +/uni0634 1377 def +/uni0635 1378 def +/uni0636 1379 def +/uni0637 1380 def +/uni0638 1381 def +/uni0639 1382 def +/uni063A 1383 def +/uni0640 1384 def +/uni0641 1385 def +/uni0642 1386 def +/uni0643 1387 def +/uni0644 1388 def +/uni0645 1389 def +/uni0646 1390 def +/uni0647 1391 def +/uni0648 1392 def +/uni0649 1393 def +/uni064A 1394 def +/uni064B 1395 def +/uni064C 1396 def +/uni064D 1397 def +/uni064E 1398 def +/uni064F 1399 def +/uni0650 1400 def +/uni0651 1401 def +/uni0652 1402 def +/uni0653 1403 def +/uni0654 1404 def +/uni0655 1405 def +/uni0657 1406 def +/uni065A 1407 def +/uni0660 1408 def +/uni0661 1409 def +/uni0662 1410 def +/uni0663 1411 def +/uni0664 1412 def +/uni0665 1413 def +/uni0666 1414 def +/uni0667 1415 def +/uni0668 1416 def +/uni0669 1417 def +/uni066A 1418 def +/uni066B 1419 def +/uni066C 1420 def +/uni066D 1421 def +/uni066E 1422 def +/uni066F 1423 def +/uni0670 1424 def +/uni0674 1425 def +/uni0679 1426 def +/uni067A 1427 def +/uni067B 1428 def +/uni067C 1429 def +/uni067D 1430 def +/uni067E 1431 def +/uni067F 1432 def +/uni0680 1433 def +/uni0681 1434 def +/uni0682 1435 def +/uni0683 1436 def +/uni0684 1437 def +/uni0685 1438 def +/uni0686 1439 def +/uni0687 1440 def +/uni0688 1441 def +/uni0689 1442 def +/uni068A 1443 def +/uni068B 1444 def +/uni068C 1445 def +/uni068D 1446 def +/uni068E 1447 def +/uni068F 1448 def +/uni0690 1449 def +/uni0691 1450 def +/uni0692 1451 def +/uni0693 1452 def +/uni0694 1453 def +/uni0695 1454 def +/uni0696 1455 def +/uni0697 1456 def +/uni0698 1457 def +/uni0699 1458 def +/uni069A 1459 def +/uni069B 1460 def +/uni069C 1461 def +/uni069D 1462 def +/uni069E 1463 def +/uni069F 1464 def +/uni06A0 1465 def +/uni06A1 1466 def +/uni06A2 1467 def +/uni06A3 1468 def +/uni06A4 1469 def +/uni06A5 1470 def +/uni06A6 1471 def +/uni06A7 1472 def +/uni06A8 1473 def +/uni06A9 1474 def +/uni06AA 1475 def +/uni06AB 1476 def +/uni06AC 1477 def +/uni06AD 1478 def +/uni06AE 1479 def +/uni06AF 1480 def +/uni06B0 1481 def +/uni06B1 1482 def +/uni06B2 1483 def +/uni06B3 1484 def +/uni06B4 1485 def +/uni06B5 1486 def +/uni06B6 1487 def +/uni06B7 1488 def +/uni06B8 1489 def +/uni06B9 1490 def +/uni06BA 1491 def +/uni06BB 1492 def +/uni06BC 1493 def +/uni06BD 1494 def +/uni06BE 1495 def +/uni06BF 1496 def +/uni06C6 1497 def +/uni06C7 1498 def +/uni06C8 1499 def +/uni06CB 1500 def +/uni06CC 1501 def +/uni06CE 1502 def +/uni06D0 1503 def +/uni06D5 1504 def +/uni06F0 1505 def +/uni06F1 1506 def +/uni06F2 1507 def +/uni06F3 1508 def +/uni06F4 1509 def +/uni06F5 1510 def +/uni06F6 1511 def +/uni06F7 1512 def +/uni06F8 1513 def +/uni06F9 1514 def +/uni07C0 1515 def +/uni07C1 1516 def +/uni07C2 1517 def +/uni07C3 1518 def +/uni07C4 1519 def +/uni07C5 1520 def +/uni07C6 1521 def +/uni07C7 1522 def +/uni07C8 1523 def +/uni07C9 1524 def +/uni07CA 1525 def +/uni07CB 1526 def +/uni07CC 1527 def +/uni07CD 1528 def +/uni07CE 1529 def +/uni07CF 1530 def +/uni07D0 1531 def +/uni07D1 1532 def +/uni07D2 1533 def +/uni07D3 1534 def +/uni07D4 1535 def +/uni07D5 1536 def +/uni07D6 1537 def +/uni07D7 1538 def +/uni07D8 1539 def +/uni07D9 1540 def +/uni07DA 1541 def +/uni07DB 1542 def +/uni07DC 1543 def +/uni07DD 1544 def +/uni07DE 1545 def +/uni07DF 1546 def +/uni07E0 1547 def +/uni07E1 1548 def +/uni07E2 1549 def +/uni07E3 1550 def +/uni07E4 1551 def +/uni07E5 1552 def +/uni07E6 1553 def +/uni07E7 1554 def +/uni07EB 1555 def +/uni07EC 1556 def +/uni07ED 1557 def +/uni07EE 1558 def +/uni07EF 1559 def +/uni07F0 1560 def +/uni07F1 1561 def +/uni07F2 1562 def +/uni07F3 1563 def +/uni07F4 1564 def +/uni07F5 1565 def +/uni07F8 1566 def +/uni07F9 1567 def +/uni07FA 1568 def +/uni0E3F 1569 def +/uni0E81 1570 def +/uni0E82 1571 def +/uni0E84 1572 def +/uni0E87 1573 def +/uni0E88 1574 def +/uni0E8A 1575 def +/uni0E8D 1576 def +/uni0E94 1577 def +/uni0E95 1578 def +/uni0E96 1579 def +/uni0E97 1580 def +/uni0E99 1581 def +/uni0E9A 1582 def +/uni0E9B 1583 def +/uni0E9C 1584 def +/uni0E9D 1585 def +/uni0E9E 1586 def +/uni0E9F 1587 def +/uni0EA1 1588 def +/uni0EA2 1589 def +/uni0EA3 1590 def +/uni0EA5 1591 def +/uni0EA7 1592 def +/uni0EAA 1593 def +/uni0EAB 1594 def +/uni0EAD 1595 def +/uni0EAE 1596 def +/uni0EAF 1597 def +/uni0EB0 1598 def +/uni0EB1 1599 def +/uni0EB2 1600 def +/uni0EB3 1601 def +/uni0EB4 1602 def +/uni0EB5 1603 def +/uni0EB6 1604 def +/uni0EB7 1605 def +/uni0EB8 1606 def +/uni0EB9 1607 def +/uni0EBB 1608 def +/uni0EBC 1609 def +/uni0EBD 1610 def +/uni0EC0 1611 def +/uni0EC1 1612 def +/uni0EC2 1613 def +/uni0EC3 1614 def +/uni0EC4 1615 def +/uni0EC6 1616 def +/uni0EC8 1617 def +/uni0EC9 1618 def +/uni0ECA 1619 def +/uni0ECB 1620 def +/uni0ECC 1621 def +/uni0ECD 1622 def +/uni0ED0 1623 def +/uni0ED1 1624 def +/uni0ED2 1625 def +/uni0ED3 1626 def +/uni0ED4 1627 def +/uni0ED5 1628 def +/uni0ED6 1629 def +/uni0ED7 1630 def +/uni0ED8 1631 def +/uni0ED9 1632 def +/uni0EDC 1633 def +/uni0EDD 1634 def +/uni10A0 1635 def +/uni10A1 1636 def +/uni10A2 1637 def +/uni10A3 1638 def +/uni10A4 1639 def +/uni10A5 1640 def +/uni10A6 1641 def +/uni10A7 1642 def +/uni10A8 1643 def +/uni10A9 1644 def +/uni10AA 1645 def +/uni10AB 1646 def +/uni10AC 1647 def +/uni10AD 1648 def +/uni10AE 1649 def +/uni10AF 1650 def +/uni10B0 1651 def +/uni10B1 1652 def +/uni10B2 1653 def +/uni10B3 1654 def +/uni10B4 1655 def +/uni10B5 1656 def +/uni10B6 1657 def +/uni10B7 1658 def +/uni10B8 1659 def +/uni10B9 1660 def +/uni10BA 1661 def +/uni10BB 1662 def +/uni10BC 1663 def +/uni10BD 1664 def +/uni10BE 1665 def +/uni10BF 1666 def +/uni10C0 1667 def +/uni10C1 1668 def +/uni10C2 1669 def +/uni10C3 1670 def +/uni10C4 1671 def +/uni10C5 1672 def +/uni10D0 1673 def +/uni10D1 1674 def +/uni10D2 1675 def +/uni10D3 1676 def +/uni10D4 1677 def +/uni10D5 1678 def +/uni10D6 1679 def +/uni10D7 1680 def +/uni10D8 1681 def +/uni10D9 1682 def +/uni10DA 1683 def +/uni10DB 1684 def +/uni10DC 1685 def +/uni10DD 1686 def +/uni10DE 1687 def +/uni10DF 1688 def +/uni10E0 1689 def +/uni10E1 1690 def +/uni10E2 1691 def +/uni10E3 1692 def +/uni10E4 1693 def +/uni10E5 1694 def +/uni10E6 1695 def +/uni10E7 1696 def +/uni10E8 1697 def +/uni10E9 1698 def +/uni10EA 1699 def +/uni10EB 1700 def +/uni10EC 1701 def +/uni10ED 1702 def +/uni10EE 1703 def +/uni10EF 1704 def +/uni10F0 1705 def +/uni10F1 1706 def +/uni10F2 1707 def +/uni10F3 1708 def +/uni10F4 1709 def +/uni10F5 1710 def +/uni10F6 1711 def +/uni10F7 1712 def +/uni10F8 1713 def +/uni10F9 1714 def +/uni10FA 1715 def +/uni10FB 1716 def +/uni10FC 1717 def +/uni1401 1718 def +/uni1402 1719 def +/uni1403 1720 def +/uni1404 1721 def +/uni1405 1722 def +/uni1406 1723 def +/uni1407 1724 def +/uni1409 1725 def +/uni140A 1726 def +/uni140B 1727 def +/uni140C 1728 def +/uni140D 1729 def +/uni140E 1730 def +/uni140F 1731 def +/uni1410 1732 def +/uni1411 1733 def +/uni1412 1734 def +/uni1413 1735 def +/uni1414 1736 def +/uni1415 1737 def +/uni1416 1738 def +/uni1417 1739 def +/uni1418 1740 def +/uni1419 1741 def +/uni141A 1742 def +/uni141B 1743 def +/uni141D 1744 def +/uni141E 1745 def +/uni141F 1746 def +/uni1420 1747 def +/uni1421 1748 def +/uni1422 1749 def +/uni1423 1750 def +/uni1424 1751 def +/uni1425 1752 def +/uni1426 1753 def +/uni1427 1754 def +/uni1428 1755 def +/uni1429 1756 def +/uni142A 1757 def +/uni142B 1758 def +/uni142C 1759 def +/uni142D 1760 def +/uni142E 1761 def +/uni142F 1762 def +/uni1430 1763 def +/uni1431 1764 def +/uni1432 1765 def +/uni1433 1766 def +/uni1434 1767 def +/uni1435 1768 def +/uni1437 1769 def +/uni1438 1770 def +/uni1439 1771 def +/uni143A 1772 def +/uni143B 1773 def +/uni143C 1774 def +/uni143D 1775 def +/uni143E 1776 def +/uni143F 1777 def +/uni1440 1778 def +/uni1441 1779 def +/uni1442 1780 def +/uni1443 1781 def +/uni1444 1782 def +/uni1445 1783 def +/uni1446 1784 def +/uni1447 1785 def +/uni1448 1786 def +/uni1449 1787 def +/uni144A 1788 def +/uni144C 1789 def +/uni144D 1790 def +/uni144E 1791 def +/uni144F 1792 def +/uni1450 1793 def +/uni1451 1794 def +/uni1452 1795 def +/uni1454 1796 def +/uni1455 1797 def +/uni1456 1798 def +/uni1457 1799 def +/uni1458 1800 def +/uni1459 1801 def +/uni145A 1802 def +/uni145B 1803 def +/uni145C 1804 def +/uni145D 1805 def +/uni145E 1806 def +/uni145F 1807 def +/uni1460 1808 def +/uni1461 1809 def +/uni1462 1810 def +/uni1463 1811 def +/uni1464 1812 def +/uni1465 1813 def +/uni1466 1814 def +/uni1467 1815 def +/uni1468 1816 def +/uni1469 1817 def +/uni146A 1818 def +/uni146B 1819 def +/uni146C 1820 def +/uni146D 1821 def +/uni146E 1822 def +/uni146F 1823 def +/uni1470 1824 def +/uni1471 1825 def +/uni1472 1826 def +/uni1473 1827 def +/uni1474 1828 def +/uni1475 1829 def +/uni1476 1830 def +/uni1477 1831 def +/uni1478 1832 def +/uni1479 1833 def +/uni147A 1834 def +/uni147B 1835 def +/uni147C 1836 def +/uni147D 1837 def +/uni147E 1838 def +/uni147F 1839 def +/uni1480 1840 def +/uni1481 1841 def +/uni1482 1842 def +/uni1483 1843 def +/uni1484 1844 def +/uni1485 1845 def +/uni1486 1846 def +/uni1487 1847 def +/uni1488 1848 def +/uni1489 1849 def +/uni148A 1850 def +/uni148B 1851 def +/uni148C 1852 def +/uni148D 1853 def +/uni148E 1854 def +/uni148F 1855 def +/uni1490 1856 def +/uni1491 1857 def +/uni1492 1858 def +/uni1493 1859 def +/uni1494 1860 def +/uni1495 1861 def +/uni1496 1862 def +/uni1497 1863 def +/uni1498 1864 def +/uni1499 1865 def +/uni149A 1866 def +/uni149B 1867 def +/uni149C 1868 def +/uni149D 1869 def +/uni149E 1870 def +/uni149F 1871 def +/uni14A0 1872 def +/uni14A1 1873 def +/uni14A2 1874 def +/uni14A3 1875 def +/uni14A4 1876 def +/uni14A5 1877 def +/uni14A6 1878 def +/uni14A7 1879 def +/uni14A8 1880 def +/uni14A9 1881 def +/uni14AA 1882 def +/uni14AB 1883 def +/uni14AC 1884 def +/uni14AD 1885 def +/uni14AE 1886 def +/uni14AF 1887 def +/uni14B0 1888 def +/uni14B1 1889 def +/uni14B2 1890 def +/uni14B3 1891 def +/uni14B4 1892 def +/uni14B5 1893 def +/uni14B6 1894 def +/uni14B7 1895 def +/uni14B8 1896 def +/uni14B9 1897 def +/uni14BA 1898 def +/uni14BB 1899 def +/uni14BC 1900 def +/uni14BD 1901 def +/uni14C0 1902 def +/uni14C1 1903 def +/uni14C2 1904 def +/uni14C3 1905 def +/uni14C4 1906 def +/uni14C5 1907 def +/uni14C6 1908 def +/uni14C7 1909 def +/uni14C8 1910 def +/uni14C9 1911 def +/uni14CA 1912 def +/uni14CB 1913 def +/uni14CC 1914 def +/uni14CD 1915 def +/uni14CE 1916 def +/uni14CF 1917 def +/uni14D0 1918 def +/uni14D1 1919 def +/uni14D2 1920 def +/uni14D3 1921 def +/uni14D4 1922 def +/uni14D5 1923 def +/uni14D6 1924 def +/uni14D7 1925 def +/uni14D8 1926 def +/uni14D9 1927 def +/uni14DA 1928 def +/uni14DB 1929 def +/uni14DC 1930 def +/uni14DD 1931 def +/uni14DE 1932 def +/uni14DF 1933 def +/uni14E0 1934 def +/uni14E1 1935 def +/uni14E2 1936 def +/uni14E3 1937 def +/uni14E4 1938 def +/uni14E5 1939 def +/uni14E6 1940 def +/uni14E7 1941 def +/uni14E8 1942 def +/uni14E9 1943 def +/uni14EA 1944 def +/uni14EC 1945 def +/uni14ED 1946 def +/uni14EE 1947 def +/uni14EF 1948 def +/uni14F0 1949 def +/uni14F1 1950 def +/uni14F2 1951 def +/uni14F3 1952 def +/uni14F4 1953 def +/uni14F5 1954 def +/uni14F6 1955 def +/uni14F7 1956 def +/uni14F8 1957 def +/uni14F9 1958 def +/uni14FA 1959 def +/uni14FB 1960 def +/uni14FC 1961 def +/uni14FD 1962 def +/uni14FE 1963 def +/uni14FF 1964 def +/uni1500 1965 def +/uni1501 1966 def +/uni1502 1967 def +/uni1503 1968 def +/uni1504 1969 def +/uni1505 1970 def +/uni1506 1971 def +/uni1507 1972 def +/uni1510 1973 def +/uni1511 1974 def +/uni1512 1975 def +/uni1513 1976 def +/uni1514 1977 def +/uni1515 1978 def +/uni1516 1979 def +/uni1517 1980 def +/uni1518 1981 def +/uni1519 1982 def +/uni151A 1983 def +/uni151B 1984 def +/uni151C 1985 def +/uni151D 1986 def +/uni151E 1987 def +/uni151F 1988 def +/uni1520 1989 def +/uni1521 1990 def +/uni1522 1991 def +/uni1523 1992 def +/uni1524 1993 def +/uni1525 1994 def +/uni1526 1995 def +/uni1527 1996 def +/uni1528 1997 def +/uni1529 1998 def +/uni152A 1999 def +/uni152B 2000 def +/uni152C 2001 def +/uni152D 2002 def +/uni152E 2003 def +/uni152F 2004 def +/uni1530 2005 def +/uni1531 2006 def +/uni1532 2007 def +/uni1533 2008 def +/uni1534 2009 def +/uni1535 2010 def +/uni1536 2011 def +/uni1537 2012 def +/uni1538 2013 def +/uni1539 2014 def +/uni153A 2015 def +/uni153B 2016 def +/uni153C 2017 def +/uni153D 2018 def +/uni153E 2019 def +/uni1540 2020 def +/uni1541 2021 def +/uni1542 2022 def +/uni1543 2023 def +/uni1544 2024 def +/uni1545 2025 def +/uni1546 2026 def +/uni1547 2027 def +/uni1548 2028 def +/uni1549 2029 def +/uni154A 2030 def +/uni154B 2031 def +/uni154C 2032 def +/uni154D 2033 def +/uni154E 2034 def +/uni154F 2035 def +/uni1550 2036 def +/uni1552 2037 def +/uni1553 2038 def +/uni1554 2039 def +/uni1555 2040 def +/uni1556 2041 def +/uni1557 2042 def +/uni1558 2043 def +/uni1559 2044 def +/uni155A 2045 def +/uni155B 2046 def +/uni155C 2047 def +/uni155D 2048 def +/uni155E 2049 def +/uni155F 2050 def +/uni1560 2051 def +/uni1561 2052 def +/uni1562 2053 def +/uni1563 2054 def +/uni1564 2055 def +/uni1565 2056 def +/uni1566 2057 def +/uni1567 2058 def +/uni1568 2059 def +/uni1569 2060 def +/uni156A 2061 def +/uni1574 2062 def +/uni1575 2063 def +/uni1576 2064 def +/uni1577 2065 def +/uni1578 2066 def +/uni1579 2067 def +/uni157A 2068 def +/uni157B 2069 def +/uni157C 2070 def +/uni157D 2071 def +/uni157E 2072 def +/uni157F 2073 def +/uni1580 2074 def +/uni1581 2075 def +/uni1582 2076 def +/uni1583 2077 def +/uni1584 2078 def +/uni1585 2079 def +/uni158A 2080 def +/uni158B 2081 def +/uni158C 2082 def +/uni158D 2083 def +/uni158E 2084 def +/uni158F 2085 def +/uni1590 2086 def +/uni1591 2087 def +/uni1592 2088 def +/uni1593 2089 def +/uni1594 2090 def +/uni1595 2091 def +/uni1596 2092 def +/uni15A0 2093 def +/uni15A1 2094 def +/uni15A2 2095 def +/uni15A3 2096 def +/uni15A4 2097 def +/uni15A5 2098 def +/uni15A6 2099 def +/uni15A7 2100 def +/uni15A8 2101 def +/uni15A9 2102 def +/uni15AA 2103 def +/uni15AB 2104 def +/uni15AC 2105 def +/uni15AD 2106 def +/uni15AE 2107 def +/uni15AF 2108 def +/uni15DE 2109 def +/uni15E1 2110 def +/uni1646 2111 def +/uni1647 2112 def +/uni166E 2113 def +/uni166F 2114 def +/uni1670 2115 def +/uni1671 2116 def +/uni1672 2117 def +/uni1673 2118 def +/uni1674 2119 def +/uni1675 2120 def +/uni1676 2121 def +/uni1680 2122 def +/uni1681 2123 def +/uni1682 2124 def +/uni1683 2125 def +/uni1684 2126 def +/uni1685 2127 def +/uni1686 2128 def +/uni1687 2129 def +/uni1688 2130 def +/uni1689 2131 def +/uni168A 2132 def +/uni168B 2133 def +/uni168C 2134 def +/uni168D 2135 def +/uni168E 2136 def +/uni168F 2137 def +/uni1690 2138 def +/uni1691 2139 def +/uni1692 2140 def +/uni1693 2141 def +/uni1694 2142 def +/uni1695 2143 def +/uni1696 2144 def +/uni1697 2145 def +/uni1698 2146 def +/uni1699 2147 def +/uni169A 2148 def +/uni169B 2149 def +/uni169C 2150 def +/uni1D00 2151 def +/uni1D01 2152 def +/uni1D02 2153 def +/uni1D03 2154 def +/uni1D04 2155 def +/uni1D05 2156 def +/uni1D06 2157 def +/uni1D07 2158 def +/uni1D08 2159 def +/uni1D09 2160 def +/uni1D0A 2161 def +/uni1D0B 2162 def +/uni1D0C 2163 def +/uni1D0D 2164 def +/uni1D0E 2165 def +/uni1D0F 2166 def +/uni1D10 2167 def +/uni1D11 2168 def +/uni1D12 2169 def +/uni1D13 2170 def +/uni1D14 2171 def +/uni1D16 2172 def +/uni1D17 2173 def +/uni1D18 2174 def +/uni1D19 2175 def +/uni1D1A 2176 def +/uni1D1B 2177 def +/uni1D1C 2178 def +/uni1D1D 2179 def +/uni1D1E 2180 def +/uni1D1F 2181 def +/uni1D20 2182 def +/uni1D21 2183 def +/uni1D22 2184 def +/uni1D23 2185 def +/uni1D26 2186 def +/uni1D27 2187 def +/uni1D28 2188 def +/uni1D29 2189 def +/uni1D2A 2190 def +/uni1D2B 2191 def +/uni1D2C 2192 def +/uni1D2D 2193 def +/uni1D2E 2194 def +/uni1D30 2195 def +/uni1D31 2196 def +/uni1D32 2197 def +/uni1D33 2198 def +/uni1D34 2199 def +/uni1D35 2200 def +/uni1D36 2201 def +/uni1D37 2202 def +/uni1D38 2203 def +/uni1D39 2204 def +/uni1D3A 2205 def +/uni1D3B 2206 def +/uni1D3C 2207 def +/uni1D3D 2208 def +/uni1D3E 2209 def +/uni1D3F 2210 def +/uni1D40 2211 def +/uni1D41 2212 def +/uni1D42 2213 def +/uni1D43 2214 def +/uni1D44 2215 def +/uni1D45 2216 def +/uni1D46 2217 def +/uni1D47 2218 def +/uni1D48 2219 def +/uni1D49 2220 def +/uni1D4A 2221 def +/uni1D4B 2222 def +/uni1D4C 2223 def +/uni1D4D 2224 def +/uni1D4E 2225 def +/uni1D4F 2226 def +/uni1D50 2227 def +/uni1D51 2228 def +/uni1D52 2229 def +/uni1D53 2230 def +/uni1D54 2231 def +/uni1D55 2232 def +/uni1D56 2233 def +/uni1D57 2234 def +/uni1D58 2235 def +/uni1D59 2236 def +/uni1D5A 2237 def +/uni1D5B 2238 def +/uni1D5D 2239 def +/uni1D5E 2240 def +/uni1D5F 2241 def +/uni1D60 2242 def +/uni1D61 2243 def +/uni1D62 2244 def +/uni1D63 2245 def +/uni1D64 2246 def +/uni1D65 2247 def +/uni1D66 2248 def +/uni1D67 2249 def +/uni1D68 2250 def +/uni1D69 2251 def +/uni1D6A 2252 def +/uni1D77 2253 def +/uni1D78 2254 def +/uni1D7B 2255 def +/uni1D7D 2256 def +/uni1D85 2257 def +/uni1D9B 2258 def +/uni1D9C 2259 def +/uni1D9D 2260 def +/uni1D9E 2261 def +/uni1D9F 2262 def +/uni1DA0 2263 def +/uni1DA1 2264 def +/uni1DA2 2265 def +/uni1DA3 2266 def +/uni1DA4 2267 def +/uni1DA5 2268 def +/uni1DA6 2269 def +/uni1DA7 2270 def +/uni1DA8 2271 def +/uni1DA9 2272 def +/uni1DAA 2273 def +/uni1DAB 2274 def +/uni1DAC 2275 def +/uni1DAD 2276 def +/uni1DAE 2277 def +/uni1DAF 2278 def +/uni1DB0 2279 def +/uni1DB1 2280 def +/uni1DB2 2281 def +/uni1DB3 2282 def +/uni1DB4 2283 def +/uni1DB5 2284 def +/uni1DB6 2285 def +/uni1DB7 2286 def +/uni1DB8 2287 def +/uni1DB9 2288 def +/uni1DBA 2289 def +/uni1DBB 2290 def +/uni1DBC 2291 def +/uni1DBD 2292 def +/uni1DBE 2293 def +/uni1DBF 2294 def +/uni1DC4 2295 def +/uni1DC5 2296 def +/uni1DC6 2297 def +/uni1DC7 2298 def +/uni1DC8 2299 def +/uni1DC9 2300 def +/uni1E00 2301 def +/uni1E01 2302 def +/uni1E02 2303 def +/uni1E03 2304 def +/uni1E04 2305 def +/uni1E05 2306 def +/uni1E06 2307 def +/uni1E07 2308 def +/uni1E08 2309 def +/uni1E09 2310 def +/uni1E0A 2311 def +/uni1E0B 2312 def +/uni1E0C 2313 def +/uni1E0D 2314 def +/uni1E0E 2315 def +/uni1E0F 2316 def +/uni1E10 2317 def +/uni1E11 2318 def +/uni1E12 2319 def +/uni1E13 2320 def +/uni1E14 2321 def +/uni1E15 2322 def +/uni1E16 2323 def +/uni1E17 2324 def +/uni1E18 2325 def +/uni1E19 2326 def +/uni1E1A 2327 def +/uni1E1B 2328 def +/uni1E1C 2329 def +/uni1E1D 2330 def +/uni1E1E 2331 def +/uni1E1F 2332 def +/uni1E20 2333 def +/uni1E21 2334 def +/uni1E22 2335 def +/uni1E23 2336 def +/uni1E24 2337 def +/uni1E25 2338 def +/uni1E26 2339 def +/uni1E27 2340 def +/uni1E28 2341 def +/uni1E29 2342 def +/uni1E2A 2343 def +/uni1E2B 2344 def +/uni1E2C 2345 def +/uni1E2D 2346 def +/uni1E2E 2347 def +/uni1E2F 2348 def +/uni1E30 2349 def +/uni1E31 2350 def +/uni1E32 2351 def +/uni1E33 2352 def +/uni1E34 2353 def +/uni1E35 2354 def +/uni1E36 2355 def +/uni1E37 2356 def +/uni1E38 2357 def +/uni1E39 2358 def +/uni1E3A 2359 def +/uni1E3B 2360 def +/uni1E3C 2361 def +/uni1E3D 2362 def +/uni1E3E 2363 def +/uni1E3F 2364 def +/uni1E40 2365 def +/uni1E41 2366 def +/uni1E42 2367 def +/uni1E43 2368 def +/uni1E44 2369 def +/uni1E45 2370 def +/uni1E46 2371 def +/uni1E47 2372 def +/uni1E48 2373 def +/uni1E49 2374 def +/uni1E4A 2375 def +/uni1E4B 2376 def +/uni1E4C 2377 def +/uni1E4D 2378 def +/uni1E4E 2379 def +/uni1E4F 2380 def +/uni1E50 2381 def +/uni1E51 2382 def +/uni1E52 2383 def +/uni1E53 2384 def +/uni1E54 2385 def +/uni1E55 2386 def +/uni1E56 2387 def +/uni1E57 2388 def +/uni1E58 2389 def +/uni1E59 2390 def +/uni1E5A 2391 def +/uni1E5B 2392 def +/uni1E5C 2393 def +/uni1E5D 2394 def +/uni1E5E 2395 def +/uni1E5F 2396 def +/uni1E60 2397 def +/uni1E61 2398 def +/uni1E62 2399 def +/uni1E63 2400 def +/uni1E64 2401 def +/uni1E65 2402 def +/uni1E66 2403 def +/uni1E67 2404 def +/uni1E68 2405 def +/uni1E69 2406 def +/uni1E6A 2407 def +/uni1E6B 2408 def +/uni1E6C 2409 def +/uni1E6D 2410 def +/uni1E6E 2411 def +/uni1E6F 2412 def +/uni1E70 2413 def +/uni1E71 2414 def +/uni1E72 2415 def +/uni1E73 2416 def +/uni1E74 2417 def +/uni1E75 2418 def +/uni1E76 2419 def +/uni1E77 2420 def +/uni1E78 2421 def +/uni1E79 2422 def +/uni1E7A 2423 def +/uni1E7B 2424 def +/uni1E7C 2425 def +/uni1E7D 2426 def +/uni1E7E 2427 def +/uni1E7F 2428 def +/Wgrave 2429 def +/wgrave 2430 def +/Wacute 2431 def +/wacute 2432 def +/Wdieresis 2433 def +/wdieresis 2434 def +/uni1E86 2435 def +/uni1E87 2436 def +/uni1E88 2437 def +/uni1E89 2438 def +/uni1E8A 2439 def +/uni1E8B 2440 def +/uni1E8C 2441 def +/uni1E8D 2442 def +/uni1E8E 2443 def +/uni1E8F 2444 def +/uni1E90 2445 def +/uni1E91 2446 def +/uni1E92 2447 def +/uni1E93 2448 def +/uni1E94 2449 def +/uni1E95 2450 def +/uni1E96 2451 def +/uni1E97 2452 def +/uni1E98 2453 def +/uni1E99 2454 def +/uni1E9A 2455 def +/uni1E9B 2456 def +/uni1E9C 2457 def +/uni1E9D 2458 def +/uni1E9E 2459 def +/uni1E9F 2460 def +/uni1EA0 2461 def +/uni1EA1 2462 def +/uni1EA2 2463 def +/uni1EA3 2464 def +/uni1EA4 2465 def +/uni1EA5 2466 def +/uni1EA6 2467 def +/uni1EA7 2468 def +/uni1EA8 2469 def +/uni1EA9 2470 def +/uni1EAA 2471 def +/uni1EAB 2472 def +/uni1EAC 2473 def +/uni1EAD 2474 def +/uni1EAE 2475 def +/uni1EAF 2476 def +/uni1EB0 2477 def +/uni1EB1 2478 def +/uni1EB2 2479 def +/uni1EB3 2480 def +/uni1EB4 2481 def +/uni1EB5 2482 def +/uni1EB6 2483 def +/uni1EB7 2484 def +/uni1EB8 2485 def +/uni1EB9 2486 def +/uni1EBA 2487 def +/uni1EBB 2488 def +/uni1EBC 2489 def +/uni1EBD 2490 def +/uni1EBE 2491 def +/uni1EBF 2492 def +/uni1EC0 2493 def +/uni1EC1 2494 def +/uni1EC2 2495 def +/uni1EC3 2496 def +/uni1EC4 2497 def +/uni1EC5 2498 def +/uni1EC6 2499 def +/uni1EC7 2500 def +/uni1EC8 2501 def +/uni1EC9 2502 def +/uni1ECA 2503 def +/uni1ECB 2504 def +/uni1ECC 2505 def +/uni1ECD 2506 def +/uni1ECE 2507 def +/uni1ECF 2508 def +/uni1ED0 2509 def +/uni1ED1 2510 def +/uni1ED2 2511 def +/uni1ED3 2512 def +/uni1ED4 2513 def +/uni1ED5 2514 def +/uni1ED6 2515 def +/uni1ED7 2516 def +/uni1ED8 2517 def +/uni1ED9 2518 def +/uni1EDA 2519 def +/uni1EDB 2520 def +/uni1EDC 2521 def +/uni1EDD 2522 def +/uni1EDE 2523 def +/uni1EDF 2524 def +/uni1EE0 2525 def +/uni1EE1 2526 def +/uni1EE2 2527 def +/uni1EE3 2528 def +/uni1EE4 2529 def +/uni1EE5 2530 def +/uni1EE6 2531 def +/uni1EE7 2532 def +/uni1EE8 2533 def +/uni1EE9 2534 def +/uni1EEA 2535 def +/uni1EEB 2536 def +/uni1EEC 2537 def +/uni1EED 2538 def +/uni1EEE 2539 def +/uni1EEF 2540 def +/uni1EF0 2541 def +/uni1EF1 2542 def +/Ygrave 2543 def +/ygrave 2544 def +/uni1EF4 2545 def +/uni1EF5 2546 def +/uni1EF6 2547 def +/uni1EF7 2548 def +/uni1EF8 2549 def +/uni1EF9 2550 def +/uni1EFA 2551 def +/uni1EFB 2552 def +/uni1F00 2553 def +/uni1F01 2554 def +/uni1F02 2555 def +/uni1F03 2556 def +/uni1F04 2557 def +/uni1F05 2558 def +/uni1F06 2559 def +/uni1F07 2560 def +/uni1F08 2561 def +/uni1F09 2562 def +/uni1F0A 2563 def +/uni1F0B 2564 def +/uni1F0C 2565 def +/uni1F0D 2566 def +/uni1F0E 2567 def +/uni1F0F 2568 def +/uni1F10 2569 def +/uni1F11 2570 def +/uni1F12 2571 def +/uni1F13 2572 def +/uni1F14 2573 def +/uni1F15 2574 def +/uni1F18 2575 def +/uni1F19 2576 def +/uni1F1A 2577 def +/uni1F1B 2578 def +/uni1F1C 2579 def +/uni1F1D 2580 def +/uni1F20 2581 def +/uni1F21 2582 def +/uni1F22 2583 def +/uni1F23 2584 def +/uni1F24 2585 def +/uni1F25 2586 def +/uni1F26 2587 def +/uni1F27 2588 def +/uni1F28 2589 def +/uni1F29 2590 def +/uni1F2A 2591 def +/uni1F2B 2592 def +/uni1F2C 2593 def +/uni1F2D 2594 def +/uni1F2E 2595 def +/uni1F2F 2596 def +/uni1F30 2597 def +/uni1F31 2598 def +/uni1F32 2599 def +/uni1F33 2600 def +/uni1F34 2601 def +/uni1F35 2602 def +/uni1F36 2603 def +/uni1F37 2604 def +/uni1F38 2605 def +/uni1F39 2606 def +/uni1F3A 2607 def +/uni1F3B 2608 def +/uni1F3C 2609 def +/uni1F3D 2610 def +/uni1F3E 2611 def +/uni1F3F 2612 def +/uni1F40 2613 def +/uni1F41 2614 def +/uni1F42 2615 def +/uni1F43 2616 def +/uni1F44 2617 def +/uni1F45 2618 def +/uni1F48 2619 def +/uni1F49 2620 def +/uni1F4A 2621 def +/uni1F4B 2622 def +/uni1F4C 2623 def +/uni1F4D 2624 def +/uni1F50 2625 def +/uni1F51 2626 def +/uni1F52 2627 def +/uni1F53 2628 def +/uni1F54 2629 def +/uni1F55 2630 def +/uni1F56 2631 def +/uni1F57 2632 def +/uni1F59 2633 def +/uni1F5B 2634 def +/uni1F5D 2635 def +/uni1F5F 2636 def +/uni1F60 2637 def +/uni1F61 2638 def +/uni1F62 2639 def +/uni1F63 2640 def +/uni1F64 2641 def +/uni1F65 2642 def +/uni1F66 2643 def +/uni1F67 2644 def +/uni1F68 2645 def +/uni1F69 2646 def +/uni1F6A 2647 def +/uni1F6B 2648 def +/uni1F6C 2649 def +/uni1F6D 2650 def +/uni1F6E 2651 def +/uni1F6F 2652 def +/uni1F70 2653 def +/uni1F71 2654 def +/uni1F72 2655 def +/uni1F73 2656 def +/uni1F74 2657 def +/uni1F75 2658 def +/uni1F76 2659 def +/uni1F77 2660 def +/uni1F78 2661 def +/uni1F79 2662 def +/uni1F7A 2663 def +/uni1F7B 2664 def +/uni1F7C 2665 def +/uni1F7D 2666 def +/uni1F80 2667 def +/uni1F81 2668 def +/uni1F82 2669 def +/uni1F83 2670 def +/uni1F84 2671 def +/uni1F85 2672 def +/uni1F86 2673 def +/uni1F87 2674 def +/uni1F88 2675 def +/uni1F89 2676 def +/uni1F8A 2677 def +/uni1F8B 2678 def +/uni1F8C 2679 def +/uni1F8D 2680 def +/uni1F8E 2681 def +/uni1F8F 2682 def +/uni1F90 2683 def +/uni1F91 2684 def +/uni1F92 2685 def +/uni1F93 2686 def +/uni1F94 2687 def +/uni1F95 2688 def +/uni1F96 2689 def +/uni1F97 2690 def +/uni1F98 2691 def +/uni1F99 2692 def +/uni1F9A 2693 def +/uni1F9B 2694 def +/uni1F9C 2695 def +/uni1F9D 2696 def +/uni1F9E 2697 def +/uni1F9F 2698 def +/uni1FA0 2699 def +/uni1FA1 2700 def +/uni1FA2 2701 def +/uni1FA3 2702 def +/uni1FA4 2703 def +/uni1FA5 2704 def +/uni1FA6 2705 def +/uni1FA7 2706 def +/uni1FA8 2707 def +/uni1FA9 2708 def +/uni1FAA 2709 def +/uni1FAB 2710 def +/uni1FAC 2711 def +/uni1FAD 2712 def +/uni1FAE 2713 def +/uni1FAF 2714 def +/uni1FB0 2715 def +/uni1FB1 2716 def +/uni1FB2 2717 def +/uni1FB3 2718 def +/uni1FB4 2719 def +/uni1FB6 2720 def +/uni1FB7 2721 def +/uni1FB8 2722 def +/uni1FB9 2723 def +/uni1FBA 2724 def +/uni1FBB 2725 def +/uni1FBC 2726 def +/uni1FBD 2727 def +/uni1FBE 2728 def +/uni1FBF 2729 def +/uni1FC0 2730 def +/uni1FC1 2731 def +/uni1FC2 2732 def +/uni1FC3 2733 def +/uni1FC4 2734 def +/uni1FC6 2735 def +/uni1FC7 2736 def +/uni1FC8 2737 def +/uni1FC9 2738 def +/uni1FCA 2739 def +/uni1FCB 2740 def +/uni1FCC 2741 def +/uni1FCD 2742 def +/uni1FCE 2743 def +/uni1FCF 2744 def +/uni1FD0 2745 def +/uni1FD1 2746 def +/uni1FD2 2747 def +/uni1FD3 2748 def +/uni1FD6 2749 def +/uni1FD7 2750 def +/uni1FD8 2751 def +/uni1FD9 2752 def +/uni1FDA 2753 def +/uni1FDB 2754 def +/uni1FDD 2755 def +/uni1FDE 2756 def +/uni1FDF 2757 def +/uni1FE0 2758 def +/uni1FE1 2759 def +/uni1FE2 2760 def +/uni1FE3 2761 def +/uni1FE4 2762 def +/uni1FE5 2763 def +/uni1FE6 2764 def +/uni1FE7 2765 def +/uni1FE8 2766 def +/uni1FE9 2767 def +/uni1FEA 2768 def +/uni1FEB 2769 def +/uni1FEC 2770 def +/uni1FED 2771 def +/uni1FEE 2772 def +/uni1FEF 2773 def +/uni1FF2 2774 def +/uni1FF3 2775 def +/uni1FF4 2776 def +/uni1FF6 2777 def +/uni1FF7 2778 def +/uni1FF8 2779 def +/uni1FF9 2780 def +/uni1FFA 2781 def +/uni1FFB 2782 def +/uni1FFC 2783 def +/uni1FFD 2784 def +/uni1FFE 2785 def +/uni2000 2786 def +/uni2001 2787 def +/uni2002 2788 def +/uni2003 2789 def +/uni2004 2790 def +/uni2005 2791 def +/uni2006 2792 def +/uni2007 2793 def +/uni2008 2794 def +/uni2009 2795 def +/uni200A 2796 def +/uni200B 2797 def +/uni200C 2798 def +/uni200D 2799 def +/uni200E 2800 def +/uni200F 2801 def +/uni2010 2802 def +/uni2011 2803 def +/figuredash 2804 def +/endash 2805 def +/emdash 2806 def +/uni2015 2807 def +/uni2016 2808 def +/underscoredbl 2809 def +/quoteleft 2810 def +/quoteright 2811 def +/quotesinglbase 2812 def +/quotereversed 2813 def +/quotedblleft 2814 def +/quotedblright 2815 def +/quotedblbase 2816 def +/uni201F 2817 def +/dagger 2818 def +/daggerdbl 2819 def +/bullet 2820 def +/uni2023 2821 def +/onedotenleader 2822 def +/twodotenleader 2823 def +/ellipsis 2824 def +/uni2027 2825 def +/uni2028 2826 def +/uni2029 2827 def +/uni202A 2828 def +/uni202B 2829 def +/uni202C 2830 def +/uni202D 2831 def +/uni202E 2832 def +/uni202F 2833 def +/perthousand 2834 def +/uni2031 2835 def +/minute 2836 def +/second 2837 def +/uni2034 2838 def +/uni2035 2839 def +/uni2036 2840 def +/uni2037 2841 def +/uni2038 2842 def +/guilsinglleft 2843 def +/guilsinglright 2844 def +/uni203B 2845 def +/exclamdbl 2846 def +/uni203D 2847 def +/uni203E 2848 def +/uni203F 2849 def +/uni2040 2850 def +/uni2041 2851 def +/uni2042 2852 def +/uni2043 2853 def +/fraction 2854 def +/uni2045 2855 def +/uni2046 2856 def +/uni2047 2857 def +/uni2048 2858 def +/uni2049 2859 def +/uni204A 2860 def +/uni204B 2861 def +/uni204C 2862 def +/uni204D 2863 def +/uni204E 2864 def +/uni204F 2865 def +/uni2050 2866 def +/uni2051 2867 def +/uni2052 2868 def +/uni2053 2869 def +/uni2054 2870 def +/uni2055 2871 def +/uni2056 2872 def +/uni2057 2873 def +/uni2058 2874 def +/uni2059 2875 def +/uni205A 2876 def +/uni205B 2877 def +/uni205C 2878 def +/uni205D 2879 def +/uni205E 2880 def +/uni205F 2881 def +/uni2060 2882 def +/uni2061 2883 def +/uni2062 2884 def +/uni2063 2885 def +/uni2064 2886 def +/uni206A 2887 def +/uni206B 2888 def +/uni206C 2889 def +/uni206D 2890 def +/uni206E 2891 def +/uni206F 2892 def +/uni2070 2893 def +/uni2071 2894 def +/uni2074 2895 def +/uni2075 2896 def +/uni2076 2897 def +/uni2077 2898 def +/uni2078 2899 def +/uni2079 2900 def +/uni207A 2901 def +/uni207B 2902 def +/uni207C 2903 def +/uni207D 2904 def +/uni207E 2905 def +/uni207F 2906 def +/uni2080 2907 def +/uni2081 2908 def +/uni2082 2909 def +/uni2083 2910 def +/uni2084 2911 def +/uni2085 2912 def +/uni2086 2913 def +/uni2087 2914 def +/uni2088 2915 def +/uni2089 2916 def +/uni208A 2917 def +/uni208B 2918 def +/uni208C 2919 def +/uni208D 2920 def +/uni208E 2921 def +/uni2090 2922 def +/uni2091 2923 def +/uni2092 2924 def +/uni2093 2925 def +/uni2094 2926 def +/uni2095 2927 def +/uni2096 2928 def +/uni2097 2929 def +/uni2098 2930 def +/uni2099 2931 def +/uni209A 2932 def +/uni209B 2933 def +/uni209C 2934 def +/uni20A0 2935 def +/colonmonetary 2936 def +/uni20A2 2937 def +/franc 2938 def +/lira 2939 def +/uni20A5 2940 def +/uni20A6 2941 def +/peseta 2942 def +/uni20A8 2943 def +/uni20A9 2944 def +/uni20AA 2945 def +/dong 2946 def +/Euro 2947 def +/uni20AD 2948 def +/uni20AE 2949 def +/uni20AF 2950 def +/uni20B0 2951 def +/uni20B1 2952 def +/uni20B2 2953 def +/uni20B3 2954 def +/uni20B4 2955 def +/uni20B5 2956 def +/uni20B8 2957 def +/uni20B9 2958 def +/uni20BA 2959 def +/uni20BD 2960 def +/uni20D0 2961 def +/uni20D1 2962 def +/uni20D6 2963 def +/uni20D7 2964 def +/uni20DB 2965 def +/uni20DC 2966 def +/uni20E1 2967 def +/uni2100 2968 def +/uni2101 2969 def +/uni2102 2970 def +/uni2103 2971 def +/uni2104 2972 def +/uni2105 2973 def +/uni2106 2974 def +/uni2107 2975 def +/uni2108 2976 def +/uni2109 2977 def +/uni210B 2978 def +/uni210C 2979 def +/uni210D 2980 def +/uni210E 2981 def +/uni210F 2982 def +/uni2110 2983 def +/Ifraktur 2984 def +/uni2112 2985 def +/uni2113 2986 def +/uni2114 2987 def +/uni2115 2988 def +/uni2116 2989 def +/uni2117 2990 def +/weierstrass 2991 def +/uni2119 2992 def +/uni211A 2993 def +/uni211B 2994 def +/Rfraktur 2995 def +/uni211D 2996 def +/prescription 2997 def +/uni211F 2998 def +/uni2120 2999 def +/uni2121 3000 def +/trademark 3001 def +/uni2123 3002 def +/uni2124 3003 def +/uni2125 3004 def +/uni2126 3005 def +/uni2127 3006 def +/uni2128 3007 def +/uni2129 3008 def +/uni212A 3009 def +/uni212B 3010 def +/uni212C 3011 def +/uni212D 3012 def +/estimated 3013 def +/uni212F 3014 def +/uni2130 3015 def +/uni2131 3016 def +/uni2132 3017 def +/uni2133 3018 def +/uni2134 3019 def +/aleph 3020 def +/uni2136 3021 def +/uni2137 3022 def +/uni2138 3023 def +/uni2139 3024 def +/uni213A 3025 def +/uni213B 3026 def +/uni213C 3027 def +/uni213D 3028 def +/uni213E 3029 def +/uni213F 3030 def +/uni2140 3031 def +/uni2141 3032 def +/uni2142 3033 def +/uni2143 3034 def +/uni2144 3035 def +/uni2145 3036 def +/uni2146 3037 def +/uni2147 3038 def +/uni2148 3039 def +/uni2149 3040 def +/uni214B 3041 def +/uni214E 3042 def +/uni2150 3043 def +/uni2151 3044 def +/uni2152 3045 def +/onethird 3046 def +/twothirds 3047 def +/uni2155 3048 def +/uni2156 3049 def +/uni2157 3050 def +/uni2158 3051 def +/uni2159 3052 def +/uni215A 3053 def +/oneeighth 3054 def +/threeeighths 3055 def +/fiveeighths 3056 def +/seveneighths 3057 def +/uni215F 3058 def +/uni2160 3059 def +/uni2161 3060 def +/uni2162 3061 def +/uni2163 3062 def +/uni2164 3063 def +/uni2165 3064 def +/uni2166 3065 def +/uni2167 3066 def +/uni2168 3067 def +/uni2169 3068 def +/uni216A 3069 def +/uni216B 3070 def +/uni216C 3071 def +/uni216D 3072 def +/uni216E 3073 def +/uni216F 3074 def +/uni2170 3075 def +/uni2171 3076 def +/uni2172 3077 def +/uni2173 3078 def +/uni2174 3079 def +/uni2175 3080 def +/uni2176 3081 def +/uni2177 3082 def +/uni2178 3083 def +/uni2179 3084 def +/uni217A 3085 def +/uni217B 3086 def +/uni217C 3087 def +/uni217D 3088 def +/uni217E 3089 def +/uni217F 3090 def +/uni2180 3091 def +/uni2181 3092 def +/uni2182 3093 def +/uni2183 3094 def +/uni2184 3095 def +/uni2185 3096 def +/uni2189 3097 def +/arrowleft 3098 def +/arrowup 3099 def +/arrowright 3100 def +/arrowdown 3101 def +/arrowboth 3102 def +/arrowupdn 3103 def +/uni2196 3104 def +/uni2197 3105 def +/uni2198 3106 def +/uni2199 3107 def +/uni219A 3108 def +/uni219B 3109 def +/uni219C 3110 def +/uni219D 3111 def +/uni219E 3112 def +/uni219F 3113 def +/uni21A0 3114 def +/uni21A1 3115 def +/uni21A2 3116 def +/uni21A3 3117 def +/uni21A4 3118 def +/uni21A5 3119 def +/uni21A6 3120 def +/uni21A7 3121 def +/arrowupdnbse 3122 def +/uni21A9 3123 def +/uni21AA 3124 def +/uni21AB 3125 def +/uni21AC 3126 def +/uni21AD 3127 def +/uni21AE 3128 def +/uni21AF 3129 def +/uni21B0 3130 def +/uni21B1 3131 def +/uni21B2 3132 def +/uni21B3 3133 def +/uni21B4 3134 def +/carriagereturn 3135 def +/uni21B6 3136 def +/uni21B7 3137 def +/uni21B8 3138 def +/uni21B9 3139 def +/uni21BA 3140 def +/uni21BB 3141 def +/uni21BC 3142 def +/uni21BD 3143 def +/uni21BE 3144 def +/uni21BF 3145 def +/uni21C0 3146 def +/uni21C1 3147 def +/uni21C2 3148 def +/uni21C3 3149 def +/uni21C4 3150 def +/uni21C5 3151 def +/uni21C6 3152 def +/uni21C7 3153 def +/uni21C8 3154 def +/uni21C9 3155 def +/uni21CA 3156 def +/uni21CB 3157 def +/uni21CC 3158 def +/uni21CD 3159 def +/uni21CE 3160 def +/uni21CF 3161 def +/arrowdblleft 3162 def +/arrowdblup 3163 def +/arrowdblright 3164 def +/arrowdbldown 3165 def +/arrowdblboth 3166 def +/uni21D5 3167 def +/uni21D6 3168 def +/uni21D7 3169 def +/uni21D8 3170 def +/uni21D9 3171 def +/uni21DA 3172 def +/uni21DB 3173 def +/uni21DC 3174 def +/uni21DD 3175 def +/uni21DE 3176 def +/uni21DF 3177 def +/uni21E0 3178 def +/uni21E1 3179 def +/uni21E2 3180 def +/uni21E3 3181 def +/uni21E4 3182 def +/uni21E5 3183 def +/uni21E6 3184 def +/uni21E7 3185 def +/uni21E8 3186 def +/uni21E9 3187 def +/uni21EA 3188 def +/uni21EB 3189 def +/uni21EC 3190 def +/uni21ED 3191 def +/uni21EE 3192 def +/uni21EF 3193 def +/uni21F0 3194 def +/uni21F1 3195 def +/uni21F2 3196 def +/uni21F3 3197 def +/uni21F4 3198 def +/uni21F5 3199 def +/uni21F6 3200 def +/uni21F7 3201 def +/uni21F8 3202 def +/uni21F9 3203 def +/uni21FA 3204 def +/uni21FB 3205 def +/uni21FC 3206 def +/uni21FD 3207 def +/uni21FE 3208 def +/uni21FF 3209 def +/universal 3210 def +/uni2201 3211 def +/partialdiff 3212 def +/existential 3213 def +/uni2204 3214 def +/emptyset 3215 def +/Delta 3216 def +/gradient 3217 def +/element 3218 def +/notelement 3219 def +/uni220A 3220 def +/suchthat 3221 def +/uni220C 3222 def +/uni220D 3223 def +/uni220E 3224 def +/product 3225 def +/uni2210 3226 def +/summation 3227 def +/minus 3228 def +/uni2213 3229 def +/uni2214 3230 def +/uni2215 3231 def +/uni2216 3232 def +/asteriskmath 3233 def +/uni2218 3234 def +/uni2219 3235 def +/radical 3236 def +/uni221B 3237 def +/uni221C 3238 def +/proportional 3239 def +/infinity 3240 def +/orthogonal 3241 def +/angle 3242 def +/uni2221 3243 def +/uni2222 3244 def +/uni2223 3245 def +/uni2224 3246 def +/uni2225 3247 def +/uni2226 3248 def +/logicaland 3249 def +/logicalor 3250 def +/intersection 3251 def +/union 3252 def +/integral 3253 def +/uni222C 3254 def +/uni222D 3255 def +/uni222E 3256 def +/uni222F 3257 def +/uni2230 3258 def +/uni2231 3259 def +/uni2232 3260 def +/uni2233 3261 def +/therefore 3262 def +/uni2235 3263 def +/uni2236 3264 def +/uni2237 3265 def +/uni2238 3266 def +/uni2239 3267 def +/uni223A 3268 def +/uni223B 3269 def +/similar 3270 def +/uni223D 3271 def +/uni223E 3272 def +/uni223F 3273 def +/uni2240 3274 def +/uni2241 3275 def +/uni2242 3276 def +/uni2243 3277 def +/uni2244 3278 def +/congruent 3279 def +/uni2246 3280 def +/uni2247 3281 def +/approxequal 3282 def +/uni2249 3283 def +/uni224A 3284 def +/uni224B 3285 def +/uni224C 3286 def +/uni224D 3287 def +/uni224E 3288 def +/uni224F 3289 def +/uni2250 3290 def +/uni2251 3291 def +/uni2252 3292 def +/uni2253 3293 def +/uni2254 3294 def +/uni2255 3295 def +/uni2256 3296 def +/uni2257 3297 def +/uni2258 3298 def +/uni2259 3299 def +/uni225A 3300 def +/uni225B 3301 def +/uni225C 3302 def +/uni225D 3303 def +/uni225E 3304 def +/uni225F 3305 def +/notequal 3306 def +/equivalence 3307 def +/uni2262 3308 def +/uni2263 3309 def +/lessequal 3310 def +/greaterequal 3311 def +/uni2266 3312 def +/uni2267 3313 def +/uni2268 3314 def +/uni2269 3315 def +/uni226A 3316 def +/uni226B 3317 def +/uni226C 3318 def +/uni226D 3319 def +/uni226E 3320 def +/uni226F 3321 def +/uni2270 3322 def +/uni2271 3323 def +/uni2272 3324 def +/uni2273 3325 def +/uni2274 3326 def +/uni2275 3327 def +/uni2276 3328 def +/uni2277 3329 def +/uni2278 3330 def +/uni2279 3331 def +/uni227A 3332 def +/uni227B 3333 def +/uni227C 3334 def +/uni227D 3335 def +/uni227E 3336 def +/uni227F 3337 def +/uni2280 3338 def +/uni2281 3339 def +/propersubset 3340 def +/propersuperset 3341 def +/notsubset 3342 def +/uni2285 3343 def +/reflexsubset 3344 def +/reflexsuperset 3345 def +/uni2288 3346 def +/uni2289 3347 def +/uni228A 3348 def +/uni228B 3349 def +/uni228C 3350 def +/uni228D 3351 def +/uni228E 3352 def +/uni228F 3353 def +/uni2290 3354 def +/uni2291 3355 def +/uni2292 3356 def +/uni2293 3357 def +/uni2294 3358 def +/circleplus 3359 def +/uni2296 3360 def +/circlemultiply 3361 def +/uni2298 3362 def +/uni2299 3363 def +/uni229A 3364 def +/uni229B 3365 def +/uni229C 3366 def +/uni229D 3367 def +/uni229E 3368 def +/uni229F 3369 def +/uni22A0 3370 def +/uni22A1 3371 def +/uni22A2 3372 def +/uni22A3 3373 def +/uni22A4 3374 def +/perpendicular 3375 def +/uni22A6 3376 def +/uni22A7 3377 def +/uni22A8 3378 def +/uni22A9 3379 def +/uni22AA 3380 def +/uni22AB 3381 def +/uni22AC 3382 def +/uni22AD 3383 def +/uni22AE 3384 def +/uni22AF 3385 def +/uni22B0 3386 def +/uni22B1 3387 def +/uni22B2 3388 def +/uni22B3 3389 def +/uni22B4 3390 def +/uni22B5 3391 def +/uni22B6 3392 def +/uni22B7 3393 def +/uni22B8 3394 def +/uni22B9 3395 def +/uni22BA 3396 def +/uni22BB 3397 def +/uni22BC 3398 def +/uni22BD 3399 def +/uni22BE 3400 def +/uni22BF 3401 def +/uni22C0 3402 def +/uni22C1 3403 def +/uni22C2 3404 def +/uni22C3 3405 def +/uni22C4 3406 def +/dotmath 3407 def +/uni22C6 3408 def +/uni22C7 3409 def +/uni22C8 3410 def +/uni22C9 3411 def +/uni22CA 3412 def +/uni22CB 3413 def +/uni22CC 3414 def +/uni22CD 3415 def +/uni22CE 3416 def +/uni22CF 3417 def +/uni22D0 3418 def +/uni22D1 3419 def +/uni22D2 3420 def +/uni22D3 3421 def +/uni22D4 3422 def +/uni22D5 3423 def +/uni22D6 3424 def +/uni22D7 3425 def +/uni22D8 3426 def +/uni22D9 3427 def +/uni22DA 3428 def +/uni22DB 3429 def +/uni22DC 3430 def +/uni22DD 3431 def +/uni22DE 3432 def +/uni22DF 3433 def +/uni22E0 3434 def +/uni22E1 3435 def +/uni22E2 3436 def +/uni22E3 3437 def +/uni22E4 3438 def +/uni22E5 3439 def +/uni22E6 3440 def +/uni22E7 3441 def +/uni22E8 3442 def +/uni22E9 3443 def +/uni22EA 3444 def +/uni22EB 3445 def +/uni22EC 3446 def +/uni22ED 3447 def +/uni22EE 3448 def +/uni22EF 3449 def +/uni22F0 3450 def +/uni22F1 3451 def +/uni22F2 3452 def +/uni22F3 3453 def +/uni22F4 3454 def +/uni22F5 3455 def +/uni22F6 3456 def +/uni22F7 3457 def +/uni22F8 3458 def +/uni22F9 3459 def +/uni22FA 3460 def +/uni22FB 3461 def +/uni22FC 3462 def +/uni22FD 3463 def +/uni22FE 3464 def +/uni22FF 3465 def +/uni2300 3466 def +/uni2301 3467 def +/house 3468 def +/uni2303 3469 def +/uni2304 3470 def +/uni2305 3471 def +/uni2306 3472 def +/uni2307 3473 def +/uni2308 3474 def +/uni2309 3475 def +/uni230A 3476 def +/uni230B 3477 def +/uni230C 3478 def +/uni230D 3479 def +/uni230E 3480 def +/uni230F 3481 def +/revlogicalnot 3482 def +/uni2311 3483 def +/uni2318 3484 def +/uni2319 3485 def +/uni231C 3486 def +/uni231D 3487 def +/uni231E 3488 def +/uni231F 3489 def +/integraltp 3490 def +/integralbt 3491 def +/uni2324 3492 def +/uni2325 3493 def +/uni2326 3494 def +/uni2327 3495 def +/uni2328 3496 def +/uni232B 3497 def +/uni232C 3498 def +/uni2373 3499 def +/uni2374 3500 def +/uni2375 3501 def +/uni237A 3502 def +/uni237D 3503 def +/uni2387 3504 def +/uni2394 3505 def +/uni239B 3506 def +/uni239C 3507 def +/uni239D 3508 def +/uni239E 3509 def +/uni239F 3510 def +/uni23A0 3511 def +/uni23A1 3512 def +/uni23A2 3513 def +/uni23A3 3514 def +/uni23A4 3515 def +/uni23A5 3516 def +/uni23A6 3517 def +/uni23A7 3518 def +/uni23A8 3519 def +/uni23A9 3520 def +/uni23AA 3521 def +/uni23AB 3522 def +/uni23AC 3523 def +/uni23AD 3524 def +/uni23AE 3525 def +/uni23CE 3526 def +/uni23CF 3527 def +/uni23E3 3528 def +/uni23E5 3529 def +/uni23E8 3530 def +/uni2422 3531 def +/uni2423 3532 def +/uni2460 3533 def +/uni2461 3534 def +/uni2462 3535 def +/uni2463 3536 def +/uni2464 3537 def +/uni2465 3538 def +/uni2466 3539 def +/uni2467 3540 def +/uni2468 3541 def +/uni2469 3542 def +/SF100000 3543 def +/uni2501 3544 def +/SF110000 3545 def +/uni2503 3546 def +/uni2504 3547 def +/uni2505 3548 def +/uni2506 3549 def +/uni2507 3550 def +/uni2508 3551 def +/uni2509 3552 def +/uni250A 3553 def +/uni250B 3554 def +/SF010000 3555 def +/uni250D 3556 def +/uni250E 3557 def +/uni250F 3558 def +/SF030000 3559 def +/uni2511 3560 def +/uni2512 3561 def +/uni2513 3562 def +/SF020000 3563 def +/uni2515 3564 def +/uni2516 3565 def +/uni2517 3566 def +/SF040000 3567 def +/uni2519 3568 def +/uni251A 3569 def +/uni251B 3570 def +/SF080000 3571 def +/uni251D 3572 def +/uni251E 3573 def +/uni251F 3574 def +/uni2520 3575 def +/uni2521 3576 def +/uni2522 3577 def +/uni2523 3578 def +/SF090000 3579 def +/uni2525 3580 def +/uni2526 3581 def +/uni2527 3582 def +/uni2528 3583 def +/uni2529 3584 def +/uni252A 3585 def +/uni252B 3586 def +/SF060000 3587 def +/uni252D 3588 def +/uni252E 3589 def +/uni252F 3590 def +/uni2530 3591 def +/uni2531 3592 def +/uni2532 3593 def +/uni2533 3594 def +/SF070000 3595 def +/uni2535 3596 def +/uni2536 3597 def +/uni2537 3598 def +/uni2538 3599 def +/uni2539 3600 def +/uni253A 3601 def +/uni253B 3602 def +/SF050000 3603 def +/uni253D 3604 def +/uni253E 3605 def +/uni253F 3606 def +/uni2540 3607 def +/uni2541 3608 def +/uni2542 3609 def +/uni2543 3610 def +/uni2544 3611 def +/uni2545 3612 def +/uni2546 3613 def +/uni2547 3614 def +/uni2548 3615 def +/uni2549 3616 def +/uni254A 3617 def +/uni254B 3618 def +/uni254C 3619 def +/uni254D 3620 def +/uni254E 3621 def +/uni254F 3622 def +/SF430000 3623 def +/SF240000 3624 def +/SF510000 3625 def +/SF520000 3626 def +/SF390000 3627 def +/SF220000 3628 def +/SF210000 3629 def +/SF250000 3630 def +/SF500000 3631 def +/SF490000 3632 def +/SF380000 3633 def +/SF280000 3634 def +/SF270000 3635 def +/SF260000 3636 def +/SF360000 3637 def +/SF370000 3638 def +/SF420000 3639 def +/SF190000 3640 def +/SF200000 3641 def +/SF230000 3642 def +/SF470000 3643 def +/SF480000 3644 def +/SF410000 3645 def +/SF450000 3646 def +/SF460000 3647 def +/SF400000 3648 def +/SF540000 3649 def +/SF530000 3650 def +/SF440000 3651 def +/uni256D 3652 def +/uni256E 3653 def +/uni256F 3654 def +/uni2570 3655 def +/uni2571 3656 def +/uni2572 3657 def +/uni2573 3658 def +/uni2574 3659 def +/uni2575 3660 def +/uni2576 3661 def +/uni2577 3662 def +/uni2578 3663 def +/uni2579 3664 def +/uni257A 3665 def +/uni257B 3666 def +/uni257C 3667 def +/uni257D 3668 def +/uni257E 3669 def +/uni257F 3670 def +/upblock 3671 def +/uni2581 3672 def +/uni2582 3673 def +/uni2583 3674 def +/dnblock 3675 def +/uni2585 3676 def +/uni2586 3677 def +/uni2587 3678 def +/block 3679 def +/uni2589 3680 def +/uni258A 3681 def +/uni258B 3682 def +/lfblock 3683 def +/uni258D 3684 def +/uni258E 3685 def +/uni258F 3686 def +/rtblock 3687 def +/ltshade 3688 def +/shade 3689 def +/dkshade 3690 def +/uni2594 3691 def +/uni2595 3692 def +/uni2596 3693 def +/uni2597 3694 def +/uni2598 3695 def +/uni2599 3696 def +/uni259A 3697 def +/uni259B 3698 def +/uni259C 3699 def +/uni259D 3700 def +/uni259E 3701 def +/uni259F 3702 def +/filledbox 3703 def +/H22073 3704 def +/uni25A2 3705 def +/uni25A3 3706 def +/uni25A4 3707 def +/uni25A5 3708 def +/uni25A6 3709 def +/uni25A7 3710 def +/uni25A8 3711 def +/uni25A9 3712 def +/H18543 3713 def +/H18551 3714 def +/filledrect 3715 def +/uni25AD 3716 def +/uni25AE 3717 def +/uni25AF 3718 def +/uni25B0 3719 def +/uni25B1 3720 def +/triagup 3721 def +/uni25B3 3722 def +/uni25B4 3723 def +/uni25B5 3724 def +/uni25B6 3725 def +/uni25B7 3726 def +/uni25B8 3727 def +/uni25B9 3728 def +/triagrt 3729 def +/uni25BB 3730 def +/triagdn 3731 def +/uni25BD 3732 def +/uni25BE 3733 def +/uni25BF 3734 def +/uni25C0 3735 def +/uni25C1 3736 def +/uni25C2 3737 def +/uni25C3 3738 def +/triaglf 3739 def +/uni25C5 3740 def +/uni25C6 3741 def +/uni25C7 3742 def +/uni25C8 3743 def +/uni25C9 3744 def +/lozenge 3745 def +/circle 3746 def +/uni25CC 3747 def +/uni25CD 3748 def +/uni25CE 3749 def +/H18533 3750 def +/uni25D0 3751 def +/uni25D1 3752 def +/uni25D2 3753 def +/uni25D3 3754 def +/uni25D4 3755 def +/uni25D5 3756 def +/uni25D6 3757 def +/uni25D7 3758 def +/invbullet 3759 def +/invcircle 3760 def +/uni25DA 3761 def +/uni25DB 3762 def +/uni25DC 3763 def +/uni25DD 3764 def +/uni25DE 3765 def +/uni25DF 3766 def +/uni25E0 3767 def +/uni25E1 3768 def +/uni25E2 3769 def +/uni25E3 3770 def +/uni25E4 3771 def +/uni25E5 3772 def +/openbullet 3773 def +/uni25E7 3774 def +/uni25E8 3775 def +/uni25E9 3776 def +/uni25EA 3777 def +/uni25EB 3778 def +/uni25EC 3779 def +/uni25ED 3780 def +/uni25EE 3781 def +/uni25EF 3782 def +/uni25F0 3783 def +/uni25F1 3784 def +/uni25F2 3785 def +/uni25F3 3786 def +/uni25F4 3787 def +/uni25F5 3788 def +/uni25F6 3789 def +/uni25F7 3790 def +/uni25F8 3791 def +/uni25F9 3792 def +/uni25FA 3793 def +/uni25FB 3794 def +/uni25FC 3795 def +/uni25FD 3796 def +/uni25FE 3797 def +/uni25FF 3798 def +/uni2600 3799 def +/uni2601 3800 def +/uni2602 3801 def +/uni2603 3802 def +/uni2604 3803 def +/uni2605 3804 def +/uni2606 3805 def +/uni2607 3806 def +/uni2608 3807 def +/uni2609 3808 def +/uni260A 3809 def +/uni260B 3810 def +/uni260C 3811 def +/uni260D 3812 def +/uni260E 3813 def +/uni260F 3814 def +/uni2610 3815 def +/uni2611 3816 def +/uni2612 3817 def +/uni2613 3818 def +/uni2614 3819 def +/uni2615 3820 def +/uni2616 3821 def +/uni2617 3822 def +/uni2618 3823 def +/uni2619 3824 def +/uni261A 3825 def +/uni261B 3826 def +/uni261C 3827 def +/uni261D 3828 def +/uni261E 3829 def +/uni261F 3830 def +/uni2620 3831 def +/uni2621 3832 def +/uni2622 3833 def +/uni2623 3834 def +/uni2624 3835 def +/uni2625 3836 def +/uni2626 3837 def +/uni2627 3838 def +/uni2628 3839 def +/uni2629 3840 def +/uni262A 3841 def +/uni262B 3842 def +/uni262C 3843 def +/uni262D 3844 def +/uni262E 3845 def +/uni262F 3846 def +/uni2630 3847 def +/uni2631 3848 def +/uni2632 3849 def +/uni2633 3850 def +/uni2634 3851 def +/uni2635 3852 def +/uni2636 3853 def +/uni2637 3854 def +/uni2638 3855 def +/uni2639 3856 def +/smileface 3857 def +/invsmileface 3858 def +/sun 3859 def +/uni263D 3860 def +/uni263E 3861 def +/uni263F 3862 def +/female 3863 def +/uni2641 3864 def +/male 3865 def +/uni2643 3866 def +/uni2644 3867 def +/uni2645 3868 def +/uni2646 3869 def +/uni2647 3870 def +/uni2648 3871 def +/uni2649 3872 def +/uni264A 3873 def +/uni264B 3874 def +/uni264C 3875 def +/uni264D 3876 def +/uni264E 3877 def +/uni264F 3878 def +/uni2650 3879 def +/uni2651 3880 def +/uni2652 3881 def +/uni2653 3882 def +/uni2654 3883 def +/uni2655 3884 def +/uni2656 3885 def +/uni2657 3886 def +/uni2658 3887 def +/uni2659 3888 def +/uni265A 3889 def +/uni265B 3890 def +/uni265C 3891 def +/uni265D 3892 def +/uni265E 3893 def +/uni265F 3894 def +/spade 3895 def +/uni2661 3896 def +/uni2662 3897 def +/club 3898 def +/uni2664 3899 def +/heart 3900 def +/diamond 3901 def +/uni2667 3902 def +/uni2668 3903 def +/uni2669 3904 def +/musicalnote 3905 def +/musicalnotedbl 3906 def +/uni266C 3907 def +/uni266D 3908 def +/uni266E 3909 def +/uni266F 3910 def +/uni2670 3911 def +/uni2671 3912 def +/uni2672 3913 def +/uni2673 3914 def +/uni2674 3915 def +/uni2675 3916 def +/uni2676 3917 def +/uni2677 3918 def +/uni2678 3919 def +/uni2679 3920 def +/uni267A 3921 def +/uni267B 3922 def +/uni267C 3923 def +/uni267D 3924 def +/uni267E 3925 def +/uni267F 3926 def +/uni2680 3927 def +/uni2681 3928 def +/uni2682 3929 def +/uni2683 3930 def +/uni2684 3931 def +/uni2685 3932 def +/uni2686 3933 def +/uni2687 3934 def +/uni2688 3935 def +/uni2689 3936 def +/uni268A 3937 def +/uni268B 3938 def +/uni268C 3939 def +/uni268D 3940 def +/uni268E 3941 def +/uni268F 3942 def +/uni2690 3943 def +/uni2691 3944 def +/uni2692 3945 def +/uni2693 3946 def +/uni2694 3947 def +/uni2695 3948 def +/uni2696 3949 def +/uni2697 3950 def +/uni2698 3951 def +/uni2699 3952 def +/uni269A 3953 def +/uni269B 3954 def +/uni269C 3955 def +/uni269E 3956 def +/uni269F 3957 def +/uni26A0 3958 def +/uni26A1 3959 def +/uni26A2 3960 def +/uni26A3 3961 def +/uni26A4 3962 def +/uni26A5 3963 def +/uni26A6 3964 def +/uni26A7 3965 def +/uni26A8 3966 def +/uni26A9 3967 def +/uni26AA 3968 def +/uni26AB 3969 def +/uni26AC 3970 def +/uni26AD 3971 def +/uni26AE 3972 def +/uni26AF 3973 def +/uni26B0 3974 def +/uni26B1 3975 def +/uni26B2 3976 def +/uni26B3 3977 def +/uni26B4 3978 def +/uni26B5 3979 def +/uni26B6 3980 def +/uni26B7 3981 def +/uni26B8 3982 def +/uni26C0 3983 def +/uni26C1 3984 def +/uni26C2 3985 def +/uni26C3 3986 def +/uni26E2 3987 def +/uni2701 3988 def +/uni2702 3989 def +/uni2703 3990 def +/uni2704 3991 def +/uni2706 3992 def +/uni2707 3993 def +/uni2708 3994 def +/uni2709 3995 def +/uni270C 3996 def +/uni270D 3997 def +/uni270E 3998 def +/uni270F 3999 def +/uni2710 4000 def +/uni2711 4001 def +/uni2712 4002 def +/uni2713 4003 def +/uni2714 4004 def +/uni2715 4005 def +/uni2716 4006 def +/uni2717 4007 def +/uni2718 4008 def +/uni2719 4009 def +/uni271A 4010 def +/uni271B 4011 def +/uni271C 4012 def +/uni271D 4013 def +/uni271E 4014 def +/uni271F 4015 def +/uni2720 4016 def +/uni2721 4017 def +/uni2722 4018 def +/uni2723 4019 def +/uni2724 4020 def +/uni2725 4021 def +/uni2726 4022 def +/uni2727 4023 def +/uni2729 4024 def +/uni272A 4025 def +/uni272B 4026 def +/uni272C 4027 def +/uni272D 4028 def +/uni272E 4029 def +/uni272F 4030 def +/uni2730 4031 def +/uni2731 4032 def +/uni2732 4033 def +/uni2733 4034 def +/uni2734 4035 def +/uni2735 4036 def +/uni2736 4037 def +/uni2737 4038 def +/uni2738 4039 def +/uni2739 4040 def +/uni273A 4041 def +/uni273B 4042 def +/uni273C 4043 def +/uni273D 4044 def +/uni273E 4045 def +/uni273F 4046 def +/uni2740 4047 def +/uni2741 4048 def +/uni2742 4049 def +/uni2743 4050 def +/uni2744 4051 def +/uni2745 4052 def +/uni2746 4053 def +/uni2747 4054 def +/uni2748 4055 def +/uni2749 4056 def +/uni274A 4057 def +/uni274B 4058 def +/uni274D 4059 def +/uni274F 4060 def +/uni2750 4061 def +/uni2751 4062 def +/uni2752 4063 def +/uni2756 4064 def +/uni2758 4065 def +/uni2759 4066 def +/uni275A 4067 def +/uni275B 4068 def +/uni275C 4069 def +/uni275D 4070 def +/uni275E 4071 def +/uni2761 4072 def +/uni2762 4073 def +/uni2763 4074 def +/uni2764 4075 def +/uni2765 4076 def +/uni2766 4077 def +/uni2767 4078 def +/uni2768 4079 def +/uni2769 4080 def +/uni276A 4081 def +/uni276B 4082 def +/uni276C 4083 def +/uni276D 4084 def +/uni276E 4085 def +/uni276F 4086 def +/uni2770 4087 def +/uni2771 4088 def +/uni2772 4089 def +/uni2773 4090 def +/uni2774 4091 def +/uni2775 4092 def +/uni2776 4093 def +/uni2777 4094 def +/uni2778 4095 def +/uni2779 4096 def +/uni277A 4097 def +/uni277B 4098 def +/uni277C 4099 def +/uni277D 4100 def +/uni277E 4101 def +/uni277F 4102 def +/uni2780 4103 def +/uni2781 4104 def +/uni2782 4105 def +/uni2783 4106 def +/uni2784 4107 def +/uni2785 4108 def +/uni2786 4109 def +/uni2787 4110 def +/uni2788 4111 def +/uni2789 4112 def +/uni278A 4113 def +/uni278B 4114 def +/uni278C 4115 def +/uni278D 4116 def +/uni278E 4117 def +/uni278F 4118 def +/uni2790 4119 def +/uni2791 4120 def +/uni2792 4121 def +/uni2793 4122 def +/uni2794 4123 def +/uni2798 4124 def +/uni2799 4125 def +/uni279A 4126 def +/uni279B 4127 def +/uni279C 4128 def +/uni279D 4129 def +/uni279E 4130 def +/uni279F 4131 def +/uni27A0 4132 def +/uni27A1 4133 def +/uni27A2 4134 def +/uni27A3 4135 def +/uni27A4 4136 def +/uni27A5 4137 def +/uni27A6 4138 def +/uni27A7 4139 def +/uni27A8 4140 def +/uni27A9 4141 def +/uni27AA 4142 def +/uni27AB 4143 def +/uni27AC 4144 def +/uni27AD 4145 def +/uni27AE 4146 def +/uni27AF 4147 def +/uni27B1 4148 def +/uni27B2 4149 def +/uni27B3 4150 def +/uni27B4 4151 def +/uni27B5 4152 def +/uni27B6 4153 def +/uni27B7 4154 def +/uni27B8 4155 def +/uni27B9 4156 def +/uni27BA 4157 def +/uni27BB 4158 def +/uni27BC 4159 def +/uni27BD 4160 def +/uni27BE 4161 def +/uni27C5 4162 def +/uni27C6 4163 def +/uni27E0 4164 def +/uni27E6 4165 def +/uni27E7 4166 def +/uni27E8 4167 def +/uni27E9 4168 def +/uni27EA 4169 def +/uni27EB 4170 def +/uni27F0 4171 def +/uni27F1 4172 def +/uni27F2 4173 def +/uni27F3 4174 def +/uni27F4 4175 def +/uni27F5 4176 def +/uni27F6 4177 def +/uni27F7 4178 def +/uni27F8 4179 def +/uni27F9 4180 def +/uni27FA 4181 def +/uni27FB 4182 def +/uni27FC 4183 def +/uni27FD 4184 def +/uni27FE 4185 def +/uni27FF 4186 def +/uni2800 4187 def +/uni2801 4188 def +/uni2802 4189 def +/uni2803 4190 def +/uni2804 4191 def +/uni2805 4192 def +/uni2806 4193 def +/uni2807 4194 def +/uni2808 4195 def +/uni2809 4196 def +/uni280A 4197 def +/uni280B 4198 def +/uni280C 4199 def +/uni280D 4200 def +/uni280E 4201 def +/uni280F 4202 def +/uni2810 4203 def +/uni2811 4204 def +/uni2812 4205 def +/uni2813 4206 def +/uni2814 4207 def +/uni2815 4208 def +/uni2816 4209 def +/uni2817 4210 def +/uni2818 4211 def +/uni2819 4212 def +/uni281A 4213 def +/uni281B 4214 def +/uni281C 4215 def +/uni281D 4216 def +/uni281E 4217 def +/uni281F 4218 def +/uni2820 4219 def +/uni2821 4220 def +/uni2822 4221 def +/uni2823 4222 def +/uni2824 4223 def +/uni2825 4224 def +/uni2826 4225 def +/uni2827 4226 def +/uni2828 4227 def +/uni2829 4228 def +/uni282A 4229 def +/uni282B 4230 def +/uni282C 4231 def +/uni282D 4232 def +/uni282E 4233 def +/uni282F 4234 def +/uni2830 4235 def +/uni2831 4236 def +/uni2832 4237 def +/uni2833 4238 def +/uni2834 4239 def +/uni2835 4240 def +/uni2836 4241 def +/uni2837 4242 def +/uni2838 4243 def +/uni2839 4244 def +/uni283A 4245 def +/uni283B 4246 def +/uni283C 4247 def +/uni283D 4248 def +/uni283E 4249 def +/uni283F 4250 def +/uni2840 4251 def +/uni2841 4252 def +/uni2842 4253 def +/uni2843 4254 def +/uni2844 4255 def +/uni2845 4256 def +/uni2846 4257 def +/uni2847 4258 def +/uni2848 4259 def +/uni2849 4260 def +/uni284A 4261 def +/uni284B 4262 def +/uni284C 4263 def +/uni284D 4264 def +/uni284E 4265 def +/uni284F 4266 def +/uni2850 4267 def +/uni2851 4268 def +/uni2852 4269 def +/uni2853 4270 def +/uni2854 4271 def +/uni2855 4272 def +/uni2856 4273 def +/uni2857 4274 def +/uni2858 4275 def +/uni2859 4276 def +/uni285A 4277 def +/uni285B 4278 def +/uni285C 4279 def +/uni285D 4280 def +/uni285E 4281 def +/uni285F 4282 def +/uni2860 4283 def +/uni2861 4284 def +/uni2862 4285 def +/uni2863 4286 def +/uni2864 4287 def +/uni2865 4288 def +/uni2866 4289 def +/uni2867 4290 def +/uni2868 4291 def +/uni2869 4292 def +/uni286A 4293 def +/uni286B 4294 def +/uni286C 4295 def +/uni286D 4296 def +/uni286E 4297 def +/uni286F 4298 def +/uni2870 4299 def +/uni2871 4300 def +/uni2872 4301 def +/uni2873 4302 def +/uni2874 4303 def +/uni2875 4304 def +/uni2876 4305 def +/uni2877 4306 def +/uni2878 4307 def +/uni2879 4308 def +/uni287A 4309 def +/uni287B 4310 def +/uni287C 4311 def +/uni287D 4312 def +/uni287E 4313 def +/uni287F 4314 def +/uni2880 4315 def +/uni2881 4316 def +/uni2882 4317 def +/uni2883 4318 def +/uni2884 4319 def +/uni2885 4320 def +/uni2886 4321 def +/uni2887 4322 def +/uni2888 4323 def +/uni2889 4324 def +/uni288A 4325 def +/uni288B 4326 def +/uni288C 4327 def +/uni288D 4328 def +/uni288E 4329 def +/uni288F 4330 def +/uni2890 4331 def +/uni2891 4332 def +/uni2892 4333 def +/uni2893 4334 def +/uni2894 4335 def +/uni2895 4336 def +/uni2896 4337 def +/uni2897 4338 def +/uni2898 4339 def +/uni2899 4340 def +/uni289A 4341 def +/uni289B 4342 def +/uni289C 4343 def +/uni289D 4344 def +/uni289E 4345 def +/uni289F 4346 def +/uni28A0 4347 def +/uni28A1 4348 def +/uni28A2 4349 def +/uni28A3 4350 def +/uni28A4 4351 def +/uni28A5 4352 def +/uni28A6 4353 def +/uni28A7 4354 def +/uni28A8 4355 def +/uni28A9 4356 def +/uni28AA 4357 def +/uni28AB 4358 def +/uni28AC 4359 def +/uni28AD 4360 def +/uni28AE 4361 def +/uni28AF 4362 def +/uni28B0 4363 def +/uni28B1 4364 def +/uni28B2 4365 def +/uni28B3 4366 def +/uni28B4 4367 def +/uni28B5 4368 def +/uni28B6 4369 def +/uni28B7 4370 def +/uni28B8 4371 def +/uni28B9 4372 def +/uni28BA 4373 def +/uni28BB 4374 def +/uni28BC 4375 def +/uni28BD 4376 def +/uni28BE 4377 def +/uni28BF 4378 def +/uni28C0 4379 def +/uni28C1 4380 def +/uni28C2 4381 def +/uni28C3 4382 def +/uni28C4 4383 def +/uni28C5 4384 def +/uni28C6 4385 def +/uni28C7 4386 def +/uni28C8 4387 def +/uni28C9 4388 def +/uni28CA 4389 def +/uni28CB 4390 def +/uni28CC 4391 def +/uni28CD 4392 def +/uni28CE 4393 def +/uni28CF 4394 def +/uni28D0 4395 def +/uni28D1 4396 def +/uni28D2 4397 def +/uni28D3 4398 def +/uni28D4 4399 def +/uni28D5 4400 def +/uni28D6 4401 def +/uni28D7 4402 def +/uni28D8 4403 def +/uni28D9 4404 def +/uni28DA 4405 def +/uni28DB 4406 def +/uni28DC 4407 def +/uni28DD 4408 def +/uni28DE 4409 def +/uni28DF 4410 def +/uni28E0 4411 def +/uni28E1 4412 def +/uni28E2 4413 def +/uni28E3 4414 def +/uni28E4 4415 def +/uni28E5 4416 def +/uni28E6 4417 def +/uni28E7 4418 def +/uni28E8 4419 def +/uni28E9 4420 def +/uni28EA 4421 def +/uni28EB 4422 def +/uni28EC 4423 def +/uni28ED 4424 def +/uni28EE 4425 def +/uni28EF 4426 def +/uni28F0 4427 def +/uni28F1 4428 def +/uni28F2 4429 def +/uni28F3 4430 def +/uni28F4 4431 def +/uni28F5 4432 def +/uni28F6 4433 def +/uni28F7 4434 def +/uni28F8 4435 def +/uni28F9 4436 def +/uni28FA 4437 def +/uni28FB 4438 def +/uni28FC 4439 def +/uni28FD 4440 def +/uni28FE 4441 def +/uni28FF 4442 def +/uni2906 4443 def +/uni2907 4444 def +/uni290A 4445 def +/uni290B 4446 def +/uni2940 4447 def +/uni2941 4448 def +/uni2983 4449 def +/uni2984 4450 def +/uni29CE 4451 def +/uni29CF 4452 def +/uni29D0 4453 def +/uni29D1 4454 def +/uni29D2 4455 def +/uni29D3 4456 def +/uni29D4 4457 def +/uni29D5 4458 def +/uni29EB 4459 def +/uni29FA 4460 def +/uni29FB 4461 def +/uni2A00 4462 def +/uni2A01 4463 def +/uni2A02 4464 def +/uni2A0C 4465 def +/uni2A0D 4466 def +/uni2A0E 4467 def +/uni2A0F 4468 def +/uni2A10 4469 def +/uni2A11 4470 def +/uni2A12 4471 def +/uni2A13 4472 def +/uni2A14 4473 def +/uni2A15 4474 def +/uni2A16 4475 def +/uni2A17 4476 def +/uni2A18 4477 def +/uni2A19 4478 def +/uni2A1A 4479 def +/uni2A1B 4480 def +/uni2A1C 4481 def +/uni2A2F 4482 def +/uni2A6A 4483 def +/uni2A6B 4484 def +/uni2A7D 4485 def +/uni2A7E 4486 def +/uni2A7F 4487 def +/uni2A80 4488 def +/uni2A81 4489 def +/uni2A82 4490 def +/uni2A83 4491 def +/uni2A84 4492 def +/uni2A85 4493 def +/uni2A86 4494 def +/uni2A87 4495 def +/uni2A88 4496 def +/uni2A89 4497 def +/uni2A8A 4498 def +/uni2A8B 4499 def +/uni2A8C 4500 def +/uni2A8D 4501 def +/uni2A8E 4502 def +/uni2A8F 4503 def +/uni2A90 4504 def +/uni2A91 4505 def +/uni2A92 4506 def +/uni2A93 4507 def +/uni2A94 4508 def +/uni2A95 4509 def +/uni2A96 4510 def +/uni2A97 4511 def +/uni2A98 4512 def +/uni2A99 4513 def +/uni2A9A 4514 def +/uni2A9B 4515 def +/uni2A9C 4516 def +/uni2A9D 4517 def +/uni2A9E 4518 def +/uni2A9F 4519 def +/uni2AA0 4520 def +/uni2AAE 4521 def +/uni2AAF 4522 def +/uni2AB0 4523 def +/uni2AB1 4524 def +/uni2AB2 4525 def +/uni2AB3 4526 def +/uni2AB4 4527 def +/uni2AB5 4528 def +/uni2AB6 4529 def +/uni2AB7 4530 def +/uni2AB8 4531 def +/uni2AB9 4532 def +/uni2ABA 4533 def +/uni2AF9 4534 def +/uni2AFA 4535 def +/uni2B00 4536 def +/uni2B01 4537 def +/uni2B02 4538 def +/uni2B03 4539 def +/uni2B04 4540 def +/uni2B05 4541 def +/uni2B06 4542 def +/uni2B07 4543 def +/uni2B08 4544 def +/uni2B09 4545 def +/uni2B0A 4546 def +/uni2B0B 4547 def +/uni2B0C 4548 def +/uni2B0D 4549 def +/uni2B0E 4550 def +/uni2B0F 4551 def +/uni2B10 4552 def +/uni2B11 4553 def +/uni2B12 4554 def +/uni2B13 4555 def +/uni2B14 4556 def +/uni2B15 4557 def +/uni2B16 4558 def +/uni2B17 4559 def +/uni2B18 4560 def +/uni2B19 4561 def +/uni2B1A 4562 def +/uni2B1F 4563 def +/uni2B20 4564 def +/uni2B21 4565 def +/uni2B22 4566 def +/uni2B23 4567 def +/uni2B24 4568 def +/uni2B53 4569 def +/uni2B54 4570 def +/uni2C60 4571 def +/uni2C61 4572 def +/uni2C62 4573 def +/uni2C63 4574 def +/uni2C64 4575 def +/uni2C65 4576 def +/uni2C66 4577 def +/uni2C67 4578 def +/uni2C68 4579 def +/uni2C69 4580 def +/uni2C6A 4581 def +/uni2C6B 4582 def +/uni2C6C 4583 def +/uni2C6D 4584 def +/uni2C6E 4585 def +/uni2C6F 4586 def +/uni2C70 4587 def +/uni2C71 4588 def +/uni2C72 4589 def +/uni2C73 4590 def +/uni2C74 4591 def +/uni2C75 4592 def +/uni2C76 4593 def +/uni2C77 4594 def +/uni2C79 4595 def +/uni2C7A 4596 def +/uni2C7B 4597 def +/uni2C7C 4598 def +/uni2C7D 4599 def +/uni2C7E 4600 def +/uni2C7F 4601 def +/uni2D00 4602 def +/uni2D01 4603 def +/uni2D02 4604 def +/uni2D03 4605 def +/uni2D04 4606 def +/uni2D05 4607 def +/uni2D06 4608 def +/uni2D07 4609 def +/uni2D08 4610 def +/uni2D09 4611 def +/uni2D0A 4612 def +/uni2D0B 4613 def +/uni2D0C 4614 def +/uni2D0D 4615 def +/uni2D0E 4616 def +/uni2D0F 4617 def +/uni2D10 4618 def +/uni2D11 4619 def +/uni2D12 4620 def +/uni2D13 4621 def +/uni2D14 4622 def +/uni2D15 4623 def +/uni2D16 4624 def +/uni2D17 4625 def +/uni2D18 4626 def +/uni2D19 4627 def +/uni2D1A 4628 def +/uni2D1B 4629 def +/uni2D1C 4630 def +/uni2D1D 4631 def +/uni2D1E 4632 def +/uni2D1F 4633 def +/uni2D20 4634 def +/uni2D21 4635 def +/uni2D22 4636 def +/uni2D23 4637 def +/uni2D24 4638 def +/uni2D25 4639 def +/uni2D30 4640 def +/uni2D31 4641 def +/uni2D32 4642 def +/uni2D33 4643 def +/uni2D34 4644 def +/uni2D35 4645 def +/uni2D36 4646 def +/uni2D37 4647 def +/uni2D38 4648 def +/uni2D39 4649 def +/uni2D3A 4650 def +/uni2D3B 4651 def +/uni2D3C 4652 def +/uni2D3D 4653 def +/uni2D3E 4654 def +/uni2D3F 4655 def +/uni2D40 4656 def +/uni2D41 4657 def +/uni2D42 4658 def +/uni2D43 4659 def +/uni2D44 4660 def +/uni2D45 4661 def +/uni2D46 4662 def +/uni2D47 4663 def +/uni2D48 4664 def +/uni2D49 4665 def +/uni2D4A 4666 def +/uni2D4B 4667 def +/uni2D4C 4668 def +/uni2D4D 4669 def +/uni2D4E 4670 def +/uni2D4F 4671 def +/uni2D50 4672 def +/uni2D51 4673 def +/uni2D52 4674 def +/uni2D53 4675 def +/uni2D54 4676 def +/uni2D55 4677 def +/uni2D56 4678 def +/uni2D57 4679 def +/uni2D58 4680 def +/uni2D59 4681 def +/uni2D5A 4682 def +/uni2D5B 4683 def +/uni2D5C 4684 def +/uni2D5D 4685 def +/uni2D5E 4686 def +/uni2D5F 4687 def +/uni2D60 4688 def +/uni2D61 4689 def +/uni2D62 4690 def +/uni2D63 4691 def +/uni2D64 4692 def +/uni2D65 4693 def +/uni2D6F 4694 def +/uni2E18 4695 def +/uni2E1F 4696 def +/uni2E22 4697 def +/uni2E23 4698 def +/uni2E24 4699 def +/uni2E25 4700 def +/uni2E2E 4701 def +/uni4DC0 4702 def +/uni4DC1 4703 def +/uni4DC2 4704 def +/uni4DC3 4705 def +/uni4DC4 4706 def +/uni4DC5 4707 def +/uni4DC6 4708 def +/uni4DC7 4709 def +/uni4DC8 4710 def +/uni4DC9 4711 def +/uni4DCA 4712 def +/uni4DCB 4713 def +/uni4DCC 4714 def +/uni4DCD 4715 def +/uni4DCE 4716 def +/uni4DCF 4717 def +/uni4DD0 4718 def +/uni4DD1 4719 def +/uni4DD2 4720 def +/uni4DD3 4721 def +/uni4DD4 4722 def +/uni4DD5 4723 def +/uni4DD6 4724 def +/uni4DD7 4725 def +/uni4DD8 4726 def +/uni4DD9 4727 def +/uni4DDA 4728 def +/uni4DDB 4729 def +/uni4DDC 4730 def +/uni4DDD 4731 def +/uni4DDE 4732 def +/uni4DDF 4733 def +/uni4DE0 4734 def +/uni4DE1 4735 def +/uni4DE2 4736 def +/uni4DE3 4737 def +/uni4DE4 4738 def +/uni4DE5 4739 def +/uni4DE6 4740 def +/uni4DE7 4741 def +/uni4DE8 4742 def +/uni4DE9 4743 def +/uni4DEA 4744 def +/uni4DEB 4745 def +/uni4DEC 4746 def +/uni4DED 4747 def +/uni4DEE 4748 def +/uni4DEF 4749 def +/uni4DF0 4750 def +/uni4DF1 4751 def +/uni4DF2 4752 def +/uni4DF3 4753 def +/uni4DF4 4754 def +/uni4DF5 4755 def +/uni4DF6 4756 def +/uni4DF7 4757 def +/uni4DF8 4758 def +/uni4DF9 4759 def +/uni4DFA 4760 def +/uni4DFB 4761 def +/uni4DFC 4762 def +/uni4DFD 4763 def +/uni4DFE 4764 def +/uni4DFF 4765 def +/uniA4D0 4766 def +/uniA4D1 4767 def +/uniA4D2 4768 def +/uniA4D3 4769 def +/uniA4D4 4770 def +/uniA4D5 4771 def +/uniA4D6 4772 def +/uniA4D7 4773 def +/uniA4D8 4774 def +/uniA4D9 4775 def +/uniA4DA 4776 def +/uniA4DB 4777 def +/uniA4DC 4778 def +/uniA4DD 4779 def +/uniA4DE 4780 def +/uniA4DF 4781 def +/uniA4E0 4782 def +/uniA4E1 4783 def +/uniA4E2 4784 def +/uniA4E3 4785 def +/uniA4E4 4786 def +/uniA4E5 4787 def +/uniA4E6 4788 def +/uniA4E7 4789 def +/uniA4E8 4790 def +/uniA4E9 4791 def +/uniA4EA 4792 def +/uniA4EB 4793 def +/uniA4EC 4794 def +/uniA4ED 4795 def +/uniA4EE 4796 def +/uniA4EF 4797 def +/uniA4F0 4798 def +/uniA4F1 4799 def +/uniA4F2 4800 def +/uniA4F3 4801 def +/uniA4F4 4802 def +/uniA4F5 4803 def +/uniA4F6 4804 def +/uniA4F7 4805 def +/uniA4F8 4806 def +/uniA4F9 4807 def +/uniA4FA 4808 def +/uniA4FB 4809 def +/uniA4FC 4810 def +/uniA4FD 4811 def +/uniA4FE 4812 def +/uniA4FF 4813 def +/uniA644 4814 def +/uniA645 4815 def +/uniA646 4816 def +/uniA647 4817 def +/uniA64C 4818 def +/uniA64D 4819 def +/uniA650 4820 def +/uniA651 4821 def +/uniA654 4822 def +/uniA655 4823 def +/uniA656 4824 def +/uniA657 4825 def +/uniA662 4826 def +/uniA663 4827 def +/uniA664 4828 def +/uniA665 4829 def +/uniA666 4830 def +/uniA667 4831 def +/uniA668 4832 def +/uniA669 4833 def +/uniA66A 4834 def +/uniA66B 4835 def +/uniA66C 4836 def +/uniA66D 4837 def +/uniA66E 4838 def +/uniA68A 4839 def +/uniA68B 4840 def +/uniA68C 4841 def +/uniA68D 4842 def +/uniA694 4843 def +/uniA695 4844 def +/uniA708 4845 def +/uniA709 4846 def +/uniA70A 4847 def +/uniA70B 4848 def +/uniA70C 4849 def +/uniA70D 4850 def +/uniA70E 4851 def +/uniA70F 4852 def +/uniA710 4853 def +/uniA711 4854 def +/uniA712 4855 def +/uniA713 4856 def +/uniA714 4857 def +/uniA715 4858 def +/uniA716 4859 def +/uniA71B 4860 def +/uniA71C 4861 def +/uniA71D 4862 def +/uniA71E 4863 def +/uniA71F 4864 def +/uniA722 4865 def +/uniA723 4866 def +/uniA724 4867 def +/uniA725 4868 def +/uniA726 4869 def +/uniA727 4870 def +/uniA728 4871 def +/uniA729 4872 def +/uniA72A 4873 def +/uniA72B 4874 def +/uniA730 4875 def +/uniA731 4876 def +/uniA732 4877 def +/uniA733 4878 def +/uniA734 4879 def +/uniA735 4880 def +/uniA736 4881 def +/uniA737 4882 def +/uniA738 4883 def +/uniA739 4884 def +/uniA73A 4885 def +/uniA73B 4886 def +/uniA73C 4887 def +/uniA73D 4888 def +/uniA73E 4889 def +/uniA73F 4890 def +/uniA740 4891 def +/uniA741 4892 def +/uniA746 4893 def +/uniA747 4894 def +/uniA748 4895 def +/uniA749 4896 def +/uniA74A 4897 def +/uniA74B 4898 def +/uniA74E 4899 def +/uniA74F 4900 def +/uniA750 4901 def +/uniA751 4902 def +/uniA752 4903 def +/uniA753 4904 def +/uniA756 4905 def +/uniA757 4906 def +/uniA764 4907 def +/uniA765 4908 def +/uniA766 4909 def +/uniA767 4910 def +/uniA780 4911 def +/uniA781 4912 def +/uniA782 4913 def +/uniA783 4914 def +/uniA789 4915 def +/uniA78A 4916 def +/uniA78B 4917 def +/uniA78C 4918 def +/uniA78D 4919 def +/uniA78E 4920 def +/uniA790 4921 def +/uniA791 4922 def +/uniA7A0 4923 def +/uniA7A1 4924 def +/uniA7A2 4925 def +/uniA7A3 4926 def +/uniA7A4 4927 def +/uniA7A5 4928 def +/uniA7A6 4929 def +/uniA7A7 4930 def +/uniA7A8 4931 def +/uniA7A9 4932 def +/uniA7AA 4933 def +/uniA7F8 4934 def +/uniA7F9 4935 def +/uniA7FA 4936 def +/uniA7FB 4937 def +/uniA7FC 4938 def +/uniA7FD 4939 def +/uniA7FE 4940 def +/uniA7FF 4941 def +/uni02E5.5 4942 def +/uni02E6.5 4943 def +/uni02E7.5 4944 def +/uni02E8.5 4945 def +/uni02E9.5 4946 def +/uni02E5.4 4947 def +/uni02E6.4 4948 def +/uni02E7.4 4949 def +/uni02E8.4 4950 def +/uni02E9.4 4951 def +/uni02E5.3 4952 def +/uni02E6.3 4953 def +/uni02E7.3 4954 def +/uni02E8.3 4955 def +/uni02E9.3 4956 def +/uni02E5.2 4957 def +/uni02E6.2 4958 def +/uni02E7.2 4959 def +/uni02E8.2 4960 def +/uni02E9.2 4961 def +/uni02E5.1 4962 def +/uni02E6.1 4963 def +/uni02E7.1 4964 def +/uni02E8.1 4965 def +/uni02E9.1 4966 def +/stem 4967 def +/uniF000 4968 def +/uniF001 4969 def +/uniF002 4970 def +/uniF003 4971 def +/uniF400 4972 def +/uniF401 4973 def +/uniF402 4974 def +/uniF403 4975 def +/uniF404 4976 def +/uniF405 4977 def +/uniF406 4978 def +/uniF407 4979 def +/uniF408 4980 def +/uniF409 4981 def +/uniF40A 4982 def +/uniF40B 4983 def +/uniF40C 4984 def +/uniF40D 4985 def +/uniF40E 4986 def +/uniF40F 4987 def +/uniF410 4988 def +/uniF411 4989 def +/uniF412 4990 def +/uniF413 4991 def +/uniF414 4992 def +/uniF415 4993 def +/uniF416 4994 def +/uniF417 4995 def +/uniF418 4996 def +/uniF419 4997 def +/uniF41A 4998 def +/uniF41B 4999 def +/uniF41C 5000 def +/uniF41D 5001 def +/uniF41E 5002 def +/uniF41F 5003 def +/uniF420 5004 def +/uniF421 5005 def +/uniF422 5006 def +/uniF423 5007 def +/uniF424 5008 def +/uniF425 5009 def +/uniF426 5010 def +/uniF428 5011 def +/uniF429 5012 def +/uniF42A 5013 def +/uniF42B 5014 def +/uniF42C 5015 def +/uniF42D 5016 def +/uniF42E 5017 def +/uniF42F 5018 def +/uniF430 5019 def +/uniF431 5020 def +/uniF432 5021 def +/uniF433 5022 def +/uniF434 5023 def +/uniF435 5024 def +/uniF436 5025 def +/uniF437 5026 def +/uniF438 5027 def +/uniF439 5028 def +/uniF43A 5029 def +/uniF43B 5030 def +/uniF43C 5031 def +/uniF43D 5032 def +/uniF43E 5033 def +/uniF43F 5034 def +/uniF440 5035 def +/uniF441 5036 def +/uniF6C5 5037 def +/uniFB00 5038 def +/fi 5039 def +/fl 5040 def +/uniFB03 5041 def +/uniFB04 5042 def +/uniFB05 5043 def +/uniFB06 5044 def +/uniFB13 5045 def +/uniFB14 5046 def +/uniFB15 5047 def +/uniFB16 5048 def +/uniFB17 5049 def +/uniFB1D 5050 def +/uniFB1E 5051 def +/uniFB1F 5052 def +/uniFB20 5053 def +/uniFB21 5054 def +/uniFB22 5055 def +/uniFB23 5056 def +/uniFB24 5057 def +/uniFB25 5058 def +/uniFB26 5059 def +/uniFB27 5060 def +/uniFB28 5061 def +/uniFB29 5062 def +/uniFB2A 5063 def +/uniFB2B 5064 def +/uniFB2C 5065 def +/uniFB2D 5066 def +/uniFB2E 5067 def +/uniFB2F 5068 def +/uniFB30 5069 def +/uniFB31 5070 def +/uniFB32 5071 def +/uniFB33 5072 def +/uniFB34 5073 def +/uniFB35 5074 def +/uniFB36 5075 def +/uniFB38 5076 def +/uniFB39 5077 def +/uniFB3A 5078 def +/uniFB3B 5079 def +/uniFB3C 5080 def +/uniFB3E 5081 def +/uniFB40 5082 def +/uniFB41 5083 def +/uniFB43 5084 def +/uniFB44 5085 def +/uniFB46 5086 def +/uniFB47 5087 def +/uniFB48 5088 def +/uniFB49 5089 def +/uniFB4A 5090 def +/uniFB4B 5091 def +/uniFB4C 5092 def +/uniFB4D 5093 def +/uniFB4E 5094 def +/uniFB4F 5095 def +/uniFB52 5096 def +/uniFB53 5097 def +/uniFB54 5098 def +/uniFB55 5099 def +/uniFB56 5100 def +/uniFB57 5101 def +/uniFB58 5102 def +/uniFB59 5103 def +/uniFB5A 5104 def +/uniFB5B 5105 def +/uniFB5C 5106 def +/uniFB5D 5107 def +/uniFB5E 5108 def +/uniFB5F 5109 def +/uniFB60 5110 def +/uniFB61 5111 def +/uniFB62 5112 def +/uniFB63 5113 def +/uniFB64 5114 def +/uniFB65 5115 def +/uniFB66 5116 def +/uniFB67 5117 def +/uniFB68 5118 def +/uniFB69 5119 def +/uniFB6A 5120 def +/uniFB6B 5121 def +/uniFB6C 5122 def +/uniFB6D 5123 def +/uniFB6E 5124 def +/uniFB6F 5125 def +/uniFB70 5126 def +/uniFB71 5127 def +/uniFB72 5128 def +/uniFB73 5129 def +/uniFB74 5130 def +/uniFB75 5131 def +/uniFB76 5132 def +/uniFB77 5133 def +/uniFB78 5134 def +/uniFB79 5135 def +/uniFB7A 5136 def +/uniFB7B 5137 def +/uniFB7C 5138 def +/uniFB7D 5139 def +/uniFB7E 5140 def +/uniFB7F 5141 def +/uniFB80 5142 def +/uniFB81 5143 def +/uniFB82 5144 def +/uniFB83 5145 def +/uniFB84 5146 def +/uniFB85 5147 def +/uniFB86 5148 def +/uniFB87 5149 def +/uniFB88 5150 def +/uniFB89 5151 def +/uniFB8A 5152 def +/uniFB8B 5153 def +/uniFB8C 5154 def +/uniFB8D 5155 def +/uniFB8E 5156 def +/uniFB8F 5157 def +/uniFB90 5158 def +/uniFB91 5159 def +/uniFB92 5160 def +/uniFB93 5161 def +/uniFB94 5162 def +/uniFB95 5163 def +/uniFB96 5164 def +/uniFB97 5165 def +/uniFB98 5166 def +/uniFB99 5167 def +/uniFB9A 5168 def +/uniFB9B 5169 def +/uniFB9C 5170 def +/uniFB9D 5171 def +/uniFB9E 5172 def +/uniFB9F 5173 def +/uniFBA0 5174 def +/uniFBA1 5175 def +/uniFBA2 5176 def +/uniFBA3 5177 def +/uniFBAA 5178 def +/uniFBAB 5179 def +/uniFBAC 5180 def +/uniFBAD 5181 def +/uniFBD3 5182 def +/uniFBD4 5183 def +/uniFBD5 5184 def +/uniFBD6 5185 def +/uniFBD7 5186 def +/uniFBD8 5187 def +/uniFBD9 5188 def +/uniFBDA 5189 def +/uniFBDB 5190 def +/uniFBDC 5191 def +/uniFBDE 5192 def +/uniFBDF 5193 def +/uniFBE4 5194 def +/uniFBE5 5195 def +/uniFBE6 5196 def +/uniFBE7 5197 def +/uniFBE8 5198 def +/uniFBE9 5199 def +/uniFBFC 5200 def +/uniFBFD 5201 def +/uniFBFE 5202 def +/uniFBFF 5203 def +/uniFE00 5204 def +/uniFE01 5205 def +/uniFE02 5206 def +/uniFE03 5207 def +/uniFE04 5208 def +/uniFE05 5209 def +/uniFE06 5210 def +/uniFE07 5211 def +/uniFE08 5212 def +/uniFE09 5213 def +/uniFE0A 5214 def +/uniFE0B 5215 def +/uniFE0C 5216 def +/uniFE0D 5217 def +/uniFE0E 5218 def +/uniFE0F 5219 def +/uniFE20 5220 def +/uniFE21 5221 def +/uniFE22 5222 def +/uniFE23 5223 def +/uniFE70 5224 def +/uniFE71 5225 def +/uniFE72 5226 def +/uniFE73 5227 def +/uniFE74 5228 def +/uniFE76 5229 def +/uniFE77 5230 def +/uniFE78 5231 def +/uniFE79 5232 def +/uniFE7A 5233 def +/uniFE7B 5234 def +/uniFE7C 5235 def +/uniFE7D 5236 def +/uniFE7E 5237 def +/uniFE7F 5238 def +/uniFE80 5239 def +/uniFE81 5240 def +/uniFE82 5241 def +/uniFE83 5242 def +/uniFE84 5243 def +/uniFE85 5244 def +/uniFE86 5245 def +/uniFE87 5246 def +/uniFE88 5247 def +/uniFE89 5248 def +/uniFE8A 5249 def +/uniFE8B 5250 def +/uniFE8C 5251 def +/uniFE8D 5252 def +/uniFE8E 5253 def +/uniFE8F 5254 def +/uniFE90 5255 def +/uniFE91 5256 def +/uniFE92 5257 def +/uniFE93 5258 def +/uniFE94 5259 def +/uniFE95 5260 def +/uniFE96 5261 def +/uniFE97 5262 def +/uniFE98 5263 def +/uniFE99 5264 def +/uniFE9A 5265 def +/uniFE9B 5266 def +/uniFE9C 5267 def +/uniFE9D 5268 def +/uniFE9E 5269 def +/uniFE9F 5270 def +/uniFEA0 5271 def +/uniFEA1 5272 def +/uniFEA2 5273 def +/uniFEA3 5274 def +/uniFEA4 5275 def +/uniFEA5 5276 def +/uniFEA6 5277 def +/uniFEA7 5278 def +/uniFEA8 5279 def +/uniFEA9 5280 def +/uniFEAA 5281 def +/uniFEAB 5282 def +/uniFEAC 5283 def +/uniFEAD 5284 def +/uniFEAE 5285 def +/uniFEAF 5286 def +/uniFEB0 5287 def +/uniFEB1 5288 def +/uniFEB2 5289 def +/uniFEB3 5290 def +/uniFEB4 5291 def +/uniFEB5 5292 def +/uniFEB6 5293 def +/uniFEB7 5294 def +/uniFEB8 5295 def +/uniFEB9 5296 def +/uniFEBA 5297 def +/uniFEBB 5298 def +/uniFEBC 5299 def +/uniFEBD 5300 def +/uniFEBE 5301 def +/uniFEBF 5302 def +/uniFEC0 5303 def +/uniFEC1 5304 def +/uniFEC2 5305 def +/uniFEC3 5306 def +/uniFEC4 5307 def +/uniFEC5 5308 def +/uniFEC6 5309 def +/uniFEC7 5310 def +/uniFEC8 5311 def +/uniFEC9 5312 def +/uniFECA 5313 def +/uniFECB 5314 def +/uniFECC 5315 def +/uniFECD 5316 def +/uniFECE 5317 def +/uniFECF 5318 def +/uniFED0 5319 def +/uniFED1 5320 def +/uniFED2 5321 def +/uniFED3 5322 def +/uniFED4 5323 def +/uniFED5 5324 def +/uniFED6 5325 def +/uniFED7 5326 def +/uniFED8 5327 def +/uniFED9 5328 def +/uniFEDA 5329 def +/uniFEDB 5330 def +/uniFEDC 5331 def +/uniFEDD 5332 def +/uniFEDE 5333 def +/uniFEDF 5334 def +/uniFEE0 5335 def +/uniFEE1 5336 def +/uniFEE2 5337 def +/uniFEE3 5338 def +/uniFEE4 5339 def +/uniFEE5 5340 def +/uniFEE6 5341 def +/uniFEE7 5342 def +/uniFEE8 5343 def +/uniFEE9 5344 def +/uniFEEA 5345 def +/uniFEEB 5346 def +/uniFEEC 5347 def +/uniFEED 5348 def +/uniFEEE 5349 def +/uniFEEF 5350 def +/uniFEF0 5351 def +/uniFEF1 5352 def +/uniFEF2 5353 def +/uniFEF3 5354 def +/uniFEF4 5355 def +/uniFEF5 5356 def +/uniFEF6 5357 def +/uniFEF7 5358 def +/uniFEF8 5359 def +/uniFEF9 5360 def +/uniFEFA 5361 def +/uniFEFB 5362 def +/uniFEFC 5363 def +/uniFEFF 5364 def +/uniFFF9 5365 def +/uniFFFA 5366 def +/uniFFFB 5367 def +/uniFFFC 5368 def +/uniFFFD 5369 def +/u10300 5370 def +/u10301 5371 def +/u10302 5372 def +/u10303 5373 def +/u10304 5374 def +/u10305 5375 def +/u10306 5376 def +/u10307 5377 def +/u10308 5378 def +/u10309 5379 def +/u1030A 5380 def +/u1030B 5381 def +/u1030C 5382 def +/u1030D 5383 def +/u1030E 5384 def +/u1030F 5385 def +/u10310 5386 def +/u10311 5387 def +/u10312 5388 def +/u10313 5389 def +/u10314 5390 def +/u10315 5391 def +/u10316 5392 def +/u10317 5393 def +/u10318 5394 def +/u10319 5395 def +/u1031A 5396 def +/u1031B 5397 def +/u1031C 5398 def +/u1031D 5399 def +/u1031E 5400 def +/u10320 5401 def +/u10321 5402 def +/u10322 5403 def +/u10323 5404 def +/u1D300 5405 def +/u1D301 5406 def +/u1D302 5407 def +/u1D303 5408 def +/u1D304 5409 def +/u1D305 5410 def +/u1D306 5411 def +/u1D307 5412 def +/u1D308 5413 def +/u1D309 5414 def +/u1D30A 5415 def +/u1D30B 5416 def +/u1D30C 5417 def +/u1D30D 5418 def +/u1D30E 5419 def +/u1D30F 5420 def +/u1D310 5421 def +/u1D311 5422 def +/u1D312 5423 def +/u1D313 5424 def +/u1D314 5425 def +/u1D315 5426 def +/u1D316 5427 def +/u1D317 5428 def +/u1D318 5429 def +/u1D319 5430 def +/u1D31A 5431 def +/u1D31B 5432 def +/u1D31C 5433 def +/u1D31D 5434 def +/u1D31E 5435 def +/u1D31F 5436 def +/u1D320 5437 def +/u1D321 5438 def +/u1D322 5439 def +/u1D323 5440 def +/u1D324 5441 def +/u1D325 5442 def +/u1D326 5443 def +/u1D327 5444 def +/u1D328 5445 def +/u1D329 5446 def +/u1D32A 5447 def +/u1D32B 5448 def +/u1D32C 5449 def +/u1D32D 5450 def +/u1D32E 5451 def +/u1D32F 5452 def +/u1D330 5453 def +/u1D331 5454 def +/u1D332 5455 def +/u1D333 5456 def +/u1D334 5457 def +/u1D335 5458 def +/u1D336 5459 def +/u1D337 5460 def +/u1D338 5461 def +/u1D339 5462 def +/u1D33A 5463 def +/u1D33B 5464 def +/u1D33C 5465 def +/u1D33D 5466 def +/u1D33E 5467 def +/u1D33F 5468 def +/u1D340 5469 def +/u1D341 5470 def +/u1D342 5471 def +/u1D343 5472 def +/u1D344 5473 def +/u1D345 5474 def +/u1D346 5475 def +/u1D347 5476 def +/u1D348 5477 def +/u1D349 5478 def +/u1D34A 5479 def +/u1D34B 5480 def +/u1D34C 5481 def +/u1D34D 5482 def +/u1D34E 5483 def +/u1D34F 5484 def +/u1D350 5485 def +/u1D351 5486 def +/u1D352 5487 def +/u1D353 5488 def +/u1D354 5489 def +/u1D355 5490 def +/u1D356 5491 def +/u1D538 5492 def +/u1D539 5493 def +/u1D53B 5494 def +/u1D53C 5495 def +/u1D53D 5496 def +/u1D53E 5497 def +/u1D540 5498 def +/u1D541 5499 def +/u1D542 5500 def +/u1D543 5501 def +/u1D544 5502 def +/u1D546 5503 def +/u1D54A 5504 def +/u1D54B 5505 def +/u1D54C 5506 def +/u1D54D 5507 def +/u1D54E 5508 def +/u1D54F 5509 def +/u1D550 5510 def +/u1D552 5511 def +/u1D553 5512 def +/u1D554 5513 def +/u1D555 5514 def +/u1D556 5515 def +/u1D557 5516 def +/u1D558 5517 def +/u1D559 5518 def +/u1D55A 5519 def +/u1D55B 5520 def +/u1D55C 5521 def +/u1D55D 5522 def +/u1D55E 5523 def +/u1D55F 5524 def +/u1D560 5525 def +/u1D561 5526 def +/u1D562 5527 def +/u1D563 5528 def +/u1D564 5529 def +/u1D565 5530 def +/u1D566 5531 def +/u1D567 5532 def +/u1D568 5533 def +/u1D569 5534 def +/u1D56A 5535 def +/u1D56B 5536 def +/u1D5A0 5537 def +/u1D5A1 5538 def +/u1D5A2 5539 def +/u1D5A3 5540 def +/u1D5A4 5541 def +/u1D5A5 5542 def +/u1D5A6 5543 def +/u1D5A7 5544 def +/u1D5A8 5545 def +/u1D5A9 5546 def +/u1D5AA 5547 def +/u1D5AB 5548 def +/u1D5AC 5549 def +/u1D5AD 5550 def +/u1D5AE 5551 def +/u1D5AF 5552 def +/u1D5B0 5553 def +/u1D5B1 5554 def +/u1D5B2 5555 def +/u1D5B3 5556 def +/u1D5B4 5557 def +/u1D5B5 5558 def +/u1D5B6 5559 def +/u1D5B7 5560 def +/u1D5B8 5561 def +/u1D5B9 5562 def +/u1D5BA 5563 def +/u1D5BB 5564 def +/u1D5BC 5565 def +/u1D5BD 5566 def +/u1D5BE 5567 def +/u1D5BF 5568 def +/u1D5C0 5569 def +/u1D5C1 5570 def +/u1D5C2 5571 def +/u1D5C3 5572 def +/u1D5C4 5573 def +/u1D5C5 5574 def +/u1D5C6 5575 def +/u1D5C7 5576 def +/u1D5C8 5577 def +/u1D5C9 5578 def +/u1D5CA 5579 def +/u1D5CB 5580 def +/u1D5CC 5581 def +/u1D5CD 5582 def +/u1D5CE 5583 def +/u1D5CF 5584 def +/u1D5D0 5585 def +/u1D5D1 5586 def +/u1D5D2 5587 def +/u1D5D3 5588 def +/u1D7D8 5589 def +/u1D7D9 5590 def +/u1D7DA 5591 def +/u1D7DB 5592 def +/u1D7DC 5593 def +/u1D7DD 5594 def +/u1D7DE 5595 def +/u1D7DF 5596 def +/u1D7E0 5597 def +/u1D7E1 5598 def +/u1D7E2 5599 def +/u1D7E3 5600 def +/u1D7E4 5601 def +/u1D7E5 5602 def +/u1D7E6 5603 def +/u1D7E7 5604 def +/u1D7E8 5605 def +/u1D7E9 5606 def +/u1D7EA 5607 def +/u1D7EB 5608 def +/u1EE00 5609 def +/u1EE01 5610 def +/u1EE02 5611 def +/u1EE03 5612 def +/u1EE05 5613 def +/u1EE06 5614 def +/u1EE07 5615 def +/u1EE08 5616 def +/u1EE09 5617 def +/u1EE0A 5618 def +/u1EE0B 5619 def +/u1EE0C 5620 def +/u1EE0D 5621 def +/u1EE0E 5622 def +/u1EE0F 5623 def +/u1EE10 5624 def +/u1EE11 5625 def +/u1EE12 5626 def +/u1EE13 5627 def +/u1EE14 5628 def +/u1EE15 5629 def +/u1EE16 5630 def +/u1EE17 5631 def +/u1EE18 5632 def +/u1EE19 5633 def +/u1EE1A 5634 def +/u1EE1B 5635 def +/u1EE1C 5636 def +/u1EE1D 5637 def +/u1EE1E 5638 def +/u1EE1F 5639 def +/u1EE21 5640 def +/u1EE22 5641 def +/u1EE24 5642 def +/u1EE27 5643 def +/u1EE29 5644 def +/u1EE2A 5645 def +/u1EE2B 5646 def +/u1EE2C 5647 def +/u1EE2D 5648 def +/u1EE2E 5649 def +/u1EE2F 5650 def +/u1EE30 5651 def +/u1EE31 5652 def +/u1EE32 5653 def +/u1EE34 5654 def +/u1EE35 5655 def +/u1EE36 5656 def +/u1EE37 5657 def +/u1EE39 5658 def +/u1EE3B 5659 def +/u1EE61 5660 def +/u1EE62 5661 def +/u1EE64 5662 def +/u1EE67 5663 def +/u1EE68 5664 def +/u1EE69 5665 def +/u1EE6A 5666 def +/u1EE6C 5667 def +/u1EE6D 5668 def +/u1EE6E 5669 def +/u1EE6F 5670 def +/u1EE70 5671 def +/u1EE71 5672 def +/u1EE72 5673 def +/u1EE74 5674 def +/u1EE75 5675 def +/u1EE76 5676 def +/u1EE77 5677 def +/u1EE79 5678 def +/u1EE7A 5679 def +/u1EE7B 5680 def +/u1EE7C 5681 def +/u1EE7E 5682 def +/u1F030 5683 def +/u1F031 5684 def +/u1F032 5685 def +/u1F033 5686 def +/u1F034 5687 def +/u1F035 5688 def +/u1F036 5689 def +/u1F037 5690 def +/u1F038 5691 def +/u1F039 5692 def +/u1F03A 5693 def +/u1F03B 5694 def +/u1F03C 5695 def +/u1F03D 5696 def +/u1F03E 5697 def +/u1F03F 5698 def +/u1F040 5699 def +/u1F041 5700 def +/u1F042 5701 def +/u1F043 5702 def +/u1F044 5703 def +/u1F045 5704 def +/u1F046 5705 def +/u1F047 5706 def +/u1F048 5707 def +/u1F049 5708 def +/u1F04A 5709 def +/u1F04B 5710 def +/u1F04C 5711 def +/u1F04D 5712 def +/u1F04E 5713 def +/u1F04F 5714 def +/u1F050 5715 def +/u1F051 5716 def +/u1F052 5717 def +/u1F053 5718 def +/u1F054 5719 def +/u1F055 5720 def +/u1F056 5721 def +/u1F057 5722 def +/u1F058 5723 def +/u1F059 5724 def +/u1F05A 5725 def +/u1F05B 5726 def +/u1F05C 5727 def +/u1F05D 5728 def +/u1F05E 5729 def +/u1F05F 5730 def +/u1F060 5731 def +/u1F061 5732 def +/u1F062 5733 def +/u1F063 5734 def +/u1F064 5735 def +/u1F065 5736 def +/u1F066 5737 def +/u1F067 5738 def +/u1F068 5739 def +/u1F069 5740 def +/u1F06A 5741 def +/u1F06B 5742 def +/u1F06C 5743 def +/u1F06D 5744 def +/u1F06E 5745 def +/u1F06F 5746 def +/u1F070 5747 def +/u1F071 5748 def +/u1F072 5749 def +/u1F073 5750 def +/u1F074 5751 def +/u1F075 5752 def +/u1F076 5753 def +/u1F077 5754 def +/u1F078 5755 def +/u1F079 5756 def +/u1F07A 5757 def +/u1F07B 5758 def +/u1F07C 5759 def +/u1F07D 5760 def +/u1F07E 5761 def +/u1F07F 5762 def +/u1F080 5763 def +/u1F081 5764 def +/u1F082 5765 def +/u1F083 5766 def +/u1F084 5767 def +/u1F085 5768 def +/u1F086 5769 def +/u1F087 5770 def +/u1F088 5771 def +/u1F089 5772 def +/u1F08A 5773 def +/u1F08B 5774 def +/u1F08C 5775 def +/u1F08D 5776 def +/u1F08E 5777 def +/u1F08F 5778 def +/u1F090 5779 def +/u1F091 5780 def +/u1F092 5781 def +/u1F093 5782 def +/u1F0A0 5783 def +/u1F0A1 5784 def +/u1F0A2 5785 def +/u1F0A3 5786 def +/u1F0A4 5787 def +/u1F0A5 5788 def +/u1F0A6 5789 def +/u1F0A7 5790 def +/u1F0A8 5791 def +/u1F0A9 5792 def +/u1F0AA 5793 def +/u1F0AB 5794 def +/u1F0AC 5795 def +/u1F0AD 5796 def +/u1F0AE 5797 def +/u1F0B1 5798 def +/u1F0B2 5799 def +/u1F0B3 5800 def +/u1F0B4 5801 def +/u1F0B5 5802 def +/u1F0B6 5803 def +/u1F0B7 5804 def +/u1F0B8 5805 def +/u1F0B9 5806 def +/u1F0BA 5807 def +/u1F0BB 5808 def +/u1F0BC 5809 def +/u1F0BD 5810 def +/u1F0BE 5811 def +/u1F0C1 5812 def +/u1F0C2 5813 def +/u1F0C3 5814 def +/u1F0C4 5815 def +/u1F0C5 5816 def +/u1F0C6 5817 def +/u1F0C7 5818 def +/u1F0C8 5819 def +/u1F0C9 5820 def +/u1F0CA 5821 def +/u1F0CB 5822 def +/u1F0CC 5823 def +/u1F0CD 5824 def +/u1F0CE 5825 def +/u1F0CF 5826 def +/u1F0D1 5827 def +/u1F0D2 5828 def +/u1F0D3 5829 def +/u1F0D4 5830 def +/u1F0D5 5831 def +/u1F0D6 5832 def +/u1F0D7 5833 def +/u1F0D8 5834 def +/u1F0D9 5835 def +/u1F0DA 5836 def +/u1F0DB 5837 def +/u1F0DC 5838 def +/u1F0DD 5839 def +/u1F0DE 5840 def +/u1F0DF 5841 def +/u1F42D 5842 def +/u1F42E 5843 def +/u1F431 5844 def +/u1F435 5845 def +/u1F600 5846 def +/u1F601 5847 def +/u1F602 5848 def +/u1F603 5849 def +/u1F604 5850 def +/u1F605 5851 def +/u1F606 5852 def +/u1F607 5853 def +/u1F608 5854 def +/u1F609 5855 def +/u1F60A 5856 def +/u1F60B 5857 def +/u1F60C 5858 def +/u1F60D 5859 def +/u1F60E 5860 def +/u1F60F 5861 def end readonly def - -systemdict/resourcestatus known - {42 /FontType resourcestatus - {pop pop false}{true}ifelse} - {true}ifelse -{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse -/FontType 3 def - /TrueState 271 string def - TrueDict begin sfnts save - 72 0 matrix defaultmatrix dtransform dup - mul exch dup mul add sqrt cvi 0 72 matrix - defaultmatrix dtransform dup mul exch dup - mul add sqrt cvi 3 -1 roll restore - TrueState initer end - /BuildGlyph{exch begin - CharStrings dup 2 index known - {exch}{exch pop /.notdef}ifelse - get dup xcheck - {currentdict systemdict begin begin exec end end} - {TrueDict begin /bander load cvlit exch TrueState render end} - ifelse - end}bind def - /BuildChar{ - 1 index /Encoding get exch get - 1 index /BuildGlyph get exec - }bind def -}if - -FontName currentdict end definefont pop + /sfnts[<0001000000120100000400204744454616f816dc0000012c0000001c47504f5344764c7500000148000000204753 +554227a43fc300000168000000964d415448093f338400000200000000f64f532f3269d8710a000002f800000056636d6170 +0013034000000350000000306376742000691d3900000380000001fe6670676d7134766a00000580000000ab676173700007 +00070000062c0000000c676c79663771325a0000063800000fb268656164085dc286000015ec00000036686865610d9f1e47 +0000162400000024686d7478305c06a40000164800005b806c6f636124d020ea000071c800002dce6d6178701b5306710000 +9f98000000206e616d6527ed3dbc00009fb8000001d4706f7374ad1958a00000a18c0000dcae707265703b07f10000017e3c +0000056800010000000c0000000000000002000200030003000116d616e5000100010000000a001c001e000144464c540008 +000400000000ffff00000000000000010000000a00920094001444464c54007a61726162008461726d6e0084627261690084 +63616e7300846368657200846379726c008467656f7200846772656b008468616e6900846865627200846b616e6100846c61 +6f2000846c61746e00846d61746800846e6b6f2000846f67616d008472756e72008474666e67008474686169008400040000 +0000ffff00000000000000000000000000010000000a00e000e80050003c0c0007dd00000000028200000460000005d50000 +0000000004600000000000000000000000000000046000000000000001680000046000000055000000000000000000000000 +00000000000000000000000000000000000000000000010e0000027600000000000000000000000000000000000000000000 +000000000000000000000000005a0000010e0000005a0000005a0000010e00000000000000000000010e0000005a0000005a +0000010e0000005a0000005a0000005a000001720000005a0000005a000002380000fb8f0000003c00000000000000000028 +000a000a000000000001000000000001040e019000050000053305990000011e05330599000003d7006602120000020b0603 +03080402020400000000020000000000000000000000506645640040ffffffff0614fe14019a076d01e30000000100000000 +0000000000020000000a000000140003000a00000014000c00000000001c00000000000000010001f6000001f60f000016d6 +013500b800cb00cb00c100aa009c01a600b800660000007100cb00a002b20085007500b800c301cb0189022d00cb00a600f0 +00d300aa008700cb03aa0400014a003300cb000000d9050200f4015400b4009c01390114013907060400044e04b4045204b8 +04e704cd0037047304cd04600473013303a2055605a60556053903c5021200c9001f00b801df007300ba03e9033303bc0444 +040e00df03cd03aa00e503aa0404000000cb008f00a4007b00b80014016f007f027b0252008f00c705cd009a009a006f00cb +00cd019e01d300f000ba018300d5009803040248009e01d500c100cb00f600830354027f00000333026600d300c700a400cd +008f009a0073040005d5010a00fe022b00a400b4009c00000062009c0000001d032d05d505d505d505f0007f007b005400a4 +06b80614072301d300b800cb00a601c301ec069300a000d3035c037103db0185042304a80448008f0139011401390360008f +05d5019a0614072306660179046004600460047b009c00000277046001aa00e904600762007b00c5007f027b000000b40252 +05cd006600bc00660077061000cd013b01850389008f007b0000001d00cd074a042f009c009c0000077d006f0000006f0335 +006a006f007b00ae00b2002d0396008f027b00f600830354063705f6008f009c04e10266008f018d02f600cd034400290066 +04ee00730000140000960000b707060504030201002c2010b002254964b040515820c859212d2cb002254964b040515820c8 +59212d2c20100720b00050b00d7920b8ffff5058041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff505804 +1b0559b0051cb0032508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b5358b00225b0022545445921212d2c45 +442d2cb00225b0022549b00525b005254960b0206368208a108a233a8a10653a2d000000000200080002ffff0003000900aa +ff6a07ad066e000b00160022002e00340039003e004200460000013436333216151406232226253436321615140623222605 +1000212000111000212000031000212000111000212000012110002000053637112303112311162536372305352316028251 +3b3a52523a3b510242527453533a3b51fc6d01bf013c013d01bdfe43fec3fec4fe4187020e01740175020cfdf4fe8bfe8cfd +f2011404dbfe95fdfcfe9402b2584ca488a44c020c481c64fd20651c03ff3b51513b3a53533a3b51513b3a5353dbfec4fe43 +01bd013c013d01c0fe40fec30175020ffdf1fe8bfe8cfdf4020c016afefefe96016adf0b2a0125fea6015afedb2a995264b8 +b866000900aaff6a07ad066e000b00170023002f0035003a003f004300470000011000212000111000212000031000212000 +1110002120000134363216152334262206152134363216152334262206150721100020000536371123031123111625363723 +05352316013101bf013c013d01bdfe43fec3fec4fe4187020e01740175020cfdf4fe8bfe8cfdf203bc8cc48c873d543dfd29 +8cc48c873d543ddf04dbfe95fdfcfe9402b2584ca488a44c020c481c64fd20651c02eafec4fe4301bd013c013d01c0fe40fe +c30175020ffdf1fe8bfe8cfdf4020c02168bc5c58b537777538bc5c58b53777753acfefefe96016adf0b2a0125fea6015afe +db2a995264b8b8660009005fff6a08f9066e00030007000c001100170023002f0059007e0000013523160536372301112311 +1617363711232521100020001334363216152334262206152134363216152334262206150510002120001114071716171615 +14062227262f010207002120012603070607062226353437363f012637100021200011342706232227262f01171617262726 +2120070607363f0107060706222706033c651c0329481c64fe4ca44ce0584ca4fd4e04dbfe95fdfcfe94588cc48c873d543d +01c98cc48c873d543dfbbd020e01740175020c025d3b181e364c1f19120b2ad1fefafe8bfe8cfef9d12b0a12191f4c361e17 +3c5c028701bf013c013d01bd011b25261f19122d8829182f9adefec2fec4e09a2f1829882d12191f4c1b0101a1b866505264 +fea6015afedb2a0b0b2a012587fefefe96016a01ae8bc5c58b537777538bc5c58b53777753a20175020ffdf1fe8b1d1b1f14 +181f2526361f193920fee9d1fefa0106d001161e39191f3626251f19131e1c1dfec4fe4301bd013c14131a1f1939882d0e0f +c79ae0e09bc6100d2d8839191f1b1400000600aaff6a07ad066e000b00160022002e0034003c000001343633321615140623 +2226253436321615140623222605100021200011100021200013100021200011100021200013211000200025211617162437 +360282513b3a52523a3b510242527453533a3b51fbe6020e01740175020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fe +c3fec4fe418d04dbfe95fdfcfe940442fc57215c8e01948e5c03ff3b51513b3a53533a3b51513b3a5353db0175020ffdf1fe +8bfe8cfdf4020c0174fec4fe4301bd013c013d01c0fe40feb9fefefe96016a7b755c8e018e5c000600aaff6a07ad066e000b +00170023002f0035003e00000134363216152334262206152134363216152334262206150510002120001110002120001310 +0021200011100021200013211000200025211617163732373602168cc48c873d543d01c98cc48c873d543dfbbd020e017401 +75020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe418d04dbfe95fdfcfe940442fc57215c8ecaca8e5c038c +8bc5c58b537777538bc5c58b53777753a20175020ffdf1fe8bfe8cfdf4020c0174fec4fe4301bd013c013d01c0fe40feb9fe +fefe96016a7b755c8e018e5c000700aaff6a07ad066e000b00170023002f003b0041004a0000011716070607062226353437 +0534363216152334262206152134363216152334262206150510002120001110002120001310002120001110002120001321 +1000200025211617163732373606a8411c01011a1b4c361bfbaf8cc48c873d543d01c98cc48c873d543dfbbd020e01740175 +020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe418d04dbfe95fdfcfe940442fc57215c8ecaca8e5c042c80 +38222b1a1b362c2335208bc5c58b537777538bc5c58b53777753a20175020ffdf1fe8bfe8cfdf4020c0174fec4fe4301bd01 +3c013d01c0fe40feb9fefefe96016a7b755c8e018e5c000600aaff6a07ad066e0006000d00190025002b0033000001251707 +170725271505273727370110002120001110002120001310002120001110002120001321100020002521161716243736047c +01274dc6c64dfed9a0fed94dc6c64dfdf5020e01740175020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe41 +8d04dbfe95fdfcfe940442fc57215c8e01948e5c0427ce6e8b8a6ece5555ce6e8a8b6efdf50175020ffdf1fe8bfe8cfdf402 +0c0174fec4fe4301bd013c013d01c0fe40feb9fefefe96016a7b755c8e018e5c0007008fff6a07c80763000f001f002a0035 +00490057005f00000137161716043736371706070620272601100021200011102f0106212027070605343632161514062322 +2625343633321614062322260134242120041514071611100021200011103726253635342421200415141736212017262322 +07163332021d731c288f01938e271d732532b6fdfcb632feef01bf013c013d01bdde0ee5fed7fed8e60ee00394527453533a +3b51fdbe513b3a52523a3b51fe0d021e017f0180021cfde2fdf4fe8bfe8cfdf2e3fe05dbbcfe43fec3fec4fe41bdf4014a01 +4b43b0deddb0b0ddde01d1472c268d018e272c483932b5b5330151fec4fe4301bd013c013de00e39390ee02a3b51513b3a53 +533a3b5151765253025c86bebe86825cfcfea5fe8cfdf4020c0174015bfc5c02364a517272514a36cff56e6e1c00000700aa +ff6a07ad07300003000e0019001d002d0039004a000001050725013436321615140623222625343633321614062322260117 +052701371617162037363717060706202726011000212000111000212000013620172511161110002120001110371102d201 +2f4dfed1023f527453533a3b51fdbe513b3a52523a3b5103044dfed14dfdc6731c288e01948e271d732532b6fdfcb632feef +01bf013c013d01bdfe43fec3fec4fe4101e284012a8401bbadfdf4fe8bfe8cfdf2ae0567d46fd5febc3b51513b3a53533a3b +515176525301ec6ed56ffd3e472c268e8e272c483932b5b5330151fec4fe4301bd013c013d01c0fe40021d2a2aecfdd0e7fe +d1fe8cfdf4020c0174012fe70230000500aaff6a07ad066e000f001b00270032003900000137161716203736371706070620 +272601100021200011100021200003100021200011100021200001343633321614062322262d011707170725021d731c288e +01948e271d732532b6fdfcb632feef01bf013c013d01bdfe43fec3fec4fe4187020e01740175020cfdf4fe8bfe8cfdf201d8 +513b3a52523a3b5101fa01274dc6c64dfed901d1472c268e8e272c483932b5b5330151fec4fe4301bd013c013d01c0fe40fe +c30175020ffdf1fe8bfe8cfdf4020c02873b515176525364ce6e8b8a6ece000500aaff6a07ad066e000f001b00270033003f +0000013716171620373637170607062027260334363216152334262206152134363216152334262206150510002120001110 +00212000131000212000111000212000021d731c288e01948e271d732532b6fdfcb6322c8cc48c873d543d01c98cc48c873d +543dfbbd020e01740175020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe4101d1472c268e8e272c483932b5 +b53301f38bc5c58b537777538bc5c58b53777753a20175020ffdf1fe8bfe8cfdf4020c0174fec4fe4301bd013c013d01c0fe +4000000500aaff6a07ad066e000b001700370043005100000134363216152334262206152134363216152334262206150510 +0021323726270623200011331400200035331407161d01361110002120000310002120001110002120000506071617163332 +37363734272604668cc48c873d543dfd298cc48c873d543dfe9401bf013c887665412b2dfefefe9487011d0194011c877b37 +d1fe43fec3fec4fe4187020e01740175020cfdf4fe8bfe8cfdf205104f5c1f26241b100d21010102038c8bc5c58b53777753 +8bc5c58b53777753a2fec4fe4329306c05016a0102cafee5011cc9d5a0646304da0133013d01c0fe40fec30175020ffdf1fe +8bfe8cfdf4020c36432428181708133905062e00000500aaff6a07ad066e000f001b00270033003f00000137161716203736 +3717060706202726011406222635331416323635211406222635331416323635011000212000111000212000131000212000 +111000212000021d731c288e01948e271d732532b6fdfcb63204008cc48c873d543dfe378cc48c873d543dfd3f020e017401 +75020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe4101d1472c268e8e272c483932b5b53302ef8bc5c58b53 +7777538bc5c58b53777753fe620175020ffdf1fe8bfe8cfdf4020c0174fec4fe4301bd013c013d01c0fe4000000500aaff6a +07ad066e0017002f003b0047005700000132171615060f012327263526373633161716173334373621321716153336373637 +32171607140f0123272627343736011000212000111000212000131000212000111000212000133716171620373637170607 +0620272605a61a193c0132a8029f3e010a263f29201c0a010d23fd493d230d010a1c20293f260a013e9f02a832013c19fe12 +020e01740175020cfdf4fe8bfe8cfdf28701bf013c013d01bdfe43fec3fec4fe41ec731c288e01948e271d732532b6fdfcb6 +3204c00e253f413ecfbe444c151944012020310e1d47471d0e312020014419154c44becf3e413f250efe2a0175020ffdf1fe +8bfe8cfdf4020c0174fec4fe4301bd013c013d01c0fe40fdaa472c268e8e272c483932b5b533000400aaff6a07ad066e0017 +00230033003c000001100021200011342723151406222635140622263d012306071000212000111000212000253716171604 +37363717060706202726032126272621200706013101bf013c013d01bd546ea6eca6a6eca66e5587020e01740175020cfdf4 +fe8bfe8cfdf20173731c288f01938e271d732532b6fdfcb6326004941618defec2fec4e01802eafec4fe4301bd013cc3a04f +648e8e64648e8e644fa0c30175020ffdf1fe8bfe8cfdf4020c5b472c268d018e272c483932b5b533033b1a19e0e019000005 +00aaff6a07ad066e000300070011001d00290000013521152135211513323736371706070621011000212000111000212000 +131000212000111000212000049801aafbd401aa6cca8e271d732532b6fefefc7e020e01740175020cfdf4fe8bfe8cfdf287 +01bf013c013d01bdfe43fec3fec4fe41038c87878787fdac8e272c483932b502390175020ffdf1fe8bfe8cfdf4020c0174fe +c4fe4301bd013c013d01c0fe40000000000100000002599974eab4d85f0f3cf5001f080000000000d17e0ee400000000d17e +0ee4f7d6fc4c0e5909dc00000008000000000000000000010000076dfe1d00000efef7d6fa510e5900010000000000000000 +00000000000016da04cd00660000000002aa0000028b00000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000085700aa085700aa +0959005f085700aa00aa00aa00aa008f00aa00aa00aa00aa00aa00aa00aa00aa000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000080010001cc023b02ab032d0396043304b905250594061a +068a071d078607d900000001000016e60354002b0068000c00020010009900080000041502160008000400000007005a0003 +0001040900000130000000030001040900010016013000030001040900020008014600030001040900030016013000030001 +040900040016013000030001040900050018014e0003000104090006001401660043006f0070007900720069006700680074 +002000280063002900200032003000300033002000620079002000420069007400730074007200650061006d002c00200049 +006e0063002e00200041006c006c0020005200690067006800740073002000520065007300650072007600650064002e000a +0043006f00700079007200690067006800740020002800630029002000320030003000360020006200790020005400610076 +006d006a006f006e00670020004200610068002e00200041006c006c00200052006900670068007400730020005200650073 +00650072007600650064002e000a00440065006a0061005600750020006300680061006e0067006500730020006100720065 +00200069006e0020007000750062006c0069006300200064006f006d00610069006e000a00440065006a0061005600750020 +00530061006e00730042006f006f006b00560065007200730069006f006e00200032002e0033003500440065006a00610056 +007500530061006e00730002000000000000ff7e005a000000000000000000000000000000000000000016e6000000010002 +0003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b +001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f00300031003200330034 +00350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d +004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100ac00a30084008500bd +009600e80086008e008b009d00a900a40102008a00da0083009300f200f3008d0097008800c300de00f1009e00aa00f500f4 +00f600a200ad00c900c700ae006200630090006400cb006500c800ca00cf00cc00cd00ce00e9006600d300d000d100af0067 +00f0009100d600d400d5006800eb00ed0089006a0069006b006d006c006e00a0006f00710070007200730075007400760077 +00ea0078007a0079007b007d007c00b800a1007f007e0080008100ec00ee00ba01030104010501060107010800fd00fe0109 +010a010b010c00ff0100010d010e010f01010110011101120113011401150116011701180119011a011b00f800f9011c011d +011e011f0120012101220123012401250126012701280129012a012b00fa00d7012c012d012e012f01300131013201330134 +01350136013701380139013a00e200e3013b013c013d013e013f014001410142014301440145014601470148014900b000b1 +014a014b014c014d014e014f015001510152015300fb00fc00e400e5015401550156015701580159015a015b015c015d015e +015f016001610162016301640165016601670168016900bb016a016b016c016d00e600e7016e016f01700171017201730174 +01750176017701780179017a017b017c017d017e017f018000a6018101820183018401850186018701880189018a018b018c +018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a5 +01a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be +01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d7 +01d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f0 +01f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0200020102020203020402050206020702080209 +020a020b020c020d020e020f0210021102120213021402150216021702180219021a021b021c021d021e021f022002210222 +0223022402250226022702280229022a022b022c022d022e022f0230023102320233023402350236023702380239023a023b +023c023d023e023f0240024102420243024402450246024702480249024a024b024c024d024e024f02500251025202530254 +02550256025702580259025a025b025c025d025e025f0260026102620263026402650266026702680269026a026b026c026d +026e026f0270027102720273027402750276027702780279027a027b027c027d027e027f0280028102820283028402850286 +028702880289028a028b028c028d028e028f0290029102920293029402950296029702980299029a029b029c029d029e029f +02a002a102a202a302a402a502a602a702a802a902aa02ab02ac02ad02ae02af02b002b102b202b300d800e102b402b502b6 +02b702b802b902ba02bb02bc02bd02be02bf02c002c102c202c300db00dc00dd00e000d900df02c402c502c602c702c802c9 +02ca02cb02cc02cd02ce02cf02d002d102d202d302d402d502d602d702d802d902da02db02dc02dd02de02df02e002e102e2 +02e302e402e502e602e702e802e902ea02eb02ec02ed02ee02ef02f002f102f202f302f402f502f602f702f802f902fa02fb +02fc02fd02fe02ff0300030103020303030403050306030703080309030a030b030c030d030e030f03100311031203130314 +03150316031703180319031a031b031c031d031e031f0320032103220323032403250326032703280329032a032b032c032d +032e032f0330033103320333033403350336033703380339033a033b033c033d033e033f0340034103420343034403450346 +034703480349034a034b034c034d034e034f0350035103520353035403550356035703580359035a035b035c035d035e035f +0360009f036103620363036403650366036703680369036a036b036c036d036e036f0370037103720373037403750376009b +037703780379037a037b037c037d037e037f0380038103820383038403850386038703880389038a038b038c038d038e038f +0390039103920393039403950396039703980399039a039b039c039d039e039f03a003a103a203a303a403a503a603a703a8 +03a903aa03ab03ac03ad03ae03af03b003b103b203b303b403b503b603b703b803b903ba03bb03bc03bd03be03bf03c003c1 +03c203c303c403c503c603c703c803c903ca03cb03cc03cd03ce03cf03d003d103d203d303d403d503d603d703d803d903da +03db03dc03dd03de03df03e003e103e203e303e403e503e603e703e803e903ea03eb03ec03ed03ee03ef03f003f103f203f3 +03f403f503f603f703f803f903fa03fb03fc03fd03fe03ff0400040104020403040404050406040704080409040a040b040c +040d040e040f0410041104120413041404150416041704180419041a041b041c041d041e041f042004210422042304240425 +0426042704280429042a042b042c042d042e042f0430043104320433043404350436043704380439043a043b043c043d043e +043f0440044104420443044404450446044704480449044a044b044c044d044e044f04500451045204530454045504560457 +04580459045a045b045c045d045e045f0460046104620463046404650466046704680469046a046b046c046d046e046f0470 +047104720473047404750476047704780479047a047b047c047d047e047f0480048104820483048404850486048704880489 +048a048b048c048d048e048f0490049104920493049404950496049704980499049a049b049c049d049e049f04a004a104a2 +04a304a404a504a604a704a804a904aa04ab04ac04ad04ae04af04b004b104b204b304b404b504b604b704b804b904ba04bb +04bc04bd04be04bf04c004c104c204c304c404c504c604c704c804c904ca04cb04cc04cd04ce04cf04d004d104d204d304d4 +04d504d604d704d804d904da04db04dc04dd04de04df04e004e104e204e304e404e504e604e704e804e904ea04eb04ec04ed +04ee04ef04f004f104f204f304f404f504f604f704f804f904fa04fb04fc04fd04fe04ff0500050105020503050405050506 +050705080509050a050b050c050d050e050f0510051105120513051405150516051705180519051a051b051c051d051e051f +0520052105220523052405250526052705280529052a052b052c052d052e052f053005310532053305340535053605370538 +0539053a053b053c053d053e053f0540054105420543054405450546054705480549054a054b054c054d054e054f05500551 +05520553055405550556055705580559055a055b055c055d055e055f0560056105620563056405650566056705680569056a +056b056c056d056e056f0570057105720573057405750576057705780579057a057b057c057d057e057f0580058105820583 +058405850586058705880589058a058b058c058d058e058f0590059105920593059405950596059705980599059a059b059c +059d059e059f05a005a105a205a305a405a505a605a705a805a905aa05ab05ac05ad05ae05af05b005b105b205b305b405b5 +05b605b705b805b905ba05bb05bc05bd05be05bf05c005c105c205c305c405c505c605c705c805c905ca05cb05cc05cd05ce +05cf05d005d105d205d305d405d505d605d705d805d905da05db05dc05dd05de05df05e005e105e205e305e405e505e605e7 +05e805e905ea05eb05ec05ed05ee05ef05f005f105f205f305f405f505f605f705f805f905fa05fb05fc05fd05fe05ff0600 +060106020603060406050606060706080609060a060b060c060d060e060f0610061106120613061406150616061706180619 +061a061b061c061d061e061f0620062106220623062406250626062706280629062a062b062c062d062e062f063006310632 +0633063406350636063706380639063a063b063c063d063e063f0640064106420643064406450646064706480649064a064b +064c064d064e064f0650065106520653065406550656065706580659065a065b065c065d065e065f06600661066206630664 +06650666066706680669066a066b066c066d066e066f0670067106720673067406750676067706780679067a067b067c067d +067e067f0680068106820683068406850686068706880689068a068b068c068d068e068f0690069106920693069406950696 +069706980699069a069b069c069d069e069f06a006a106a206a306a406a506a606a706a806a906aa06ab06ac06ad06ae06af +06b006b106b206b306b406b506b606b706b806b906ba06bb06bc06bd06be06bf06c006c106c206c306c406c506c606c706c8 +06c906ca06cb06cc06cd06ce06cf06d006d106d206d306d406d506d606d706d806d906da06db06dc06dd06de06df06e006e1 +06e206e306e406e506e606e706e806e906ea06eb06ec06ed06ee06ef06f006f106f206f306f406f506f606f706f806f906fa +06fb06fc06fd06fe06ff0700070107020703070407050706070707080709070a070b070c070d070e070f0710071107120713 +071407150716071707180719071a071b071c071d071e071f0720072107220723072407250726072707280729072a072b072c +072d072e072f0730073107320733073407350736073707380739073a073b073c073d073e073f074007410742074307440745 +0746074707480749074a074b074c074d074e074f0750075107520753075407550756075707580759075a075b075c075d075e +075f0760076107620763076407650766076707680769076a076b076c076d076e076f07700771077207730774077507760777 +07780779077a077b077c077d077e077f0780078107820783078407850786078707880789078a078b078c078d078e078f0790 +079107920793079407950796079707980799079a079b079c079d079e079f07a007a107a207a307a407a507a607a707a807a9 +07aa07ab07ac07ad07ae07af07b007b107b207b307b407b507b607b707b807b907ba07bb07bc07bd07be07bf07c007c107c2 +07c307c407c507c607c707c807c907ca07cb07cc07cd07ce07cf07d007d107d207d307d407d507d607d707d807d907da07db +07dc07dd07de07df07e007e107e207e307e407e507e607e707e807e907ea07eb07ec07ed07ee07ef07f007f107f207f307f4 +07f507f607f707f807f907fa07fb07fc07fd07fe07ff0800080108020803080408050806080708080809080a080b080c080d +080e080f0810081108120813081408150816081708180819081a081b081c081d081e081f0820082108220823082408250826 +082708280829082a082b082c082d082e082f0830083108320833083408350836083708380839083a083b083c083d083e083f +0840084108420843084408450846084708480849084a084b084c084d084e084f085008510852085308540855085608570858 +0859085a085b085c085d085e085f0860086108620863086408650866086708680869086a086b086c086d086e086f08700871 +08720873087408750876087708780879087a087b087c087d087e087f0880088108820883088408850886088708880889088a +088b088c088d088e088f0890089108920893089408950896089708980899089a089b089c089d089e089f08a008a108a208a3 +08a408a508a608a708a808a908aa08ab08ac08ad08ae08af08b008b108b208b308b408b508b608b708b808b908ba08bb08bc +08bd08be08bf08c008c108c208c308c408c508c608c708c808c908ca08cb08cc08cd08ce08cf08d008d108d208d308d408d5 +08d608d708d808d908da08db08dc08dd08de08df08e008e108e208e308e408e508e608e708e808e908ea08eb08ec08ed08ee +08ef08f008f108f208f308f408f508f608f708f808f908fa08fb08fc08fd08fe08ff09000901090209030904090509060907 +09080909090a090b090c090d090e090f0910091109120913091409150916091709180919091a091b091c091d091e091f0920 +092109220923092409250926092709280929092a092b092c092d092e092f0930093109320933093409350936093709380939 +093a093b093c093d093e093f0940094109420943094409450946094709480949094a094b094c094d094e094f095009510952 +0953095409550956095709580959095a095b095c095d095e095f0960096109620963096409650966096709680969096a096b +096c096d096e096f0970097109720973097409750976097709780979097a097b097c097d097e097f09800981098209830984 +09850986098709880989098a098b098c098d098e098f0990099109920993099409950996099709980999099a099b099c099d +099e099f09a009a109a209a309a409a509a609a709a809a909aa09ab09ac09ad09ae09af09b009b109b209b309b409b509b6 +09b709b809b909ba09bb09bc09bd09be09bf09c009c109c209c309c409c509c609c709c809c909ca09cb09cc09cd09ce09cf +09d009d109d209d309d409d509d609d709d809d909da09db09dc09dd09de09df09e009e109e209e309e409e509e609e709e8 +09e909ea09eb09ec09ed09ee09ef09f009f109f209f309f409f509f609f709f809f909fa09fb09fc09fd09fe09ff0a000a01 +0a020a030a040a050a060a070a080a090a0a0a0b0a0c0a0d0a0e0a0f0a100a110a120a130a140a150a160a170a180a190a1a +0a1b0a1c0a1d0a1e0a1f0a200a210a220a230a240a250a260a270a280a290a2a0a2b0a2c0a2d0a2e0a2f0a300a310a320a33 +0a340a350a360a370a380a390a3a0a3b0a3c0a3d0a3e0a3f0a400a410a420a430a440a450a460a470a480a490a4a0a4b0a4c +0a4d0a4e0a4f0a500a510a520a530a540a550a560a570a580a590a5a0a5b0a5c0a5d0a5e0a5f0a600a610a620a630a640a65 +0a660a670a680a690a6a0a6b0a6c0a6d0a6e0a6f0a700a710a720a730a740a750a760a770a780a790a7a0a7b0a7c0a7d0a7e +0a7f0a800a810a820a830a840a850a860a870a880a890a8a0a8b0a8c0a8d0a8e0a8f0a900a910a920a930a940a950a960a97 +0a980a990a9a0a9b0a9c0a9d0a9e0a9f0aa00aa10aa20aa30aa40aa50aa60aa70aa80aa90aaa0aab0aac0aad0aae0aaf0ab0 +0ab10ab20ab30ab40ab50ab60ab70ab80ab90aba0abb0abc0abd0abe0abf0ac00ac10ac20ac30ac40ac50ac60ac70ac80ac9 +0aca0acb0acc0acd0ace0acf0ad00ad10ad20ad30ad40ad50ad60ad70ad80ad90ada0adb0adc0add0ade0adf0ae00ae10ae2 +0ae30ae40ae50ae60ae70ae80ae90aea0aeb0aec0aed0aee0aef0af00af10af20af30af40af50af60af70af80af90afa0afb +0afc0afd0afe0aff0b000b010b020b030b040b050b060b070b080b090b0a0b0b0b0c0b0d0b0e0b0f0b100b110b120b130b14 +0b150b1600b200b30b170b180b1900b600b700c40b1a00b400b500c50b1b008200c200870b1c0b1d0b1e00ab0b1f0b200b21 +0b220b230b240b250b260b2700c60b280b290b2a0b2b0b2c0b2d0b2e0b2f00be00bf0b300b310b320b330b340b350b360b37 +0b3800bc0b390b3a0b3b0b3c0b3d0b3e0b3f0b400b410b420b430b440b450b460b470b480b490b4a0b4b0b4c0b4d0b4e0b4f +0b500b510b520b530b540b550b560b570b580b590b5a0b5b0b5c0b5d0b5e0b5f0b600b610b620b630b640b650b660b670b68 +0b690b6a0b6b0b6c0b6d0b6e0b6f0b700b710b720b730b740b750b760b770b780b790b7a0b7b0b7c0b7d0b7e0b7f0b800b81 +0b820b830b840b850b860b870b880b890b8a0b8b00f70b8c0b8d0b8e0b8f0b900b910b920b930b940b950b960b970b980b99 +0b9a0b9b0b9c0b9d0b9e0b9f0ba00ba10ba20ba30ba40ba50ba60ba70ba80ba90baa0bab0bac0bad0bae0baf0bb00bb10bb2 +0bb30bb40bb50bb60bb70bb80bb90bba0bbb0bbc0bbd0bbe0bbf0bc00bc10bc20bc30bc40bc50bc60bc70bc80bc9008c0bca +0bcb0bcc0bcd0bce0bcf0bd00bd10bd20bd30bd40bd50bd60bd70bd80bd90bda0bdb0bdc0bdd0bde0bdf0be00be10be20be3 +0be40be50be60be70be80be90bea0beb0bec0bed0bee0bef0bf00bf10bf20bf30bf40bf50bf60bf70bf80bf90bfa0bfb0bfc +0bfd0bfe0bff0c000c010c020c030c040c050c060c070c080c090c0a0c0b0c0c0c0d0c0e0c0f0c100c110c120c130c140c15 +0c160c170c180c190c1a0c1b0c1c0c1d0c1e0c1f0c200c210c220c230c240c250c260c270c280c290c2a0c2b0c2c0c2d0c2e +0c2f0c300c310c320c330c340c350c360c370c380c390c3a0c3b0c3c0c3d0c3e0c3f0c400c410c420c430c440c450c460c47 +0c480c490c4a0c4b0c4c0c4d0c4e0c4f0c500c510c520c530c540c550c560c570c580c590c5a0c5b0c5c0c5d0c5e0c5f0c60 +0c610c620c630c640c650c660c670c680c690c6a0c6b0c6c0c6d0c6e0c6f0c700c710c720c730c740c750c760c770c780c79 +0c7a0c7b0c7c0c7d0c7e0c7f0c800c810c820c830c840c850c860c870c880c890c8a0c8b0c8c0c8d0c8e0c8f0c900c910c92 +0c930c940c950c960c970c980c990c9a0c9b00980c9c0c9d0c9e00a80c9f0ca00ca10ca20ca30ca40ca50ca6009a0ca70099 +00ef0ca80ca90caa0cab0cac0cad0cae00a50caf0cb00cb100920cb20cb30cb40cb50cb60cb70cb80cb90cba0cbb0cbc0cbd +009c0cbe0cbf0cc00cc10cc20cc30cc40cc50cc60cc70cc80cc90cca0ccb0ccc0ccd0cce0ccf0cd00cd10cd20cd30cd40cd5 +0cd60cd70cd80cd900a70cda0cdb0cdc0cdd0cde0cdf0ce00ce10ce20ce30ce40ce50ce60ce70ce80ce90cea0ceb0cec0ced +0cee0cef0cf0008f0cf10cf20cf3009400950cf40cf50cf60cf70cf80cf90cfa0cfb0cfc0cfd0cfe0cff0d000d010d020d03 +0d040d050d060d070d080d090d0a0d0b0d0c0d0d0d0e0d0f0d100d110d120d130d140d150d160d170d180d190d1a0d1b0d1c +0d1d0d1e0d1f0d200d210d220d230d240d250d260d270d280d290d2a0d2b0d2c0d2d0d2e0d2f0d300d310d320d330d340d35 +0d360d370d380d390d3a0d3b0d3c0d3d0d3e0d3f0d400d410d420d430d440d450d460d470d480d490d4a0d4b0d4c0d4d0d4e +0d4f0d500d510d520d530d540d550d560d570d580d590d5a0d5b0d5c0d5d0d5e0d5f0d600d610d620d630d640d650d660d67 +0d680d690d6a0d6b0d6c0d6d0d6e0d6f0d700d710d720d730d740d750d760d770d780d790d7a0d7b0d7c0d7d0d7e0d7f0d80 +0d810d820d830d840d850d860d870d880d890d8a0d8b0d8c0d8d0d8e0d8f0d900d910d920d930d940d950d960d970d980d99 +0d9a0d9b0d9c0d9d0d9e0d9f0da00da10da20da30da40da50da60da70da80da90daa0dab0dac0dad0dae0daf0db00db10db2 +0db30db40db50db60db70db80db90dba0dbb0dbc0dbd0dbe0dbf0dc00dc10dc20dc30dc40dc50dc60dc70dc80dc90dca0dcb +0dcc0dcd0dce0dcf0dd00dd10dd20dd30dd40dd50dd60dd70dd80dd90dda0ddb0ddc0ddd0dde0ddf0de00de10de20de30de4 +0de50de60de70de80de90dea0deb0dec0ded0dee0def0df00df10df20df30df40df50df60df70df80df90dfa0dfb0dfc0dfd +0dfe0dff0e000e010e020e030e040e050e060e070e080e090e0a0e0b0e0c0e0d0e0e0e0f0e100e110e120e130e140e150e16 +0e170e180e190e1a0e1b0e1c0e1d0e1e0e1f0e200e210e220e230e240e250e260e270e280e290e2a0e2b0e2c0e2d0e2e0e2f +0e300e310e320e330e340e350e360e370e380e390e3a0e3b0e3c0e3d0e3e0e3f0e400e410e420e430e440e450e460e470e48 +0e490e4a0e4b0e4c0e4d0e4e0e4f0e500e510e520e530e540e550e560e570e580e590e5a0e5b0e5c0e5d0e5e0e5f0e600e61 +0e620e630e640e650e660e670e680e690e6a0e6b0e6c0e6d0e6e0e6f0e700e710e720e730e740e750e760e770e780e790e7a +0e7b0e7c0e7d0e7e0e7f0e800e810e820e830e840e850e860e870e880e890e8a0e8b0e8c0e8d0e8e0e8f0e900e910e920e93 +0e940e950e960e970e980e990e9a0e9b0e9c0e9d0e9e0e9f0ea00ea10ea20ea30ea400b90ea50ea60ea70ea80ea90eaa0eab +0eac0ead0eae0eaf0eb00eb10eb20eb30eb40eb50eb60eb70eb80eb90eba0ebb0ebc0ebd0ebe0ebf0ec00ec10ec20ec30ec4 +0ec50ec60ec70ec80ec90eca0ecb0ecc0ecd0ece0ecf0ed00ed10ed20ed30ed40ed50ed60ed70ed80ed90eda0edb0edc0edd +0ede0edf0ee00ee10ee20ee30ee40ee50ee60ee70ee80ee90eea0eeb0eec0eed0eee0eef0ef00ef10ef20ef30ef40ef50ef6 +0ef70ef80ef90efa0efb0efc0efd0efe0eff0f000f010f020f030f040f050f060f070f080f090f0a0f0b0f0c0f0d0f0e0f0f +0f100f110f120f130f140f150f160f170f180f190f1a0f1b0f1c0f1d0f1e0f1f0f200f210f220f230f240f250f260f270f28 +0f290f2a0f2b0f2c0f2d0f2e0f2f0f300f310f320f330f340f350f360f370f380f390f3a0f3b0f3c0f3d0f3e0f3f0f400f41 +0f420f430f440f450f460f470f480f490f4a0f4b0f4c0f4d0f4e0f4f0f500f510f520f530f540f550f560f570f580f590f5a +0f5b0f5c0f5d0f5e0f5f0f600f610f620f630f640f650f660f670f680f690f6a0f6b0f6c0f6d0f6e0f6f0f700f710f720f73 +0f740f750f760f770f780f790f7a0f7b0f7c0f7d0f7e0f7f0f800f810f820f830f840f850f860f870f880f890f8a0f8b0f8c +0f8d0f8e0f8f0f900f910f920f930f940f950f960f970f980f990f9a0f9b0f9c0f9d0f9e0f9f0fa00fa10fa20fa30fa40fa5 +0fa60fa70fa80fa90faa0fab0fac0fad0fae0faf0fb00fb10fb20fb30fb40fb50fb60fb70fb80fb90fba0fbb0fbc0fbd0fbe +0fbf0fc00fc10fc20fc30fc40fc50fc60fc70fc80fc90fca0fcb0fcc0fcd0fce0fcf0fd00fd10fd20fd30fd40fd50fd60fd7 +0fd80fd90fda0fdb0fdc0fdd0fde0fdf0fe00fe10fe20fe30fe40fe50fe60fe70fe80fe90fea0feb0fec0fed0fee0fef0ff0 +0ff10ff20ff30ff40ff50ff60ff70ff80ff90ffa0ffb0ffc0ffd0ffe0fff1000100110021003100410051006100710081009 +100a100b100c100d100e100f1010101110121013101410151016101710181019101a101b101c101d101e101f102010211022 +1023102410251026102710281029102a102b102c102d102e102f1030103110321033103410351036103710381039103a103b +103c103d103e103f1040104110421043104410451046104710481049104a104b104c104d104e104f10501051105210531054 +10551056105710581059105a105b105c105d105e105f1060106110621063106410651066106710681069106a106b106c106d +106e106f1070107110721073107410751076107710781079107a107b107c107d107e107f1080108110821083108410851086 +108710881089108a108b108c108d108e108f1090109110921093109410951096109710981099109a109b109c109d109e109f +10a010a110a210a310a410a510a610a710a810a910aa10ab10ac10ad10ae10af10b010b110b210b310b410b510b610b710b8 +10b910ba10bb10bc10bd10be10bf10c010c110c210c310c410c510c610c710c810c910ca10cb10cc10cd10ce10cf10d010d1 +10d210d310d410d510d610d710d810d910da10db10dc10dd10de10df10e010e110e210e310e410e510e610e710e810e910ea +10eb10ec10ed10ee10ef10f010f110f210f310f410f510f610f710f810f910fa10fb10fc10fd10fe10ff1100110111021103 +110411051106110711081109110a110b110c110d110e110f1110111111121113111411151116111711181119111a111b111c +111d111e111f1120112111221123112411251126112711281129112a112b112c112d112e112f113011311132113311341135 +1136113711381139113a113b113c113d113e113f1140114111421143114411451146114711481149114a114b114c114d114e +114f1150115111521153115411551156115711581159115a115b115c115d115e115f11601161116211631164116511661167 +11681169116a116b116c116d116e116f1170117111721173117411751176117711781179117a117b117c117d117e117f1180 +118111821183118411851186118711881189118a118b118c118d118e118f1190119111921193119411951196119711981199 +119a119b119c119d119e119f11a011a111a211a311a411a511a611a711a811a911aa11ab11ac11ad11ae11af11b011b111b2 +11b311b411b511b611b711b811b911ba11bb11bc11bd11be11bf11c011c111c211c311c411c511c611c711c811c911ca11cb +11cc11cd11ce11cf11d011d111d211d311d411d511d611d711d811d911da11db11dc11dd11de11df11e011e111e211e311e4 +11e511e611e711e811e911ea11eb11ec11ed11ee11ef11f011f111f211f311f411f511f611f711f811f911fa11fb11fc11fd +11fe11ff1200120112021203120412051206120712081209120a120b120c120d120e120f1210121112121213121412151216 +121712181219121a121b121c121d121e121f1220122112221223122412251226122712281229122a122b122c122d122e122f +1230123112321233123412351236123712381239123a123b123c123d123e123f124012411242124312441245124612471248 +1249124a124b124c124d124e124f1250125112521253125412551256125712581259125a125b125c125d125e125f12601261 +12621263126412651266126712681269126a126b126c126d126e126f1270127112721273127412751276127712781279127a +127b127c127d127e127f1280128112821283128412851286128712881289128a128b128c128d128e128f1290129112921293 +129412951296129712981299129a129b129c129d129e129f12a012a112a212a312a412a512a612a712a812a912aa12ab12ac +12ad12ae12af12b012b112b212b312b412b512b612b712b812b912ba12bb12bc12bd12be12bf12c012c112c212c312c412c5 +12c612c712c812c912ca12cb12cc12cd12ce12cf12d012d112d212d312d412d512d612d712d812d912da12db12dc12dd12de +12df12e012e112e212e312e412e512e612e712e812e912ea12eb12ec12ed12ee12ef12f012f112f212f312f412f512f612f7 +12f812f912fa12fb12fc12fd12fe12ff1300130113021303130413051306130713081309130a130b130c130d130e130f1310 +131113121313131413151316131713181319131a131b131c131d131e131f1320132113221323132413251326132713281329 +132a132b132c132d132e132f1330133113321333133413351336133713381339133a133b133c133d133e133f134013411342 +1343134413451346134713481349134a134b134c134d134e134f1350135113521353135413551356135713581359135a135b +135c135d135e135f1360136113621363136413651366136713681369136a136b136c136d136e136f13701371137213731374 +13751376137713781379137a137b137c137d137e137f1380138113821383138413851386138713881389138a138b138c138d +138e138f1390139113921393139413951396139713981399139a139b139c139d139e139f13a013a113a213a313a413a513a6 +13a713a813a913aa13ab13ac13ad13ae13af13b013b100c000c113b213b313b413b513b613b713b813b913ba13bb13bc13bd +13be13bf13c013c113c213c313c413c513c613c713c813c913ca13cb13cc13cd13ce13cf13d013d113d213d313d413d513d6 +13d713d813d913da13db13dc13dd13de13df13e013e113e213e313e413e513e613e713e813e913ea13eb13ec13ed13ee13ef +13f013f113f213f313f413f513f613f713f813f913fa13fb13fc13fd13fe13ff140014011402140314041405140614071408 +1409140a140b140c140d140e140f1410141114121413141414151416141714181419141a141b141c141d141e141f14201421 +14221423142414251426142714281429142a142b142c142d142e142f1430143114321433143414351436143714381439143a +143b143c143d143e143f1440144114421443144414451446144714481449144a144b144c144d144e144f1450145114521453 +145414551456145714581459145a145b145c145d145e145f1460146114621463146414651466146714681469146a146b146c +146d146e146f1470147114721473147414751476147714781479147a147b147c147d147e147f148014811482148314841485 +1486148714881489148a148b148c148d148e148f1490149114921493149414951496149714981499149a149b149c149d149e +149f14a014a114a214a314a414a514a614a714a814a914aa14ab14ac14ad14ae14af14b014b114b214b314b414b514b614b7 +14b814b914ba14bb14bc14bd14be14bf14c014c114c214c314c414c514c614c714c814c914ca14cb14cc14cd14ce14cf14d0 +14d114d214d314d414d514d614d714d814d914da14db14dc14dd14de14df14e014e114e214e314e414e514e614e714e814e9 +14ea14eb14ec14ed14ee14ef14f014f114f214f314f414f514f614f714f814f914fa14fb14fc14fd14fe14ff150015011502 +1503150415051506150715081509150a150b150c150d150e150f1510151115121513151415151516151715181519151a151b +151c151d151e151f1520152115221523152415251526152715281529152a152b152c152d152e152f15301531153215331534 +15351536153715381539153a153b153c153d153e153f1540154115421543154415451546154715481549154a154b154c154d +154e154f1550155115521553155415551556155715581559155a155b155c155d155e155f1560156115621563156415651566 +156715681569156a156b156c156d156e156f1570157115721573157415751576157715781579157a157b157c157d157e157f +1580158115821583158415851586158715881589158a158b158c158d158e158f159015911592159315941595159615971598 +1599159a159b159c159d159e159f15a015a115a215a315a415a515a615a715a815a915aa15ab15ac15ad15ae15af15b015b1 +15b215b315b415b515b615b715b815b915ba15bb15bc15bd15be15bf15c015c115c215c315c415c515c615c715c815c915ca +15cb15cc15cd15ce15cf15d015d115d215d315d415d515d615d715d815d915da15db15dc15dd15de15df15e015e115e215e3 +15e415e515e615e715e815e915ea15eb15ec15ed15ee15ef15f015f115f215f315f415f515f615f715f815f915fa15fb15fc +15fd15fe15ff1600160116021603160416051606160716081609160a160b160c160d160e160f161016111612161316141615 +1616161716181619161a161b161c161d161e161f1620162116221623162416251626162716281629162a162b162c162d162e +162f1630163116321633163416351636163716381639163a163b163c163d163e163f16401641164216431644164516461647 +16481649164a164b164c164d164e164f1650165116521653165416551656165716581659165a165b165c165d165e165f1660 +166116621663166416651666166716681669166a166b166c166d166e166f1670167116721673167416751676167716781679 +167a167b167c167d167e167f1680168116821683168416851686168716881689168a168b168c168d168e168f169016911692 +1693169416951696169716981699169a169b169c169d169e169f16a016a116a216a316a416a516a616a716a816a916aa16ab +16ac16ad16ae16af16b016b116b216b316b416b516b616b716b816b916ba16bb16bc16bd16be16bf16c016c116c216c316c4 +16c516c616c716c816c916ca16cb16cc16cd16ce16cf16d016d116d216d316d416d516d616d716d816d916da16db16dc16dd +16de16df16e016e116e216e316e416e516e60973667468797068656e07416d6163726f6e07616d6163726f6e064162726576 +650661627265766507416f676f6e656b07616f676f6e656b0b4363697263756d666c65780b6363697263756d666c65780a43 +646f74616363656e740a63646f74616363656e7406446361726f6e06646361726f6e064463726f617407456d6163726f6e07 +656d6163726f6e06456272657665066562726576650a45646f74616363656e740a65646f74616363656e7407456f676f6e65 +6b07656f676f6e656b06456361726f6e06656361726f6e0b4763697263756d666c65780b6763697263756d666c65780a4764 +6f74616363656e740a67646f74616363656e740c47636f6d6d61616363656e740c67636f6d6d61616363656e740b48636972 +63756d666c65780b6863697263756d666c657804486261720468626172064974696c6465066974696c646507496d6163726f +6e07696d6163726f6e064962726576650669627265766507496f676f6e656b07696f676f6e656b02494a02696a0b4a636972 +63756d666c65780b6a63697263756d666c65780c4b636f6d6d61616363656e740c6b636f6d6d61616363656e740c6b677265 +656e6c616e646963064c6163757465066c61637574650c4c636f6d6d61616363656e740c6c636f6d6d61616363656e74064c +6361726f6e066c6361726f6e044c646f74046c646f74064e6163757465066e61637574650c4e636f6d6d61616363656e740c +6e636f6d6d61616363656e74064e6361726f6e066e6361726f6e0b6e61706f7374726f70686503456e6703656e67074f6d61 +63726f6e076f6d6163726f6e064f6272657665066f62726576650d4f68756e676172756d6c6175740d6f68756e676172756d +6c61757406526163757465067261637574650c52636f6d6d61616363656e740c72636f6d6d61616363656e7406526361726f +6e06726361726f6e06536163757465067361637574650b5363697263756d666c65780b7363697263756d666c65780c54636f +6d6d61616363656e740c74636f6d6d61616363656e7406546361726f6e06746361726f6e0454626172047462617206557469 +6c6465067574696c646507556d6163726f6e07756d6163726f6e0655627265766506756272657665055572696e6705757269 +6e670d5568756e676172756d6c6175740d7568756e676172756d6c61757407556f676f6e656b07756f676f6e656b0b576369 +7263756d666c65780b7763697263756d666c65780b5963697263756d666c65780b7963697263756d666c6578065a61637574 +65067a61637574650a5a646f74616363656e740a7a646f74616363656e74056c6f6e677307756e693031383007756e693031 +383107756e693031383207756e693031383307756e693031383407756e693031383507756e693031383607756e6930313837 +07756e693031383807756e693031383907756e693031384107756e693031384207756e693031384307756e69303138440775 +6e693031384507756e693031384607756e693031393007756e693031393107756e693031393307756e693031393407756e69 +3031393507756e693031393607756e693031393707756e693031393807756e693031393907756e693031394107756e693031 +394207756e693031394307756e693031394407756e693031394507756e6930313946054f686f726e056f686f726e07756e69 +3031413207756e693031413307756e693031413407756e693031413507756e693031413607756e693031413707756e693031 +413807756e693031413907756e693031414107756e693031414207756e693031414307756e693031414407756e6930314145 +0555686f726e0575686f726e07756e693031423107756e693031423207756e693031423307756e693031423407756e693031 +423507756e693031423607756e693031423707756e693031423807756e693031423907756e693031424107756e6930314242 +07756e693031424307756e693031424407756e693031424507756e693031424607756e693031433007756e69303143310775 +6e693031433207756e693031433307756e693031433407756e693031433507756e693031433607756e693031433707756e69 +3031433807756e693031433907756e693031434107756e693031434207756e693031434307756e693031434407756e693031 +434507756e693031434607756e693031443007756e693031443107756e693031443207756e693031443307756e6930314434 +07756e693031443507756e693031443607756e693031443707756e693031443807756e693031443907756e69303144410775 +6e693031444207756e693031444307756e693031444407756e693031444507756e693031444607756e693031453007756e69 +3031453107756e693031453207756e693031453307756e693031453407756e693031453506476361726f6e06676361726f6e +07756e693031453807756e693031453907756e693031454107756e693031454207756e693031454307756e69303145440775 +6e693031454507756e693031454607756e693031463007756e693031463107756e693031463207756e693031463307756e69 +3031463407756e693031463507756e693031463607756e693031463707756e693031463807756e69303146390a4172696e67 +61637574650a6172696e676163757465074145616375746507616561637574650b4f736c61736861637574650b6f736c6173 +68616375746507756e693032303007756e693032303107756e693032303207756e693032303307756e693032303407756e69 +3032303507756e693032303607756e693032303707756e693032303807756e693032303907756e693032304107756e693032 +304207756e693032304307756e693032304407756e693032304507756e693032304607756e693032313007756e6930323131 +07756e693032313207756e693032313307756e693032313407756e693032313507756e693032313607756e69303231370c53 +636f6d6d61616363656e740c73636f6d6d61616363656e7407756e693032314107756e693032314207756e69303231430775 +6e693032314407756e693032314507756e693032314607756e693032323007756e693032323107756e693032323207756e69 +3032323307756e693032323407756e693032323507756e693032323607756e693032323707756e693032323807756e693032 +323907756e693032324107756e693032324207756e693032324307756e693032324407756e693032324507756e6930323246 +07756e693032333007756e693032333107756e693032333207756e693032333307756e693032333407756e69303233350775 +6e693032333608646f746c6573736a07756e693032333807756e693032333907756e693032334107756e693032334207756e +693032334307756e693032334407756e693032334507756e693032334607756e693032343007756e693032343107756e6930 +32343207756e693032343307756e693032343407756e693032343507756e693032343607756e693032343707756e69303234 +3807756e693032343907756e693032344107756e693032344207756e693032344307756e693032344407756e693032344507 +756e693032344607756e693032353007756e693032353107756e693032353207756e693032353307756e693032353407756e +693032353507756e693032353607756e693032353707756e693032353807756e693032353907756e693032354107756e6930 +32354207756e693032354307756e693032354407756e693032354507756e693032354607756e693032363007756e69303236 +3107756e693032363207756e693032363307756e693032363407756e693032363507756e693032363607756e693032363707 +756e693032363807756e693032363907756e693032364107756e693032364207756e693032364307756e693032364407756e +693032364507756e693032364607756e693032373007756e693032373107756e693032373207756e693032373307756e6930 +32373407756e693032373507756e693032373607756e693032373707756e693032373807756e693032373907756e69303237 +4107756e693032374207756e693032374307756e693032374407756e693032374507756e693032374607756e693032383007 +756e693032383107756e693032383207756e693032383307756e693032383407756e693032383507756e693032383607756e +693032383707756e693032383807756e693032383907756e693032384107756e693032384207756e693032384307756e6930 +32384407756e693032384507756e693032384607756e693032393007756e693032393107756e693032393207756e69303239 +3307756e693032393407756e693032393507756e693032393607756e693032393707756e693032393807756e693032393907 +756e693032394107756e693032394207756e693032394307756e693032394407756e693032394507756e693032394607756e +693032413007756e693032413107756e693032413207756e693032413307756e693032413407756e693032413507756e6930 +32413607756e693032413707756e693032413807756e693032413907756e693032414107756e693032414207756e69303241 +4307756e693032414407756e693032414507756e693032414607756e693032423007756e693032423107756e693032423207 +756e693032423307756e693032423407756e693032423507756e693032423607756e693032423707756e693032423807756e +693032423907756e693032424107756e693032424207756e693032424307756e693032424407756e693032424507756e6930 +32424607756e693032433007756e693032433107756e693032433207756e693032433307756e693032433407756e69303243 +3507756e693032433807756e693032433907756e693032434107756e693032434207756e693032434307756e693032434407 +756e693032434507756e693032434607756e693032443007756e693032443107756e693032443207756e693032443307756e +693032443407756e693032443507756e693032443607756e693032443707756e693032444507756e693032444607756e6930 +32453007756e693032453107756e693032453207756e693032453307756e693032453407756e693032453507756e69303245 +3607756e693032453707756e693032453807756e693032453907756e693032454307756e693032454407756e693032454507 +756e693032463307756e6930324637096772617665636f6d62096163757465636f6d6207756e69303330320974696c646563 +6f6d6207756e693033303407756e693033303507756e693033303607756e693033303707756e69303330380d686f6f6b6162 +6f7665636f6d6207756e693033304107756e693033304207756e693033304307756e693033304407756e693033304507756e +693033304607756e693033313007756e693033313107756e693033313207756e693033313307756e693033313407756e6930 +33313507756e693033313607756e693033313707756e693033313807756e693033313907756e693033314107756e69303331 +4207756e693033314307756e693033314407756e693033314507756e693033314607756e693033323007756e693033323107 +756e69303332320c646f7462656c6f77636f6d6207756e693033323407756e693033323507756e693033323607756e693033 +323707756e693033323807756e693033323907756e693033324107756e693033324207756e693033324307756e6930333244 +07756e693033324507756e693033324607756e693033333007756e693033333107756e693033333207756e69303333330775 +6e693033333407756e693033333507756e693033333607756e693033333707756e693033333807756e693033333907756e69 +3033334107756e693033334207756e693033334307756e693033334407756e693033334507756e693033334607756e693033 +343007756e693033343107756e693033343207756e693033343307756e693033343407756e693033343507756e6930333436 +07756e693033343707756e693033343807756e693033343907756e693033344107756e693033344207756e69303334430775 +6e693033344407756e693033344507756e693033344607756e693033353107756e693033353207756e693033353307756e69 +3033353707756e693033353807756e693033354107756e693033354307756e693033354407756e693033354507756e693033 +354607756e693033363007756e693033363107756e693033363207756e693033373007756e693033373107756e6930333732 +07756e693033373307756e693033373407756e693033373507756e693033373607756e693033373707756e69303337410775 +6e693033374207756e693033374307756e693033374407756e693033374505746f6e6f730d6469657265736973746f6e6f73 +0a416c706861746f6e6f7309616e6f74656c6569610c457073696c6f6e746f6e6f7308457461746f6e6f7309496f7461746f +6e6f730c4f6d6963726f6e746f6e6f730c557073696c6f6e746f6e6f730a4f6d656761746f6e6f7311696f74616469657265 +736973746f6e6f7305416c70686104426574610547616d6d6107756e693033393407457073696c6f6e045a65746103457461 +05546865746104496f7461054b61707061064c616d626461024d75024e75025869074f6d6963726f6e0250690352686f0553 +69676d610354617507557073696c6f6e0350686903436869035073690c496f746164696572657369730f557073696c6f6e64 +696572657369730a616c706861746f6e6f730c657073696c6f6e746f6e6f7308657461746f6e6f7309696f7461746f6e6f73 +14757073696c6f6e6469657265736973746f6e6f7305616c70686104626574610567616d6d610564656c746107657073696c +6f6e047a6574610365746105746865746104696f7461056b61707061066c616d62646107756e6930334243026e7502786907 +6f6d6963726f6e0372686f067369676d6131057369676d610374617507757073696c6f6e037068690363686903707369056f +6d6567610c696f746164696572657369730f757073696c6f6e64696572657369730c6f6d6963726f6e746f6e6f730c757073 +696c6f6e746f6e6f730a6f6d656761746f6e6f7307756e693033434607756e69303344300674686574613108557073696c6f +6e3107756e693033443307756e69303344340470686931066f6d6567613107756e693033443707756e693033443807756e69 +3033443907756e693033444107756e693033444207756e693033444307756e693033444407756e693033444507756e693033 +444607756e693033453007756e693033453107756e693033453207756e693033453307756e693033453407756e6930334535 +07756e693033453607756e693033453707756e693033453807756e693033453907756e693033454107756e69303345420775 +6e693033454307756e693033454407756e693033454507756e693033454607756e693033463007756e693033463107756e69 +3033463207756e693033463307756e693033463407756e693033463507756e693033463607756e693033463707756e693033 +463807756e693033463907756e693033464107756e693033464207756e693033464307756e693033464407756e6930334645 +07756e693033464607756e693034303007756e693034303107756e693034303207756e693034303307756e69303430340775 +6e693034303507756e693034303607756e693034303707756e693034303807756e693034303907756e693034304107756e69 +3034304207756e693034304307756e693034304407756e693034304507756e693034304607756e693034313007756e693034 +313107756e693034313207756e693034313307756e693034313407756e693034313507756e693034313607756e6930343137 +07756e693034313807756e693034313907756e693034314107756e693034314207756e693034314307756e69303431440775 +6e693034314507756e693034314607756e693034323007756e693034323107756e693034323207756e693034323307756e69 +3034323407756e693034323507756e693034323607756e693034323707756e693034323807756e693034323907756e693034 +324107756e693034324207756e693034324307756e693034324407756e693034324507756e693034324607756e6930343330 +07756e693034333107756e693034333207756e693034333307756e693034333407756e693034333507756e69303433360775 +6e693034333707756e693034333807756e693034333907756e693034334107756e693034334207756e693034334307756e69 +3034334407756e693034334507756e693034334607756e693034343007756e693034343107756e693034343207756e693034 +343307756e693034343407756e693034343507756e693034343607756e693034343707756e693034343807756e6930343439 +07756e693034344107756e693034344207756e693034344307756e693034344407756e693034344507756e69303434460775 +6e693034353007756e693034353107756e693034353207756e693034353307756e693034353407756e693034353507756e69 +3034353607756e693034353707756e693034353807756e693034353907756e693034354107756e693034354207756e693034 +354307756e693034354407756e693034354507756e693034354607756e693034363007756e693034363107756e6930343632 +07756e693034363307756e693034363407756e693034363507756e693034363607756e693034363707756e69303436380775 +6e693034363907756e693034364107756e693034364207756e693034364307756e693034364407756e693034364507756e69 +3034364607756e693034373007756e693034373107756e693034373207756e693034373307756e693034373407756e693034 +373507756e693034373607756e693034373707756e693034373807756e693034373907756e693034374107756e6930343742 +07756e693034374307756e693034374407756e693034374507756e693034374607756e693034383007756e69303438310775 +6e693034383207756e693034383307756e693034383407756e693034383507756e693034383607756e693034383707756e69 +3034383807756e693034383907756e693034384107756e693034384207756e693034384307756e693034384407756e693034 +384507756e693034384607756e693034393007756e693034393107756e693034393207756e693034393307756e6930343934 +07756e693034393507756e693034393607756e693034393707756e693034393807756e693034393907756e69303439410775 +6e693034394207756e693034394307756e693034394407756e693034394507756e693034394607756e693034413007756e69 +3034413107756e693034413207756e693034413307756e693034413407756e693034413507756e693034413607756e693034 +413707756e693034413807756e693034413907756e693034414107756e693034414207756e693034414307756e6930344144 +07756e693034414507756e693034414607756e693034423007756e693034423107756e693034423207756e69303442330775 +6e693034423407756e693034423507756e693034423607756e693034423707756e693034423807756e693034423907756e69 +3034424107756e693034424207756e693034424307756e693034424407756e693034424507756e693034424607756e693034 +433007756e693034433107756e693034433207756e693034433307756e693034433407756e693034433507756e6930344336 +07756e693034433707756e693034433807756e693034433907756e693034434107756e693034434207756e69303443430775 +6e693034434407756e693034434507756e693034434607756e693034443007756e693034443107756e693034443207756e69 +3034443307756e693034443407756e693034443507756e693034443607756e693034443707756e693034443807756e693034 +443907756e693034444107756e693034444207756e693034444307756e693034444407756e693034444507756e6930344446 +07756e693034453007756e693034453107756e693034453207756e693034453307756e693034453407756e69303445350775 +6e693034453607756e693034453707756e693034453807756e693034453907756e693034454107756e693034454207756e69 +3034454307756e693034454407756e693034454507756e693034454607756e693034463007756e693034463107756e693034 +463207756e693034463307756e693034463407756e693034463507756e693034463607756e693034463707756e6930344638 +07756e693034463907756e693034464107756e693034464207756e693034464307756e693034464407756e69303446450775 +6e693034464607756e693035303007756e693035303107756e693035303207756e693035303307756e693035303407756e69 +3035303507756e693035303607756e693035303707756e693035303807756e693035303907756e693035304107756e693035 +304207756e693035304307756e693035304407756e693035304507756e693035304607756e693035313007756e6930353131 +07756e693035313207756e693035313307756e693035313407756e693035313507756e693035313607756e69303531370775 +6e693035313807756e693035313907756e693035314107756e693035314207756e693035314307756e693035314407756e69 +3035314507756e693035314607756e693035323007756e693035323107756e693035323207756e693035323307756e693035 +323407756e693035323507756e693035333107756e693035333207756e693035333307756e693035333407756e6930353335 +07756e693035333607756e693035333707756e693035333807756e693035333907756e693035334107756e69303533420775 +6e693035334307756e693035334407756e693035334507756e693035334607756e693035343007756e693035343107756e69 +3035343207756e693035343307756e693035343407756e693035343507756e693035343607756e693035343707756e693035 +343807756e693035343907756e693035344107756e693035344207756e693035344307756e693035344407756e6930353445 +07756e693035344607756e693035353007756e693035353107756e693035353207756e693035353307756e69303535340775 +6e693035353507756e693035353607756e693035353907756e693035354107756e693035354207756e693035354307756e69 +3035354407756e693035354507756e693035354607756e693035363107756e693035363207756e693035363307756e693035 +363407756e693035363507756e693035363607756e693035363707756e693035363807756e693035363907756e6930353641 +07756e693035364207756e693035364307756e693035364407756e693035364507756e693035364607756e69303537300775 +6e693035373107756e693035373207756e693035373307756e693035373407756e693035373507756e693035373607756e69 +3035373707756e693035373807756e693035373907756e693035374107756e693035374207756e693035374307756e693035 +374407756e693035374507756e693035374607756e693035383007756e693035383107756e693035383207756e6930353833 +07756e693035383407756e693035383507756e693035383607756e693035383707756e693035383907756e69303538410775 +6e693035423007756e693035423107756e693035423207756e693035423307756e693035423407756e693035423507756e69 +3035423607756e693035423707756e693035423807756e693035423907756e693035424107756e693035424207756e693035 +424307756e693035424407756e693035424507756e693035424607756e693035433007756e693035433107756e6930354332 +07756e693035433307756e693035433607756e693035433707756e693035443007756e693035443107756e69303544320775 +6e693035443307756e693035443407756e693035443507756e693035443607756e693035443707756e693035443807756e69 +3035443907756e693035444107756e693035444207756e693035444307756e693035444407756e693035444507756e693035 +444607756e693035453007756e693035453107756e693035453207756e693035453307756e693035453407756e6930354535 +07756e693035453607756e693035453707756e693035453807756e693035453907756e693035454107756e69303546300775 +6e693035463107756e693035463207756e693035463307756e693035463407756e693036303607756e693036303707756e69 +3036303907756e693036304107756e693036304307756e693036313507756e693036314207756e693036314607756e693036 +323107756e693036323207756e693036323307756e693036323407756e693036323507756e693036323607756e6930363237 +07756e693036323807756e693036323907756e693036324107756e693036324207756e693036324307756e69303632440775 +6e693036324507756e693036324607756e693036333007756e693036333107756e693036333207756e693036333307756e69 +3036333407756e693036333507756e693036333607756e693036333707756e693036333807756e693036333907756e693036 +334107756e693036343007756e693036343107756e693036343207756e693036343307756e693036343407756e6930363435 +07756e693036343607756e693036343707756e693036343807756e693036343907756e693036344107756e69303634420775 +6e693036344307756e693036344407756e693036344507756e693036344607756e693036353007756e693036353107756e69 +3036353207756e693036353307756e693036353407756e693036353507756e693036353707756e693036354107756e693036 +363007756e693036363107756e693036363207756e693036363307756e693036363407756e693036363507756e6930363636 +07756e693036363707756e693036363807756e693036363907756e693036364107756e693036364207756e69303636430775 +6e693036364407756e693036364507756e693036364607756e693036373007756e693036373407756e693036373907756e69 +3036374107756e693036374207756e693036374307756e693036374407756e693036374507756e693036374607756e693036 +383007756e693036383107756e693036383207756e693036383307756e693036383407756e693036383507756e6930363836 +07756e693036383707756e693036383807756e693036383907756e693036384107756e693036384207756e69303638430775 +6e693036384407756e693036384507756e693036384607756e693036393007756e693036393107756e693036393207756e69 +3036393307756e693036393407756e693036393507756e693036393607756e693036393707756e693036393807756e693036 +393907756e693036394107756e693036394207756e693036394307756e693036394407756e693036394507756e6930363946 +07756e693036413007756e693036413107756e693036413207756e693036413307756e693036413407756e69303641350775 +6e693036413607756e693036413707756e693036413807756e693036413907756e693036414107756e693036414207756e69 +3036414307756e693036414407756e693036414507756e693036414607756e693036423007756e693036423107756e693036 +423207756e693036423307756e693036423407756e693036423507756e693036423607756e693036423707756e6930364238 +07756e693036423907756e693036424107756e693036424207756e693036424307756e693036424407756e69303642450775 +6e693036424607756e693036433607756e693036433707756e693036433807756e693036434207756e693036434307756e69 +3036434507756e693036443007756e693036443507756e693036463007756e693036463107756e693036463207756e693036 +463307756e693036463407756e693036463507756e693036463607756e693036463707756e693036463807756e6930364639 +07756e693037433007756e693037433107756e693037433207756e693037433307756e693037433407756e69303743350775 +6e693037433607756e693037433707756e693037433807756e693037433907756e693037434107756e693037434207756e69 +3037434307756e693037434407756e693037434507756e693037434607756e693037443007756e693037443107756e693037 +443207756e693037443307756e693037443407756e693037443507756e693037443607756e693037443707756e6930374438 +07756e693037443907756e693037444107756e693037444207756e693037444307756e693037444407756e69303744450775 +6e693037444607756e693037453007756e693037453107756e693037453207756e693037453307756e693037453407756e69 +3037453507756e693037453607756e693037453707756e693037454207756e693037454307756e693037454407756e693037 +454507756e693037454607756e693037463007756e693037463107756e693037463207756e693037463307756e6930374634 +07756e693037463507756e693037463807756e693037463907756e693037464107756e693045334607756e69304538310775 +6e693045383207756e693045383407756e693045383707756e693045383807756e693045384107756e693045384407756e69 +3045393407756e693045393507756e693045393607756e693045393707756e693045393907756e693045394107756e693045 +394207756e693045394307756e693045394407756e693045394507756e693045394607756e693045413107756e6930454132 +07756e693045413307756e693045413507756e693045413707756e693045414107756e693045414207756e69304541440775 +6e693045414507756e693045414607756e693045423007756e693045423107756e693045423207756e693045423307756e69 +3045423407756e693045423507756e693045423607756e693045423707756e693045423807756e693045423907756e693045 +424207756e693045424307756e693045424407756e693045433007756e693045433107756e693045433207756e6930454333 +07756e693045433407756e693045433607756e693045433807756e693045433907756e693045434107756e69304543420775 +6e693045434307756e693045434407756e693045443007756e693045443107756e693045443207756e693045443307756e69 +3045443407756e693045443507756e693045443607756e693045443707756e693045443807756e693045443907756e693045 +444307756e693045444407756e693130413007756e693130413107756e693130413207756e693130413307756e6931304134 +07756e693130413507756e693130413607756e693130413707756e693130413807756e693130413907756e69313041410775 +6e693130414207756e693130414307756e693130414407756e693130414507756e693130414607756e693130423007756e69 +3130423107756e693130423207756e693130423307756e693130423407756e693130423507756e693130423607756e693130 +423707756e693130423807756e693130423907756e693130424107756e693130424207756e693130424307756e6931304244 +07756e693130424507756e693130424607756e693130433007756e693130433107756e693130433207756e69313043330775 +6e693130433407756e693130433507756e693130443007756e693130443107756e693130443207756e693130443307756e69 +3130443407756e693130443507756e693130443607756e693130443707756e693130443807756e693130443907756e693130 +444107756e693130444207756e693130444307756e693130444407756e693130444507756e693130444607756e6931304530 +07756e693130453107756e693130453207756e693130453307756e693130453407756e693130453507756e69313045360775 +6e693130453707756e693130453807756e693130453907756e693130454107756e693130454207756e693130454307756e69 +3130454407756e693130454507756e693130454607756e693130463007756e693130463107756e693130463207756e693130 +463307756e693130463407756e693130463507756e693130463607756e693130463707756e693130463807756e6931304639 +07756e693130464107756e693130464207756e693130464307756e693134303107756e693134303207756e69313430330775 +6e693134303407756e693134303507756e693134303607756e693134303707756e693134303907756e693134304107756e69 +3134304207756e693134304307756e693134304407756e693134304507756e693134304607756e693134313007756e693134 +313107756e693134313207756e693134313307756e693134313407756e693134313507756e693134313607756e6931343137 +07756e693134313807756e693134313907756e693134314107756e693134314207756e693134314407756e69313431450775 +6e693134314607756e693134323007756e693134323107756e693134323207756e693134323307756e693134323407756e69 +3134323507756e693134323607756e693134323707756e693134323807756e693134323907756e693134324107756e693134 +324207756e6931343243077500><6e693134324407756e693134324507756e693134324607756e693134333007756e693134 +333107756e693134333207756e693134333307756e693134333407756e693134333507756e693134333707756e6931343338 +07756e693134333907756e693134334107756e693134334207756e693134334307756e693134334407756e69313433450775 +6e693134334607756e693134343007756e693134343107756e693134343207756e693134343307756e693134343407756e69 +3134343507756e693134343607756e693134343707756e693134343807756e693134343907756e693134344107756e693134 +344307756e693134344407756e693134344507756e693134344607756e693134353007756e693134353107756e6931343532 +07756e693134353407756e693134353507756e693134353607756e693134353707756e693134353807756e69313435390775 +6e693134354107756e693134354207756e693134354307756e693134354407756e693134354507756e693134354607756e69 +3134363007756e693134363107756e693134363207756e693134363307756e693134363407756e693134363507756e693134 +363607756e693134363707756e693134363807756e693134363907756e693134364107756e693134364207756e6931343643 +07756e693134364407756e693134364507756e693134364607756e693134373007756e693134373107756e69313437320775 +6e693134373307756e693134373407756e693134373507756e693134373607756e693134373707756e693134373807756e69 +3134373907756e693134374107756e693134374207756e693134374307756e693134374407756e693134374507756e693134 +374607756e693134383007756e693134383107756e693134383207756e693134383307756e693134383407756e6931343835 +07756e693134383607756e693134383707756e693134383807756e693134383907756e693134384107756e69313438420775 +6e693134384307756e693134384407756e693134384507756e693134384607756e693134393007756e693134393107756e69 +3134393207756e693134393307756e693134393407756e693134393507756e693134393607756e693134393707756e693134 +393807756e693134393907756e693134394107756e693134394207756e693134394307756e693134394407756e6931343945 +07756e693134394607756e693134413007756e693134413107756e693134413207756e693134413307756e69313441340775 +6e693134413507756e693134413607756e693134413707756e693134413807756e693134413907756e693134414107756e69 +3134414207756e693134414307756e693134414407756e693134414507756e693134414607756e693134423007756e693134 +423107756e693134423207756e693134423307756e693134423407756e693134423507756e693134423607756e6931344237 +07756e693134423807756e693134423907756e693134424107756e693134424207756e693134424307756e69313442440775 +6e693134433007756e693134433107756e693134433207756e693134433307756e693134433407756e693134433507756e69 +3134433607756e693134433707756e693134433807756e693134433907756e693134434107756e693134434207756e693134 +434307756e693134434407756e693134434507756e693134434607756e693134443007756e693134443107756e6931344432 +07756e693134443307756e693134443407756e693134443507756e693134443607756e693134443707756e69313444380775 +6e693134443907756e693134444107756e693134444207756e693134444307756e693134444407756e693134444507756e69 +3134444607756e693134453007756e693134453107756e693134453207756e693134453307756e693134453407756e693134 +453507756e693134453607756e693134453707756e693134453807756e693134453907756e693134454107756e6931344543 +07756e693134454407756e693134454507756e693134454607756e693134463007756e693134463107756e69313446320775 +6e693134463307756e693134463407756e693134463507756e693134463607756e693134463707756e693134463807756e69 +3134463907756e693134464107756e693134464207756e693134464307756e693134464407756e693134464507756e693134 +464607756e693135303007756e693135303107756e693135303207756e693135303307756e693135303407756e6931353035 +07756e693135303607756e693135303707756e693135313007756e693135313107756e693135313207756e69313531330775 +6e693135313407756e693135313507756e693135313607756e693135313707756e693135313807756e693135313907756e69 +3135314107756e693135314207756e693135314307756e693135314407756e693135314507756e693135314607756e693135 +323007756e693135323107756e693135323207756e693135323307756e693135323407756e693135323507756e6931353236 +07756e693135323707756e693135323807756e693135323907756e693135324107756e693135324207756e69313532430775 +6e693135324407756e693135324507756e693135324607756e693135333007756e693135333107756e693135333207756e69 +3135333307756e693135333407756e693135333507756e693135333607756e693135333707756e693135333807756e693135 +333907756e693135334107756e693135334207756e693135334307756e693135334407756e693135334507756e6931353430 +07756e693135343107756e693135343207756e693135343307756e693135343407756e693135343507756e69313534360775 +6e693135343707756e693135343807756e693135343907756e693135344107756e693135344207756e693135344307756e69 +3135344407756e693135344507756e693135344607756e693135353007756e693135353207756e693135353307756e693135 +353407756e693135353507756e693135353607756e693135353707756e693135353807756e693135353907756e6931353541 +07756e693135354207756e693135354307756e693135354407756e693135354507756e693135354607756e69313536300775 +6e693135363107756e693135363207756e693135363307756e693135363407756e693135363507756e693135363607756e69 +3135363707756e693135363807756e693135363907756e693135364107756e693135373407756e693135373507756e693135 +373607756e693135373707756e693135373807756e693135373907756e693135374107756e693135374207756e6931353743 +07756e693135374407756e693135374507756e693135374607756e693135383007756e693135383107756e69313538320775 +6e693135383307756e693135383407756e693135383507756e693135384107756e693135384207756e693135384307756e69 +3135384407756e693135384507756e693135384607756e693135393007756e693135393107756e693135393207756e693135 +393307756e693135393407756e693135393507756e693135393607756e693135413007756e693135413107756e6931354132 +07756e693135413307756e693135413407756e693135413507756e693135413607756e693135413707756e69313541380775 +6e693135413907756e693135414107756e693135414207756e693135414307756e693135414407756e693135414507756e69 +3135414607756e693135444507756e693135453107756e693136343607756e693136343707756e693136364507756e693136 +364607756e693136373007756e693136373107756e693136373207756e693136373307756e693136373407756e6931363735 +07756e693136373607756e693136383007756e693136383107756e693136383207756e693136383307756e69313638340775 +6e693136383507756e693136383607756e693136383707756e693136383807756e693136383907756e693136384107756e69 +3136384207756e693136384307756e693136384407756e693136384507756e693136384607756e693136393007756e693136 +393107756e693136393207756e693136393307756e693136393407756e693136393507756e693136393607756e6931363937 +07756e693136393807756e693136393907756e693136394107756e693136394207756e693136394307756e69314430300775 +6e693144303107756e693144303207756e693144303307756e693144303407756e693144303507756e693144303607756e69 +3144303707756e693144303807756e693144303907756e693144304107756e693144304207756e693144304307756e693144 +304407756e693144304507756e693144304607756e693144313007756e693144313107756e693144313207756e6931443133 +07756e693144313407756e693144313607756e693144313707756e693144313807756e693144313907756e69314431410775 +6e693144314207756e693144314307756e693144314407756e693144314507756e693144314607756e693144323007756e69 +3144323107756e693144323207756e693144323307756e693144323607756e693144323707756e693144323807756e693144 +323907756e693144324107756e693144324207756e693144324307756e693144324407756e693144324507756e6931443330 +07756e693144333107756e693144333207756e693144333307756e693144333407756e693144333507756e69314433360775 +6e693144333707756e693144333807756e693144333907756e693144334107756e693144334207756e693144334307756e69 +3144334407756e693144334507756e693144334607756e693144343007756e693144343107756e693144343207756e693144 +343307756e693144343407756e693144343507756e693144343607756e693144343707756e693144343807756e6931443439 +07756e693144344107756e693144344207756e693144344307756e693144344407756e693144344507756e69314434460775 +6e693144353007756e693144353107756e693144353207756e693144353307756e693144353407756e693144353507756e69 +3144353607756e693144353707756e693144353807756e693144353907756e693144354107756e693144354207756e693144 +354407756e693144354507756e693144354607756e693144363007756e693144363107756e693144363207756e6931443633 +07756e693144363407756e693144363507756e693144363607756e693144363707756e693144363807756e69314436390775 +6e693144364107756e693144373707756e693144373807756e693144374207756e693144374407756e693144383507756e69 +3144394207756e693144394307756e693144394407756e693144394507756e693144394607756e693144413007756e693144 +413107756e693144413207756e693144413307756e693144413407756e693144413507756e693144413607756e6931444137 +07756e693144413807756e693144413907756e693144414107756e693144414207756e693144414307756e69314441440775 +6e693144414507756e693144414607756e693144423007756e693144423107756e693144423207756e693144423307756e69 +3144423407756e693144423507756e693144423607756e693144423707756e693144423807756e693144423907756e693144 +424107756e693144424207756e693144424307756e693144424407756e693144424507756e693144424607756e6931444334 +07756e693144433507756e693144433607756e693144433707756e693144433807756e693144433907756e69314530300775 +6e693145303107756e693145303207756e693145303307756e693145303407756e693145303507756e693145303607756e69 +3145303707756e693145303807756e693145303907756e693145304107756e693145304207756e693145304307756e693145 +304407756e693145304507756e693145304607756e693145313007756e693145313107756e693145313207756e6931453133 +07756e693145313407756e693145313507756e693145313607756e693145313707756e693145313807756e69314531390775 +6e693145314107756e693145314207756e693145314307756e693145314407756e693145314507756e693145314607756e69 +3145323007756e693145323107756e693145323207756e693145323307756e693145323407756e693145323507756e693145 +323607756e693145323707756e693145323807756e693145323907756e693145324107756e693145324207756e6931453243 +07756e693145324407756e693145324507756e693145324607756e693145333007756e693145333107756e69314533320775 +6e693145333307756e693145333407756e693145333507756e693145333607756e693145333707756e693145333807756e69 +3145333907756e693145334107756e693145334207756e693145334307756e693145334407756e693145334507756e693145 +334607756e693145343007756e693145343107756e693145343207756e693145343307756e693145343407756e6931453435 +07756e693145343607756e693145343707756e693145343807756e693145343907756e693145344107756e69314534420775 +6e693145344307756e693145344407756e693145344507756e693145344607756e693145353007756e693145353107756e69 +3145353207756e693145353307756e693145353407756e693145353507756e693145353607756e693145353707756e693145 +353807756e693145353907756e693145354107756e693145354207756e693145354307756e693145354407756e6931453545 +07756e693145354607756e693145363007756e693145363107756e693145363207756e693145363307756e69314536340775 +6e693145363507756e693145363607756e693145363707756e693145363807756e693145363907756e693145364107756e69 +3145364207756e693145364307756e693145364407756e693145364507756e693145364607756e693145373007756e693145 +373107756e693145373207756e693145373307756e693145373407756e693145373507756e693145373607756e6931453737 +07756e693145373807756e693145373907756e693145374107756e693145374207756e693145374307756e69314537440775 +6e693145374507756e6931453746065767726176650677677261766506576163757465067761637574650957646965726573 +69730977646965726573697307756e693145383607756e693145383707756e693145383807756e693145383907756e693145 +384107756e693145384207756e693145384307756e693145384407756e693145384507756e693145384607756e6931453930 +07756e693145393107756e693145393207756e693145393307756e693145393407756e693145393507756e69314539360775 +6e693145393707756e693145393807756e693145393907756e693145394107756e693145394207756e693145394307756e69 +3145394407756e693145394507756e693145394607756e693145413007756e693145413107756e693145413207756e693145 +413307756e693145413407756e693145413507756e693145413607756e693145413707756e693145413807756e6931454139 +07756e693145414107756e693145414207756e693145414307756e693145414407756e693145414507756e69314541460775 +6e693145423007756e693145423107756e693145423207756e693145423307756e693145423407756e693145423507756e69 +3145423607756e693145423707756e693145423807756e693145423907756e693145424107756e693145424207756e693145 +424307756e693145424407756e693145424507756e693145424607756e693145433007756e693145433107756e6931454332 +07756e693145433307756e693145433407756e693145433507756e693145433607756e693145433707756e69314543380775 +6e693145433907756e693145434107756e693145434207756e693145434307756e693145434407756e693145434507756e69 +3145434607756e693145443007756e693145443107756e693145443207756e693145443307756e693145443407756e693145 +443507756e693145443607756e693145443707756e693145443807756e693145443907756e693145444107756e6931454442 +07756e693145444307756e693145444407756e693145444507756e693145444607756e693145453007756e69314545310775 +6e693145453207756e693145453307756e693145453407756e693145453507756e693145453607756e693145453707756e69 +3145453807756e693145453907756e693145454107756e693145454207756e693145454307756e693145454407756e693145 +454507756e693145454607756e693145463007756e6931454631065967726176650679677261766507756e69314546340775 +6e693145463507756e693145463607756e693145463707756e693145463807756e693145463907756e693145464107756e69 +3145464207756e693146303007756e693146303107756e693146303207756e693146303307756e693146303407756e693146 +303507756e693146303607756e693146303707756e693146303807756e693146303907756e693146304107756e6931463042 +07756e693146304307756e693146304407756e693146304507756e693146304607756e693146313007756e69314631310775 +6e693146313207756e693146313307756e693146313407756e693146313507756e693146313807756e693146313907756e69 +3146314107756e693146314207756e693146314307756e693146314407756e693146323007756e693146323107756e693146 +323207756e693146323307756e693146323407756e693146323507756e693146323607756e693146323707756e6931463238 +07756e693146323907756e693146324107756e693146324207756e693146324307756e693146324407756e69314632450775 +6e693146324607756e693146333007756e693146333107756e693146333207756e693146333307756e693146333407756e69 +3146333507756e693146333607756e693146333707756e693146333807756e693146333907756e693146334107756e693146 +334207756e693146334307756e693146334407756e693146334507756e693146334607756e693146343007756e6931463431 +07756e693146343207756e693146343307756e693146343407756e693146343507756e693146343807756e69314634390775 +6e693146344107756e693146344207756e693146344307756e693146344407756e693146353007756e693146353107756e69 +3146353207756e693146353307756e693146353407756e693146353507756e693146353607756e693146353707756e693146 +353907756e693146354207756e693146354407756e693146354607756e693146363007756e693146363107756e6931463632 +07756e693146363307756e693146363407756e693146363507756e693146363607756e693146363707756e69314636380775 +6e693146363907756e693146364107756e693146364207756e693146364307756e693146364407756e693146364507756e69 +3146364607756e693146373007756e693146373107756e693146373207756e693146373307756e693146373407756e693146 +373507756e693146373607756e693146373707756e693146373807756e693146373907756e693146374107756e6931463742 +07756e693146374307756e693146374407756e693146383007756e693146383107756e693146383207756e69314638330775 +6e693146383407756e693146383507756e693146383607756e693146383707756e693146383807756e693146383907756e69 +3146384107756e693146384207756e693146384307756e693146384407756e693146384507756e693146384607756e693146 +393007756e693146393107756e693146393207756e693146393307756e693146393407756e693146393507756e6931463936 +07756e693146393707756e693146393807756e693146393907756e693146394107756e693146394207756e69314639430775 +6e693146394407756e693146394507756e693146394607756e693146413007756e693146413107756e693146413207756e69 +3146413307756e693146413407756e693146413507756e693146413607756e693146413707756e693146413807756e693146 +413907756e693146414107756e693146414207756e693146414307756e693146414407756e693146414507756e6931464146 +07756e693146423007756e693146423107756e693146423207756e693146423307756e693146423407756e69314642360775 +6e693146423707756e693146423807756e693146423907756e693146424107756e693146424207756e693146424307756e69 +3146424407756e693146424507756e693146424607756e693146433007756e693146433107756e693146433207756e693146 +433307756e693146433407756e693146433607756e693146433707756e693146433807756e693146433907756e6931464341 +07756e693146434207756e693146434307756e693146434407756e693146434507756e693146434607756e69314644300775 +6e693146443107756e693146443207756e693146443307756e693146443607756e693146443707756e693146443807756e69 +3146443907756e693146444107756e693146444207756e693146444407756e693146444507756e693146444607756e693146 +453007756e693146453107756e693146453207756e693146453307756e693146453407756e693146453507756e6931464536 +07756e693146453707756e693146453807756e693146453907756e693146454107756e693146454207756e69314645430775 +6e693146454407756e693146454507756e693146454607756e693146463207756e693146463307756e693146463407756e69 +3146463607756e693146463707756e693146463807756e693146463907756e693146464107756e693146464207756e693146 +464307756e693146464407756e693146464507756e693230303007756e693230303107756e693230303207756e6932303033 +07756e693230303407756e693230303507756e693230303607756e693230303707756e693230303807756e69323030390775 +6e693230304107756e693230304207756e693230304307756e693230304407756e693230304507756e693230304607756e69 +3230313007756e69323031310a6669677572656461736807756e693230313507756e69323031360d756e64657273636f7265 +64626c0d71756f7465726576657273656407756e693230314607756e69323032330e6f6e65646f74656e6c65616465720e74 +776f646f74656e6c656164657207756e693230323707756e693230323807756e693230323907756e693230324107756e6932 +30324207756e693230324307756e693230324407756e693230324507756e693230324607756e6932303331066d696e757465 +067365636f6e6407756e693230333407756e693230333507756e693230333607756e693230333707756e693230333807756e +6932303342096578636c616d64626c07756e693230334407756e693230334507756e693230334607756e693230343007756e +693230343107756e693230343207756e693230343307756e693230343507756e693230343607756e693230343707756e6932 +30343807756e693230343907756e693230344107756e693230344207756e693230344307756e693230344407756e69323034 +4507756e693230344607756e693230353007756e693230353107756e693230353207756e693230353307756e693230353407 +756e693230353507756e693230353607756e693230353707756e693230353807756e693230353907756e693230354107756e +693230354207756e693230354307756e693230354407756e693230354507756e693230354607756e693230363007756e6932 +30363107756e693230363207756e693230363307756e693230363407756e693230364107756e693230364207756e69323036 +4307756e693230364407756e693230364507756e693230364607756e693230373007756e693230373107756e693230373407 +756e693230373507756e693230373607756e693230373707756e693230373807756e693230373907756e693230374107756e +693230374207756e693230374307756e693230374407756e693230374507756e693230374607756e693230383007756e6932 +30383107756e693230383207756e693230383307756e693230383407756e693230383507756e693230383607756e69323038 +3707756e693230383807756e693230383907756e693230384107756e693230384207756e693230384307756e693230384407 +756e693230384507756e693230393007756e693230393107756e693230393207756e693230393307756e693230393407756e +693230393507756e693230393607756e693230393707756e693230393807756e693230393907756e693230394107756e6932 +30394207756e693230394307756e69323041300d636f6c6f6e6d6f6e657461727907756e6932304132046c69726107756e69 +3230413507756e69323041360670657365746107756e693230413807756e693230413907756e693230414104646f6e670445 +75726f07756e693230414407756e693230414507756e693230414607756e693230423007756e693230423107756e69323042 +3207756e693230423307756e693230423407756e693230423507756e693230423807756e693230423907756e693230424107 +756e693230424407756e693230443007756e693230443107756e693230443607756e693230443707756e693230444207756e +693230444307756e693230453107756e693231303007756e693231303107756e693231303207756e693231303307756e6932 +31303407756e693231303507756e693231303607756e693231303707756e693231303807756e693231303907756e69323130 +4207756e693231304307756e693231304407756e693231304507756e693231304607756e693231313008496672616b747572 +07756e693231313207756e693231313307756e693231313407756e693231313507756e693231313607756e69323131370b77 +6569657273747261737307756e693231313907756e693231314107756e693231314208526672616b74757207756e69323131 +440c707265736372697074696f6e07756e693231314607756e693231323007756e693231323107756e693231323307756e69 +3231323407756e693231323507756e693231323607756e693231323707756e693231323807756e693231323907756e693231 +324107756e693231324207756e693231324307756e693231324409657374696d6174656407756e693231324607756e693231 +333007756e693231333107756e693231333207756e693231333307756e693231333405616c65706807756e69323133360775 +6e693231333707756e693231333807756e693231333907756e693231334107756e693231334207756e693231334307756e69 +3231334407756e693231334507756e693231334607756e693231343007756e693231343107756e693231343207756e693231 +343307756e693231343407756e693231343507756e693231343607756e693231343707756e693231343807756e6932313439 +07756e693231344207756e693231344507756e693231353007756e693231353107756e6932313532086f6e65746869726409 +74776f74686972647307756e693231353507756e693231353607756e693231353707756e693231353807756e693231353907 +756e6932313541096f6e656569676874680c7468726565656967687468730b66697665656967687468730c736576656e6569 +676874687307756e693231354607756e693231363007756e693231363107756e693231363207756e693231363307756e6932 +31363407756e693231363507756e693231363607756e693231363707756e693231363807756e693231363907756e69323136 +4107756e693231364207756e693231364307756e693231364407756e693231364507756e693231364607756e693231373007 +756e693231373107756e693231373207756e693231373307756e693231373407756e693231373507756e693231373607756e +693231373707756e693231373807756e693231373907756e693231374107756e693231374207756e693231374307756e6932 +31374407756e693231374507756e693231374607756e693231383007756e693231383107756e693231383207756e69323138 +3307756e693231383407756e693231383507756e6932313839096172726f776c656674076172726f7775700a6172726f7772 +69676874096172726f77646f776e096172726f77626f7468096172726f777570646e07756e693231393607756e6932313937 +07756e693231393807756e693231393907756e693231394107756e693231394207756e693231394307756e69323139440775 +6e693231394507756e693231394607756e693231413007756e693231413107756e693231413207756e693231413307756e69 +3231413407756e693231413507756e693231413607756e69323141370c6172726f777570646e62736507756e693231413907 +756e693231414107756e693231414207756e693231414307756e693231414407756e693231414507756e693231414607756e +693231423007756e693231423107756e693231423207756e693231423307756e69323142340e636172726961676572657475 +726e07756e693231423607756e693231423707756e693231423807756e693231423907756e693231424107756e6932314242 +07756e693231424307756e693231424407756e693231424507756e693231424607756e693231433007756e69323143310775 +6e693231433207756e693231433307756e693231433407756e693231433507756e693231433607756e693231433707756e69 +3231433807756e693231433907756e693231434107756e693231434207756e693231434307756e693231434407756e693231 +434507756e69323143460c6172726f7764626c6c6566740a6172726f7764626c75700d6172726f7764626c72696768740c61 +72726f7764626c646f776e0c6172726f7764626c626f746807756e693231443507756e693231443607756e69323144370775 +6e693231443807756e693231443907756e693231444107756e693231444207756e693231444307756e693231444407756e69 +3231444507756e693231444607756e693231453007756e693231453107756e693231453207756e693231453307756e693231 +453407756e693231453507756e693231453607756e693231453707756e693231453807756e693231453907756e6932314541 +07756e693231454207756e693231454307756e693231454407756e693231454507756e693231454607756e69323146300775 +6e693231463107756e693231463207756e693231463307756e693231463407756e693231463507756e693231463607756e69 +3231463707756e693231463807756e693231463907756e693231464107756e693231464207756e693231464307756e693231 +464407756e693231464507756e693231464609756e6976657273616c07756e69323230310b6578697374656e7469616c0775 +6e693232303408656d707479736574086772616469656e7407656c656d656e740a6e6f74656c656d656e7407756e69323230 +4108737563687468617407756e693232304307756e693232304407756e693232304507756e693232313007756e6932323133 +07756e693232313407756e693232313507756e69323231360c617374657269736b6d61746807756e693232313807756e6932 +32313907756e693232314207756e69323231430c70726f706f7274696f6e616c0a6f7274686f676f6e616c05616e676c6507 +756e693232323107756e693232323207756e693232323307756e693232323407756e693232323507756e69323232360a6c6f +676963616c616e64096c6f676963616c6f720c696e74657273656374696f6e05756e696f6e07756e693232324307756e6932 +32324407756e693232324507756e693232324607756e693232333007756e693232333107756e693232333207756e69323233 +33097468657265666f726507756e693232333507756e693232333607756e693232333707756e693232333807756e69323233 +3907756e693232334107756e69323233420773696d696c617207756e693232334407756e693232334507756e693232334607 +756e693232343007756e693232343107756e693232343207756e693232343307756e693232343409636f6e677275656e7407 +756e693232343607756e693232343707756e693232343907756e693232344107756e693232344207756e693232344307756e +693232344407756e693232344507756e693232344607756e693232353007756e693232353107756e693232353207756e6932 +32353307756e693232353407756e693232353507756e693232353607756e693232353707756e693232353807756e69323235 +3907756e693232354107756e693232354207756e693232354307756e693232354407756e693232354507756e69323235460b +6571756976616c656e636507756e693232363207756e693232363307756e693232363607756e693232363707756e69323236 +3807756e693232363907756e693232364107756e693232364207756e693232364307756e693232364407756e693232364507 +756e693232364607756e693232373007756e693232373107756e693232373207756e693232373307756e693232373407756e +693232373507756e693232373607756e693232373707756e693232373807756e693232373907756e693232374107756e6932 +32374207756e693232374307756e693232374407756e693232374507756e693232374607756e693232383007756e69323238 +310c70726f7065727375627365740e70726f7065727375706572736574096e6f7473756273657407756e69323238350c7265 +666c65787375627365740e7265666c6578737570657273657407756e693232383807756e693232383907756e693232384107 +756e693232384207756e693232384307756e693232384407756e693232384507756e693232384607756e693232393007756e +693232393107756e693232393207756e693232393307756e69323239340a636972636c65706c757307756e69323239360e63 +6972636c656d756c7469706c7907756e693232393807756e693232393907756e693232394107756e693232394207756e6932 +32394307756e693232394407756e693232394507756e693232394607756e693232413007756e693232413107756e69323241 +3207756e693232413307756e69323241340d70657270656e646963756c617207756e693232413607756e693232413707756e +693232413807756e693232413907756e693232414107756e693232414207756e693232414307756e693232414407756e6932 +32414507756e693232414607756e693232423007756e693232423107756e693232423207756e693232423307756e69323242 +3407756e693232423507756e693232423607756e693232423707756e693232423807756e693232423907756e693232424107 +756e693232424207756e693232424307756e693232424407756e693232424507756e693232424607756e693232433007756e +693232433107756e693232433207756e693232433307756e693232433407646f746d61746807756e693232433607756e6932 +32433707756e693232433807756e693232433907756e693232434107756e693232434207756e693232434307756e69323243 +4407756e693232434507756e693232434607756e693232443007756e693232443107756e693232443207756e693232443307 +756e693232443407756e693232443507756e693232443607756e693232443707756e693232443807756e693232443907756e +693232444107756e693232444207756e693232444307756e693232444407756e693232444507756e693232444607756e6932 +32453007756e693232453107756e693232453207756e693232453307756e693232453407756e693232453507756e69323245 +3607756e693232453707756e693232453807756e693232453907756e693232454107756e693232454207756e693232454307 +756e693232454407756e693232454507756e693232454607756e693232463007756e693232463107756e693232463207756e +693232463307756e693232463407756e693232463507756e693232463607756e693232463707756e693232463807756e6932 +32463907756e693232464107756e693232464207756e693232464307756e693232464407756e693232464507756e69323246 +4607756e693233303007756e693233303105686f75736507756e693233303307756e693233303407756e693233303507756e +693233303607756e693233303707756e693233303807756e693233303907756e693233304107756e693233304207756e6932 +33304307756e693233304407756e693233304507756e69323330460d7265766c6f676963616c6e6f7407756e693233313107 +756e693233313807756e693233313907756e693233314307756e693233314407756e693233314507756e69323331460a696e +74656772616c74700a696e74656772616c627407756e693233323407756e693233323507756e693233323607756e69323332 +3707756e693233323807756e693233324207756e693233324307756e693233373307756e693233373407756e693233373507 +756e693233374107756e693233374407756e693233383707756e693233393407756e693233394207756e693233394307756e +693233394407756e693233394507756e693233394607756e693233413007756e693233413107756e693233413207756e6932 +33413307756e693233413407756e693233413507756e693233413607756e693233413707756e693233413807756e69323341 +3907756e693233414107756e693233414207756e693233414307756e693233414407756e693233414507756e693233434507 +756e693233434607756e693233453307756e693233453507756e693233453807756e693234323207756e693234323307756e +693234363007756e693234363107756e693234363207756e693234363307756e693234363407756e693234363507756e6932 +34363607756e693234363707756e693234363807756e693234363908534631303030303007756e6932353031085346313130 +30303007756e693235303307756e693235303407756e693235303507756e693235303607756e693235303707756e69323530 +3807756e693235303907756e693235304107756e693235304208534630313030303007756e693235304407756e6932353045 +07756e693235304608534630333030303007756e693235313107756e693235313207756e6932353133085346303230303030 +07756e693235313507756e693235313607756e693235313708534630343030303007756e693235313907756e693235314107 +756e693235314208534630383030303007756e693235314407756e693235314507756e693235314607756e69323532300775 +6e693235323107756e693235323207756e693235323308534630393030303007756e693235323507756e693235323607756e +693235323707756e693235323807756e693235323907756e693235324107756e693235324208534630363030303007756e69 +3235324407756e693235324507756e693235324607756e693235333007756e693235333107756e693235333207756e693235 +333308534630373030303007756e693235333507756e693235333607756e693235333707756e693235333807756e69323533 +3907756e693235334107756e693235334208534630353030303007756e693235334407756e693235334507756e6932353346 +07756e693235343007756e693235343107756e693235343207756e693235343307756e693235343407756e69323534350775 +6e693235343607756e693235343707756e693235343807756e693235343907756e693235344107756e693235344207756e69 +3235344307756e693235344407756e693235344507756e693235344608534634333030303008534632343030303008534635 +3130303030085346353230303030085346333930303030085346323230303030085346323130303030085346323530303030 +0853463530303030300853463439303030300853463338303030300853463238303030300853463237303030300853463236 +3030303008534633363030303008534633373030303008534634323030303008534631393030303008534632303030303008 +5346323330303030085346343730303030085346343830303030085346343130303030085346343530303030085346343630 +30303008534634303030303008534635343030303008534635333030303008534634343030303007756e693235364407756e +693235364507756e693235364607756e693235373007756e693235373107756e693235373207756e693235373307756e6932 +35373407756e693235373507756e693235373607756e693235373707756e693235373807756e693235373907756e69323537 +4107756e693235374207756e693235374307756e693235374407756e693235374507756e6932353746077570626c6f636b07 +756e693235383107756e693235383207756e693235383307646e626c6f636b07756e693235383507756e693235383607756e +693235383705626c6f636b07756e693235383907756e693235384107756e6932353842076c66626c6f636b07756e69323538 +4407756e693235384507756e6932353846077274626c6f636b076c74736861646505736861646507646b736861646507756e +693235393407756e693235393507756e693235393607756e693235393707756e693235393807756e693235393907756e6932 +35394107756e693235394207756e693235394307756e693235394407756e693235394507756e69323539460966696c6c6564 +626f780648323230373307756e693235413207756e693235413307756e693235413407756e693235413507756e6932354136 +07756e693235413707756e693235413807756e693235413906483138353433064831383535310a66696c6c65647265637407 +756e693235414407756e693235414507756e693235414607756e693235423007756e6932354231077472696167757007756e +693235423307756e693235423407756e693235423507756e693235423607756e693235423707756e693235423807756e6932 +354239077472696167727407756e6932354242077472696167646e07756e693235424407756e693235424507756e69323542 +4607756e693235433007756e693235433107756e693235433207756e69323543330774726961676c6607756e693235433507 +756e693235433607756e693235433707756e693235433807756e693235433906636972636c6507756e693235434307756e69 +3235434407756e69323543450648313835333307756e693235443007756e693235443107756e693235443207756e69323544 +3307756e693235443407756e693235443507756e693235443607756e693235443709696e7662756c6c657409696e76636972 +636c6507756e693235444107756e693235444207756e693235444307756e693235444407756e693235444507756e69323544 +4607756e693235453007756e693235453107756e693235453207756e693235453307756e693235453407756e69323545350a +6f70656e62756c6c657407756e693235453707756e693235453807756e693235453907756e693235454107756e6932354542 +07756e693235454307756e693235454407756e693235454507756e693235454607756e693235463007756e69323546310775 +6e693235463207756e693235463307756e693235463407756e693235463507756e693235463607756e693235463707756e69 +3235463807756e693235463907756e693235464107756e693235464207756e693235464307756e693235464407756e693235 +464507756e693235464607756e693236303007756e693236303107756e693236303207756e693236303307756e6932363034 +07756e693236303507756e693236303607756e693236303707756e693236303807756e693236303907756e69323630410775 +6e693236304207756e693236304307756e693236304407756e693236304507756e693236304607756e693236313007756e69 +3236313107756e693236313207756e693236313307756e693236313407756e693236313507756e693236313607756e693236 +313707756e693236313807756e693236313907756e693236314107756e693236314207756e693236314307756e6932363144 +07756e693236314507756e693236314607756e693236323007756e693236323107756e693236323207756e69323632330775 +6e693236323407756e693236323507756e693236323607756e693236323707756e693236323807756e693236323907756e69 +3236324107756e693236324207756e693236324307756e693236324407756e693236324507756e693236324607756e693236 +333007756e693236333107756e693236333207756e693236333307756e693236333407756e693236333507756e6932363336 +07756e693236333707756e693236333807756e693236333909736d696c65666163650c696e76736d696c6566616365037375 +6e07756e693236334407756e693236334507756e69323633460666656d616c6507756e6932363431046d616c6507756e6932 +36343307756e693236343407756e693236343507756e693236343607756e693236343707756e693236343807756e69323634 +3907756e693236344107756e693236344207756e693236344307756e693236344407756e693236344507756e693236344607 +756e693236353007756e693236353107756e693236353207756e693236353307756e693236353407756e693236353507756e +693236353607756e693236353707756e693236353807756e693236353907756e693236354107756e693236354207756e6932 +36354307756e693236354407756e693236354507756e693236354605737061646507756e693236363107756e693236363204 +636c756207756e6932363634056865617274076469616d6f6e6407756e693236363707756e693236363807756e6932363639 +0b6d75736963616c6e6f74650e6d75736963616c6e6f746564626c07756e693236364307756e693236364407756e69323636 +4507756e693236364607756e693236373007756e693236373107756e693236373207756e693236373307756e693236373407 +756e693236373507756e693236373607756e693236373707756e693236373807756e693236373907756e693236374107756e +693236374207756e693236374307756e693236374407756e693236374507756e693236374607756e693236383007756e6932 +36383107756e693236383207756e693236383307756e693236383407756e693236383507756e693236383607756e69323638 +3707756e693236383807756e693236383907756e693236384107756e693236384207756e693236384307756e693236384407 +756e693236384507756e693236384607756e693236393007756e693236393107756e693236393207756e693236393307756e +693236393407756e693236393507756e693236393607756e693236393707756e693236393807756e693236393907756e6932 +36394107756e693236394207756e693236394307756e693236394507756e693236394607756e693236413007756e69323641 +3107756e693236413207756e693236413307756e693236413407756e693236413507756e693236413607756e693236413707 +756e693236413807756e693236413907756e693236414107756e693236414207756e693236414307756e693236414407756e +693236414507756e693236414607756e693236423007756e693236423107756e693236423207756e693236423307756e6932 +36423407756e693236423507756e693236423607756e693236423707756e693236423807756e693236433007756e69323643 +3107756e693236433207756e693236433307756e693236453207756e693237303107756e693237303207756e693237303307 +756e693237303407756e693237303607756e693237303707756e693237303807756e693237303907756e693237304307756e +693237304407756e693237304507756e693237304607756e693237313007756e693237313107756e693237313207756e6932 +37313307756e693237313407756e693237313507756e693237313607756e693237313707756e693237313807756e69323731 +3907756e693237314107756e693237314207756e693237314307756e693237314407756e693237314507756e693237314607 +756e693237323007756e693237323107756e693237323207756e693237323307756e693237323407756e693237323507756e +693237323607756e693237323707756e693237323907756e693237324107756e693237324207756e693237324307756e6932 +37324407756e693237324507756e693237324607756e693237333007756e693237333107756e693237333207756e69323733 +3307756e693237333407756e693237333507756e693237333607756e693237333707756e693237333807756e693237333907 +756e693237334107756e693237334207756e693237334307756e693237334407756e693237334507756e693237334607756e +693237343007756e693237343107756e693237343207756e693237343307756e693237343407756e693237343507756e6932 +37343607756e693237343707756e693237343807756e693237343907756e693237344107756e693237344207756e69323734 +4407756e693237344607756e693237353007756e693237353107756e693237353207756e693237353607756e693237353807 +756e693237353907756e693237354107756e693237354207756e693237354307756e693237354407756e693237354507756e +693237363107756e693237363207756e693237363307756e693237363407756e693237363507756e693237363607756e6932 +37363707756e693237363807756e693237363907756e693237364107756e693237364207756e693237364307756e69323736 +4407756e693237364507756e693237364607756e693237373007756e693237373107756e693237373207756e693237373307 +756e693237373407756e693237373507756e693237373607756e693237373707756e693237373807756e693237373907756e +693237374107756e693237374207756e693237374307756e693237374407756e693237374507756e693237374607756e6932 +37383007756e693237383107756e693237383207756e693237383307756e693237383407756e693237383507756e69323738 +3607756e693237383707756e693237383807756e693237383907756e693237384107756e693237384207756e693237384307 +756e693237384407756e693237384507756e693237384607756e693237393007756e693237393107756e693237393207756e +693237393307756e693237393407756e693237393807756e693237393907756e693237394107756e693237394207756e6932 +37394307756e693237394407756e693237394507756e693237394607756e693237413007756e693237413107756e69323741 +3207756e693237413307756e693237413407756e693237413507756e693237413607756e693237413707756e693237413807 +756e693237413907756e693237414107756e693237414207756e693237414307756e693237414407756e693237414507756e +693237414607756e693237423107756e693237423207756e693237423307756e693237423407756e693237423507756e6932 +37423607756e693237423707756e693237423807756e693237423907756e693237424107756e693237424207756e69323742 +4307756e693237424407756e693237424507756e693237433507756e693237433607756e693237453007756e693237453607 +756e693237453707756e693237453807756e693237453907756e693237454107756e693237454207756e693237463007756e +693237463107756e693237463207756e693237463307756e693237463407756e693237463507756e693237463607756e6932 +37463707756e693237463807756e693237463907756e693237464107756e693237464207756e693237464307756e69323746 +4407756e693237464507756e693237464607756e693238303007756e693238303107756e693238303207756e693238303307 +756e693238303407756e693238303507756e693238303607756e693238303707756e693238303807756e693238303907756e +693238304107756e693238304207756e693238304307756e693238304407756e693238304507756e693238304607756e6932 +38313007756e693238313107756e693238313207756e693238313307756e693238313407756e693238313507756e69323831 +3607756e693238313707756e693238313807756e693238313907756e693238314107756e693238314207756e693238314307 +756e693238314407756e693238314507756e693238314607756e693238323007756e693238323107756e693238323207756e +693238323307756e693238323407756e693238323507756e693238323607756e693238323707756e693238323807756e6932 +38323907756e693238324107756e693238324207756e693238324307756e693238324407756e693238324507756e69323832 +4607756e693238333007756e693238333107756e693238333207756e693238333307756e693238333407756e693238333507 +756e693238333607756e693238333707756e693238333807756e693238333907756e693238334107756e693238334207756e +693238334307756e693238334407756e693238334507756e693238334607756e693238343007756e693238343107756e6932 +38343207756e693238343307756e693238343407756e693238343507756e693238343607756e693238343707756e69323834 +3807756e693238343907756e693238344107756e693238344207756e693238344307756e693238344407756e693238344507 +756e693238344607756e693238353007756e693238353107756e693238353207756e693238353307756e693238353407756e +693238353507756e693238353607756e693238353707756e693238353807756e693238353907756e693238354107756e6932 +38354207756e693238354307756e693238354407756e693238354507756e693238354607756e693238363007756e69323836 +3107756e693238363207756e693238363307756e693238363407756e693238363507756e693238363607756e693238363707 +756e693238363807756e693238363907756e693238364107756e693238364207756e693238364307756e693238364407756e +693238364507756e693238364607756e693238373007756e693238373107756e693238373207756e693238373307756e6932 +38373407756e693238373507756e693238373607756e693238373707756e693238373807756e693238373907756e69323837 +4107756e693238374207756e693238374307756e693238374407756e693238374507756e693238374607756e693238383007 +756e693238383107756e693238383207756e693238383307756e693238383407756e693238383507756e693238383607756e +693238383707756e693238383807756e693238383907756e693238384107756e693238384207756e693238384307756e6932 +38384407756e693238384507756e693238384607756e693238393007756e693238393107756e693238393207756e69323839 +3307756e693238393407756e693238393507756e693238393607756e693238393707756e693238393807756e693238393907 +756e693238394107756e693238394207756e693238394307756e693238394407756e693238394507756e693238394607756e +693238413007756e693238413107756e693238413207756e693238413307756e693238413407756e693238413507756e6932 +38413607756e693238413707756e693238413807756e693238413907756e693238414107756e693238414207756e69323841 +4307756e693238414407756e693238414507756e693238414607756e693238423007756e693238423107756e693238423207 +756e693238423307756e693238423407756e693238423507756e693238423607756e693238423707756e693238423807756e +693238423907756e693238424107756e693238424207756e693238424307756e693238424407756e693238424507756e6932 +38424607756e693238433007756e693238433107756e693238433207756e693238433307756e693238433407756e69323843 +3507756e693238433607756e693238433707756e693238433807756e693238433907756e693238434107756e693238434207 +756e693238434307756e693238434407756e693238434507756e693238434607756e693238443007756e693238443107756e +693238443207756e693238443307756e693238443407756e693238443507756e693238443607756e693238443707756e6932 +38443807756e693238443907756e693238444107756e693238444207756e693238444307756e693238444407756e69323844 +4507756e693238444607756e693238453007756e693238453107756e693238453207756e693238453307756e693238453407 +756e693238453507756e693238453607756e693238453707756e693238453807756e693238453907756e693238454107756e +693238454207756e693238454307756e693238454407756e693238454507756e693238454607756e693238463007756e6932 +38463107756e693238463207756e693238463307756e693238463407756e693238463507756e693238463607756e69323846 +3707756e693238463807756e693238463907756e693238464107756e693238464207756e693238464307756e693238464407 +756e693238464507756e693238464607756e693239303607756e693239303707756e693239304107756e693239304207756e +693239343007756e693239343107756e693239383307756e693239383407756e693239434507756e693239434607756e6932 +39443007756e693239443107756e693239443207756e693239443307756e693239443407756e693239443507756e69323945 +4207756e693239464107756e693239464207756e693241303007756e693241303107756e693241303207756e693241304307 +756e693241304407756e693241304507756e693241304607756e693241313007756e693241313107756e693241313207756e +693241313307756e693241313407756e693241313507756e693241313607756e693241313707756e693241313807756e6932 +41313907756e693241314107756e693241314207756e693241314307756e693241324607756e693241364107756e69324136 +4207756e693241374407756e693241374507756e693241374607756e693241383007756e693241383107756e693241383207 +756e693241383307756e693241383407756e693241383507756e693241383607756e693241383707756e693241383807756e +693241383907756e693241384107756e693241384207756e693241384307756e693241384407756e693241384507756e6932 +41384607756e693241393007756e693241393107756e693241393207756e693241393307756e693241393407756e69324139 +3507756e693241393607756e693241393707756e693241393807756e693241393907756e693241394107756e693241394207 +756e693241394307756e693241394407756e693241394507756e693241394607756e693241413007756e693241414507756e +693241414607756e693241423007756e693241423107756e693241423207756e693241423307756e693241423407756e6932 +41423507756e693241423607756e693241423707756e693241423807756e693241423907756e693241424107756e69324146 +3907756e693241464107756e693242303007756e693242303107756e693242303207756e693242303307756e693242303407 +756e693242303507756e693242303607756e693242303707756e693242303807756e693242303907756e693242304107756e +693242304207756e693242304307756e693242304407756e693242304507756e693242304607756e693242313007756e6932 +42313107756e693242313207756e693242313307756e693242313407756e693242313507756e693242313607756e69324231 +3707756e693242313807756e693242313907756e693242314107756e693242314607756e693242323007756e693242323107 +756e693242323207756e693242323307756e693242323407756e693242353307756e693242353407756e693243363007756e +693243363107756e693243363207756e693243363307756e693243363407756e693243363507756e693243363607756e6932 +43363707756e693243363807756e693243363907756e693243364107756e693243364207756e693243364307756e69324336 +4407756e693243364507756e693243364607756e693243373007756e693243373107756e693243373207756e693243373307 +756e693243373407756e693243373507756e693243373607756e693243373707756e693243373907756e693243374107756e +693243374207756e693243374307756e693243374407756e693243374507756e693243374607756e693244303007756e6932 +44303107756e693244303207756e693244303307756e693244303407756e693244303507756e693244303607756e69324430 +3707756e693244303807756e693244303907756e693244304107756e693244304207756e693244304307756e693244304407 +756e693244304507756e693244304607756e693244313007756e693244313107756e693244313207756e693244313307756e +693244313407756e693244313507756e693244313607756e693244313707756e693244313807756e693244313907756e6932 +44314107756e693244314207756e693244314307756e693244314407756e693244314507756e693244314607756e69324432 +3007756e693244323107756e693244323207756e693244323307756e693244323407756e693244323507756e693244333007 +756e693244333107756e693244333207756e693244333307756e693244333407756e693244333507756e693244333607756e +693244333707756e693244333807756e693244333907756e693244334107756e693244334207756e693244334307756e6932 +44334407756e693244334507756e693244334607756e693244343007756e693244343107756e693244343207756e69324434 +3307756e693244343407756e693244343507756e693244343607756e693244343707756e693244343807756e693244343907 +756e693244344107756e693244344207756e693244344307756e693244344407756e693244344507756e693244344607756e +693244353007756e693244353107756e693244353207756e693244353307756e693244353407756e693244353507756e6932 +44353607756e693244353707756e693244353807756e693244353907756e693244354107756e693244354207756e69324435 +4307756e693244354407756e693244354507756e693244354607756e693244363007756e693244363107756e693244363207 +756e693244363307756e693244363407756e693244363507756e693244364607756e693245313807756e693245314607756e +693245323207756e693245323307756e693245323407756e693245323507756e693245324507756e693444433007756e6934 +44433107756e693444433207756e693444433307756e693444433407756e693444433507756e693444433607756e69344443 +3707756e693444433807756e693444433907756e693444434107756e693444434207756e693444434307756e693444434407 +756e693444434507756e693444434607756e693444443007756e693444443107756e693444443207756e693444443307756e +693444443407756e693444443507756e693444443607756e693444443707756e693444443807756e693444443907756e6934 +44444107756e693444444207756e693444444307756e693444444407756e693444444507756e693444444607756e69344445 +3007756e693444453107756e693444453207756e693444453307756e693444453407756e693444453507756e693444453607 +756e693444453707756e693444453807756e693444453907756e693444454107756e693444454207756e693444454307756e +693444454407756e693444454507756e693444454607756e693444463007756e693444463107756e693444463207756e6934 +44463307756e693444463407756e693444463507756e693444463607756e693444463707756e693444463807756e69344446 +3907756e693444464107756e693444464207756e693444464307756e693444464407756e693444464507756e693444464607 +756e694134443007756e694134443107756e694134443207756e694134443307756e694134443407756e694134443507756e +694134443607756e694134443707756e694134443807756e694134443907756e694134444107756e694134444207756e6941 +34444307756e694134444407756e694134444507756e694134444607756e694134453007756e694134453107756e69413445 +3207756e694134453307756e694134453407756e694134453507756e694134453607756e694134453707756e694134453807 +756e694134453907756e694134454107756e694134454207756e694134454307756e694134454407756e694134454507756e +694134454607756e694134463007756e694134463107756e694134463207756e694134463307756e694134463407756e6941 +34463507756e694134463607756e694134463707756e694134463807756e694134463907756e694134464107756e69413446 +4207756e694134464307756e694134464407756e694134464507756e694134464607756e694136343407756e694136343507 +756e694136343607756e694136343707756e694136344307756e694136344407756e694136353007756e694136353107756e +694136353407756e694136353507756e694136353607756e694136353707756e694136363207756e694136363307756e6941 +36363407756e694136363507756e694136363607756e694136363707756e694136363807756e694136363907756e69413636 +4107756e694136364207756e694136364307756e694136364407756e694136364507756e694136384107756e694136384207 +756e694136384307756e694136384407756e694136393407756e694136393507756e694137303807756e694137303907756e +694137304107756e694137304207756e694137304307756e694137304407756e694137304507756e694137304607756e6941 +37313007756e694137313107756e694137313207756e694137313307756e694137313407756e694137313507756e69413731 +3607756e694137314207756e694137314307756e694137314407756e694137314507756e694137314607756e694137323207 +756e694137323307756e694137323407756e694137323507756e694137323607756e694137323707756e694137323807756e +694137323907756e694137324107756e694137324207756e694137333007756e694137333107756e694137333207756e6941 +37333307756e694137333407756e694137333507756e694137333607756e694137333707756e694137333807756e69413733 +3907756e694137334107756e694137334207756e694137334307756e694137334407756e694137334507756e694137334607 +756e694137343007756e694137343107756e694137343607756e694137343707756e694137343807756e694137343907756e +694137344107756e694137344207756e694137344507756e694137344607756e694137353007756e694137353107756e6941 +37353207756e694137353307756e694137353607756e694137353707756e694137363407756e694137363507756e69413736 +3607756e694137363707756e694137383007756e694137383107756e694137383207756e694137383307756e694137383907 +756e694137384107756e694137384207756e694137384307756e694137384407756e694137384507756e694137393007756e +694137393107756e694137413007756e694137413107756e694137413207756e694137413307756e694137413407756e6941 +37413507756e694137413607756e694137413707756e694137413807756e694137413907756e694137414107756e69413746 +3807756e694137463907756e694137464107756e694137464207756e694137464307756e694137464407756e694137464507 +756e694137464609756e69303245352e3509756e69303245362e3509756e69303245372e3509756e69303245382e3509756e +69303245392e3509756e69303245352e3409756e69303245362e3409756e69303245372e3409756e69303245382e3409756e +69303245392e3409756e69303245352e3309756e69303245362e3309756e69303245372e3309756e69303245382e3309756e +69303245392e3309756e69303245352e3209756e69303245362e3209756e69303245372e3209756e69303245382e3209756e +69303245392e3209756e69303245352e3109756e69303245362e3109756e69303245372e3109756e69303245382e3109756e +69303245392e31047374656d07756e694630303007756e694630303107756e694630303207756e694630303307756e694634 +303007756e694634303107756e694634303207756e694634303307756e694634303407756e694634303507756e6946343036 +07756e694634303707756e694634303807756e694634303907756e694634304107756e694634304207756e69463430430775 +6e694634304407756e694634304507756e694634304607756e694634313007756e694634313107756e694634313207756e69 +4634313307756e694634313407756e694634313507756e694634313607756e694634313707756e694634313807756e694634 +313907756e694634314107756e694634314207756e694634314307756e694634314407756e694634314507756e6946343146 +07756e694634323007756e694634323107756e694634323207756e694634323307756e694634323407756e69463432350775 +6e694634323607756e694634323807756e694634323907756e694634324107756e694634324207756e694634324307756e69 +4634324407756e694634324507756e694634324607756e694634333007756e694634333107756e694634333207756e694634 +333307756e694634333407756e694634333507756e694634333607756e694634333707756e694634333807756e6946343339 +07756e694634334107756e694634334207756e694634334307756e694634334407756e694634334507756e69463433460775 +6e694634343007756e694634343107756e694636433507756e694642303007756e694642303307756e694642303407756e69 +4642303507756e694642303607756e694642313307756e694642313407756e694642313507756e694642313607756e694642 +313707756e694642314407756e694642314507756e694642314607756e694642323007756e694642323107756e6946423232 +07756e694642323307756e694642323407756e694642323507756e694642323607756e694642323707756e69464232380775 +6e694642323907756e694642324107756e694642324207756e694642324307756e694642324407756e694642324507756e69 +4642324607756e694642333007756e694642333107756e694642333207756e694642333307756e694642333407756e694642 +333507756e694642333607756e694642333807756e694642333907756e694642334107756e694642334207756e6946423343 +07756e694642334507756e694642343007756e694642343107756e694642343307756e694642343407756e69464234360775 +6e694642343707756e694642343807756e694642343907756e694642344107756e694642344207756e694642344307756e69 +4642344407756e694642344507756e694642344607756e694642353207756e694642353307756e694642353407756e694642 +353507756e694642353607756e694642353707756e694642353807756e694642353907756e694642354107756e6946423542 +07756e694642354307756e694642354407756e694642354507756e694642354607756e694642363007756e69464236310775 +6e694642363207756e694642363307756e694642363407756e694642363507756e694642363607756e694642363707756e69 +4642363807756e694642363907756e694642364107756e694642364207756e694642364307756e694642364407756e694642 +364507756e694642364607756e694642373007756e694642373107756e694642373207756e694642373307756e6946423734 +07756e694642373507756e694642373607756e694642373707756e694642373807756e694642373907756e69464237410775 +6e694642374207756e694642374307756e694642374407756e694642374507756e694642374607756e694642383007756e69 +4642383107756e694642383207756e694642383307756e694642383407756e694642383507756e694642383607756e694642 +383707756e694642383807756e694642383907756e694642384107756e694642384207756e694642384307756e6946423844 +07756e694642384507756e694642384607756e694642393007756e694642393107756e694642393207756e69464239330775 +6e694642393407756e694642393507756e694642393607756e694642393707756e694642393807756e694642393907756e69 +4642394107756e694642394207756e694642394307756e694642394407756e694642394507756e694642394607756e694642 +413007756e694642413107756e694642413207756e694642413307756e694642414107756e694642414207756e6946424143 +07756e694642414407756e694642443307756e694642443407756e694642443507756e694642443607756e69464244370775 +6e694642443807756e694642443907756e694642444107756e694642444207756e694642444307756e694642444507756e69 +4642444607756e694642453407756e694642453507756e694642453607756e694642453707756e694642453807756e694642 +453907756e694642464307756e694642464407756e694642464507756e694642464607756e694645303007756e6946453031 +07756e694645303207756e694645303307756e694645303407756e694645303507756e694645303607756e69464530370775 +6e694645303807756e694645303907756e694645304107756e694645304207756e694645304307756e694645304407756e69 +4645304507756e694645304607756e694645323007756e694645323107756e694645323207756e694645323307756e694645 +373007756e694645373107756e694645373207756e694645373307756e694645373407756e694645373607756e6946453737 +07756e694645373807756e694645373907756e694645374107756e694645374207756e694645374307756e69464537440775 +6e694645374507756e694645374607756e694645383007756e694645383107756e694645383207756e694645383307756e69 +4645383407756e694645383507756e694645383607756e694645383707756e694645383807756e694645383907756e694645 +384107756e694645384207756e694645384307756e694645384407756e694645384507756e694645384607756e6946453930 +07756e694645393107756e694645393207756e694645393307756e694645393407756e694645393507756e69464539360775 +6e694645393707756e694645393807756e694645393907756e694645394107756e694645394207756e694645394307756e69 +4645394407756e694645394507756e694645394607756e694645413007756e694645413107756e694645413207756e694645 +413307756e694645413407756e694645413507756e694645413607756e694645413707756e694645413807756e6946454139 +07756e694645414107756e694645414207756e694645414307756e694645414407756e694645414507756e69464541460775 +6e694645423007756e694645423107756e694645423207756e694645423307756e694645423407756e694645423507756e69 +4645423607756e694645423707756e694645423807756e694645423907756e694645424107756e694645424207756e694645 +424307756e694645424407756e694645424507756e694645424607756e694645433007756e694645433107756e6946454332 +07756e694645433307756e694645433407756e694645433507756e694645433607756e694645433707756e69464543380775 +6e694645433907756e694645434107756e694645434207756e694645434307756e694645434407756e694645434507756e69 +4645434607756e694645443007756e694645443107756e694645443207756e694645443307756e694645443407756e694645 +443507756e694645443607756e694645443707756e694645443807756e694645443907756e694645444107756e6946454442 +07756e694645444307756e694645444407756e694645444507756e694645444607756e694645453007756e69464545310775 +6e694645453207756e694645453307756e694645453407756e694645453507756e694645453607756e694645453707756e69 +4645453807756e694645453907756e694645454107756e694645454207756e694645454307756e694645454407756e694645 +454507756e694645454607756e694645463007756e694645463107756e694645463207756e694645463307756e6946454634 +07756e694645463507756e694645463607756e694645463707756e694645463807756e694645463907756e69464546410775 +6e694645464207756e694645464307756e694645464607756e694646463907756e694646464107756e694646464207756e69 +4646464307756e69464646440675313033303006753130333031067531303330320675313033303306753130333034067531 +3033303506753130333036067531303330370675313033303806753130333039067531303330410675313033304206753130 +3330430675313033304406753130333045067531303330460675313033313006753130333131067531303331320675313033 +3133067531303331340675313033313506753130333136067531303331370675313033313806753130333139067531303331 +4106753130333142067531303331430675313033314406753130333145067531303332300675313033323106753130333232 +0675313033323306753144333030067531443330310675314433303206753144333033067531443330340675314433303506 +7531443330360675314433303706753144333038067531443330390675314433304106753144333042067531443330430675 +3144333044067531443330450675314433304606753144333130067531443331310675314433313206753144333133067531 +4433313406753144333135067531443331360675314433313706753144333138067531443331390675314433314106753144 +3331420675314433314306753144333144067531443331450675314433314606753144333230067531443332310675314433 +3232067531443332330675314433323406753144333235067531443332360675314433323706753144333238067531443332 +3906753144333241067531443332420675314433324306753144333244067531443332450675314433324606753144333330 +0675314433333106753144333332067531443333330675314433333406753144333335067531443333360675314433333706 +7531443333380675314433333906753144333341067531443333420675314433334306753144333344067531443333450675 +3144333346067531443334300675314433343106753144333432067531443334330675314433343406753144333435067531 +4433343606753144333437067531443334380675314433343906753144333441067531443334420675314433344306753144 +3334440675314433344506753144333446067531443335300675314433353106753144333532067531443335330675314433 +3534067531443335350675314433353606753144353338067531443533390675314435334206753144353343067531443533 +4406753144353345067531443534300675314435343106753144353432067531443534330675314435343406753144353436 +0675314435344106753144353442067531443534430675314435344406753144353445067531443534460675314435353006 +7531443535320675314435353306753144353534067531443535350675314435353606753144353537067531443535380675 +3144353539067531443535410675314435354206753144353543067531443535440675314435354506753144353546067531 +4435363006753144353631067531443536320675314435363306753144353634067531443536350675314435363606753144 +3536370675314435363806753144353639067531443536410675314435364206753144354130067531443541310675314435 +4132067531443541330675314435413406753144354135067531443541360675314435413706753144354138067531443541 +3906753144354141067531443541420675314435414306753144354144067531443541450675314435414606753144354230 +0675314435423106753144354232067531443542330675314435423406753144354235067531443542360675314435423706 +7531443542380675314435423906753144354241067531443542420675314435424306753144354244067531443542450675 +3144354246067531443543300675314435433106753144354332067531443543330675314435433406753144354335067531 +4435433606753144354337067531443543380675314435433906753144354341067531443543420675314435434306753144 +3543440675314435434506753144354346067531443544300675314435443106753144354432067531443544330675314437 +4438067531443744390675314437444106753144374442067531443744430675314437444406753144374445067531443744 +4606753144374530067531443745310675314437453206753144374533067531443745340675314437453506753144374536 +0675314437453706753144374538067531443745390675314437454106753144374542067531454530300675314545303106 +7531454530320675314545303306753145453035067531454530360675314545303706753145453038067531454530390675 +3145453041067531454530420675314545304306753145453044067531454530450675314545304606753145453130067531 +4545313106753145453132067531454531330675314545313406753145453135067531454531360675314545313706753145 +4531380675314545313906753145453141067531454531420675314545314306753145453144067531454531450675314545 +3146067531454532310675314545323206753145453234067531454532370675314545323906753145453241067531454532 +4206753145453243067531454532440675314545324506753145453246067531454533300675314545333106753145453332 +0675314545333406753145453335067531454533360675314545333706753145453339067531454533420675314545363106 +7531454536320675314545363406753145453637067531454536380675314545363906753145453641067531454536430675 +3145453644067531454536450675314545364606753145453730067531454537310675314545373206753145453734067531 +4545373506753145453736067531454537370675314545373906753145453741067531454537420675314545374306753145 +4537450675314630333006753146303331067531463033320675314630333306753146303334067531463033350675314630 +3336067531463033370675314630333806753146303339067531463033410675314630334206753146303343067531463033 +4406753146303345067531463033460675314630343006753146303431067531463034320675314630343306753146303434 +0675314630343506753146303436067531463034370675314630343806753146303439067531463034410675314630344206 +7531463034430675314630344406753146303445067531463034460675314630353006753146303531067531463035320675 +3146303533067531463035340675314630353506753146303536067531463035370675314630353806753146303539067531 +4630354106753146303542067531463035430675314630354406753146303545067531463035460675314630363006753146 +3036310675314630363206753146303633067531463036340675314630363506753146303636067531463036370675314630 +3638067531463036390675314630364106753146303642067531463036430675314630364406753146303645067531463036 +4606753146303730067531463037310675314630373206753146303733067531463037340675314630373506753146303736 +0675314630373706753146303738067531463037390675314630374106753146303742067531463037430675314630374406 +7531463037450675314630374606753146303830067531463038310675314630383206753146303833067531463038340675 +3146303835067531463038360675314630383706753146303838067531463038390675314630384106753146303842067531 +4630384306753146303844067531463038450675314630384606753146303930067531463039310675314630393206753146 +3039330675314630413006753146304131067531463041320675314630413306753146304134067531463041350675314630 +4136067531463041370675314630413806753146304139067531463041410675314630414206753146304143067531463041 +4406753146304145067531463042310675314630423206753146304233067531463042340675314630423506753146304236 +0675314630423706753146304238067531463042390675314630424106753146304242067531463042430675314630424406 +7531463042450675314630433106753146304332067531463043330675314630433406753146304335067531463043360675 +3146304337067531463043380675314630433906753146304341067531463043420675314630434306753146304344067531 +4630434506753146304346067531463044310675314630443206753146304433067531463044340675314630443506753146 +3044360675314630443706753146304438067531463044390675314630444106753146304442067531463044430675314630 +4444067531463044450675314630444606753146343244067531463432450675314634333106753146343335067531463630 +3006753146363031067531463630320675314636303306753146363034067531463630350675314636303606753146363037 +0675314636303806753146363039067531463630410675314636304206753146363043067531463630440675314636304506 +7531463630460000b8028040fffbfe03fa1403f92503f83203f79603f60e03f5fe03f4fe03f32503f20e03f19603f02503ef +8a4105effe03ee9603ed9603ecfa03ebfa03eafe03e93a03e84203e7fe03e63203e5e45305e59603e48a4105e45303e3e22f +05e3fa03e22f03e1fe03e0fe03df3203de1403dd9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d67d03d5d44705d57d +03d44703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e05ccfe03cb1e03ca3203c9fe03c6851105c6 +1c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe03bcfe03bbfe03ba1103b9862505b9fe03b8b7bb05b8 +fe03b7b65d05b7bb03b78004b6b52505b65d40ff03b64004b52503b4fe03b39603b2fe03b1fe03b0fe03affe03ae6403ad0e +03acab2505ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03a51203a4fe03a3a20e05a33203a20e03 +a16403a08a4105a096039ffe039e9d0c059efe039d0c039c9b19059c64039b9a10059b19039a1003990a0398fe0397960d05 +97fe03960d03958a410595960394930e05942803930e0392fa039190bb0591fe03908f5d0590bb039080048f8e25058f5d03 +8f40048e25038dfe038c8b2e058cfe038b2e038a8625058a410389880b05891403880b038786250587640386851105862503 +85110384fe038382110583fe0382110381fe0380fe037ffe0340ff7e7d7d057efe037d7d037c64037b5415057b25037afe03 +79fe03780e03770c03760a0375fe0374fa0373fa0372fa0371fa0370fe036ffe036efe036c21036bfe036a1142056a530369 +fe03687d036711420566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035dfe035bfe035afe0359580a0559fa0358 +0a035716190557320356fe035554150555420354150353011005531803521403514a130551fe03500b034ffe034e4d10054e +fe034d10034cfe034b4a13054bfe034a4910054a1303491d0d05491003480d0347fe0346960345960344fe0343022d0543fa +0342bb03414b0340fe033ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff0f033b0d033afe0339fe033837140538 +fa033736100537140336350b05361003350b03341e03330d0332310b0532fe03310b03302f0b05300d032f0b032e2d09052e +10032d09032c32032b2a25052b64032a2912052a25032912032827250528410327250326250b05260f03250b0324fe0323fe +03220f03210110052112032064031ffa031e1d0d051e64031d0d031c1142051cfe031bfa031a42031911420519fe03186403 +1716190517fe031601100516190315fe0314fe0313fe031211420512fe0311022d05114203107d030f64030efe030d0c1605 +0dfe030c0110050c16030bfe030a100309fe0308022d0508fe030714030664030401100504fe03401503022d0503fe030201 +1005022d0301100300fe0301b80164858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00>]def + FontName currentdict end definefont pop end %%EndProlog mpldict begin -18 180 translate -576 432 0 0 clipbox +0 0 translate +0 0 576 432 rectclip gsave 0 0 m 576 0 l 576 432 l 0 432 l cl -1.000 setgray +1 setgray fill grestore -0.000 setgray -/DejaVuSans 27.000 selectfont +0 setgray +/Cmr10-0 16.000 selectfont +gsave + +195.836 362.531 translate +0 rotate +0 0 m /T glyphshow +11.5625 0 m /h glyphshow +20.4531 0 m /e glyphshow +27.5625 0 m /r glyphshow +33.8281 0 m /e glyphshow +40.9375 0 m /space glyphshow +46.2656 0 m /a glyphshow +54.2656 0 m /r glyphshow +60.5312 0 m /e glyphshow +67.6406 0 m /space glyphshow +72.9688 0 m /b glyphshow +81.8594 0 m /a glyphshow +89.8594 0 m /s glyphshow +96.1719 0 m /i glyphshow +100.609 0 m /c glyphshow +107.719 0 m /space glyphshow +113.047 0 m /c glyphshow +120.156 0 m /h glyphshow +129.047 0 m /a glyphshow +137.047 0 m /r glyphshow +143.312 0 m /a glyphshow +151.312 0 m /c glyphshow +158.422 0 m /t glyphshow +164.641 0 m /e glyphshow +171.75 0 m /r glyphshow +178.016 0 m /s glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +34.5859 347.781 translate +0 rotate +0 0 m /A glyphshow +12 0 m /B glyphshow +23.3281 0 m /C glyphshow +34.8906 0 m /D glyphshow +47.1094 0 m /E glyphshow +58 0 m /F glyphshow +68.4375 0 m /G glyphshow +80.9844 0 m /H glyphshow +92.9844 0 m /I glyphshow +98.7656 0 m /J glyphshow +106.984 0 m /K glyphshow +119.422 0 m /L glyphshow +129.422 0 m /M glyphshow +144.078 0 m /N glyphshow +156.078 0 m /O glyphshow +168.516 0 m /P glyphshow +179.406 0 m /Q glyphshow +191.844 0 m /R glyphshow +203.625 0 m /S glyphshow +212.516 0 m /T glyphshow +224.078 0 m /U glyphshow +236.078 0 m /V glyphshow +248.078 0 m /W glyphshow +264.516 0 m /X glyphshow +276.516 0 m /Y glyphshow +288.516 0 m /Z glyphshow +298.297 0 m /space glyphshow +303.625 0 m /a glyphshow +311.625 0 m /b glyphshow +320.516 0 m /c glyphshow +327.625 0 m /d glyphshow +336.516 0 m /e glyphshow +343.625 0 m /f glyphshow +348.516 0 m /g glyphshow +356.516 0 m /h glyphshow +365.406 0 m /i glyphshow +369.844 0 m /j glyphshow +374.734 0 m /k glyphshow +383.172 0 m /l glyphshow +387.609 0 m /m glyphshow +400.938 0 m /n glyphshow +409.828 0 m /o glyphshow +417.828 0 m /p glyphshow +426.719 0 m /q glyphshow +435.156 0 m /r glyphshow +441.422 0 m /s glyphshow +447.734 0 m /t glyphshow +453.953 0 m /u glyphshow +462.844 0 m /v glyphshow +471.281 0 m /w glyphshow +482.844 0 m /x glyphshow +491.281 0 m /y glyphshow +499.719 0 m /z glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +122.281 332.484 translate +0 rotate +0 0 m /zero glyphshow +8 0 m /one glyphshow +16 0 m /two glyphshow +24 0 m /three glyphshow +32 0 m /four glyphshow +40 0 m /five glyphshow +48 0 m /six glyphshow +56 0 m /seven glyphshow +64 0 m /eight glyphshow +72 0 m /nine glyphshow +80 0 m /space glyphshow +85.3281 0 m /exclam glyphshow +89.7656 0 m /quotedblright glyphshow +97.7656 0 m /numbersign glyphshow +111.094 0 m /dollar glyphshow +119.094 0 m /percent glyphshow +132.422 0 m /ampersand glyphshow +144.859 0 m /quoteright glyphshow +149.297 0 m /parenleft glyphshow +155.516 0 m /parenright glyphshow +161.734 0 m /asterisk glyphshow +169.734 0 m /plus glyphshow +182.172 0 m /comma glyphshow +186.609 0 m /hyphen glyphshow +191.938 0 m /period glyphshow +196.375 0 m /slash glyphshow +204.375 0 m /colon glyphshow +208.812 0 m /semicolon glyphshow +213.25 0 m /exclamdown glyphshow +217.688 0 m /equal glyphshow +230.125 0 m /questiondown glyphshow +237.688 0 m /question glyphshow +245.25 0 m /at glyphshow +257.688 0 m /bracketleft glyphshow +262.125 0 m /quotedblleft glyphshow +270.125 0 m /bracketright glyphshow +274.562 0 m /circumflex glyphshow +282.562 0 m /dotaccent glyphshow +287 0 m /quoteleft glyphshow +291.438 0 m /emdash glyphshow +299.438 0 m /endash glyphshow +315.438 0 m /hungarumlaut glyphshow +323.438 0 m /tilde glyphshow +grestore +/Cmr10-0 16.000 selectfont +gsave + +203.922 317.203 translate +0 rotate +0 0 m /a glyphshow +8 0 m /n glyphshow +16.8906 0 m /d glyphshow +25.7812 0 m /space glyphshow +31.1094 0 m /a glyphshow +39.1094 0 m /c glyphshow +46.2188 0 m /c glyphshow +53.3281 0 m /e glyphshow +60.4375 0 m /n glyphshow +69.3281 0 m /t glyphshow +75.5469 0 m /e glyphshow +82.6562 0 m /d glyphshow +91.5469 0 m /space glyphshow +96.875 0 m /c glyphshow +103.984 0 m /h glyphshow +112.875 0 m /a glyphshow +120.875 0 m /r glyphshow +127.141 0 m /a glyphshow +135.141 0 m /c glyphshow +142.25 0 m /t glyphshow +148.469 0 m /e glyphshow +155.578 0 m /r glyphshow +161.844 0 m /s glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +144.602 299.047 translate +0 rotate +0 0 m /Aring glyphshow +10.9531 0 m /AE glyphshow +26.5469 0 m /Ccedilla glyphshow +37.7188 0 m /Egrave glyphshow +47.8281 0 m /Eacute glyphshow +57.9375 0 m /Ecircumflex glyphshow +68.0469 0 m /Edieresis glyphshow +78.1562 0 m /Igrave glyphshow +82.875 0 m /Iacute glyphshow +87.5938 0 m /Icircumflex glyphshow +92.3125 0 m /Idieresis glyphshow +97.0312 0 m /Eth glyphshow +109.438 0 m /Ntilde glyphshow +121.406 0 m /Ograve glyphshow +134 0 m /Oacute glyphshow +146.594 0 m /Ocircumflex glyphshow +159.188 0 m /Otilde glyphshow +171.781 0 m /Odieresis glyphshow +184.375 0 m /multiply glyphshow +197.781 0 m /Oslash glyphshow +210.375 0 m /Ugrave glyphshow +222.094 0 m /Uacute glyphshow +233.812 0 m /Ucircumflex glyphshow +245.531 0 m /Udieresis glyphshow +257.25 0 m /Yacute glyphshow +267.031 0 m /Thorn glyphshow +276.719 0 m /germandbls glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +136.82 281.703 translate +0 rotate +0 0 m /agrave glyphshow +9.8125 0 m /aacute glyphshow +19.625 0 m /acircumflex glyphshow +29.4375 0 m /atilde glyphshow +39.25 0 m /adieresis glyphshow +49.0625 0 m /aring glyphshow +58.875 0 m /ae glyphshow +74.5938 0 m /ccedilla glyphshow +83.3906 0 m /egrave glyphshow +93.2344 0 m /eacute glyphshow +103.078 0 m /ecircumflex glyphshow +112.922 0 m /edieresis glyphshow +122.766 0 m /igrave glyphshow +127.219 0 m /iacute glyphshow +131.672 0 m /icircumflex glyphshow +136.125 0 m /idieresis glyphshow +140.578 0 m /eth glyphshow +150.375 0 m /ntilde glyphshow +160.516 0 m /ograve glyphshow +170.312 0 m /oacute glyphshow +180.109 0 m /ocircumflex glyphshow +189.906 0 m /otilde glyphshow +199.703 0 m /odieresis glyphshow +209.5 0 m /divide glyphshow +222.906 0 m /oslash glyphshow +232.703 0 m /ugrave glyphshow +242.844 0 m /uacute glyphshow +252.984 0 m /ucircumflex glyphshow +263.125 0 m /udieresis glyphshow +273.266 0 m /yacute glyphshow +282.734 0 m /thorn glyphshow +292.891 0 m /ydieresis glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +121.945 263.234 translate +0 rotate +0 0 m /Amacron glyphshow +10.9531 0 m /amacron glyphshow +20.7656 0 m /Abreve glyphshow +31.7188 0 m /abreve glyphshow +41.5312 0 m /Aogonek glyphshow +52.4844 0 m /aogonek glyphshow +62.2969 0 m /Cacute glyphshow +73.4688 0 m /cacute glyphshow +82.2656 0 m /Ccircumflex glyphshow +93.4375 0 m /ccircumflex glyphshow +102.234 0 m /Cdotaccent glyphshow +113.406 0 m /cdotaccent glyphshow +122.203 0 m /Ccaron glyphshow +133.375 0 m /ccaron glyphshow +142.172 0 m /Dcaron glyphshow +154.5 0 m /dcaron glyphshow +164.656 0 m /Dcroat glyphshow +177.062 0 m /dcroat glyphshow +187.219 0 m /Emacron glyphshow +197.328 0 m /emacron glyphshow +207.172 0 m /Ebreve glyphshow +217.281 0 m /ebreve glyphshow +227.125 0 m /Edotaccent glyphshow +237.234 0 m /edotaccent glyphshow +247.078 0 m /Eogonek glyphshow +257.188 0 m /eogonek glyphshow +267.031 0 m /Ecaron glyphshow +277.141 0 m /ecaron glyphshow +286.984 0 m /Gcircumflex glyphshow +299.391 0 m /gcircumflex glyphshow +309.547 0 m /Gbreve glyphshow +321.953 0 m /gbreve glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +164.969 245.047 translate +0 rotate +0 0 m /Gdotaccent glyphshow +12.4062 0 m /gdotaccent glyphshow +22.5625 0 m /Gcommaaccent glyphshow +34.9688 0 m /gcommaaccent glyphshow +45.125 0 m /Hcircumflex glyphshow +57.1562 0 m /hcircumflex glyphshow +67.2969 0 m /Hbar glyphshow +81.9531 0 m /hbar glyphshow +93.0781 0 m /Itilde glyphshow +97.7969 0 m /itilde glyphshow +102.25 0 m /Imacron glyphshow +106.969 0 m /imacron glyphshow +111.422 0 m /Ibreve glyphshow +116.141 0 m /ibreve glyphshow +120.594 0 m /Iogonek glyphshow +125.312 0 m /iogonek glyphshow +129.766 0 m /Idotaccent glyphshow +134.484 0 m /dotlessi glyphshow +138.938 0 m /IJ glyphshow +148.375 0 m /ij glyphshow +157.266 0 m /Jcircumflex glyphshow +161.984 0 m /jcircumflex glyphshow +166.438 0 m /Kcommaaccent glyphshow +176.938 0 m /kcommaaccent glyphshow +186.203 0 m /kgreenlandic glyphshow +195.469 0 m /Lacute glyphshow +204.391 0 m /lacute glyphshow +208.844 0 m /Lcommaaccent glyphshow +217.766 0 m /lcommaaccent glyphshow +222.219 0 m /Lcaron glyphshow +231.141 0 m /lcaron glyphshow +237.141 0 m /Ldot glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +123.125 226.188 translate +0 rotate +0 0 m /ldot glyphshow +5.46875 0 m /Lslash glyphshow +14.4688 0 m /lslash glyphshow +19.0156 0 m /Nacute glyphshow +30.9844 0 m /nacute glyphshow +41.125 0 m /Ncommaaccent glyphshow +53.0938 0 m /ncommaaccent glyphshow +63.2344 0 m /Ncaron glyphshow +75.2031 0 m /ncaron glyphshow +85.3438 0 m /napostrophe glyphshow +98.3594 0 m /Eng glyphshow +110.328 0 m /eng glyphshow +120.469 0 m /Omacron glyphshow +133.062 0 m /omacron glyphshow +142.859 0 m /Obreve glyphshow +155.453 0 m /obreve glyphshow +165.25 0 m /Ohungarumlaut glyphshow +177.844 0 m /ohungarumlaut glyphshow +187.641 0 m /OE glyphshow +204.766 0 m /oe glyphshow +221.141 0 m /Racute glyphshow +232.266 0 m /racute glyphshow +238.844 0 m /Rcommaaccent glyphshow +249.969 0 m /rcommaaccent glyphshow +256.547 0 m /Rcaron glyphshow +267.672 0 m /rcaron glyphshow +274.25 0 m /Sacute glyphshow +284.406 0 m /sacute glyphshow +292.75 0 m /Scircumflex glyphshow +302.906 0 m /scircumflex glyphshow +311.25 0 m /Scedilla glyphshow +321.406 0 m /scedilla glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +128.219 207.516 translate +0 rotate +0 0 m /Scaron glyphshow +10.1562 0 m /scaron glyphshow +18.5 0 m /Tcommaaccent glyphshow +28.2812 0 m /tcommaaccent glyphshow +34.5625 0 m /Tcaron glyphshow +44.3438 0 m /tcaron glyphshow +50.625 0 m /Tbar glyphshow +60.4062 0 m /tbar glyphshow +66.6875 0 m /Utilde glyphshow +78.4062 0 m /utilde glyphshow +88.5469 0 m /Umacron glyphshow +100.266 0 m /umacron glyphshow +110.406 0 m /Ubreve glyphshow +122.125 0 m /ubreve glyphshow +132.266 0 m /Uring glyphshow +143.984 0 m /uring glyphshow +154.125 0 m /Uhungarumlaut glyphshow +165.844 0 m /uhungarumlaut glyphshow +175.984 0 m /Uogonek glyphshow +187.703 0 m /uogonek glyphshow +197.844 0 m /Wcircumflex glyphshow +213.672 0 m /wcircumflex glyphshow +226.766 0 m /Ycircumflex glyphshow +236.547 0 m /ycircumflex glyphshow +246.016 0 m /Ydieresis glyphshow +255.797 0 m /Zacute glyphshow +266.766 0 m /zacute glyphshow +275.172 0 m /Zdotaccent glyphshow +286.141 0 m /zdotaccent glyphshow +294.547 0 m /Zcaron glyphshow +305.516 0 m /zcaron glyphshow +313.922 0 m /longs glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +120.906 189.406 translate +0 rotate +0 0 m /uni0180 glyphshow +10.1562 0 m /uni0181 glyphshow +21.9219 0 m /uni0182 glyphshow +32.9062 0 m /uni0183 glyphshow +43.0625 0 m /uni0184 glyphshow +54.0469 0 m /uni0185 glyphshow +64.2031 0 m /uni0186 glyphshow +75.4531 0 m /uni0187 glyphshow +86.625 0 m /uni0188 glyphshow +95.4219 0 m /uni0189 glyphshow +107.828 0 m /uni018A glyphshow +120.938 0 m /uni018B glyphshow +131.922 0 m /uni018C glyphshow +142.078 0 m /uni018D glyphshow +151.875 0 m /uni018E glyphshow +161.984 0 m /uni018F glyphshow +174.578 0 m /uni0190 glyphshow +184.406 0 m /uni0191 glyphshow +193.609 0 m /florin glyphshow +199.25 0 m /uni0193 glyphshow +211.656 0 m /uni0194 glyphshow +222.641 0 m /uni0195 glyphshow +238.391 0 m /uni0196 glyphshow +244.047 0 m /uni0197 glyphshow +248.766 0 m /uni0198 glyphshow +260.703 0 m /uni0199 glyphshow +269.969 0 m /uni019A glyphshow +274.422 0 m /uni019B glyphshow +283.891 0 m /uni019C glyphshow +299.484 0 m /uni019D glyphshow +311.453 0 m /uni019E glyphshow +321.594 0 m /uni019F glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +124.211 173.891 translate +0 rotate +0 0 m /Ohorn glyphshow +14.6094 0 m /ohorn glyphshow +24.4062 0 m /uni01A2 glyphshow +39.5938 0 m /uni01A3 glyphshow +51.75 0 m /uni01A4 glyphshow +62.1875 0 m /uni01A5 glyphshow +72.3438 0 m /uni01A6 glyphshow +83.4688 0 m /uni01A7 glyphshow +93.625 0 m /uni01A8 glyphshow +101.969 0 m /uni01A9 glyphshow +112.078 0 m /uni01AA glyphshow +117.453 0 m /uni01AB glyphshow +123.734 0 m /uni01AC glyphshow +133.516 0 m /uni01AD glyphshow +139.797 0 m /uni01AE glyphshow +149.578 0 m /Uhorn glyphshow +163.312 0 m /uhorn glyphshow +173.453 0 m /uni01B1 glyphshow +185.688 0 m /uni01B2 glyphshow +197.219 0 m /uni01B3 glyphshow +209.125 0 m /uni01B4 glyphshow +220.812 0 m /uni01B5 glyphshow +231.781 0 m /uni01B6 glyphshow +240.188 0 m /uni01B7 glyphshow +250.844 0 m /uni01B8 glyphshow +261.5 0 m /uni01B9 glyphshow +270.75 0 m /uni01BA glyphshow +279.156 0 m /uni01BB glyphshow +289.344 0 m /uni01BC glyphshow +300 0 m /uni01BD glyphshow +309.25 0 m /uni01BE glyphshow +317.422 0 m /uni01BF glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +110.68 153.734 translate +0 rotate +0 0 m /uni01C0 glyphshow +4.71875 0 m /uni01C1 glyphshow +12.5938 0 m /uni01C2 glyphshow +19.9375 0 m /uni01C3 glyphshow +24.6719 0 m /uni01C4 glyphshow +47.4219 0 m /uni01C5 glyphshow +68.2031 0 m /uni01C6 glyphshow +86.6719 0 m /uni01C7 glyphshow +100.047 0 m /uni01C8 glyphshow +112.641 0 m /uni01C9 glyphshow +119.953 0 m /uni01CA glyphshow +134.859 0 m /uni01CB glyphshow +149.641 0 m /uni01CC glyphshow +162.406 0 m /uni01CD glyphshow +173.359 0 m /uni01CE glyphshow +183.172 0 m /uni01CF glyphshow +187.891 0 m /uni01D0 glyphshow +192.344 0 m /uni01D1 glyphshow +204.938 0 m /uni01D2 glyphshow +214.734 0 m /uni01D3 glyphshow +226.453 0 m /uni01D4 glyphshow +236.594 0 m /uni01D5 glyphshow +248.312 0 m /uni01D6 glyphshow +258.453 0 m /uni01D7 glyphshow +270.172 0 m /uni01D8 glyphshow +280.312 0 m /uni01D9 glyphshow +292.031 0 m /uni01DA glyphshow +302.172 0 m /uni01DB glyphshow +313.891 0 m /uni01DC glyphshow +324.031 0 m /uni01DD glyphshow +333.875 0 m /uni01DE glyphshow +344.828 0 m /uni01DF glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +90.0078 134 translate +0 rotate +0 0 m /uni01E0 glyphshow +10.9531 0 m /uni01E1 glyphshow +20.7656 0 m /uni01E2 glyphshow +36.3594 0 m /uni01E3 glyphshow +52.0781 0 m /uni01E4 glyphshow +64.4844 0 m /uni01E5 glyphshow +74.6406 0 m /Gcaron glyphshow +87.0469 0 m /gcaron glyphshow +97.2031 0 m /uni01E8 glyphshow +107.703 0 m /uni01E9 glyphshow +116.969 0 m /uni01EA glyphshow +129.562 0 m /uni01EB glyphshow +139.359 0 m /uni01EC glyphshow +151.953 0 m /uni01ED glyphshow +161.75 0 m /uni01EE glyphshow +172.406 0 m /uni01EF glyphshow +181.656 0 m /uni01F0 glyphshow +186.109 0 m /uni01F1 glyphshow +208.859 0 m /uni01F2 glyphshow +229.641 0 m /uni01F3 glyphshow +248.109 0 m /uni01F4 glyphshow +260.516 0 m /uni01F5 glyphshow +270.672 0 m /uni01F6 glyphshow +288.484 0 m /uni01F7 glyphshow +299.406 0 m /uni01F8 glyphshow +311.375 0 m /uni01F9 glyphshow +321.516 0 m /Aringacute glyphshow +332.469 0 m /aringacute glyphshow +342.281 0 m /AEacute glyphshow +357.875 0 m /aeacute glyphshow +373.594 0 m /Oslashacute glyphshow +386.188 0 m /oslashacute glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +138.602 115.719 translate +0 rotate +0 0 m /uni0200 glyphshow +10.9531 0 m /uni0201 glyphshow +20.7656 0 m /uni0202 glyphshow +31.7188 0 m /uni0203 glyphshow +41.5312 0 m /uni0204 glyphshow +51.6406 0 m /uni0205 glyphshow +61.4844 0 m /uni0206 glyphshow +71.5938 0 m /uni0207 glyphshow +81.4375 0 m /uni0208 glyphshow +86.1562 0 m /uni0209 glyphshow +90.6094 0 m /uni020A glyphshow +95.3281 0 m /uni020B glyphshow +99.7812 0 m /uni020C glyphshow +112.375 0 m /uni020D glyphshow +122.172 0 m /uni020E glyphshow +134.766 0 m /uni020F glyphshow +144.562 0 m /uni0210 glyphshow +155.688 0 m /uni0211 glyphshow +162.266 0 m /uni0212 glyphshow +173.391 0 m /uni0213 glyphshow +179.969 0 m /uni0214 glyphshow +191.688 0 m /uni0215 glyphshow +201.828 0 m /uni0216 glyphshow +213.547 0 m /uni0217 glyphshow +223.688 0 m /Scommaaccent glyphshow +233.844 0 m /scommaaccent glyphshow +242.188 0 m /uni021A glyphshow +251.969 0 m /uni021B glyphshow +258.25 0 m /uni021C glyphshow +268.281 0 m /uni021D glyphshow +276.625 0 m /uni021E glyphshow +288.656 0 m /uni021F glyphshow +grestore +/DejaVuSans-0 16.000 selectfont +gsave + +118.953 95.4688 translate +0 rotate +0 0 m /uni0220 glyphshow +11.7656 0 m /uni0221 glyphshow +25.1719 0 m /uni0222 glyphshow +36.3438 0 m /uni0223 glyphshow +46.1094 0 m /uni0224 glyphshow +57.0781 0 m /uni0225 glyphshow +65.4844 0 m /uni0226 glyphshow +76.4375 0 m /uni0227 glyphshow +86.25 0 m /uni0228 glyphshow +96.3594 0 m /uni0229 glyphshow +106.203 0 m /uni022A glyphshow +118.797 0 m /uni022B glyphshow +128.594 0 m /uni022C glyphshow +141.188 0 m /uni022D glyphshow +150.984 0 m /uni022E glyphshow +163.578 0 m /uni022F glyphshow +173.375 0 m /uni0230 glyphshow +185.969 0 m /uni0231 glyphshow +195.766 0 m /uni0232 glyphshow +205.547 0 m /uni0233 glyphshow +215.016 0 m /uni0234 glyphshow +222.609 0 m /uni0235 glyphshow +236.094 0 m /uni0236 glyphshow +243.734 0 m /dotlessj glyphshow +248.188 0 m /uni0238 glyphshow +264.156 0 m /uni0239 glyphshow +280.125 0 m /uni023A glyphshow +291.078 0 m /uni023B glyphshow +302.25 0 m /uni023C glyphshow +311.047 0 m /uni023D glyphshow +319.969 0 m /uni023E glyphshow +329.75 0 m /uni023F glyphshow +grestore +/DejaVuSans-0 16.000 selectfont gsave -86.4 205.2 translate +79.4297 76.8125 translate 0 rotate -0.000000 0 m /T glyphshow -16.492676 0 m /h glyphshow -33.604980 0 m /e glyphshow -50.216309 0 m /r glyphshow -61.316895 0 m /e glyphshow -77.928223 0 m /space glyphshow -86.510742 0 m /a glyphshow -103.056152 0 m /r glyphshow -114.156738 0 m /e glyphshow -130.768066 0 m /space glyphshow +0 0 m /uni0240 glyphshow +8.40625 0 m /uni0241 glyphshow +18.0625 0 m /uni0242 glyphshow +25.7344 0 m /uni0243 glyphshow +36.7188 0 m /uni0244 glyphshow +48.4375 0 m /uni0245 glyphshow +59.3906 0 m /uni0246 glyphshow +69.5 0 m /uni0247 glyphshow +79.3438 0 m /uni0248 glyphshow +84.0625 0 m /uni0249 glyphshow +88.5156 0 m /uni024A glyphshow +101.016 0 m /uni024B glyphshow +111.172 0 m /uni024C glyphshow +122.297 0 m /uni024D glyphshow +128.875 0 m /uni024E glyphshow +138.656 0 m /uni024F glyphshow grestore -/WenQuanYiZenHei 27.000 selectfont +/DejaVuSans-1 16.000 selectfont gsave -86.4 205.2 translate +79.4297 76.8125 translate 0 rotate -139.350586 0 m /uni51E0 glyphshow -166.350586 0 m /uni4E2A glyphshow -193.350586 0 m /uni6C49 glyphshow -220.350586 0 m /uni5B57 glyphshow +148.125 0 m /u1F600 glyphshow +164.812 0 m /u1F601 glyphshow +181.5 0 m /u1F602 glyphshow +200.203 0 m /u1F603 glyphshow +216.891 0 m /u1F604 glyphshow +233.578 0 m /u1F605 glyphshow +250.266 0 m /u1F606 glyphshow +266.953 0 m /u1F607 glyphshow +283.641 0 m /u1F608 glyphshow +300.328 0 m /u1F609 glyphshow +317.016 0 m /u1F60A glyphshow +333.703 0 m /u1F60B glyphshow +350.391 0 m /u1F60C glyphshow +367.078 0 m /u1F60D glyphshow +383.766 0 m /u1F60E glyphshow +400.453 0 m /u1F60F glyphshow grestore -/DejaVuSans 27.000 selectfont +/Cmr10-0 16.000 selectfont gsave -86.4 205.2 translate +248.008 61.4844 translate 0 rotate -247.350586 0 m /space glyphshow -255.933105 0 m /i glyphshow -263.434570 0 m /n glyphshow -280.546875 0 m /space glyphshow -289.129395 0 m /b glyphshow -306.268066 0 m /e glyphshow -322.879395 0 m /t glyphshow -333.465820 0 m /w glyphshow -355.548340 0 m /e glyphshow -372.159668 0 m /e glyphshow -388.770996 0 m /n glyphshow -405.883301 0 m /exclam glyphshow +0 0 m /i glyphshow +4.4375 0 m /n glyphshow +13.3281 0 m /space glyphshow +18.6562 0 m /b glyphshow +27.5469 0 m /e glyphshow +34.6562 0 m /t glyphshow +40.875 0 m /w glyphshow +52.4375 0 m /e glyphshow +59.5469 0 m /e glyphshow +66.6562 0 m /n glyphshow +75.5469 0 m /exclam glyphshow grestore end diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type3.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type3.eps new file mode 100644 index 000000000000..cc4bff1ec687 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type3.eps @@ -0,0 +1,1483 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 +%%Title: ttc_type3.eps +%%Creator: Matplotlib v3.11.0.dev2075+g5a13dc378e, https://matplotlib.org/ +%%CreationDate: Fri Mar 13 23:13:37 2026 +%%Orientation: portrait +%%BoundingBox: 0 0 504 72 +%%HiResBoundingBox: 0.000000 0.000000 504.000000 72.000000 +%%EndComments +%%BeginProlog +/mpldict 10 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHei-0 def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/W /e /n /Q /u /a /Y /i /space /Z /H /colon /A /B /C /D /E /F /G /I /J /K /L /M /N /O /P /R /S /T /U /V /X] def +/CharStrings 34 dict dup begin +/.notdef 0 def +/W{839 0 11 0 831 702 sc +831 702 m +663 0 l +574 0 l +422 545 l +279 0 l +189 0 l +11 702 l +101 702 l +242 133 l +244 133 l +392 702 l +463 702 l +623 133 l +625 133 l +755 702 l +831 702 l + +ce} _d +/e{552 0 38 -10 506 534 sc +506 254 m +128 254 l +127 187 143 136 174 101 c +200 72 236 57 283 57 c +341 57 384 82 411 133 c +415 140 419 149 422 158 c +498 142 l +479 82 439 38 378 11 c +346 -3 312 -10 276 -10 c +187 -10 121 27 78 100 c +51 145 38 199 38 260 c +38 346 64 415 116 468 c +159 512 213 534 279 534 c +369 534 434 495 473 417 c +495 374 506 323 506 266 c +506 254 l + +418 313 m +419 370 401 413 366 442 c +342 462 313 472 279 472 c +226 472 186 448 158 400 c +142 374 133 345 132 313 c +418 313 l + +ce} _d +/n{552 0 71 0 482 534 sc +482 0 m +402 0 l +402 322 l +402 366 398 396 391 412 c +390 414 389 416 388 418 c +373 444 348 459 313 464 c +308 465 304 465 299 465 c +234 465 190 432 166 366 c +156 337 151 306 151 272 c +151 0 l +71 0 l +71 520 l +146 520 l +146 424 l +148 424 l +173 477 211 511 263 526 c +278 531 294 534 310 534 c +375 534 423 508 455 457 c +458 453 l +474 425 482 377 482 309 c +482 0 l + +ce} _d +/Q{675 0 42 -167 626 713 sc +626 351 m +626 244 599 158 546 91 c +509 46 462 16 407 1 c +407 -14 l +407 -55 425 -80 461 -88 c +468 -90 476 -91 485 -91 c +500 -91 532 -89 580 -84 c +580 -154 l +553 -163 528 -167 504 -167 c +412 -167 357 -130 340 -55 c +337 -41 335 -26 335 -10 c +224 -7 142 41 91 135 c +58 195 42 265 42 346 c +42 456 69 545 124 613 c +177 680 247 713 334 713 c +441 713 520 666 572 572 c +608 509 626 436 626 351 c + +530 349 m +530 482 496 569 428 610 c +401 627 370 635 333 635 c +234 635 172 576 147 459 c +140 424 136 386 136 344 c +136 239 160 161 207 112 c +238 79 278 62 327 62 c +425 62 488 116 516 225 c +525 263 530 304 530 349 c + +ce} _d +/u{552 0 71 -10 479 520 sc +479 0 m +407 0 l +407 103 l +404 103 l +386 58 355 25 310 4 c +289 -5 268 -10 245 -10 c +144 -10 87 45 74 156 c +72 171 71 187 71 204 c +71 520 l +151 520 l +151 204 l +151 113 181 65 242 59 c +242 59 245 59 252 59 c +311 59 354 89 380 148 c +393 177 399 209 399 244 c +399 520 l +479 520 l +479 0 l + +ce} _d +/a{552 0 56 -10 509 534 sc +509 0 m +429 0 l +420 97 l +385 26 324 -10 238 -10 c +171 -10 120 11 87 53 c +66 79 56 110 56 147 c +56 198 78 240 122 273 c +131 281 142 287 153 292 c +198 313 272 324 375 323 c +420 323 l +420 345 l +420 409 397 448 352 463 c +316 470 l +307 471 298 471 288 471 c +205 471 158 440 145 379 c +71 391 l +80 458 124 502 205 522 c +233 530 263 534 295 534 c +384 534 442 510 469 463 c +488 431 498 380 498 311 c +498 110 l +498 63 502 26 509 0 c + +420 228 m +420 262 l +353 262 l +212 262 142 222 142 143 c +142 100 165 71 211 58 c +224 54 239 52 255 52 c +312 52 356 76 388 123 c +409 154 420 189 420 228 c + +ce} _d +/Y{573 0 11 0 568 702 sc +568 702 m +334 296 l +334 0 l +246 0 l +246 296 l +11 702 l +114 702 l +300 379 l +488 702 l +568 702 l + +ce} _d +/i{245 0 79 0 167 702 sc +167 612 m +79 612 l +79 702 l +167 702 l +167 612 l + +163 0 m +83 0 l +83 520 l +163 520 l +163 0 l + +ce} _d +/space{307 0 0 0 0 0 sc +ce} _d +/Z{552 0 17 0 513 702 sc +513 0 m +17 0 l +17 76 l +399 630 l +35 630 l +35 702 l +502 702 l +502 644 l +116 76 l +513 76 l +513 0 l + +ce} _d +/H{675 0 83 0 585 702 sc +585 0 m +497 0 l +497 334 l +171 334 l +171 0 l +83 0 l +83 702 l +171 702 l +171 411 l +497 411 l +497 702 l +585 702 l +585 0 l + +ce} _d +/colon{307 0 101 0 215 520 sc +215 404 m +101 404 l +101 520 l +215 520 l +215 404 l + +215 0 m +101 0 l +101 116 l +215 116 l +215 0 l + +ce} _d +/A{573 0 11 0 568 702 sc +568 0 m +478 0 l +408 205 l +147 205 l +85 0 l +11 0 l +239 702 l +340 702 l +568 0 l + +388 271 m +281 608 l +172 271 l +388 271 l + +ce} _d +/B{634 0 83 0 588 702 sc +588 194 m +588 130 565 80 520 45 c +484 16 434 1 370 0 c +347 0 l +83 0 l +83 702 l +348 702 l +405 702 448 694 477 679 c +482 676 487 673 493 670 c +541 637 565 591 566 531 c +566 470 542 424 494 394 c +462 380 l +456 377 449 375 442 374 c +442 372 l +501 359 543 327 568 276 c +581 251 588 224 588 194 c + +479 520 m +479 585 443 621 370 630 c +361 631 351 631 341 631 c +171 631 l +171 401 l +317 401 l +425 401 479 441 479 520 c + +500 198 m +500 241 485 276 454 303 c +453 304 451 306 449 307 c +426 325 389 334 338 334 c +171 334 l +171 75 l +343 75 l +432 75 483 105 497 166 c +499 175 500 186 500 198 c + +ce} _d +/C{634 0 43 -10 589 713 sc +589 219 m +558 90 490 16 386 -5 c +367 -8 348 -10 329 -10 c +224 -10 146 38 94 135 c +60 199 43 273 43 358 c +43 471 72 560 131 626 c +183 684 251 713 336 713 c +435 713 508 669 555 582 c +570 554 582 523 589 488 c +506 473 l +479 573 430 628 359 637 c +350 638 342 639 333 639 c +248 639 190 590 159 492 c +146 449 139 402 139 351 c +139 261 158 189 197 134 c +230 87 274 63 329 63 c +418 63 477 117 506 225 c +507 229 508 233 509 237 c +589 219 l + +ce} _d +/D{675 0 87 0 636 702 sc +636 353 m +636 247 607 160 550 93 c +496 31 425 0 338 0 c +87 0 l +87 702 l +309 702 l +394 702 462 682 512 642 c +525 631 538 618 551 603 c +608 537 636 454 636 353 c + +547 353 m +547 436 525 504 482 557 c +449 599 403 623 344 628 c +304 629 l +175 629 l +175 75 l +304 75 l +382 75 440 97 478 140 c +484 147 490 155 496 164 c +530 216 547 279 547 353 c + +ce} _d +/E{573 0 87 0 542 702 sc +542 0 m +87 0 l +87 702 l +531 702 l +531 627 l +175 627 l +175 403 l +458 403 l +458 333 l +175 333 l +175 76 l +542 76 l +542 0 l + +ce} _d +/F{491 0 84 0 514 702 sc +514 627 m +172 627 l +172 403 l +456 403 l +456 333 l +172 333 l +172 0 l +84 0 l +84 702 l +514 702 l +514 627 l + +ce} _d +/G{675 0 49 -10 614 713 sc +614 -5 m +560 -5 l +537 82 l +497 24 436 -7 355 -10 c +350 -10 346 -10 342 -10 c +237 -10 157 35 104 125 c +67 187 49 260 49 344 c +49 452 77 541 133 610 c +188 679 262 713 353 713 c +457 713 532 670 579 585 c +591 563 600 538 607 511 c +524 490 l +512 553 480 597 428 622 c +403 633 376 639 347 639 c +256 639 195 589 164 488 c +151 447 144 400 144 348 c +144 251 167 176 212 123 c +247 82 292 61 348 61 c +418 61 469 88 500 141 c +516 170 524 203 524 242 c +524 281 l +349 281 l +349 354 l +614 354 l +614 -5 l + +ce} _d +/I{266 0 88 0 176 702 sc +176 0 m +88 0 l +88 702 l +176 702 l +176 0 l + +ce} _d +/J{409 0 17 -10 331 702 sc +331 229 m +331 104 290 29 207 2 c +182 -6 153 -10 120 -10 c +86 -10 52 -5 17 4 c +17 76 l +50 69 81 66 108 66 c +171 66 211 83 227 118 c +238 139 243 176 243 229 c +243 702 l +331 702 l +331 229 l + +ce} _d +/K{634 0 88 0 643 702 sc +643 0 m +546 0 l +337 387 l +176 187 l +176 0 l +88 0 l +88 702 l +176 702 l +176 295 l +493 702 l +588 702 l +398 457 l +643 0 l + +ce} _d +/L{512 0 86 0 501 702 sc +501 0 m +86 0 l +86 702 l +174 702 l +174 78 l +501 78 l +501 0 l + +ce} _d +/M{839 0 82 0 761 702 sc +761 0 m +673 0 l +673 613 l +669 613 l +446 0 l +387 0 l +160 613 l +156 613 l +156 0 l +82 0 l +82 702 l +213 702 l +425 140 l +632 702 l +761 702 l +761 0 l + +ce} _d +/N{675 0 84 0 601 702 sc +601 0 m +515 0 l +158 612 l +158 0 l +84 0 l +84 702 l +195 702 l +527 130 l +527 702 l +601 702 l +601 0 l + +ce} _d +/O{675 0 45 -10 623 713 sc +623 359 m +623 258 598 173 548 102 c +495 27 424 -10 335 -10 c +230 -10 151 36 98 128 c +63 189 45 262 45 346 c +45 453 71 540 124 609 c +176 678 246 713 334 713 c +435 713 513 669 566 580 c +604 517 623 443 623 359 c + +530 354 m +530 449 509 522 468 575 c +435 618 392 640 337 640 c +250 640 191 591 159 492 c +144 448 137 399 137 345 c +137 258 157 187 197 133 c +232 85 278 61 335 61 c +417 61 474 108 507 203 c +522 248 530 299 530 354 c + +ce} _d +/P{573 0 66 0 542 702 sc +542 492 m +542 436 523 388 485 348 c +480 343 l +441 305 385 286 311 286 c +154 286 l +154 0 l +66 0 l +66 702 l +301 702 l +376 702 433 686 470 655 c +510 622 534 575 541 515 c +542 507 542 499 542 492 c + +453 492 m +453 557 425 600 370 620 c +351 626 331 629 308 629 c +154 629 l +154 358 l +302 358 l +375 358 422 384 442 435 c +449 452 453 471 453 492 c + +ce} _d +/R{634 0 83 0 588 702 sc +588 0 m +496 0 l +366 304 l +171 304 l +171 0 l +83 0 l +83 702 l +346 702 l +436 702 502 676 544 624 c +563 599 575 568 580 532 c +581 524 581 515 581 506 c +581 445 559 396 515 359 c +496 344 474 333 450 326 c +588 0 l + +493 507 m +493 577 454 616 377 625 c +367 626 357 627 346 627 c +171 627 l +171 376 l +336 376 l +422 376 472 406 487 467 c +491 479 493 492 493 507 c + +ce} _d +/S{634 0 43 -10 590 713 sc +590 201 m +590 114 550 53 469 17 c +428 -1 381 -10 328 -10 c +184 -10 89 56 43 189 c +122 207 l +143 134 191 89 266 72 c +286 67 307 65 330 65 c +398 65 447 83 476 120 c +491 139 499 162 499 189 c +499 237 469 273 408 296 c +343 314 l +264 334 221 345 214 348 c +179 359 153 373 135 389 c +97 423 78 466 78 519 c +78 599 115 655 189 688 c +227 705 269 713 316 713 c +413 713 485 678 534 608 c +554 571 l +559 562 563 552 566 541 c +486 519 l +477 565 448 600 398 624 c +371 637 343 643 314 643 c +265 643 226 629 195 601 c +174 582 164 558 164 531 c +164 485 193 451 250 430 c +264 425 308 414 383 397 c +450 382 497 363 524 340 c +568 305 590 258 590 201 c + +ce} _d +/T{512 0 11 0 499 702 sc +499 626 m +299 626 l +299 0 l +211 0 l +211 626 l +11 626 l +11 702 l +499 702 l +499 626 l + +ce} _d +/U{655 0 83 -10 567 702 sc +567 258 m +567 184 556 128 534 91 c +528 83 522 75 516 68 c +473 16 410 -10 327 -10 c +210 -10 135 32 103 117 c +90 154 83 201 83 258 c +83 702 l +171 702 l +171 258 l +171 179 187 126 218 99 c +243 78 282 68 334 68 c +417 68 468 103 485 174 c +491 197 494 225 494 258 c +494 702 l +567 702 l +567 258 l + +ce} _d +/V{573 0 10 0 569 702 sc +569 702 m +332 0 l +248 0 l +10 702 l +102 702 l +298 123 l +493 702 l +569 702 l + +ce} _d +/X{552 0 9 0 552 702 sc +552 0 m +453 0 l +275 299 l +91 0 l +9 0 l +234 364 l +34 702 l +132 702 l +287 442 l +443 702 l +524 702 l +328 381 l +552 0 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHeiMono-0 def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/W /e /n /Q /u /a /Y /i /space /Z /H /M /o /colon /A /B /C /D /E /F /G /I /J /K /L /N /O /P /R /S /T /U /V /X] def +/CharStrings 35 dict dup begin +/.notdef 0 def +/W{512 0 26 18 486 766 sc +157 141 m +159 141 l +214 664 l +306 664 l +361 141 l +364 141 l +410 766 l +486 766 l +425 18 l +313 18 l +257 551 l +255 551 l +199 18 l +87 18 l +26 766 l +111 766 l +157 141 l + +ce} _d +/e{512 0 56 8 445 561 sc +141 258 m +144 195 157 149 180 121 c +203 93 237 79 282 79 c +323 79 369 91 420 116 c +420 34 l +369 17 321 8 276 8 c +129 8 56 100 56 285 c +56 381 73 451 107 495 c +142 539 193 561 261 561 c +323 561 369 540 399 497 c +430 455 445 386 445 290 c +445 283 444 272 443 258 c +141 258 l + +141 326 m +365 326 l +364 435 330 490 261 490 c +222 490 193 478 174 453 c +155 429 144 387 141 326 c + +ce} _d +/n{512 0 72 18 451 561 sc +451 356 m +451 18 l +372 18 l +372 338 l +372 398 365 438 351 458 c +338 478 313 488 276 488 c +242 488 213 469 189 431 c +165 393 153 341 153 276 c +153 18 l +72 18 l +72 551 l +147 551 l +150 474 l +152 474 l +165 500 184 521 211 537 c +238 553 266 561 297 561 c +351 561 390 545 414 514 c +439 483 451 431 451 356 c + +ce} _d +/Q{512 0 41 -135 492 776 sc +93 689 m +128 747 183 776 256 776 c +329 776 383 747 418 689 c +453 632 471 533 471 392 c +471 206 437 89 369 40 c +369 38 l +397 23 422 1 443 -30 c +465 -61 481 -96 492 -135 c +401 -135 l +387 -82 369 -44 346 -23 c +323 -2 293 8 256 8 c +183 8 128 37 93 94 c +58 152 41 251 41 392 c +41 533 58 632 93 689 c + +183 108 m +202 91 226 82 256 82 c +286 82 310 91 328 108 c +347 125 361 157 372 203 c +383 250 389 313 389 392 c +389 471 383 534 372 580 c +361 627 347 659 328 676 c +310 693 286 702 256 702 c +226 702 202 693 183 676 c +165 659 150 627 139 580 c +128 534 123 471 123 392 c +123 313 128 250 139 203 c +150 157 165 125 183 108 c + +ce} _d +/u{512 0 67 8 435 551 sc +67 203 m +67 551 l +145 551 l +145 221 l +145 164 151 127 164 108 c +177 90 201 81 236 81 c +269 81 297 100 320 137 c +343 175 354 227 354 293 c +354 551 l +435 551 l +435 18 l +359 18 l +357 95 l +355 95 l +342 68 323 46 298 31 c +273 16 245 8 215 8 c +162 8 124 23 101 52 c +78 81 67 132 67 203 c + +ce} _d +/a{512 0 61 8 440 561 sc +261 561 m +330 561 377 547 402 520 c +427 493 440 442 440 367 c +440 18 l +367 18 l +365 95 l +362 95 l +330 37 279 8 210 8 c +165 8 129 22 102 50 c +75 79 61 118 61 167 c +61 229 82 277 124 310 c +166 344 229 361 312 361 c +361 361 l +361 387 l +361 426 353 453 338 469 c +323 485 298 493 261 493 c +238 493 210 489 175 480 c +140 472 111 463 87 452 c +87 525 l +111 536 140 544 174 551 c +208 558 237 561 261 561 c + +361 300 m +312 300 l +196 300 138 257 138 172 c +138 141 146 118 161 101 c +177 85 198 77 225 77 c +266 77 298 93 323 126 c +348 159 361 205 361 264 c +361 300 l + +ce} _d +/Y{512 0 31 18 481 766 sc +257 402 m +259 402 l +394 766 l +481 766 l +298 315 l +298 18 l +214 18 l +214 315 l +31 766 l +123 766 l +257 402 l + +ce} _d +/i{512 0 97 18 435 797 sc +324 551 m +324 88 l +435 88 l +435 18 l +97 18 l +97 88 l +240 88 l +240 481 l +128 481 l +128 551 l +324 551 l + +219 674 m +219 797 l +324 797 l +324 674 l +219 674 l + +ce} _d +/space{512 0 0 0 0 0 sc +ce} _d +/Z{512 0 77 18 435 766 sc +343 692 m +343 694 l +77 694 l +77 766 l +435 766 l +435 694 l +169 92 l +169 90 l +435 90 l +435 18 l +77 18 l +77 90 l +343 692 l + +ce} _d +/H{512 0 61 18 451 766 sc +143 766 m +143 461 l +365 461 l +365 766 l +451 766 l +451 18 l +365 18 l +365 387 l +143 387 l +143 18 l +61 18 l +61 766 l +143 766 l + +ce} _d +/M{512 0 41 18 471 766 sc +387 571 m +385 571 l +295 223 l +213 223 l +123 571 l +121 571 l +121 18 l +41 18 l +41 766 l +135 766 l +257 305 l +259 305 l +381 766 l +471 766 l +471 18 l +387 18 l +387 571 l + +ce} _d +/o{512 0 51 8 461 561 sc +51 284 m +51 469 119 561 256 561 c +393 561 461 469 461 284 c +461 100 393 8 256 8 c +119 8 51 100 51 284 c + +164 125 m +184 94 215 79 256 79 c +297 79 328 94 347 125 c +367 156 377 209 377 284 c +377 359 367 412 347 443 c +328 474 297 490 256 490 c +215 490 184 474 164 443 c +145 412 135 359 135 284 c +135 209 145 156 164 125 c + +ce} _d +/colon{512 0 195 18 317 571 sc +195 418 m +195 571 l +317 571 l +317 418 l +195 418 l + +195 18 m +195 172 l +317 172 l +317 18 l +195 18 l + +ce} _d +/A{512 0 20 18 492 766 sc +255 694 m +253 694 l +168 305 l +340 305 l +255 694 l + +356 233 m +152 233 l +104 18 l +20 18 l +205 766 l +307 766 l +492 18 l +403 18 l +356 233 l + +ce} _d +/B{512 0 77 8 466 776 sc +161 459 m +207 459 l +262 459 301 469 326 489 c +351 509 364 540 364 582 c +364 621 352 651 327 672 c +302 694 267 705 222 705 c +197 705 177 702 161 696 c +161 459 l + +161 387 m +161 88 l +186 83 217 80 253 80 c +339 80 382 135 382 244 c +382 339 327 387 217 387 c +161 387 l + +466 233 m +466 83 389 8 236 8 c +181 8 128 13 77 24 c +77 761 l +128 771 181 776 236 776 c +375 776 445 715 445 592 c +445 550 435 515 414 486 c +393 457 365 438 328 429 c +328 427 l +367 420 400 398 426 361 c +453 325 466 282 466 233 c + +ce} _d +/C{512 0 56 8 435 776 sc +56 392 m +56 527 77 624 118 685 c +159 746 221 776 302 776 c +347 776 390 766 430 745 c +430 669 l +389 691 348 702 307 702 c +194 702 138 599 138 392 c +138 280 152 200 181 153 c +210 106 252 82 307 82 c +350 82 392 95 435 121 c +435 39 l +395 18 351 8 302 8 c +219 8 157 37 116 96 c +76 155 56 253 56 392 c + +ce} _d +/D{512 0 67 8 476 776 sc +392 392 m +392 507 377 587 347 633 c +318 679 271 702 207 702 c +184 702 166 699 151 694 c +151 90 l +166 85 184 82 207 82 c +251 82 286 90 311 107 c +337 124 357 155 371 200 c +385 246 392 310 392 392 c + +476 392 m +476 251 454 151 411 94 c +368 37 300 8 207 8 c +159 8 112 13 67 24 c +67 761 l +112 771 159 776 207 776 c +300 776 368 747 411 688 c +454 630 476 531 476 392 c + +ce} _d +/E{512 0 82 18 430 766 sc +166 692 m +166 459 l +420 459 l +420 387 l +166 387 l +166 92 l +430 92 l +430 18 l +82 18 l +82 766 l +430 766 l +430 692 l +166 692 l + +ce} _d +/F{512 0 92 18 430 766 sc +176 387 m +176 18 l +92 18 l +92 766 l +430 766 l +430 692 l +176 692 l +176 459 l +420 459 l +420 387 l +176 387 l + +ce} _d +/G{512 0 41 8 461 776 sc +379 105 m +379 387 l +220 387 l +220 459 l +461 459 l +461 49 l +406 22 348 8 287 8 c +206 8 145 38 103 99 c +62 160 41 257 41 392 c +41 526 63 623 106 684 c +149 745 215 776 302 776 c +342 776 386 768 435 751 c +435 672 l +390 692 346 702 302 702 c +243 702 198 677 167 628 c +136 579 121 501 121 392 c +121 185 178 82 292 82 c +323 82 352 90 379 105 c + +ce} _d +/I{512 0 92 18 420 766 sc +420 18 m +92 18 l +92 90 l +213 90 l +213 694 l +92 694 l +92 766 l +420 766 l +420 694 l +299 694 l +299 90 l +420 90 l +420 18 l + +ce} _d +/J{512 0 61 8 410 766 sc +410 766 m +410 213 l +410 138 395 85 365 54 c +336 23 286 8 215 8 c +159 8 108 18 61 39 c +61 128 l +81 117 107 106 138 96 c +170 87 196 82 215 82 c +251 82 278 92 296 113 c +314 134 323 168 323 215 c +323 694 l +154 694 l +154 766 l +410 766 l + +ce} _d +/K{512 0 77 18 476 766 sc +161 428 m +163 428 l +374 766 l +471 766 l +241 408 l +476 18 l +379 18 l +163 387 l +161 387 l +161 18 l +77 18 l +77 766 l +161 766 l +161 428 l + +ce} _d +/L{512 0 102 18 430 766 sc +186 766 m +186 92 l +430 92 l +430 18 l +102 18 l +102 766 l +186 766 l + +ce} _d +/N{512 0 67 18 445 766 sc +155 582 m +153 582 l +153 18 l +67 18 l +67 766 l +153 766 l +361 203 l +364 203 l +364 766 l +445 766 l +445 18 l +364 18 l +155 582 l + +ce} _d +/O{512 0 41 8 471 776 sc +93 689 m +128 747 183 776 256 776 c +329 776 383 747 418 689 c +453 632 471 533 471 392 c +471 251 453 152 418 94 c +383 37 329 8 256 8 c +183 8 128 37 93 94 c +58 152 41 251 41 392 c +41 533 58 632 93 689 c + +183 108 m +202 91 226 82 256 82 c +286 82 310 91 328 108 c +347 125 361 157 372 203 c +383 250 389 313 389 392 c +389 471 383 534 372 580 c +361 627 347 659 328 676 c +310 693 286 702 256 702 c +226 702 202 693 183 676 c +165 659 150 627 139 580 c +128 534 123 471 123 392 c +123 313 128 250 139 203 c +150 157 165 125 183 108 c + +ce} _d +/P{512 0 77 18 466 776 sc +384 551 m +384 605 372 644 347 668 c +322 693 284 705 232 705 c +204 705 180 702 161 696 c +161 397 l +181 394 205 392 232 392 c +285 392 323 404 347 428 c +372 453 384 494 384 551 c + +466 551 m +466 469 448 410 412 374 c +376 339 320 321 243 321 c +219 321 192 323 161 326 c +161 18 l +77 18 l +77 761 l +130 771 185 776 241 776 c +318 776 375 758 411 722 c +448 687 466 630 466 551 c + +ce} _d +/R{512 0 72 18 481 776 sc +379 571 m +379 660 328 705 227 705 c +199 705 175 702 156 696 c +156 418 l +217 418 l +276 418 318 429 342 452 c +367 475 379 514 379 571 c + +156 346 m +156 18 l +72 18 l +72 761 l +125 771 180 776 236 776 c +312 776 368 759 405 725 c +442 692 461 640 461 571 c +461 474 424 408 349 374 c +349 372 l +370 361 393 317 417 238 c +481 18 l +393 18 l +333 240 l +321 283 307 312 290 325 c +274 339 246 346 207 346 c +156 346 l + +ce} _d +/S{512 0 72 8 451 776 sc +266 702 m +234 702 208 692 187 671 c +166 650 156 624 156 592 c +156 558 163 530 178 507 c +193 485 217 466 251 451 c +327 417 379 382 408 345 c +437 309 451 262 451 203 c +451 138 433 90 398 57 c +363 24 312 8 246 8 c +184 8 128 27 77 65 c +77 162 l +132 109 190 82 251 82 c +328 82 367 122 367 203 c +367 240 358 271 340 296 c +322 321 292 342 251 361 c +187 390 141 422 113 459 c +86 496 72 540 72 592 c +72 647 89 691 124 725 c +159 759 204 776 261 776 c +297 776 327 773 350 768 c +373 763 400 752 430 735 c +430 643 l +377 682 323 702 266 702 c + +ce} _d +/T{512 0 56 18 456 766 sc +214 18 m +214 694 l +56 694 l +56 766 l +456 766 l +456 694 l +298 694 l +298 18 l +214 18 l + +ce} _d +/U{512 0 61 8 451 766 sc +402 58 m +369 25 321 8 256 8 c +191 8 143 25 110 58 c +77 91 61 143 61 213 c +61 766 l +147 766 l +147 233 l +147 178 156 139 174 116 c +193 93 221 82 258 82 c +295 82 323 93 341 116 c +360 139 369 178 369 233 c +369 766 l +451 766 l +451 213 l +451 143 435 91 402 58 c + +ce} _d +/V{512 0 31 18 481 766 sc +259 90 m +397 766 l +481 766 l +307 18 l +205 18 l +31 766 l +119 766 l +257 90 l +259 90 l + +ce} _d +/X{512 0 51 18 461 766 sc +257 469 m +259 469 l +374 766 l +459 766 l +307 402 l +461 18 l +369 18 l +255 331 l +253 331 l +138 18 l +51 18 l +205 402 l +53 766 l +143 766 l +257 469 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +0 0 translate +0 0 504 72 rectclip +gsave +0 0 m +504 0 l +504 72 l +0 72 l +cl +1 setgray +fill +grestore +gsave +0 36 m +504 36 l +504 72 l +0 72 l +0 36 l +cl +grestore +0 setgray +/WenQuanYiZenHei-0 16.000 selectfont +gsave + +55.4375 49.2031 translate +0 rotate +0 0 m /W glyphshow +13.1094 0 m /e glyphshow +21.7344 0 m /n glyphshow +30.3594 0 m /Q glyphshow +40.9062 0 m /u glyphshow +49.5312 0 m /a glyphshow +58.1562 0 m /n glyphshow +66.7812 0 m /Y glyphshow +75.7344 0 m /i glyphshow +79.5625 0 m /space glyphshow +84.3594 0 m /Z glyphshow +92.9844 0 m /e glyphshow +101.609 0 m /n glyphshow +110.234 0 m /space glyphshow +115.031 0 m /H glyphshow +125.578 0 m /e glyphshow +134.203 0 m /i glyphshow +138.031 0 m /colon glyphshow +142.828 0 m /space glyphshow +147.625 0 m /A glyphshow +156.578 0 m /B glyphshow +166.484 0 m /C glyphshow +176.391 0 m /D glyphshow +186.938 0 m /E glyphshow +195.891 0 m /F glyphshow +203.562 0 m /G glyphshow +214.109 0 m /H glyphshow +224.656 0 m /I glyphshow +228.812 0 m /J glyphshow +235.203 0 m /K glyphshow +245.109 0 m /L glyphshow +253.109 0 m /M glyphshow +266.219 0 m /N glyphshow +276.766 0 m /O glyphshow +287.312 0 m /P glyphshow +296.266 0 m /Q glyphshow +306.812 0 m /R glyphshow +316.719 0 m /S glyphshow +326.625 0 m /T glyphshow +334.625 0 m /U glyphshow +344.859 0 m /V glyphshow +353.812 0 m /W glyphshow +366.922 0 m /X glyphshow +375.547 0 m /Y glyphshow +384.5 0 m /Z glyphshow +grestore +gsave +0 0 m +504 0 l +504 36 l +0 36 l +0 0 l +cl +grestore +/WenQuanYiZenHeiMono-0 16.000 selectfont +gsave + +52 13.2031 translate +0 rotate +0 0 m /W glyphshow +8 0 m /e glyphshow +16 0 m /n glyphshow +24 0 m /Q glyphshow +32 0 m /u glyphshow +40 0 m /a glyphshow +48 0 m /n glyphshow +56 0 m /Y glyphshow +64 0 m /i glyphshow +72 0 m /space glyphshow +80 0 m /Z glyphshow +88 0 m /e glyphshow +96 0 m /n glyphshow +104 0 m /space glyphshow +112 0 m /H glyphshow +120 0 m /e glyphshow +128 0 m /i glyphshow +136 0 m /space glyphshow +144 0 m /M glyphshow +152 0 m /o glyphshow +160 0 m /n glyphshow +168 0 m /o glyphshow +176 0 m /colon glyphshow +184 0 m /space glyphshow +192 0 m /A glyphshow +200 0 m /B glyphshow +208 0 m /C glyphshow +216 0 m /D glyphshow +224 0 m /E glyphshow +232 0 m /F glyphshow +240 0 m /G glyphshow +248 0 m /H glyphshow +256 0 m /I glyphshow +264 0 m /J glyphshow +272 0 m /K glyphshow +280 0 m /L glyphshow +288 0 m /M glyphshow +296 0 m /N glyphshow +304 0 m /O glyphshow +312 0 m /P glyphshow +320 0 m /Q glyphshow +328 0 m /R glyphshow +336 0 m /S glyphshow +344 0 m /T glyphshow +352 0 m /U glyphshow +360 0 m /V glyphshow +368 0 m /W glyphshow +376 0 m /X glyphshow +384 0 m /Y glyphshow +392 0 m /Z glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type42.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type42.eps new file mode 100644 index 000000000000..94c59cb556a8 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/ttc_type42.eps @@ -0,0 +1,1483 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 +%%Title: ttc_type42.eps +%%Creator: Matplotlib v3.11.0.dev2075+g5a13dc378e, https://matplotlib.org/ +%%CreationDate: Fri Mar 13 23:13:37 2026 +%%Orientation: portrait +%%BoundingBox: 0 0 504 72 +%%HiResBoundingBox: 0.000000 0.000000 504.000000 72.000000 +%%EndComments +%%BeginProlog +/mpldict 10 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHei-0 def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/W /e /n /Q /u /a /Y /i /space /Z /H /colon /A /B /C /D /E /F /G /I /J /K /L /M /N /O /P /R /S /T /U /V /X] def +/CharStrings 34 dict dup begin +/.notdef 0 def +/W{839 0 11 0 831 702 sc +831 702 m +663 0 l +574 0 l +422 545 l +279 0 l +189 0 l +11 702 l +101 702 l +242 133 l +244 133 l +392 702 l +463 702 l +623 133 l +625 133 l +755 702 l +831 702 l + +ce} _d +/e{552 0 38 -10 506 534 sc +506 254 m +128 254 l +127 187 143 136 174 101 c +200 72 236 57 283 57 c +341 57 384 82 411 133 c +415 140 419 149 422 158 c +498 142 l +479 82 439 38 378 11 c +346 -3 312 -10 276 -10 c +187 -10 121 27 78 100 c +51 145 38 199 38 260 c +38 346 64 415 116 468 c +159 512 213 534 279 534 c +369 534 434 495 473 417 c +495 374 506 323 506 266 c +506 254 l + +418 313 m +419 370 401 413 366 442 c +342 462 313 472 279 472 c +226 472 186 448 158 400 c +142 374 133 345 132 313 c +418 313 l + +ce} _d +/n{552 0 71 0 482 534 sc +482 0 m +402 0 l +402 322 l +402 366 398 396 391 412 c +390 414 389 416 388 418 c +373 444 348 459 313 464 c +308 465 304 465 299 465 c +234 465 190 432 166 366 c +156 337 151 306 151 272 c +151 0 l +71 0 l +71 520 l +146 520 l +146 424 l +148 424 l +173 477 211 511 263 526 c +278 531 294 534 310 534 c +375 534 423 508 455 457 c +458 453 l +474 425 482 377 482 309 c +482 0 l + +ce} _d +/Q{675 0 42 -167 626 713 sc +626 351 m +626 244 599 158 546 91 c +509 46 462 16 407 1 c +407 -14 l +407 -55 425 -80 461 -88 c +468 -90 476 -91 485 -91 c +500 -91 532 -89 580 -84 c +580 -154 l +553 -163 528 -167 504 -167 c +412 -167 357 -130 340 -55 c +337 -41 335 -26 335 -10 c +224 -7 142 41 91 135 c +58 195 42 265 42 346 c +42 456 69 545 124 613 c +177 680 247 713 334 713 c +441 713 520 666 572 572 c +608 509 626 436 626 351 c + +530 349 m +530 482 496 569 428 610 c +401 627 370 635 333 635 c +234 635 172 576 147 459 c +140 424 136 386 136 344 c +136 239 160 161 207 112 c +238 79 278 62 327 62 c +425 62 488 116 516 225 c +525 263 530 304 530 349 c + +ce} _d +/u{552 0 71 -10 479 520 sc +479 0 m +407 0 l +407 103 l +404 103 l +386 58 355 25 310 4 c +289 -5 268 -10 245 -10 c +144 -10 87 45 74 156 c +72 171 71 187 71 204 c +71 520 l +151 520 l +151 204 l +151 113 181 65 242 59 c +242 59 245 59 252 59 c +311 59 354 89 380 148 c +393 177 399 209 399 244 c +399 520 l +479 520 l +479 0 l + +ce} _d +/a{552 0 56 -10 509 534 sc +509 0 m +429 0 l +420 97 l +385 26 324 -10 238 -10 c +171 -10 120 11 87 53 c +66 79 56 110 56 147 c +56 198 78 240 122 273 c +131 281 142 287 153 292 c +198 313 272 324 375 323 c +420 323 l +420 345 l +420 409 397 448 352 463 c +316 470 l +307 471 298 471 288 471 c +205 471 158 440 145 379 c +71 391 l +80 458 124 502 205 522 c +233 530 263 534 295 534 c +384 534 442 510 469 463 c +488 431 498 380 498 311 c +498 110 l +498 63 502 26 509 0 c + +420 228 m +420 262 l +353 262 l +212 262 142 222 142 143 c +142 100 165 71 211 58 c +224 54 239 52 255 52 c +312 52 356 76 388 123 c +409 154 420 189 420 228 c + +ce} _d +/Y{573 0 11 0 568 702 sc +568 702 m +334 296 l +334 0 l +246 0 l +246 296 l +11 702 l +114 702 l +300 379 l +488 702 l +568 702 l + +ce} _d +/i{245 0 79 0 167 702 sc +167 612 m +79 612 l +79 702 l +167 702 l +167 612 l + +163 0 m +83 0 l +83 520 l +163 520 l +163 0 l + +ce} _d +/space{307 0 0 0 0 0 sc +ce} _d +/Z{552 0 17 0 513 702 sc +513 0 m +17 0 l +17 76 l +399 630 l +35 630 l +35 702 l +502 702 l +502 644 l +116 76 l +513 76 l +513 0 l + +ce} _d +/H{675 0 83 0 585 702 sc +585 0 m +497 0 l +497 334 l +171 334 l +171 0 l +83 0 l +83 702 l +171 702 l +171 411 l +497 411 l +497 702 l +585 702 l +585 0 l + +ce} _d +/colon{307 0 101 0 215 520 sc +215 404 m +101 404 l +101 520 l +215 520 l +215 404 l + +215 0 m +101 0 l +101 116 l +215 116 l +215 0 l + +ce} _d +/A{573 0 11 0 568 702 sc +568 0 m +478 0 l +408 205 l +147 205 l +85 0 l +11 0 l +239 702 l +340 702 l +568 0 l + +388 271 m +281 608 l +172 271 l +388 271 l + +ce} _d +/B{634 0 83 0 588 702 sc +588 194 m +588 130 565 80 520 45 c +484 16 434 1 370 0 c +347 0 l +83 0 l +83 702 l +348 702 l +405 702 448 694 477 679 c +482 676 487 673 493 670 c +541 637 565 591 566 531 c +566 470 542 424 494 394 c +462 380 l +456 377 449 375 442 374 c +442 372 l +501 359 543 327 568 276 c +581 251 588 224 588 194 c + +479 520 m +479 585 443 621 370 630 c +361 631 351 631 341 631 c +171 631 l +171 401 l +317 401 l +425 401 479 441 479 520 c + +500 198 m +500 241 485 276 454 303 c +453 304 451 306 449 307 c +426 325 389 334 338 334 c +171 334 l +171 75 l +343 75 l +432 75 483 105 497 166 c +499 175 500 186 500 198 c + +ce} _d +/C{634 0 43 -10 589 713 sc +589 219 m +558 90 490 16 386 -5 c +367 -8 348 -10 329 -10 c +224 -10 146 38 94 135 c +60 199 43 273 43 358 c +43 471 72 560 131 626 c +183 684 251 713 336 713 c +435 713 508 669 555 582 c +570 554 582 523 589 488 c +506 473 l +479 573 430 628 359 637 c +350 638 342 639 333 639 c +248 639 190 590 159 492 c +146 449 139 402 139 351 c +139 261 158 189 197 134 c +230 87 274 63 329 63 c +418 63 477 117 506 225 c +507 229 508 233 509 237 c +589 219 l + +ce} _d +/D{675 0 87 0 636 702 sc +636 353 m +636 247 607 160 550 93 c +496 31 425 0 338 0 c +87 0 l +87 702 l +309 702 l +394 702 462 682 512 642 c +525 631 538 618 551 603 c +608 537 636 454 636 353 c + +547 353 m +547 436 525 504 482 557 c +449 599 403 623 344 628 c +304 629 l +175 629 l +175 75 l +304 75 l +382 75 440 97 478 140 c +484 147 490 155 496 164 c +530 216 547 279 547 353 c + +ce} _d +/E{573 0 87 0 542 702 sc +542 0 m +87 0 l +87 702 l +531 702 l +531 627 l +175 627 l +175 403 l +458 403 l +458 333 l +175 333 l +175 76 l +542 76 l +542 0 l + +ce} _d +/F{491 0 84 0 514 702 sc +514 627 m +172 627 l +172 403 l +456 403 l +456 333 l +172 333 l +172 0 l +84 0 l +84 702 l +514 702 l +514 627 l + +ce} _d +/G{675 0 49 -10 614 713 sc +614 -5 m +560 -5 l +537 82 l +497 24 436 -7 355 -10 c +350 -10 346 -10 342 -10 c +237 -10 157 35 104 125 c +67 187 49 260 49 344 c +49 452 77 541 133 610 c +188 679 262 713 353 713 c +457 713 532 670 579 585 c +591 563 600 538 607 511 c +524 490 l +512 553 480 597 428 622 c +403 633 376 639 347 639 c +256 639 195 589 164 488 c +151 447 144 400 144 348 c +144 251 167 176 212 123 c +247 82 292 61 348 61 c +418 61 469 88 500 141 c +516 170 524 203 524 242 c +524 281 l +349 281 l +349 354 l +614 354 l +614 -5 l + +ce} _d +/I{266 0 88 0 176 702 sc +176 0 m +88 0 l +88 702 l +176 702 l +176 0 l + +ce} _d +/J{409 0 17 -10 331 702 sc +331 229 m +331 104 290 29 207 2 c +182 -6 153 -10 120 -10 c +86 -10 52 -5 17 4 c +17 76 l +50 69 81 66 108 66 c +171 66 211 83 227 118 c +238 139 243 176 243 229 c +243 702 l +331 702 l +331 229 l + +ce} _d +/K{634 0 88 0 643 702 sc +643 0 m +546 0 l +337 387 l +176 187 l +176 0 l +88 0 l +88 702 l +176 702 l +176 295 l +493 702 l +588 702 l +398 457 l +643 0 l + +ce} _d +/L{512 0 86 0 501 702 sc +501 0 m +86 0 l +86 702 l +174 702 l +174 78 l +501 78 l +501 0 l + +ce} _d +/M{839 0 82 0 761 702 sc +761 0 m +673 0 l +673 613 l +669 613 l +446 0 l +387 0 l +160 613 l +156 613 l +156 0 l +82 0 l +82 702 l +213 702 l +425 140 l +632 702 l +761 702 l +761 0 l + +ce} _d +/N{675 0 84 0 601 702 sc +601 0 m +515 0 l +158 612 l +158 0 l +84 0 l +84 702 l +195 702 l +527 130 l +527 702 l +601 702 l +601 0 l + +ce} _d +/O{675 0 45 -10 623 713 sc +623 359 m +623 258 598 173 548 102 c +495 27 424 -10 335 -10 c +230 -10 151 36 98 128 c +63 189 45 262 45 346 c +45 453 71 540 124 609 c +176 678 246 713 334 713 c +435 713 513 669 566 580 c +604 517 623 443 623 359 c + +530 354 m +530 449 509 522 468 575 c +435 618 392 640 337 640 c +250 640 191 591 159 492 c +144 448 137 399 137 345 c +137 258 157 187 197 133 c +232 85 278 61 335 61 c +417 61 474 108 507 203 c +522 248 530 299 530 354 c + +ce} _d +/P{573 0 66 0 542 702 sc +542 492 m +542 436 523 388 485 348 c +480 343 l +441 305 385 286 311 286 c +154 286 l +154 0 l +66 0 l +66 702 l +301 702 l +376 702 433 686 470 655 c +510 622 534 575 541 515 c +542 507 542 499 542 492 c + +453 492 m +453 557 425 600 370 620 c +351 626 331 629 308 629 c +154 629 l +154 358 l +302 358 l +375 358 422 384 442 435 c +449 452 453 471 453 492 c + +ce} _d +/R{634 0 83 0 588 702 sc +588 0 m +496 0 l +366 304 l +171 304 l +171 0 l +83 0 l +83 702 l +346 702 l +436 702 502 676 544 624 c +563 599 575 568 580 532 c +581 524 581 515 581 506 c +581 445 559 396 515 359 c +496 344 474 333 450 326 c +588 0 l + +493 507 m +493 577 454 616 377 625 c +367 626 357 627 346 627 c +171 627 l +171 376 l +336 376 l +422 376 472 406 487 467 c +491 479 493 492 493 507 c + +ce} _d +/S{634 0 43 -10 590 713 sc +590 201 m +590 114 550 53 469 17 c +428 -1 381 -10 328 -10 c +184 -10 89 56 43 189 c +122 207 l +143 134 191 89 266 72 c +286 67 307 65 330 65 c +398 65 447 83 476 120 c +491 139 499 162 499 189 c +499 237 469 273 408 296 c +343 314 l +264 334 221 345 214 348 c +179 359 153 373 135 389 c +97 423 78 466 78 519 c +78 599 115 655 189 688 c +227 705 269 713 316 713 c +413 713 485 678 534 608 c +554 571 l +559 562 563 552 566 541 c +486 519 l +477 565 448 600 398 624 c +371 637 343 643 314 643 c +265 643 226 629 195 601 c +174 582 164 558 164 531 c +164 485 193 451 250 430 c +264 425 308 414 383 397 c +450 382 497 363 524 340 c +568 305 590 258 590 201 c + +ce} _d +/T{512 0 11 0 499 702 sc +499 626 m +299 626 l +299 0 l +211 0 l +211 626 l +11 626 l +11 702 l +499 702 l +499 626 l + +ce} _d +/U{655 0 83 -10 567 702 sc +567 258 m +567 184 556 128 534 91 c +528 83 522 75 516 68 c +473 16 410 -10 327 -10 c +210 -10 135 32 103 117 c +90 154 83 201 83 258 c +83 702 l +171 702 l +171 258 l +171 179 187 126 218 99 c +243 78 282 68 334 68 c +417 68 468 103 485 174 c +491 197 494 225 494 258 c +494 702 l +567 702 l +567 258 l + +ce} _d +/V{573 0 10 0 569 702 sc +569 702 m +332 0 l +248 0 l +10 702 l +102 702 l +298 123 l +493 702 l +569 702 l + +ce} _d +/X{552 0 9 0 552 702 sc +552 0 m +453 0 l +275 299 l +91 0 l +9 0 l +234 364 l +34 702 l +132 702 l +287 442 l +443 702 l +524 702 l +328 381 l +552 0 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHeiMono-0 def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/W /e /n /Q /u /a /Y /i /space /Z /H /M /o /colon /A /B /C /D /E /F /G /I /J /K /L /N /O /P /R /S /T /U /V /X] def +/CharStrings 35 dict dup begin +/.notdef 0 def +/W{512 0 26 18 486 766 sc +157 141 m +159 141 l +214 664 l +306 664 l +361 141 l +364 141 l +410 766 l +486 766 l +425 18 l +313 18 l +257 551 l +255 551 l +199 18 l +87 18 l +26 766 l +111 766 l +157 141 l + +ce} _d +/e{512 0 56 8 445 561 sc +141 258 m +144 195 157 149 180 121 c +203 93 237 79 282 79 c +323 79 369 91 420 116 c +420 34 l +369 17 321 8 276 8 c +129 8 56 100 56 285 c +56 381 73 451 107 495 c +142 539 193 561 261 561 c +323 561 369 540 399 497 c +430 455 445 386 445 290 c +445 283 444 272 443 258 c +141 258 l + +141 326 m +365 326 l +364 435 330 490 261 490 c +222 490 193 478 174 453 c +155 429 144 387 141 326 c + +ce} _d +/n{512 0 72 18 451 561 sc +451 356 m +451 18 l +372 18 l +372 338 l +372 398 365 438 351 458 c +338 478 313 488 276 488 c +242 488 213 469 189 431 c +165 393 153 341 153 276 c +153 18 l +72 18 l +72 551 l +147 551 l +150 474 l +152 474 l +165 500 184 521 211 537 c +238 553 266 561 297 561 c +351 561 390 545 414 514 c +439 483 451 431 451 356 c + +ce} _d +/Q{512 0 41 -135 492 776 sc +93 689 m +128 747 183 776 256 776 c +329 776 383 747 418 689 c +453 632 471 533 471 392 c +471 206 437 89 369 40 c +369 38 l +397 23 422 1 443 -30 c +465 -61 481 -96 492 -135 c +401 -135 l +387 -82 369 -44 346 -23 c +323 -2 293 8 256 8 c +183 8 128 37 93 94 c +58 152 41 251 41 392 c +41 533 58 632 93 689 c + +183 108 m +202 91 226 82 256 82 c +286 82 310 91 328 108 c +347 125 361 157 372 203 c +383 250 389 313 389 392 c +389 471 383 534 372 580 c +361 627 347 659 328 676 c +310 693 286 702 256 702 c +226 702 202 693 183 676 c +165 659 150 627 139 580 c +128 534 123 471 123 392 c +123 313 128 250 139 203 c +150 157 165 125 183 108 c + +ce} _d +/u{512 0 67 8 435 551 sc +67 203 m +67 551 l +145 551 l +145 221 l +145 164 151 127 164 108 c +177 90 201 81 236 81 c +269 81 297 100 320 137 c +343 175 354 227 354 293 c +354 551 l +435 551 l +435 18 l +359 18 l +357 95 l +355 95 l +342 68 323 46 298 31 c +273 16 245 8 215 8 c +162 8 124 23 101 52 c +78 81 67 132 67 203 c + +ce} _d +/a{512 0 61 8 440 561 sc +261 561 m +330 561 377 547 402 520 c +427 493 440 442 440 367 c +440 18 l +367 18 l +365 95 l +362 95 l +330 37 279 8 210 8 c +165 8 129 22 102 50 c +75 79 61 118 61 167 c +61 229 82 277 124 310 c +166 344 229 361 312 361 c +361 361 l +361 387 l +361 426 353 453 338 469 c +323 485 298 493 261 493 c +238 493 210 489 175 480 c +140 472 111 463 87 452 c +87 525 l +111 536 140 544 174 551 c +208 558 237 561 261 561 c + +361 300 m +312 300 l +196 300 138 257 138 172 c +138 141 146 118 161 101 c +177 85 198 77 225 77 c +266 77 298 93 323 126 c +348 159 361 205 361 264 c +361 300 l + +ce} _d +/Y{512 0 31 18 481 766 sc +257 402 m +259 402 l +394 766 l +481 766 l +298 315 l +298 18 l +214 18 l +214 315 l +31 766 l +123 766 l +257 402 l + +ce} _d +/i{512 0 97 18 435 797 sc +324 551 m +324 88 l +435 88 l +435 18 l +97 18 l +97 88 l +240 88 l +240 481 l +128 481 l +128 551 l +324 551 l + +219 674 m +219 797 l +324 797 l +324 674 l +219 674 l + +ce} _d +/space{512 0 0 0 0 0 sc +ce} _d +/Z{512 0 77 18 435 766 sc +343 692 m +343 694 l +77 694 l +77 766 l +435 766 l +435 694 l +169 92 l +169 90 l +435 90 l +435 18 l +77 18 l +77 90 l +343 692 l + +ce} _d +/H{512 0 61 18 451 766 sc +143 766 m +143 461 l +365 461 l +365 766 l +451 766 l +451 18 l +365 18 l +365 387 l +143 387 l +143 18 l +61 18 l +61 766 l +143 766 l + +ce} _d +/M{512 0 41 18 471 766 sc +387 571 m +385 571 l +295 223 l +213 223 l +123 571 l +121 571 l +121 18 l +41 18 l +41 766 l +135 766 l +257 305 l +259 305 l +381 766 l +471 766 l +471 18 l +387 18 l +387 571 l + +ce} _d +/o{512 0 51 8 461 561 sc +51 284 m +51 469 119 561 256 561 c +393 561 461 469 461 284 c +461 100 393 8 256 8 c +119 8 51 100 51 284 c + +164 125 m +184 94 215 79 256 79 c +297 79 328 94 347 125 c +367 156 377 209 377 284 c +377 359 367 412 347 443 c +328 474 297 490 256 490 c +215 490 184 474 164 443 c +145 412 135 359 135 284 c +135 209 145 156 164 125 c + +ce} _d +/colon{512 0 195 18 317 571 sc +195 418 m +195 571 l +317 571 l +317 418 l +195 418 l + +195 18 m +195 172 l +317 172 l +317 18 l +195 18 l + +ce} _d +/A{512 0 20 18 492 766 sc +255 694 m +253 694 l +168 305 l +340 305 l +255 694 l + +356 233 m +152 233 l +104 18 l +20 18 l +205 766 l +307 766 l +492 18 l +403 18 l +356 233 l + +ce} _d +/B{512 0 77 8 466 776 sc +161 459 m +207 459 l +262 459 301 469 326 489 c +351 509 364 540 364 582 c +364 621 352 651 327 672 c +302 694 267 705 222 705 c +197 705 177 702 161 696 c +161 459 l + +161 387 m +161 88 l +186 83 217 80 253 80 c +339 80 382 135 382 244 c +382 339 327 387 217 387 c +161 387 l + +466 233 m +466 83 389 8 236 8 c +181 8 128 13 77 24 c +77 761 l +128 771 181 776 236 776 c +375 776 445 715 445 592 c +445 550 435 515 414 486 c +393 457 365 438 328 429 c +328 427 l +367 420 400 398 426 361 c +453 325 466 282 466 233 c + +ce} _d +/C{512 0 56 8 435 776 sc +56 392 m +56 527 77 624 118 685 c +159 746 221 776 302 776 c +347 776 390 766 430 745 c +430 669 l +389 691 348 702 307 702 c +194 702 138 599 138 392 c +138 280 152 200 181 153 c +210 106 252 82 307 82 c +350 82 392 95 435 121 c +435 39 l +395 18 351 8 302 8 c +219 8 157 37 116 96 c +76 155 56 253 56 392 c + +ce} _d +/D{512 0 67 8 476 776 sc +392 392 m +392 507 377 587 347 633 c +318 679 271 702 207 702 c +184 702 166 699 151 694 c +151 90 l +166 85 184 82 207 82 c +251 82 286 90 311 107 c +337 124 357 155 371 200 c +385 246 392 310 392 392 c + +476 392 m +476 251 454 151 411 94 c +368 37 300 8 207 8 c +159 8 112 13 67 24 c +67 761 l +112 771 159 776 207 776 c +300 776 368 747 411 688 c +454 630 476 531 476 392 c + +ce} _d +/E{512 0 82 18 430 766 sc +166 692 m +166 459 l +420 459 l +420 387 l +166 387 l +166 92 l +430 92 l +430 18 l +82 18 l +82 766 l +430 766 l +430 692 l +166 692 l + +ce} _d +/F{512 0 92 18 430 766 sc +176 387 m +176 18 l +92 18 l +92 766 l +430 766 l +430 692 l +176 692 l +176 459 l +420 459 l +420 387 l +176 387 l + +ce} _d +/G{512 0 41 8 461 776 sc +379 105 m +379 387 l +220 387 l +220 459 l +461 459 l +461 49 l +406 22 348 8 287 8 c +206 8 145 38 103 99 c +62 160 41 257 41 392 c +41 526 63 623 106 684 c +149 745 215 776 302 776 c +342 776 386 768 435 751 c +435 672 l +390 692 346 702 302 702 c +243 702 198 677 167 628 c +136 579 121 501 121 392 c +121 185 178 82 292 82 c +323 82 352 90 379 105 c + +ce} _d +/I{512 0 92 18 420 766 sc +420 18 m +92 18 l +92 90 l +213 90 l +213 694 l +92 694 l +92 766 l +420 766 l +420 694 l +299 694 l +299 90 l +420 90 l +420 18 l + +ce} _d +/J{512 0 61 8 410 766 sc +410 766 m +410 213 l +410 138 395 85 365 54 c +336 23 286 8 215 8 c +159 8 108 18 61 39 c +61 128 l +81 117 107 106 138 96 c +170 87 196 82 215 82 c +251 82 278 92 296 113 c +314 134 323 168 323 215 c +323 694 l +154 694 l +154 766 l +410 766 l + +ce} _d +/K{512 0 77 18 476 766 sc +161 428 m +163 428 l +374 766 l +471 766 l +241 408 l +476 18 l +379 18 l +163 387 l +161 387 l +161 18 l +77 18 l +77 766 l +161 766 l +161 428 l + +ce} _d +/L{512 0 102 18 430 766 sc +186 766 m +186 92 l +430 92 l +430 18 l +102 18 l +102 766 l +186 766 l + +ce} _d +/N{512 0 67 18 445 766 sc +155 582 m +153 582 l +153 18 l +67 18 l +67 766 l +153 766 l +361 203 l +364 203 l +364 766 l +445 766 l +445 18 l +364 18 l +155 582 l + +ce} _d +/O{512 0 41 8 471 776 sc +93 689 m +128 747 183 776 256 776 c +329 776 383 747 418 689 c +453 632 471 533 471 392 c +471 251 453 152 418 94 c +383 37 329 8 256 8 c +183 8 128 37 93 94 c +58 152 41 251 41 392 c +41 533 58 632 93 689 c + +183 108 m +202 91 226 82 256 82 c +286 82 310 91 328 108 c +347 125 361 157 372 203 c +383 250 389 313 389 392 c +389 471 383 534 372 580 c +361 627 347 659 328 676 c +310 693 286 702 256 702 c +226 702 202 693 183 676 c +165 659 150 627 139 580 c +128 534 123 471 123 392 c +123 313 128 250 139 203 c +150 157 165 125 183 108 c + +ce} _d +/P{512 0 77 18 466 776 sc +384 551 m +384 605 372 644 347 668 c +322 693 284 705 232 705 c +204 705 180 702 161 696 c +161 397 l +181 394 205 392 232 392 c +285 392 323 404 347 428 c +372 453 384 494 384 551 c + +466 551 m +466 469 448 410 412 374 c +376 339 320 321 243 321 c +219 321 192 323 161 326 c +161 18 l +77 18 l +77 761 l +130 771 185 776 241 776 c +318 776 375 758 411 722 c +448 687 466 630 466 551 c + +ce} _d +/R{512 0 72 18 481 776 sc +379 571 m +379 660 328 705 227 705 c +199 705 175 702 156 696 c +156 418 l +217 418 l +276 418 318 429 342 452 c +367 475 379 514 379 571 c + +156 346 m +156 18 l +72 18 l +72 761 l +125 771 180 776 236 776 c +312 776 368 759 405 725 c +442 692 461 640 461 571 c +461 474 424 408 349 374 c +349 372 l +370 361 393 317 417 238 c +481 18 l +393 18 l +333 240 l +321 283 307 312 290 325 c +274 339 246 346 207 346 c +156 346 l + +ce} _d +/S{512 0 72 8 451 776 sc +266 702 m +234 702 208 692 187 671 c +166 650 156 624 156 592 c +156 558 163 530 178 507 c +193 485 217 466 251 451 c +327 417 379 382 408 345 c +437 309 451 262 451 203 c +451 138 433 90 398 57 c +363 24 312 8 246 8 c +184 8 128 27 77 65 c +77 162 l +132 109 190 82 251 82 c +328 82 367 122 367 203 c +367 240 358 271 340 296 c +322 321 292 342 251 361 c +187 390 141 422 113 459 c +86 496 72 540 72 592 c +72 647 89 691 124 725 c +159 759 204 776 261 776 c +297 776 327 773 350 768 c +373 763 400 752 430 735 c +430 643 l +377 682 323 702 266 702 c + +ce} _d +/T{512 0 56 18 456 766 sc +214 18 m +214 694 l +56 694 l +56 766 l +456 766 l +456 694 l +298 694 l +298 18 l +214 18 l + +ce} _d +/U{512 0 61 8 451 766 sc +402 58 m +369 25 321 8 256 8 c +191 8 143 25 110 58 c +77 91 61 143 61 213 c +61 766 l +147 766 l +147 233 l +147 178 156 139 174 116 c +193 93 221 82 258 82 c +295 82 323 93 341 116 c +360 139 369 178 369 233 c +369 766 l +451 766 l +451 213 l +451 143 435 91 402 58 c + +ce} _d +/V{512 0 31 18 481 766 sc +259 90 m +397 766 l +481 766 l +307 18 l +205 18 l +31 766 l +119 766 l +257 90 l +259 90 l + +ce} _d +/X{512 0 51 18 461 766 sc +257 469 m +259 469 l +374 766 l +459 766 l +307 402 l +461 18 l +369 18 l +255 331 l +253 331 l +138 18 l +51 18 l +205 402 l +53 766 l +143 766 l +257 469 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +0 0 translate +0 0 504 72 rectclip +gsave +0 0 m +504 0 l +504 72 l +0 72 l +cl +1 setgray +fill +grestore +gsave +0 36 m +504 36 l +504 72 l +0 72 l +0 36 l +cl +grestore +0 setgray +/WenQuanYiZenHei-0 16.000 selectfont +gsave + +55.4375 49.2031 translate +0 rotate +0 0 m /W glyphshow +13.1094 0 m /e glyphshow +21.7344 0 m /n glyphshow +30.3594 0 m /Q glyphshow +40.9062 0 m /u glyphshow +49.5312 0 m /a glyphshow +58.1562 0 m /n glyphshow +66.7812 0 m /Y glyphshow +75.7344 0 m /i glyphshow +79.5625 0 m /space glyphshow +84.3594 0 m /Z glyphshow +92.9844 0 m /e glyphshow +101.609 0 m /n glyphshow +110.234 0 m /space glyphshow +115.031 0 m /H glyphshow +125.578 0 m /e glyphshow +134.203 0 m /i glyphshow +138.031 0 m /colon glyphshow +142.828 0 m /space glyphshow +147.625 0 m /A glyphshow +156.578 0 m /B glyphshow +166.484 0 m /C glyphshow +176.391 0 m /D glyphshow +186.938 0 m /E glyphshow +195.891 0 m /F glyphshow +203.562 0 m /G glyphshow +214.109 0 m /H glyphshow +224.656 0 m /I glyphshow +228.812 0 m /J glyphshow +235.203 0 m /K glyphshow +245.109 0 m /L glyphshow +253.109 0 m /M glyphshow +266.219 0 m /N glyphshow +276.766 0 m /O glyphshow +287.312 0 m /P glyphshow +296.266 0 m /Q glyphshow +306.812 0 m /R glyphshow +316.719 0 m /S glyphshow +326.625 0 m /T glyphshow +334.625 0 m /U glyphshow +344.859 0 m /V glyphshow +353.812 0 m /W glyphshow +366.922 0 m /X glyphshow +375.547 0 m /Y glyphshow +384.5 0 m /Z glyphshow +grestore +gsave +0 0 m +504 0 l +504 36 l +0 36 l +0 0 l +cl +grestore +/WenQuanYiZenHeiMono-0 16.000 selectfont +gsave + +52 13.2031 translate +0 rotate +0 0 m /W glyphshow +8 0 m /e glyphshow +16 0 m /n glyphshow +24 0 m /Q glyphshow +32 0 m /u glyphshow +40 0 m /a glyphshow +48 0 m /n glyphshow +56 0 m /Y glyphshow +64 0 m /i glyphshow +72 0 m /space glyphshow +80 0 m /Z glyphshow +88 0 m /e glyphshow +96 0 m /n glyphshow +104 0 m /space glyphshow +112 0 m /H glyphshow +120 0 m /e glyphshow +128 0 m /i glyphshow +136 0 m /space glyphshow +144 0 m /M glyphshow +152 0 m /o glyphshow +160 0 m /n glyphshow +168 0 m /o glyphshow +176 0 m /colon glyphshow +184 0 m /space glyphshow +192 0 m /A glyphshow +200 0 m /B glyphshow +208 0 m /C glyphshow +216 0 m /D glyphshow +224 0 m /E glyphshow +232 0 m /F glyphshow +240 0 m /G glyphshow +248 0 m /H glyphshow +256 0 m /I glyphshow +264 0 m /J glyphshow +272 0 m /K glyphshow +280 0 m /L glyphshow +288 0 m /M glyphshow +296 0 m /N glyphshow +304 0 m /O glyphshow +312 0 m /P glyphshow +320 0 m /Q glyphshow +328 0 m /R glyphshow +336 0 m /S glyphshow +344 0 m /T glyphshow +352 0 m /U glyphshow +360 0 m /V glyphshow +368 0 m /W glyphshow +376 0 m /X glyphshow +384 0 m /Y glyphshow +392 0 m /Z glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/type3.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/type3.eps deleted file mode 100644 index 9c9645b47cf0..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_backend_ps/type3.eps +++ /dev/null @@ -1,112 +0,0 @@ -%!PS-Adobe-3.0 EPSF-3.0 -%%Orientation: portrait -%%BoundingBox: 18.0 180.0 594.0 612.0 -%%EndComments -%%BeginProlog -/mpldict 11 dict def -mpldict begin -/d { bind def } bind def -/m { moveto } d -/l { lineto } d -/r { rlineto } d -/c { curveto } d -/cl { closepath } d -/ce { closepath eofill } d -/box { - m - 1 index 0 r - 0 exch r - neg 0 r - cl - } d -/clipbox { - box - clip - newpath - } d -/sc { setcachedevice } d -%!PS-Adobe-3.0 Resource-Font -%%Creator: Converted from TrueType to Type 3 by Matplotlib. -10 dict begin -/FontName /DejaVuSans def -/PaintType 0 def -/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def -/FontBBox [-2090 -948 3673 2524] def -/FontType 3 def -/Encoding [/I /J /slash] def -/CharStrings 4 dict dup begin -/.notdef 0 def -/I{604 0 201 0 403 1493 sc -201 1493 m -403 1493 l -403 0 l -201 0 l -201 1493 l - -ce} d -/J{604 0 -106 -410 403 1493 sc -201 1493 m -403 1493 l -403 104 l -403 -76 369 -207 300 -288 c -232 -369 122 -410 -29 -410 c --106 -410 l --106 -240 l --43 -240 l -46 -240 109 -215 146 -165 c -183 -115 201 -25 201 104 c -201 1493 l - -ce} d -/slash{690 0 0 -190 690 1493 sc -520 1493 m -690 1493 l -170 -190 l -0 -190 l -520 1493 l - -ce} d -end readonly def - -/BuildGlyph { - exch begin - CharStrings exch - 2 copy known not {pop /.notdef} if - true 3 1 roll get exec - end -} d - -/BuildChar { - 1 index /Encoding get exch get - 1 index /BuildGlyph get exec -} d - -FontName currentdict end definefont pop -end -%%EndProlog -mpldict begin -18 180 translate -576 432 0 0 clipbox -gsave -0 0 m -576 0 l -576 432 l -0 432 l -cl -1.000 setgray -fill -grestore -0.000 setgray -/DejaVuSans findfont -12.000 scalefont -setfont -gsave -288.000000 216.000000 translate -0.000000 rotate -0.000000 0 m /I glyphshow -3.539062 0 m /slash glyphshow -7.582031 0 m /J glyphshow -grestore - -end -showpage diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output.svg index 8c912e5cd66c..8dea73a697d6 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output.svg +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output.svg @@ -1,1102 +1,798 @@ - - + + + + + + 2026-04-02T23:47:58.161774 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - - - - - - - - - + - - - - - + + + + + - - - - - - + - + - - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - + - + - - - - - - + + + + + + - - - - - - + - + - - - - - - + + + + + + - - - - - - + - + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - - + - + - - - - - - - - - - - - - - - - +M 947 1747 +Q 947 1113 1208 752 +Q 1469 391 1925 391 +Q 2381 391 2643 752 +Q 2906 1113 2906 1747 +Q 2906 2381 2643 2742 +Q 2381 3103 1925 3103 +Q 1469 3103 1208 2742 +Q 947 2381 947 1747 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + - - - - - - - - - + - + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - + + - + - - + + - - - - + - - + - + - - - - - - - - - - - - - +M 2181 722 +Q 2541 722 2730 984 +Q 2919 1247 2919 1747 +Q 2919 2247 2730 2509 +Q 2541 2772 2181 2772 +Q 1825 2772 1636 2509 +Q 1447 2247 1447 1747 +Q 1447 1247 1636 984 +Q 1825 722 2181 722 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + - - + + - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg index 9fe5ce39b941..2922e6bd59f4 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg @@ -6,11 +6,11 @@ - 2024-03-27T18:41:46.786798 + 2026-03-13T23:13:43.457201 image/svg+xml - Matplotlib v3.9.0.dev1430+g883dca40e7, https://matplotlib.org/ + Matplotlib v3.11.0.dev2075+g5a13dc378e, https://matplotlib.org/ @@ -21,61 +21,61 @@ - - - + - - - - - @@ -84,8 +84,8 @@ L 0 -4 - @@ -93,7 +93,7 @@ L 0 4 - 0 + 0 @@ -108,7 +108,7 @@ L 0 4 - 1 + 1 @@ -123,7 +123,7 @@ L 0 4 - 2 + 2 @@ -138,7 +138,7 @@ L 0 4 - 3 + 3 @@ -153,7 +153,7 @@ L 0 4 - 4 + 4 @@ -168,7 +168,7 @@ L 0 4 - 5 + 5 @@ -183,7 +183,7 @@ L 0 4 - 6 + 6 @@ -198,7 +198,7 @@ L 0 4 - 7 + 7 @@ -213,7 +213,7 @@ L 0 4 - 8 + 8 @@ -228,19 +228,19 @@ L 0 4 - 9 + 9 - nonbold-xlabel + nonbold-xlabel - @@ -249,8 +249,8 @@ L 4 0 - @@ -258,7 +258,7 @@ L -4 0 - 0 + 0 @@ -273,7 +273,7 @@ L -4 0 - 1 + 1 @@ -288,7 +288,7 @@ L -4 0 - 2 + 2 @@ -303,7 +303,7 @@ L -4 0 - 3 + 3 @@ -318,7 +318,7 @@ L -4 0 - 4 + 4 @@ -333,7 +333,7 @@ L -4 0 - 5 + 5 @@ -348,7 +348,7 @@ L -4 0 - 6 + 6 @@ -363,7 +363,7 @@ L -4 0 - 7 + 7 @@ -378,7 +378,7 @@ L -4 0 - 8 + 8 @@ -393,20 +393,20 @@ L -4 0 - 9 + 9 - bold-ylabel + bold-ylabel - bold-title + bold-title - + diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg index 7184700caf17..07d400c94e03 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg @@ -6,11 +6,11 @@ - 2022-08-09T18:12:28.920123 + 2026-04-03T00:27:07.918200 image/svg+xml - Matplotlib v3.6.0.dev2839+gb0bf8fb1de.d20220809, https://matplotlib.org/ + Matplotlib v3.11.0.dev2222+g4230aa142, https://matplotlib.org/ @@ -29,10 +29,13757 @@ z " style="fill: #ffffff"/> - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg index 373103f61b9f..f2671f5fe247 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg @@ -6,11 +6,11 @@ - 2022-08-09T18:42:37.025191 + 2026-04-03T00:27:08.459316 image/svg+xml - Matplotlib v3.6.0.dev2840+g372782e258.d20220809, https://matplotlib.org/ + Matplotlib v3.11.0.dev2222+g4230aa142, https://matplotlib.org/ @@ -29,7 +29,24 @@ z " style="fill: #ffffff"/> - There are 几个汉字 in between! + There are basic characters + ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz + 0123456789 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ + and accented characters + ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß + àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ + ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ + ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ + ŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞş + ŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ + ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟ + ƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ + ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟ + ǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ + ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ + ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ + ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏 + in between! diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_fixed_aspect.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_fixed_aspect.png index 0fd7a35e3303..1411966bfdf2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_fixed_aspect.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_fixed_aspect.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_inset_rasterized.pdf b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_inset_rasterized.pdf new file mode 100644 index 000000000000..46facee23546 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_inset_rasterized.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.pdf b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.pdf index 9214e27e528c..b7a90a37dcf1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.pdf and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.png index f2827b5b78ea..aea824adc864 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.svg b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.svg index 1e6229adef8a..d08fff8810b2 100644 --- a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.svg +++ b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight.svg @@ -1,531 +1,1062 @@ - - + + + + + + 2025-04-15T18:37:34.962367 + image/svg+xml + + + Matplotlib v3.11.0.dev671+ga5ce26bb9e.d20250415, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - +" style="fill: #ffffff"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - +"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - +"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - +"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - +"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - +"/> - +" style="fill: #ffffff; stroke: #000000; stroke-linejoin: miter"/> - - - + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" clip-path="url(#p95703875b0)" style="fill: #1f77b4"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #1f77b4"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #1f77b4"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #1f77b4"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #1f77b4"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #ff7f0e"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #ff7f0e"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #ff7f0e"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #ff7f0e"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #ff7f0e"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #2ca02c"/> - - + + - - + +" clip-path="url(#p95703875b0)" style="fill: #2ca02c"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #2ca02c"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #2ca02c"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #d62728"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #d62728"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #d62728"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #d62728"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #d62728"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #9467bd"/> - - + +" clip-path="url(#p95703875b0)" style="fill: #9467bd"/> - - - - - + + - - + + - - + + - - - - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + - - + +" style="fill: #ffffff; opacity: 0.8; stroke: #cccccc; stroke-linejoin: miter"/> - - + +" style="fill: #1f77b4"/> - - + +"/> - - + +" style="fill: #ff7f0e"/> - - + +"/> - - + + + + + + + + + + +"/> + + + + + + - - + + + + + + + + + + + + + + + + +" style="fill: #2ca02c"/> - - + +"/> - - + +" style="fill: #d62728"/> - - + +"/> - - + +" style="fill: #9467bd"/> - - + +"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png index 657eaed42267..dbaf03567fc7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.pdf b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.pdf index 5e3b389b1dea..26c9dd7576b2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.pdf and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.png index b0aedc586ef6..3349e460c76e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg index 505ea417de02..7154475f7320 100644 --- a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg +++ b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg @@ -1,221 +1,162 @@ - - + + + + + + 2026-04-02T23:53:17.806175 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - + - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.pdf b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.pdf index 0e43da0d15a0..82c5dba74f90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.pdf and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.png index 4b781c866dd9..dd685652c72d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.svg b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.svg index 5cf932d60cb7..a5c7e7582ebe 100644 --- a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.svg +++ b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_legend.svg @@ -1,16 +1,16 @@ - + - 2022-07-07T13:08:55.409721 + 2026-04-02T23:53:17.217505 image/svg+xml - Matplotlib v3.5.0.dev5238+g5d127c48a6.d20220707, https://matplotlib.org/ + Matplotlib v3.11.0.dev2221+gef9968a6c, https://matplotlib.org/ @@ -21,82 +21,39 @@ - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - + - + - - + - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - + + - + - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - + + - + - + - + - - + - - - - - - - - - - - - - - - - - - - - - - + - + - - - + - - - - - - - - - + + + + + + - - - - - - - - - + - + - + - + - + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + - + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - + + + + + + + + + + - + - - + + - + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_non_default.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_non_default.png index 453ea566804d..51317ece2560 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_non_default.png and b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_suptile_non_default.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.pdf deleted file mode 100644 index c4019065ae7a..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.png index 41c593aae20b..fcc73e5cc1b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.svg deleted file mode 100644 index f2b8d40dced5..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__add_positions.svg +++ /dev/null @@ -1,811 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.pdf deleted file mode 100644 index 3da31c23f37b..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.png index 25a4b88c1812..c60c2b92b74a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.svg deleted file mode 100644 index dea1649a020e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__append_positions.svg +++ /dev/null @@ -1,812 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.pdf deleted file mode 100644 index 24edb67d2989..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.png index dc66aa67f62b..765e44e5cc34 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.svg deleted file mode 100644 index ed927a817852..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__default.svg +++ /dev/null @@ -1,692 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.pdf deleted file mode 100644 index 350240c3a1ef..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.png index 2d19605709d1..283fc62315df 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.svg deleted file mode 100644 index 02e2f614bd11..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__extend_positions.svg +++ /dev/null @@ -1,798 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.pdf deleted file mode 100644 index 897d90653212..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.png index 83410f1f4c5f..2c5adf013a9c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.svg deleted file mode 100644 index 1be6be46f002..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_color.svg +++ /dev/null @@ -1,651 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.pdf deleted file mode 100644 index 1de0c4066bc0..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.png index c2b74c3df453..e3d61cc50058 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.svg deleted file mode 100644 index c6864997c697..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linelength.svg +++ /dev/null @@ -1,762 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.pdf deleted file mode 100644 index 17ac2ec9a703..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.png index bc551d5cdcb3..821b14d61f95 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.svg deleted file mode 100644 index ebb92a50afb4..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_lineoffset.svg +++ /dev/null @@ -1,718 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.pdf deleted file mode 100644 index 4a4c75b5a6af..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.png index b7a95455eb7f..b01e28fadddb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.svg deleted file mode 100644 index 2da07c3d9810..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linestyle.svg +++ /dev/null @@ -1,655 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.pdf deleted file mode 100644 index 240cf285f2cf..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.png index 75cdf721a585..55ce408ee2fe 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.svg deleted file mode 100644 index a4e63b10de3b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_linewidth.svg +++ /dev/null @@ -1,696 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.pdf deleted file mode 100644 index e8eb805afcfa..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.png index 3d914bf61421..8445a0065101 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.svg deleted file mode 100644 index 6d8187c688ff..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_orientation.svg +++ /dev/null @@ -1,686 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.pdf deleted file mode 100644 index 5e7c3569af07..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.png index 3bf008464e9b..e026997d029d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.svg deleted file mode 100644 index c6fb8e815924..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__set_positions.svg +++ /dev/null @@ -1,760 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.pdf deleted file mode 100644 index 78ffdf083661..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.png index 1e1e4e64bc0c..bd086fc047b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.svg deleted file mode 100644 index 8d804f5b85fe..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation.svg +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.pdf b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.pdf deleted file mode 100644 index aa7db682592b..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.png b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.png index 813fbda30b01..18788420ed17 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.png and b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.svg b/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.svg deleted file mode 100644 index 856034c6ffbb..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_collections/EventCollection_plot__switch_orientation__2x.svg +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_collections/cap_and_joinstyle.png b/lib/matplotlib/tests/baseline_images/test_collections/cap_and_joinstyle.png index ef18c1311d89..491085095296 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_collections/cap_and_joinstyle.png and b/lib/matplotlib/tests/baseline_images/test_collections/cap_and_joinstyle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_locationing.png b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_locationing.png index e2855f2a2427..d9b7e396a580 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_locationing.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_locationing.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_sharing.png b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_sharing.png index 92d7ea5d956b..e82603f0d296 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_sharing.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_sharing.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_with_orientation.png b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_with_orientation.png index 2736884f74f2..e71e390536b1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_with_orientation.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/cbar_with_orientation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_change_lim_scale.png b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_change_lim_scale.png index 2dfb3b0366da..ee5c887ae77f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_change_lim_scale.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_change_lim_scale.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_extend_alpha.png b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_extend_alpha.png index 68cbf3d341f3..411a2511dc54 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_extend_alpha.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_extend_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_keeping_xlabel.png b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_keeping_xlabel.png index 410b9f5b0878..8d3ab44f3e7a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_keeping_xlabel.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_keeping_xlabel.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png b/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png index b139a4664c17..adfd661fb6e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/double_cbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg b/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg index 85f92c3c8d64..585548061a1d 100644 --- a/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg +++ b/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg @@ -6,11 +6,11 @@ - 2021-12-06T14:23:33.155376 + 2026-03-12T19:44:58.015301 image/svg+xml - Matplotlib v3.6.0.dev954+ged9e9c2ef2.d20211206, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -37,100 +37,98 @@ L 54 8.64 z " style="fill: #ffffff"/> - - - +" clip-path="url(#p207280a877)" style="fill: #440154"/> +" clip-path="url(#p207280a877)" style="fill: #3b528b"/> +" clip-path="url(#p207280a877)" style="fill: #21918c"/> +" clip-path="url(#p207280a877)" style="fill: #5ec962"/> +" clip-path="url(#p207280a877)" style="fill: #fde725"/> - - + - 0 + 0 - + - 1 + 1 - + - 2 + 2 - + - 3 + 3 - + - 4 + 4 + - + + diff --git a/lib/matplotlib/tests/baseline_images/test_colors/boundarynorm_and_colorbar.png b/lib/matplotlib/tests/baseline_images/test_colors/boundarynorm_and_colorbar.png index 59062a1a1900..99b97338a2e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colors/boundarynorm_and_colorbar.png and b/lib/matplotlib/tests/baseline_images/test_colors/boundarynorm_and_colorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colors/levels_and_colors.png b/lib/matplotlib/tests/baseline_images/test_colors/levels_and_colors.png index bb0e9a2538da..f8065ce646c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colors/levels_and_colors.png and b/lib/matplotlib/tests/baseline_images/test_colors/levels_and_colors.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colors/light_source_shading_topo.png b/lib/matplotlib/tests/baseline_images/test_colors/light_source_shading_topo.png index 222ebca02d82..b014a8f1eaa5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colors/light_source_shading_topo.png and b/lib/matplotlib/tests/baseline_images/test_colors/light_source_shading_topo.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png b/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png new file mode 100644 index 000000000000..c2169461d075 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout1.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout1.png index 25eade2b6297..bfbbfd6f7eeb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout1.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout10.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout10.png index 1d23c1db38de..2f0998c5e66c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout10.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11.png index f337d370dc33..e850318d8d8f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11rat.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11rat.png index 534903300f7a..4d150d3cf06f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11rat.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout11rat.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout12.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout12.png index 07e35e9d800a..6f5625ae2605 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout12.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout13.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout13.png index 4233f58a8ce4..9fa680160c3d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout13.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout14.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout14.png index cfe9dca14c88..3a908eb8bf58 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout14.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout15.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout15.png index 6790ac835838..02f7f29fe7de 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout15.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout16.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout16.png index 841cf77b7e08..f2a8172ffb7d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout16.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout17.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout17.png index 8af582f00926..7ac78631798e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout17.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout2.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout2.png index 575cd9a45b7e..1d8e03c3cd54 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout2.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout3.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout3.png index ae6420dd04e9..77e90bc09062 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout3.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout3.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.png index ef6d9e417f91..42b87f03f1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout5.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout5.png index 89e71b765154..297391b17837 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout5.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout5.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout6.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout6.png index 6ba96e41a34d..190ba3758ffd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout6.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout6.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout8.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout8.png index a239947ca46c..99ba9f294a7a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout8.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout8.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout9.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout9.png index 2ac44b8a18ac..a1e9da294e64 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout9.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout9.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bbox.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bbox.png index f21e247f21fa..e638623b560b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bbox.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bbox.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bboxtight.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bboxtight.png index 6e5414ee0a25..e7dea38efcc9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bboxtight.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_bboxtight.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbar_location.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbar_location.png index 2ca5a5c29eb1..716c729f48a4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbar_location.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbar_location.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapH.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapH.png index d91bcdf22b7a..189474e52df0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapH.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapH.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapV.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapV.png index 1768fc2fdc35..231615afb00e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapV.png and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_colorbars_no_overlapV.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_suptitle_colorbar.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_suptitle_colorbar.png new file mode 100644 index 000000000000..df4ecaf939c7 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_suptitle_colorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_supylabel_colorbar.png b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_supylabel_colorbar.png new file mode 100644 index 000000000000..cd6e38e62f00 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/test_compressed_supylabel_colorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png index 11e17fc64d7e..7cdc21e820b9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_datetime_axis.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_disconnected_segments.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_disconnected_segments.png index ceb700e09de2..5a576317f6f3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_disconnected_segments.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_disconnected_segments.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_log_locator.svg b/lib/matplotlib/tests/baseline_images/test_contour/contour_log_locator.svg index a4a397104ef7..c1289945297d 100644 --- a/lib/matplotlib/tests/baseline_images/test_contour/contour_log_locator.svg +++ b/lib/matplotlib/tests/baseline_images/test_contour/contour_log_locator.svg @@ -6,11 +6,11 @@ - 2022-12-11T14:01:58.700972 + 2026-03-12T19:44:40.936001 image/svg+xml - Matplotlib v3.6.0.dev2642+g907c10911d.d20221211, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -37,7 +37,7 @@ L 57.6 41.472 z " style="fill: #ffffff"/> - + - - - - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #48186a"/> - - - - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #424086"/> - - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #33638d"/> - - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #26828e"/> - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #1fa088"/> - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #3fbc73"/> - - +" clip-path="url(#pc21cd2e7a9)" style="fill: #84d44b"/> +" clip-path="url(#pc21cd2e7a9)" style="fill: #d8e219"/> - - + - + - - + - + - + - - - + + - + - + - - - + + - + - + - - - + + - + - + - - - + + @@ -2963,74 +2941,74 @@ z - - + - - + + - + - - - + + + - + - - - + + + - + - - - + + + - + - - - + + + @@ -3065,76 +3043,74 @@ L 361.152 41.472 z " style="fill: #ffffff"/> - - - +" clip-path="url(#pec182bf4fc)" style="fill: #48186a"/> +" clip-path="url(#pec182bf4fc)" style="fill: #424086"/> +" clip-path="url(#pec182bf4fc)" style="fill: #33638d"/> +" clip-path="url(#pec182bf4fc)" style="fill: #26828e"/> +" clip-path="url(#pec182bf4fc)" style="fill: #1fa088"/> +" clip-path="url(#pec182bf4fc)" style="fill: #3fbc73"/> +" clip-path="url(#pec182bf4fc)" style="fill: #84d44b"/> +" clip-path="url(#pec182bf4fc)" style="fill: #d8e219"/> - + + - - + - + - - - - - - + + + + - + - + - - - - - + + + + - + - - - - - + + + + + - + - + - - - - - + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + + - + diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.pdf b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.pdf index f46eb90b46a2..93765a09f0ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.pdf and b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.png index 2b257bc152f1..584d8c8fac72 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.svg b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.svg index 8a3952e2f846..929487f29122 100644 --- a/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.svg +++ b/lib/matplotlib/tests/baseline_images/test_contour/contour_manual_labels.svg @@ -1,23 +1,23 @@ - + - + - 2020-11-06T19:00:48.952407 + 2026-03-13T23:13:18.517824 image/svg+xml - Matplotlib v3.3.2.post1573+gcdb08ceb8, https://matplotlib.org/ + Matplotlib v3.11.0.dev2075+g5a13dc378e, https://matplotlib.org/ - + @@ -26,7 +26,7 @@ L 432 144 L 432 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -35,81 +35,81 @@ L 388.8 128.16 L 388.8 17.28 L 54 17.28 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + - + - + - + - + @@ -118,77 +118,70 @@ L 0 3.5 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - - - - - + + - - - - - + - - - - - + - - - - - + - - - + +" clip-path="url(#pd10b4bf82c)" style="fill: none; stroke: #90d743; stroke-width: 1.5"/> + - +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - + - + - - + - + +" transform="scale(0.015625)"/> - - - + + + - + - + - - + +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_rasterization.pdf b/lib/matplotlib/tests/baseline_images/test_contour/contour_rasterization.pdf new file mode 100644 index 000000000000..da86441fdd91 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_contour/contour_rasterization.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contour_test_label_transforms.png b/lib/matplotlib/tests/baseline_images/test_contour/contour_test_label_transforms.png index 08a321297e9d..5cf16ce9899d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_contour/contour_test_label_transforms.png and b/lib/matplotlib/tests/baseline_images/test_contour/contour_test_label_transforms.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png b/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png new file mode 100644 index 000000000000..18d949773ded Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_contour/contourf_hatch_colors.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/DateFormatter_fractionalSeconds.png b/lib/matplotlib/tests/baseline_images/test_dates/DateFormatter_fractionalSeconds.png index 67c50f3eded3..ea96ff664f32 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/DateFormatter_fractionalSeconds.png and b/lib/matplotlib/tests/baseline_images/test_dates/DateFormatter_fractionalSeconds.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/RRuleLocator_bounds.png b/lib/matplotlib/tests/baseline_images/test_dates/RRuleLocator_bounds.png index c65bff221274..7792ab44c149 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/RRuleLocator_bounds.png and b/lib/matplotlib/tests/baseline_images/test_dates/RRuleLocator_bounds.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/date_axhline.png b/lib/matplotlib/tests/baseline_images/test_dates/date_axhline.png index 57c4ad763f37..ec5accd3239c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/date_axhline.png and b/lib/matplotlib/tests/baseline_images/test_dates/date_axhline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/date_axhspan.png b/lib/matplotlib/tests/baseline_images/test_dates/date_axhspan.png index f13d879e9f53..ef6ae34292c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/date_axhspan.png and b/lib/matplotlib/tests/baseline_images/test_dates/date_axhspan.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/date_axvline.png b/lib/matplotlib/tests/baseline_images/test_dates/date_axvline.png index f6b19c37af88..3019e344541a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/date_axvline.png and b/lib/matplotlib/tests/baseline_images/test_dates/date_axvline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/date_axvspan.png b/lib/matplotlib/tests/baseline_images/test_dates/date_axvspan.png index c0714e9d0df1..241a745df79d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/date_axvspan.png and b/lib/matplotlib/tests/baseline_images/test_dates/date_axvspan.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_dates/date_inverted_limit.png b/lib/matplotlib/tests/baseline_images/test_dates/date_inverted_limit.png index 4b8d15155717..119cd45b6316 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_dates/date_inverted_limit.png and b/lib/matplotlib/tests/baseline_images/test_dates/date_inverted_limit.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.png index 02c99021186a..42fd1f453d69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.svg b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.svg index 9ec923dc434e..c4d1b5c412c1 100644 --- a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.svg +++ b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_labels.svg @@ -1,2724 +1,1649 @@ - - + + + + + + 2026-04-02T23:50:14.646768 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - + - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - - - - - - + - + - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + - - - - - - - - - + - + - + - - + + - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - - + + - + - + - + - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - + - - + + - + - + - + + + + + + + + + + + + + - + - - - - - + + + + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + + + + + + + - - - - - - + - + - - - - - + + + + + + + + + - - - - - - + - + - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + - + + + + + - + - - - - - + + + + + - - - - - - - - - + + - + - - - + + + + + - - - - - - - + + - + - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - + - - - - + + + + - + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - + - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + - - + + - - + + - - + + - - + + + + - - - - - - - - + + + - + - + - - - - + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - + + + + + + + + + + + + - + + + + + + + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - + - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + - - - - + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - + + + + + + + + + + + + - + + + + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - + - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - + + - - + + - - + + - - + + - - + + + + - - - - - - - - + + + - + - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - + + + + + + + + + + + + - + + + + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - + + + + + + - - - - - - - + + - + - - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - + + - - + + - + - - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + - - + + + + - + - - - - - - - + + + + - - + + - + - + - - - - - - + + + + + + - + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_constrained.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_constrained.png index 78dffc18e20c..9d2003008d19 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_constrained.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_constrained.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_tight.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_tight.png index f719ae6931f0..641f5ab24c69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_tight.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_align_titles_tight.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.pdf b/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.pdf deleted file mode 100644 index 4c9d292ad180..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.png index 9e11ae73386b..557337ae37b7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.svg b/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.svg deleted file mode 100644 index d3be94a5e07c..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_figure/figure_legend.svg +++ /dev/null @@ -1,882 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.pdf b/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.pdf deleted file mode 100644 index a240dc52d2ed..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.png index 5e208019682c..811c7c23219e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.svg b/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.svg deleted file mode 100644 index 04c69d7df02a..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_figure/figure_suptitle.svg +++ /dev/null @@ -1,541 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.pdf b/lib/matplotlib/tests/baseline_images/test_figure/figure_today.pdf deleted file mode 100644 index d1909c6c80ee..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.png b/lib/matplotlib/tests/baseline_images/test_figure/figure_today.png index 110cee484e75..411e24fc2123 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.png and b/lib/matplotlib/tests/baseline_images/test_figure/figure_today.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.svg b/lib/matplotlib/tests/baseline_images/test_figure/figure_today.svg deleted file mode 100644 index e94c8e3c72c2..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_figure/figure_today.svg +++ /dev/null @@ -1,715 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure.png b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure.png index bcfc4494b944..61078e30e64f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure.png and b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_double.png b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_double.png index 594ce7d4e72f..f15229e7684c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_double.png and b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_double.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_ss.png b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_ss.png index d06a5db7a5dd..f4c3c9152e54 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_ss.png and b/lib/matplotlib/tests/baseline_images/test_figure/test_subfigure_ss.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.pdf b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.pdf new file mode 100644 index 000000000000..bd1423219b57 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.png b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.png new file mode 100644 index 000000000000..db85f9bf0923 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.svg b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.svg new file mode 100644 index 000000000000..2cd3fc88180f --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_ft2font/last_resort.svg @@ -0,0 +1,806 @@ + + + + + + + + 2026-03-12T19:47:32.918677 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/affine_fill_to_edges.png b/lib/matplotlib/tests/baseline_images/test_image/affine_fill_to_edges.png new file mode 100644 index 000000000000..88858eda5d94 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/affine_fill_to_edges.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/alignment_half_display_pixels.png b/lib/matplotlib/tests/baseline_images/test_image/alignment_half_display_pixels.png new file mode 100644 index 000000000000..e822ced620f2 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/alignment_half_display_pixels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.pdf b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.pdf index e0baa115a6b3..4fa9dc748b02 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.pdf and b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.png b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.png index c593b2163997..0d2b7b459ecb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.png and b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.svg b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.svg index 406a278f2f3f..268c9d14f605 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/bbox_image_inverted.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-01-30T01:57:12.969851 + image/svg+xml + + + Matplotlib v3.11.0.dev1729+g1f7cad29d, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,57 +35,57 @@ L 414.72 307.584 L 414.72 41.472 L 57.6 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + @@ -83,47 +94,47 @@ L 0 3.5 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + @@ -131,22 +142,22 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> diff --git a/lib/matplotlib/tests/baseline_images/test_image/downsampling.png b/lib/matplotlib/tests/baseline_images/test_image/downsampling.png new file mode 100644 index 000000000000..e7c6ca7b1c35 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/downsampling.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png b/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png new file mode 100644 index 000000000000..daa1dc248116 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.pdf b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.pdf index f5923c481fa7..f4d80f2a5ec4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.pdf and b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.png b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.png index 880cc891887b..301a55c03ad5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.png and b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.svg b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.svg index dd6884710e69..47b489e391bb 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/image_alpha.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/image_alpha.svg @@ -6,11 +6,11 @@ - 2023-04-16T19:11:24.492650 + 2026-01-30T01:57:05.781655 image/svg+xml - Matplotlib v3.8.0.dev855+gc9636b5044.d20230417, https://matplotlib.org/ + Matplotlib v3.11.0.dev1729+g1f7cad29d, https://matplotlib.org/ @@ -37,7 +37,7 @@ L 72 150.352941 z " style="fill: #ffffff"/> - + @@ -239,7 +239,7 @@ L 229.552941 150.352941 z " style="fill: #ffffff"/> - + @@ -421,9 +421,9 @@ L 387.105882 150.352941 z " style="fill: #ffffff"/> - + +iVBORw0KGgoAAAANSUhEUgAAALYAAAC2CAYAAAB08HcEAAAC8klEQVR4nO3cMYqeBRSG0ftLCsHYOmBptiAIgqgIip2pBoug01gJdraDYJfUWo4MCFaDnSBYzAKyBMHOdCIopIh8KbIGufBwzgbe5uGW9zTfHpez4PjrtDE7549+WNmdmbmYq5Xdj0/vrezOzPxxfLey+9LKKvzPhE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJuk08+/Kt9V/nr66MTt37/63sjsz89OznQ+zT44vVnZnZq7nwcqui02SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbpDvHV6+sDB+vr8zO5dIr35mZ8093dh/MmzvDM/P4y3dWdl1skoRNkrBJEjZJwiZJ2CQJmyRhkyRskoRNkrBJEjZJwiZJ2CQJmyRhkyRskoRNkrBJEjZJwiZJ2CQJmyRhkyRskoRNkrBJEjZJwibp9Mvx7uXG8Mun243ZeX9+Xdl94Wxp92Zpd+Z465uVXRebJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEk6fXj8vPJt9fr0ycbs/Liy+sLD4/eV3T8v7q3szsx8f/XZyq6LTZKwSRI2ScImSdgkCZskYZMkbJKETZKwSRI2ScImSdgkCZskYZMkbJKETZKwSRI2ScImSdgkCZskYZMkbJKETZKwSRI2ScImSdgkCZukO5/P1crw2c3K7Hx0/42d4Zl5bb5e2X376reV3ZmZ278/WNl1sUkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbJGGTJGyShE2SsEkSNknCJknYJAmbpOcW2THVhlsXJwAAAABJRU5ErkJggg==" id="image2b7b2b7d47" transform="scale(1 -1) translate(0 -131.04)" x="387.36" y="-150.48" width="131.04" height="131.04"/> + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_bounds_handling.png b/lib/matplotlib/tests/baseline_images/test_image/image_bounds_handling.png new file mode 100644 index 000000000000..3a817f331c42 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/image_bounds_handling.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_clip.pdf b/lib/matplotlib/tests/baseline_images/test_image/image_clip.pdf index 63581cea0a06..a30b6ffe64e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_clip.pdf and b/lib/matplotlib/tests/baseline_images/test_image/image_clip.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_clip.png b/lib/matplotlib/tests/baseline_images/test_image/image_clip.png index 6a25e74d2987..79db3d01c382 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_clip.png and b/lib/matplotlib/tests/baseline_images/test_image/image_clip.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_clip.svg b/lib/matplotlib/tests/baseline_images/test_image/image_clip.svg index ac14e6b273f0..f6f8d2780a22 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/image_clip.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/image_clip.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.564380 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,272 +35,277 @@ L 369.216 307.584 L 369.216 41.472 L 103.104 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - + + - + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + - + - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + - + - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + - + - - - - - + + + + + - + - - - - - + + + + + @@ -298,151 +314,151 @@ z - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - - - - - + + + + + + - + - - - - - - + + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + @@ -450,27 +466,27 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - + - - + + + + + + 2026-03-12T19:45:18.012205 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,236 +35,240 @@ L 369.216 307.584 L 369.216 41.472 L 103.104 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - +" transform="scale(0.015625)"/> + + @@ -262,83 +277,83 @@ z - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + @@ -346,28 +361,28 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.pdf b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.pdf index e7d205bfa8e0..b8cd42f9dde4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.pdf and b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.png b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.png index c9e9f343c5db..6c772a880861 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.png and b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.svg b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.svg index 7f1678715ba3..de9e2ca0d936 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/image_composite_alpha.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-03T19:56:08.323770 + image/svg+xml + + + Matplotlib v3.11.0.dev1757+g00c32c31d, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 576 432 L 576 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,112 +35,112 @@ L 468 388.8 L 468 43.2 L 122.4 43.2 z -" style="fill:#008000;"/> +" style="fill: #008000"/> - - + + +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + - + - + @@ -138,82 +149,82 @@ L 0 4 - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + - + - + @@ -221,8 +232,8 @@ L -4 0 - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.pdf b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.pdf index 85a87bddb14c..873ee0ef14ce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.pdf and b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.png b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.png index e7af5c29dd29..d02f898cbe52 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.png and b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.svg b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.svg index e63869fd57ff..f0fdcd26b455 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/image_composite_background.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-01-30T01:57:11.186332 + image/svg+xml + + + Matplotlib v3.11.0.dev1729+g1f7cad29d, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,64 +35,64 @@ L 342.6048 307.584 L 342.6048 41.472 L 129.7152 41.472 z -" style="fill:#ff0000;fill-opacity:0.5;"/> +" style="fill: #ff0000; fill-opacity: 0.5"/> - - + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + - + @@ -90,61 +101,61 @@ L 0 3.5 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + - + - + @@ -152,28 +163,28 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers.png index ec189bb949e5..2b13a801e1c8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers.png and b/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png index ce3404488ebb..8de0a4e34204 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png and b/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_endianess.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_endianess.png index 28a7ef2a66e9..8d3005b781db 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/imshow_endianess.png and b/lib/matplotlib/tests/baseline_images/test_image/imshow_endianess.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf index 1d14a9d2f60c..c7683cc6f939 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf and b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png index 72918a27fbc1..292d3b1c0c2a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png and b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg index 8123e200c27a..977667d6abc2 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg @@ -6,11 +6,11 @@ - 2023-04-16T19:34:05.748213 + 2026-02-08T04:35:48.823370 image/svg+xml - Matplotlib v3.8.0.dev855+gc9636b5044.d20230417, https://matplotlib.org/ + Matplotlib v3.11.0.dev1781+g34b8e3347, https://matplotlib.org/ @@ -29,167 +29,167 @@ z " style="fill: #ffffff"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAMr0lEQVR4nM2caYwcxRXHf1Xdu7OsD1gfYO/aGF/YrA+IUWTJAYdwBJMEECSEhA+RgCj5EIXDBHDAQEJAGEHsEAmRcAgBQkKKAoQQgRFHwMFgCARjbIwPjE/M4nuvObq78qHv6arZWXt3xk9q73TVq/de/esdVbWzFju2j1EYyBIi0yZNvGh4NeN1fL7crGSdfhOvNMi1hIZ37CYtbznZeRUKiRtPGr87+vzUhrk8vWsuUijumvA8c07aHvXNfflm3luwxCh8/qu/4c1z/xi9r93ayq3bLgLgZ2Pf4dIpH0V9e3e0ZsAsKR/MY9t2VDWZgSL3yykA2KUAcd+QrBNtyp/A+u1jAChOSK/CVxtGV1TyxbZ0/0Evx8fbx4FQbBxxQqqvhAKlMj5R6nMqA08l5QJg55VVkXFiroOZE3YhURnwWqd1sLXC2BmTd6bepfCYM2EbABMa96T68iq7MKYQHmzqUUUAxFtbJqWsslC83zuJHcUWTjlmF5cM3UpLmz/JNVvbWLj5MjZsbGX4mE7WXPyHPhXNeP52ujqGMGPaDh6Y+DemnuiHbGHXRJ7rHsGG/FhObNzD6U1+uEqN986csOvIZttP2rp9DBZg51VDqsNC8cjGeXR2DGXWtO1cNW1l1DdMuhSWjmXbizdy/qhfVqVo/JU7Wb7/Udp+dD+jHoi9NNe6hWdXnseqdZMZ3XqAh9qfTtlQTyoE6u1uLxc1WsLzOwsNyB6LzmIuNajbkzR0OQAs3/NX4OE+FS3f/ygATfuy2eNA4RhEQdJTaKRbNfo24PV7MgNNYaqxe7w0AFJ4/HzG2/RMzzGqoZPXPz+Z9sZOALYWWhh37yZO33sLrUMP4U2dUrEs9u6awAUbr2FvbzMTR+zmf4UhfGNnGxaCdSWLKwqr2HP8pzTLAkk7ZJ0B6vT8hcqElVQeN89YHr23P3cHox9uBuBbS97libmPR30Lpi9iOfcYlZx9+d28tmJx9L7oo0v53eK5eA2CzisPsfqiu6K+ZzedFttQZ3BCTOy811CZ871jeeul6wEYe9svUl3L15uBAXh9xa1ADM6H+8ez4sWbADh59rIUb7eX04aUFLUHKgKn02uqmAALLQOXHJssJ5Y7Mj1pEzhWHcAJ87Dd46ZzjiU8Jjy+BLujgebpB1j/zQdpuOkGAJ4aNZdzzrqb1/99CwDKsGVPkgiAP+ucJSxse5oXwjHXwKkvLKZz03Go4wv82nsjtqHO1arTawJA/Hb1JRlL7jn12ehzOQAiYXh/wOlL1pK1C1J9SYBunPFKn3oGkh5aPx8Au9ftI+fUiMo9GOoTUgA9YbXqcpP7nPq5c2iHrKMNIXW5TUhU1nPqBVCv669WPapTOfUEttiOlz54enUCp6T8Y6bpvqeWVFIWUijsvGvX2xYAdLmv3l5s9ziNR0Wcm+yoh23hQtlKCVyVduXZv1rGiPUFdn47x6GrxzE8uIm7/sMfw5zDV3rnmh9w+6wXATi0cxyTn1xK25sl9p3SiKcEntJdtdYenDDE7V4ndufQkNUPLgTg0JPjmLdsIWvxjw9TN9+Z2quIKjZrSf5Jn9zFHfwTgPY/L2PTvb4eXobvXnTtUeHBAPnQc4qu+SZweNsORp1fjN4LB5uOSKl7sDH63PKZk+orlOW+egLV4wSVs+hYlD8hrdwykT0z4wmJnHtESkVzDMjedps1W9ui96JrpZ6CY0dPrang2hRcGzHzH7dllsj14tvb5lzsOY4r6S004joSy/YYPbyL00bu4OKWD5nfVCTXuoXCrom8nW/g+QNzWL2vjY5DQ3FdiW17NOeKNFoxwN2FRkTgIY12GniR8Jz3Ftw7cDOvgua/6p8l7ZJjpQwBsN86lmE7XfbMllzxw5dY2P4qAPOW38TqC+M7mM80e5Jc6xbOBs5mA5DOS+e9cR2vfOcBAJauO5e//P0CRqxTdI6TqDP3ZewAtG2DTWGqsR0nfccvBDTlFXaPh9Vr0eU2ZQYdLqlENfKURDgCO+8iHUkpkF0PMMopX/JD2VaJEBJCoRQcmKboGmdTHOnwypencOy6XgC6Ps0x5+qljF7xFV3to3jmvtP5ydQPjEqe2jCXeYvuZ9i6vew+ezRfdw/hkfVn0OPleGn3DPJtJXY32zhDXJo8EdgwiLOuksJFtD03aY0AAWPbOxiey7O7cxjbN4/moRXfByA/Nc/l177JdXd+wH/yLSx6+Cp+yn+NSqY/sYw/3vcY85s6+dO+WTz28Tzuf+ZSX1ZriRPG72dc+wEOFo9h69ctsRV1BqgYFAHpOZLUU5JMPe5rzhi5mZbmXpq+shn7ToEx7xZQeYvfz36BlradXDj5E9peO1hRSeuKXr43eS1D27axeNa/8LoaGLOqxJhVJRo7bFqHHuSCUZ8wbXgHnmtFj+sEjytx3dr/as9xJI4jsSlJyvPqF50j6HUb2NfdjLIUPccHG0XXY8WWSZw58XMAeluHVFTS3Rpfh7z/xYmwHHpH+blFWdDRM4yVB6fwZe9wvJKETL4RGdtqQW6wnREnPnyvOQPqDDMZq0ukRt7qxhvDqx+6tAneyJt+t0VZtVLVCsu0VVhiUwUSmpdEW2pUSkaW1yTXLCNoqsBr45Tf64oqJ1OhLdPuv2iBr1rukYIfNJS1RSM1IW1rL940/qwEZM6ZQmXbQqN06UNz6tby0g8PrtShtc0gV2ObLRydwRr309oQeoTOCE2boV2PmcGD+5XHqtNl4rWlo2OIOY3ZWmg+mhawjwSsSz3Jl/5MqC8bRMrwyuFoC0ffkXo9whXQVx3DbZAh8vpnQ/Web5QB2MIrQ7JMrjbXJJWJMkMTvEkbM0b0Ywuz+eYbquQcGJq49H6gPKw0blwxZBLA+GCpuEHHanDjfuWsGpAoBWerTFjR/xwi0v8YhMUv2vEmfXUASQZXS7YpJ5nCCcx5LM2UkFPeZQJBW9Kr0DXQFGxvbFkylVKqW0lV9jPkEUG49aeC6dTVw3OcRFiZS6m5XVXYSEdls5L39FNfLUkGX18sS8gJ0oCR6dZNXkD5rrtP8I/QuwaaRJRzvOzyKyEyYZLyEkUqdEg+4RAP7RGi6rxSwTMHm0SUc7Se41tV7hGJLv+j9ENHSYFKHO6FAjwQKrFvirzMVOZ1VvYxi0GiEBNbJr4eXCl0dBTxh0uc5NV5TVnH0ba/CSksUulTeV+GifSEpOe7oPBUHEbC9yjPEigrEXaa07+xpNebPN82WzqalSzzAKHiSSoZn0eko7CK/iOLCuEplBR4OYGTk7iNMUBCxbIiXQmwK2yua07SUSAMOSdabcLEGuQgKVCW8vOLAqsEdq+iocvB6ikhXA9lSdwhDYihNgiJiwDpy5FuOgcpGecgofmrGV1+qgUlco7Sl+2gsgjlh40SAiUVHiJqlyWFlXexu0vIrjw4Ltj+5bTXEHiOBOWF4PiyIE7i4aWW8NCElKqLJ0XgpCpK1BuXcuGpAAw/ofiTExFowgM8D1zP9xzh8whXBfmIaO8T8QNRgkqGleZGoB7lPFxAWxazlSbtOTF4KvjtSRhW0vHzDEKAFfylVOA5BJ6lJGlwgg2WskiV/2jvdBRQHFaOLtaDCQd5QCTAUS4gA29y/Q6vwYLmRoSrUJbw3/H7rQIx0IkNp/D6SL51PT6EnlPK1lg/EZq/8ZCqMAKULXGthriqBX9NK0seykvPMuIB/y7QkHTrWbVCh9EeH4y71bIIVEL4T6MI3oN+Ff8Umq1CWrzyc1w1NtSI4rNV0csYkj4omq0MvcSvPCJOnmGyVire/MlwjE6eZocd6ag9SpHnyFLiG1Wm31cZSEiBcoX/M7n6SgXnqzKvDItTX4Cnh9ScZOAwNm75cZvU3yzH9y66ZfXzRnSHDH6I6EI17EvqIesZIvOh9hSVclEyfAnS9M0P3ak6ubsN+4N81Jfsip5Urx1yUKRsUSzpjdAlScjyhsAkvUUKEAJRJUBG2aa2QabQYWzhZD1HSQE6h6pkaAiSEH4SFgFAoAc6lH+UeQ2ACD0HtwwFITLXnGF7NW3RrjcZaq5hvElupfYakAgwsSka/isNWeb3lSaR8AzhiTQwOv5q2iq1DzJFYaVKZeBE/99MwqMM+Uckkm8GTE/jfuU84Vi9cH17LajkH67E+S1Xa84Jmklod7GmiVU5ngTA1cjWgWviNRaC6nltlS/0X7GBVztRY9XzZaRWptoKmRhfjb7DXQBbOYZfXB3G6qcnOkhgm+yoAFYmNCqCHaeTGJzMADer0A3lZI6OGl4vMi5rW1lbWDHLQFJauwI7MoB6Btl6G8jYnLW3j9IC54rLDIKrzzflYFbi7VduGuRU8H9gIesmPBHE0gAAAABJRU5ErkJggg==" id="imagec211d42f6f" transform="scale(1 -1) translate(0 -50.4)" x="57.6" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAIKklEQVR4nNVcS6tcRRD+uk/fXBcigg9Com5U4gtEMWsFQXDjQn+DG5fBrf4ExY0/QdyKgiAu9A+IoCiRmIXRJOALTHIzd+acLhfn1Y+qfsyZGzIFIff2VH/19ddVdfqcmTvq0m+nCQAaBdY0P4yGG1M8CIfRQPBlMERfBlnmEPtqAbfH0TB2+MWSO2m2bnL2J1vOl3qQULgZYwaxoGi+j8H4BgvvBmTPl0Zemvf1OMwW+lvqYNYk5QbQqFmxzfBjA2J9XZTNNN/32RAvSI8b+A5xvEwQ5k/xvEzolx4KOq8jzJpuwJ7RzTFxBYLBkReCE4j1JV/g1PweoyxWlS/xpSa3izmfzC06kAO4paYsQpOIyxg1QlE0nxNawvAWT/PrXG/1hZp9zS17iIZZeBQ4I1TvmxFwqdgZoSUMLXDIiW1WdACOEycYR7IPzviKAu5I7JDDCYhtjuwhH8RVWArCEJqEorzv3Sw2MGaOa94lXQiiYkAusEf0hMR+4/EfWaycfXX5HM8h7DmxA0NS+XX7sPkPLx1ex2OPXhcJ/HHlNL5bP4g/2/t6jBMSexsL181tjDmypwYywlWDIfOQuYE3n/g+S+Dso9dxFtfx+a/P4cr6gQBXaJK5kirI7BI7sod++XFldWwP4lEkzjjK4vw9v1UROX/4Ny6uzvS4ws4nNyfy3V6U0bgLUSi2OepOBWQy/YCApx67VkXk9CPXsPrhxTSux4HfUUnAbcwtK0lsc2RPJXdCEqvWxvKdCDEZlMrWiNdCofp1p1uJWVtTHFi6RJbY7e5gwGAOaeJhLHNhWGBhO+HWbEbSksOuCHG9TRKbv81Y3mdcG9tJam3muIszZzR+l7cj6W2CQIg95Uq+C8vqmKmYnoNzzlkx4qRTX76LT9nt7qBA7BmbE2WpIB6foAeyZbVK9ILUeK2trS+qJDYr4AmU++3uICm2VgSzsXwmSBOVInz888t45+lvi4l88st5rK5lNoG7m67oSbXGtZMQ1xy3JukwmnLGv/n3HN6+egUHZy4XEfni7+ex6fhNUBUlvKssBgC3nUi4ZmPlx6TSpN9v3I+3Lr2OV366iAvPfC3O/+inV/HtP0/ir5v3Rq+lRcmPL+0/K6EHuvHMuo13VAsP5d0FXb15Hz65eR6ffvmS7BvcZYhZmSBYOl5rYTvhxFbPfvbeNCq9UcER4nY+Nc4JLvuWx2N9Wc+6AygAmK6Ty2p8Lt15Y+WinJTYJZmdipWK551zWqas2IVWEJIX2v+/L2JPmSMFloJgGleObznG3Sw2MJxzqOtdoqkq+sEj5A+WN9p9EBvoBTe21aLDPDiDebCigFThm47HQjCxZIztxAYAg86fUbIgnqUrYIZQhdiqypeJFcRjIdwxtyGjZa5W4m6kxkIBy7MnJTZVZFpJa6gR2ygnc4hJRe5XcUxy2DexBzNuq1dLRGHG91fs3oxqC4gL45Sp8X0X2+hBnNxCOdxwCnG7dFJiV+DK8dJim/GRSbRQDjUTWBbL9ZVLokhsiYfALcUhl9lGtXwQ10/e0fSYh5Fz2JqDnNkyRlkr8a5WUuCyjJAw4pe3EXvXmV0ittFh5iCvsGLGZIw48L6I7ZdVVe9IE6pJ57tN7NGM+DZUtm4LbE/FHi0qq1yKbh+Yn7ZU7KqSymL4k+ayktKr6jzCT0mJtTReUSzJMnyN7sBazYJqMmWp2CeW2QyGUS0lHXowAW1HpbYk03788AIfMGPPvvuBHGswMXPciSp8GJBJXS/gnRS7wlQnwDsD8QnZcSjZ0anBB7cppIafnTk1Ytf2tVoruRD5V6sMyfBlRQAIUJYwHQkUQBogrfr/HYHuWKMusKmdpM45uuNvBClHknpxdAvolqA6guoAagBrFKwBrOlF4rInwr7D2cO1kzCeeM7J7tyQMc2G0KwIem2hOwvbaHSHGt09GooUbEN9NlVlhCr23dZK7gyMHtKrhnw/AdAdQR8TmlWHZtVCtRbaaChrhnm670VKxVmQyJRFPanQdMvjuthGdfz9SWpHAUARQbWA3liodv4HoBep1aCGACiQdp7cJkUJOcTOom+lhe2EK3ejNxSPRoTGNHfuXYkA61ytlAI1al6Q7fuQRt+gtysf552CHYky2thOUrhT5rDGiDKBEQCiqWzsgYayqm/AjYIiDFcw50o2YcS44sZo4eXF55x8xfiZEzmkd276e45GAdAgTUN/GRZvyX+8z4kCoX+M8cKryo5SRwt3Bq4ZvXG2lXl/T0x9F5domOv0pEEY5Q3HYkfYzEce5Ev69kpNSZG6fVCdI47/cYVhbsEuBzZyVjrOymyf6ajcd8FH39h2EojtZw4Si9cxUfkeaQi8rdiCr/gnvVsYfyHy46nXXnifxEVKhOSPK0RDObF9Xwm3nENK7JgDPzzyMGhtHJv7SoJUEFYUoSRGbDarSnB5CunMFpqvsLYxY43aOOfocTejKwTDSNw5ZqE1Ytfguti7FhuAQeegLhCFJbTPYiPMnC2CsLuxA1EWiy1xq8hAg/XwNRla6E4Vgae634Eorp242AK2obbjXxU+f6pqCHGC75HYBpt14MAsyBHK/7APR1JoCIzYrNASLnBnxQZgiOs5CTLigtjP7wulOvhmhY4wdih2QRsx1MricMGoEDhHgl2I+Ln9dDZ73LLZPFqXbR1R5iixhJwA4/1YSV8ajwqBSDQ7e3w53/FblBTnK/HwBHFvrlU8HAg6cjOg4N7KOzfwKa24hub4kv+VJIOvMz/XU9yzlxI2yxVwfDbj4QolOA57a2P4AvgfOvJ3K/qieAoAAAAASUVORK5CYII=" id="image83dbd7e4e1" transform="scale(1 -1) translate(0 -50.4)" x="118.8" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAGx0lEQVR4nNWcvZLkNBDH/5J7Zgtq7wIg4BVI4A2o4mJehJC6J4AqQhIyXoOY4hmoIiAkIASSO3b3Zm1JBLbH+ujWhz2zrDu5vXar+6+fWrJn1rXqjz8/dphMI7WO8QFAp1Ti48aPOZhYZrwUO+ZOs3MapFgt5O2UpBog/z/2nGgx4yfy/c4lAq13PcwxxXoCrXNLrJeDix1z2ynvkpnTIMXaKW8MyTg75Ugh0Z1dnJ1ySUCH1DcWmcybZMcvjicxzNUxbrkbZluW6wwwksh1ZbEjmWnSyXn94AVoBtRYJPXr2RddekrYwfbfCnsyunOHwNEJE9XKIjZponKOFliuCraUg4M95uBqnYMDP93Zm2lQOvmgcNBVUmwBYCFHC+xVnR3lKMGmd3PnJKuUER93BAfl2rAv3NkcbLqfOicp4hOWijCCzqB2DhvwO2c2vwiTbBSaJuQKB0L3BhvemRMGMCL9tquA0jFQ9gab7u0xW4QTU4yN4rfC7oS8fEe0LWIONp3sgRkSnfCS0EwBvwgbvylWmihz+9/Q2XRvjoFDvEWyyRy++fQnNh4Avv3tyyA2mzfQIHVgvltff/KzqAUAfvj9CyEvD/C8rbKrUeHj7N4cm2FnY30NQmxWjw0boQSbHi2lAbkWb7ATl7sBdvGs8mMrtN0b+YGXmzM9mEMxSBJUgvXgrRSft75bWzpbstrFmo1OJh2QGxhOSPq2Z7QHc9gMW3rE37qtamDTOwZOvvXzQHyLwbfA5qDI270O1IM5NMGmd962ajkPalZuBt8Cm43dCGW2+Hwt5VWvfvmajZAGqhaAG2G3nElNGiph02kotz5wASirYFd01ZVgAwA9mq5CZGjCF3RN4vcAm3rDfFMvzL4J4EbYLaC4Wq052AO5NwvNlo6QBMlCW2K3AbhEZwMAGaZzFkHjvybw1UO5FuxrdXYcT8OQPrewE20QJE90/HcvsM+dIxWWiuDsV15sfY49wCZnxpBkqEp+CASFzvqDdk+wyQ6aDQidS7IgrQjQNcTm67EpmFpyjnWwAYBgwhE1E+JV+gALghpgq6ZYplZUj03h+/wDGQNztxJXI+eLAdZ3Tw62a+i0mqOhBTYpr3Mc04rcf0WfFLA32JORf9SrLVAY/35hj0ZqqBCe8bvMKu0dNukJDjtJaaBif/RyCAFCjnQ8r0GtzCvXy8Om+VN8PNbFkRWFkxwNt2mo3Pj1nbKls0kNfBG+IyoESV1VClitIX/+FDs7oyO4W0mF6zpCypFeXgP70p1dA5t03DkoE1aMT86RFv5fYU/+Gtjhtmo6O/KCWtr5ucGejbK/l2tZpYbCzx32bMm2KrXo+sL8sOcMe9lWUns1PY/wQ3LiL13vkrBJG7DWMqGWTrna5K8Am9TgsgFjMiHbhbbac+00sXP8gSr+MmCzoH3ATp+Qr1AkHr8X2OHdqiCyWMQt/qDYE8H+9cfXTMLFPvvq+/p6AEgb/rOJqxSpLNA9OujeoTs52IOCuVEwRwVLackW2M2Hb8FaGgHIPOfUrlz36HD41+Dwtkf35gTz8gb9iwNw2yWx9XlVk4ZaU9L5KmgjPd2tWqHMl7qTw+FND/r7LfDXP6CPPgDcSzhScF23Mm/9mdTSPcnnyML5Q8rwn09qV1QPDnqwUIOB7QfowUD3BsoAysTf2Ep5hUvMQdsCO7a5EWrPVtK9S73JwBkKs6LOwR47qBfvQbsPYW/fhz12gHOLGFFQaft4vynYAGU27rEl14HnzmGNgRLvceUAe9DA7Q3coYM90ggHCOEEghjY0sJo4XLraRzpqYEddk4SEF5zikmmFexRwx41cBu+tps+ffMdmL2lx6ud6cCS6d41wVavPv9uUcr8fq/0gLU+lg/lXnmQb+n8hTWf0sPxo5OU8b7QCV9XmMbWrLJwJmVgiytnXH2ssN2D+BWw53mQ7sNvu0or6l9eOkW49e4YNgCQ6k1+4CyInWhcpGGVnjlsACAMNhEZDGQEScW5InuGTar3Hhv9/VmCIh6GzESlv/+wNa+f+9KwARCMlzW5bdaLZwXNsDfkfVLY0TYLO2dFEXY1LgBlM2xJW64Do7yEx94L4F5kqi983vcXgOLb1WHHuefnHDf4Xu9n4f1T1SJo57AJ/WMUwEzIAxW+7MOJFA4EBjYLWsoLPC1sAOTmbdUgSJwU+w6/8Ia8Tp46MhNlbklP0NnkuAO5UDiYFFdAEiTEshMS3+GXYSfaNsIm52+rQjIXuBrE7xE2or/ZFf8hnrMgF71toDScdZFLjoWNH9snKFE9pRRgo/EzAE5bHDvF83mZr2a04vVO9h+iyFFqJHx9PAAAAABJRU5ErkJggg==" id="imagedb195dd65f" transform="scale(1 -1) translate(0 -50.4)" x="180" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAMKElEQVR4nM2cW4xkR3nHf1Wnunume27r2Y335vV4vPgSTOwHIsWOsROw5exLkCMhHhAClAdHRFEUO04UBIqEkhgSYucpspGIlIcgESkOyLLAimyQEIGsAWFksBexa+/Ve53d2XH3dJ9zqioP5zKnz6nq6Z7d6fYnjba7zne++upf/+9yLr3i1MndFocEQlTGpEsRCHDoOs736nosu31w60qH3UB4dPf82jleFhVVJklE237MAkBXJk8cMvTrSuf5Hl0h0BXLib5JVef3nRq0husu+p2DAKiu7Uc8wEmkyn61ZHLeJS24YJqcjBZpmwYt2WNXcJWl2hWawtJKd69rjdOum0lQ3rRxSs8ms6uOUfmgFFVgMrDaNtFriZhdgWFRTtOzMd8Pd/K10/fzxpH91FYC4nnD/oPn+ezS93ikeZrF/We4dGovHdtvIwe7zDB3NI5VujYGQLVtbWPUupkjhaFjGukXaJoeTRHSsZq2qbMe1xBaIGMgFnRjxVUzTdsammdupq17tK2iY2q5DfCw1IL0sHdcsmaSMFftbNFAIDzUt5ZdQRuAC7rFf6x9gNfX9rLcvMiX7nmeT/Mj53lPvX6I167u5+65U3yweYwltQqQAyUd8/nCepzSsYIAi+rYBpLUyZJf3ZRVS7WL3KwEc/tO8cLRu/jaT36XPS/V+Olv3c6XeN47ybOHH2TvS4of/t77+Pzvr/Hw8hEAfnZ8P11bo23qtESY67vBcm/YdkrXBgCorqlVDmZOZqHUMQ2mRRKHXVOncbLOwnePEk/dOnCSqZN15l85wrv7bmflgVY+HmA5r2cJsASysCOFj3ICoGSShb/46pv3W184lR3smAZN2WMu6LIruErbNPhxZ5mXz9/O22cXMVdriFbM/t2X+cjuI9w/c4SWCOnaGuf1bH6+L3RcfkgMjx78+TUudzR5+dhtAKiurVXCKaPyLrWWKK/+Jt85fDfTpwM+96lv8EcHf5brHuKXFePHgR8AtlSmb/vi00Tzhgd+5xd8fOdhANqmkYdv0Y9JhFMmbVsHQHVMPR8s7mhT9mjJHgA/v7SXm75tmf6fn9D+ZIOtyvK//pp4eQ+vHjjAx3cepil7XNIz9BWFCYKSSTfFRPUcOSeT8/EsgbDMNbqcuEuxo3YPHXNuy5OuPLzM+qJkoXmWS3omTcoNOqbubz49Ib+d0s7A6eh634GsEbwcNTm8uoTE8uyt/8nSU2cBeOILH0MUFlIOnaIU9Q48908cf+wvky//Ap/98SdY1zX2T1+mKcO+8/oAmUBlzwpRHlZlOr/Tnef/3lgGYOm+s/n4G1d3b2nCE489CfxV/v3lt2+ju9rg3juP8r6Z88Dkm79MshyowsLlQyYSy7n1WeZer1MmxokrC9fFgfitGeZPCo7tXeSm5mWnzqTyT0enzFnX/Tkn2712WKd5rupcpz11XRyoXxbMntashjUqPjiu8cYpWTSpdV3z0jma2b6rQNOAcCa5yFpP856zQ54AUL00mlSog8pBKSz1QLPiaICbre51caC7J0JPKRYbIT2T+bDhyyRAySTbLNUtULpI54aKmbrjCgD/+9Yt3HfLWwDc0Fzf0oR3Pf4MPL3xffeBFdo31tnVbOPzYVKSMycyVeZAknvu2JlUkU+9+hmm/3yGueMxNzSO95VvMaDCFPW+/ie/zb2nv0J3QbD6YJf7Fo4C0NU1up7QnkSPA+Q5UPZiRfEv0gFRGmpTQcRUEBGuTLHvv97mB998kq6uVrdh5FDrDD/6xhPs+rdX4UyS1KeDiFArQh3Q1Sr/C01AaIIcuHFL5keec0SJzj0dAHPJsVbMhYdu5sPH/oHeFsH573dv5sFHvszVHR9EL0ZcCZuEKqSrVW7zvRBSAGEeVrp6hz69PczKehOA2bl1Oh/tcTWWzIV1Pvfaoxyae41X15e5/evPcOTv/qJi457HnuZv//QPeXT+p7z07vt58cQHuPBYkq92NiIurTe5RGLfVRMnCVQvzsHpzzlFR2MtEQLmm+vcvXiGG2ptXjz+fr7/+Xt56oXn+YNbn+TNo48Dj1dneA4eOPVlvvjit/jwh/6ed/7M8Mk7D3MxmuHN1Rs5tzYLQE1VnzxMmkEZk5V2MCeT7FikA86uJ4uJjSScTcf3LMBR/yThbAJ8NFdD64iT3R2ERrEWNqhsigcQ3/h2SmSS9ak4rlarskOdXp234kVOrS0QxwHn7rXcsfQMb89N89Szh/ibu75dsfHKsds49wLcsfwMJ28MqKkur6/sSRJ+Yc7YuDdnEqBkEsYO5rhiXwhLqCUhCq0lQWBo3bRGbUkTGMmzhx/k37/wMLMnLI1VTTgjWTsg6bwSMbe0Sv2gRsUBvVBxfiVJ8IHSeB6IThSUTOIUE2X1hpd9bon+DzaWICxGSLSW1FVyT5lQ0liBhV+1UScvYnbOE08t0L0xIL5BUlcaKSw6DjBagADhCuUclEIPNaFnWFnIKxP3O+p1KHXexIKQGmEv7T8ahtU7NZ19LUQ8g1WWuGUxLU3Y29AzRuTr7mOrk66TZU+cYqLIwEkdcrOn/4suFRjb0oQzcXWSXrDp4gexdVJi0s0TB577x+o2+XbOuVDPDC4bXt0hz2dEpjl0vTnNoauE7h+1wlY1RwHFpbAlsMs+DGL2EP6mNsq3dQelEVV++0P0Lcp1ks9Ydcg6kuzIdl0KW2Z21cagNKJE7OLekBMDdgDNxbWA8h4AW8kCOIMW6rVbGN44fxP2DRifONiFj6p4y6RvoT6rAxwqHxoabI8NJ9ib+FCWawFbiXIFHujkEA75WLWZwpZ92CawScNqs4mHY4TPxiAnh7Ah4OhfP+E5YXvkln/+CgBK6EGLdyPsKwBuG0Xd6wD2GCQrUv1hNVLuGDw2Cp2HAXucInXis/Lew950oUPItYI9aHwbJYsmJUsJebN84NxlWx2r2LkeYI9JvGFV8dXZH5RUbPW48IFUsjvMfOMWkV41KFm9hQsMpv7QzecWwR6kOw7JMFEiLm27s/QO8FIkf1YUzjVuNjnPpYrBpEMti6Yk52xSjgc+1ZRgAwEyDSebAmNAmELrl4fZJkCX3ZkAUBvMcYXVJo1bxooMjOTHHiIHQGibZPysEhbyjxPoUUJtDNLPnHzUrWw9uUMAaJARlN9Pyxc34GrhmvLaNkohIbtDxvoWJZLQsMn9dmRsUV1L0DXI2GICgZ6SxNMCXdvQ63vN71o77W0WmeZhb5/j9UmACWySRCwEPUtjJaJ+oY1Y72Gn6kSLLbq76pggoZcwaajZ/jn8gGwcmASD5EZYFW52l8On1OQJmybgjDnGEvQsarUH5y6ir6wiZ1ootYdgViGmBVYKhLHIOE3QQvRVtqIMUwDGIWIjIXvu3FQSRPavyJ9lC5NQUEQawggbx9goRkQaGdt0ByxSg9Q2DS1bAcfL1gk9uNoIq8jd5/gkL9dZL2MtthEgd8wT1GuIqSlMI3lWFUQJIMIk4OQMLJV1f6M4GQb1hZW/Q3UcEOmCJYj0V3a6WYffWEBEsxgl0dM1rAAZ2vx1lmLPAwkwlfBxhZr/PYdtk6xIKRE73kH37JyzpEuBng7Q044XL6PSJX9fou3PdRXJeyaX+9srhbAqLMCxS5tVDivBKpFUJgnYpDLJOAkjYW1iQ4CvF+qzKyeTZ4qSN4FCF8Ap7lKeD6o73NchC4G1AoxIFmayPGPJkr1IfrhZuXRwFgBdnG/iCbmf+t78Iyv36hGkuUMnvw/vA6/0q1+MI8f45nNszDglw0SJKKGLt3Lkv8IvjA3TpKU6g8Aun7/BlEn3OSlziBOURgWlIrZUp4VIK9Lwdt8zfc4Gc0rXD1lC3AyU8phNLynSXe8DppjohwF7Um8tZdOn0aQqL9uMCopnXIAb6BHtTiIpZ0WqypxRdtMxZouleKssmfCF5wZzwgikpw0dhTlZgr0GlmwK9phEpHlY2VhT4b3HITESWK6XIkdgjw/scUgaTeKRHX/seO3NsTDfDjoB8zDRYcMJuM/udWT4MLrKho7/qWYENngX53Sier716vpAGx/wyob9P1sedXJnuzZKSF0r4OBlutM3L9Or8atsXH1FdthQsYAYkiHA0LubsGmMAHv8FdWLILc8JD7mOHv4nXSC6HNslIIwSh7y6PqA/H9WMNcW9RCPZAAAAABJRU5ErkJggg==" id="image5d806340be" transform="scale(1 -1) translate(0 -50.4)" x="241.2" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAIJUlEQVR4nN1cO48kRRL+Iiu7Z3ZZFqRFJ5B4SQgwMBBC553OQgjpJAwkdMb9AfAQAouHhYUBfwMXc5HuDISHy50DujseEsYdAwLNTE91VQVGPTofEZmV/RhmCWm1M9mRX3z5ZURkdXX10Nff3s8AUCG2ikgYBYzkC8VXwFB9BWSdg8QCMAJ2RYrvA1+J46PRfwZxNuCy/R7Ek4TrMeRV2xX7Eypw5GQArCNAYM2ybxQcwFrArYiAAEOK1WMQgM73JcI6hh182wBXFqAiQsNtNG5gYM866wQTFiAsCgCMIEwlzNcwDBAJ02OIEDCSuHI4MZsMYgF6DMF3mG9PebEZ5YQY1EVjkS8r45CF1zAkIQBZfJEvs5zBovCC7wBpT7sjZ3JCAIeDJFTvO2++hqFtjMxhfpYCg+DBSzmx7SrInM3EgoVKoqgC3jli2zMnc7wgrlBqmseEJqGiXbqaYk8YwhLtaXfkB3aDaGAUE9ICH1rsFx/7QsTK2Sf/flLmEJVVGFhdaO/4ypOfTmPP/ek9/P2zt0R/dq4rPvryWay6hei3q9jb2GlQMdLG2LNu6RCSThk5q0bThAmtAsMtYa/GZ4hSCaJomT3HRi6pzLYXym7qx+l2hP7X3O0TirI1cwkxQ8ASmw6ihNj2rF16A+oR6ZB84fE3cfvL94dflas2AOREfu38r7i1ONVxPQ5ytmoCbmPeJYwi9lRWuT7j2ihMif3juyfwl0f+uSEklklJo95NqLNumW0jtnbePuSCa0fkHPvlq3tx/uBCFFu/oJOyaj/ZE7YTab32vM077YPQ8sTEhBSx5fd4u/cZ18Z2klqbPW8Xaorua5cAoD1mjBuh4YpXuZrvjmV1oVWME8/WbXynpiT159r6oVok5Auy4SKJsqsgrp13wUEkldXKKatDiDLagw+cYNVKt0f8zRE5HCCz51SMXXfSPT6dEBF7xzcldtP1e+n6K1i1ckPW4pX0pFK7aKUs9nHtRWOTDqPRHgithRKWcEuyaluTs9i3qedoi5cmfv7fh/HHR78pIvNLfeztVlqU/Piu/UfLYq8hr1vhRrRy0Tsu6NV//Q3L2/1tx6frt/HTyQ3QyQL2lNAdAc2tNe6+dYo/X39jmntRV3pWKgs4ZA8M24kkNj318TveqES0pNT0DJw3X4tX5Ct6lottWyFzNoT6/1tvrEAoBbdk8ZcpdtSQm2Zek1Q+HirKnqsqttZGvMyRfNRTahp3jvUiAUt8fxuxLbcbF7n5+BDiogoa7Z0ktu0aIzrEM3vAeQKW+OqxomFp7EBiA4DFKE5yQcEvmQVxjpDnK4W4GmJbTGXlLl7bjdRY8GLBgi5TbCrwtdT60Zg4ZpsURbNLEHtym8F3wAhv66baiA0/X6ds+Whg8RALdV+MKzlsJbaMkWojlhrpampmYADCQwpTYNpFlCsgtjWOOKmFqrjO8GZ+JvsS47+52M6P1r1l4i1UQ00QCl+aLbaCIYqd4RDaLmJbakpIziCkZVXOIcNB5XEosTGUlb74UeE5JDWMFMkZGAcSO7dmALDUphYvK6wdADJGHHjfYm+DEc0XJvlltafA4cuHFluPl8dNrdkmP8QsScfCwJpvGYfDim1N0JBz/WD7wPK0qyx2VFYR5hYClJCfE69kw/YptjXy47m77752iOy4+Fy8fYptqeGkQw+moLm+mR3bV6aF/l98+Ho6sGJPvfGBHmuwvudkSif6VJMCHwLYDDXJ/T9i3pyPtIXYQiyJ27ZGrQLvDFiSyqpgR4kANugf1R/EIQakU3CO2KlYKW6lNucg8k+rbAblfcVAl907ZtjUThJtxJpWfm/iBpZEGQGoY9gLRrVimIbBFdAcGzTHBK42b02mp2kLGnXoH/rukj3SQRRmtnqdk9o5JgCmB6hqxtGPDZY/nINOV+CjJdb3Xcfq1gLNNQBEXv+ZlxEkv7xrLblQM94ZWOOcVjzzOocNTQ+hVTVj8XMN+v7/6E5+grl5Awt7P5q7KrTLCjAM6oQelCiTOT3J9d/GtIPIxbTUKnduUjvK3H/rjftSQtMBTQNe1+B6DVq3oBZDmVEvTqulTipbY2e1LAstbCcSB2vCr7olRBl3lCuAu+G47gA+qkD33ERlLejaMdpri0E4gLshc4Qvnm1wA5sG/KxW6G1lYztJ4XqZE1kgyghGLaYOSwy0xxb4w01Qc6MvuWv9czhV3W0EYBcjxtUYslFe2vk6R7h9EmDGmeM5xakXcTKEbmnQLZfhK6CGfQEUUZInUniq7Cl1jPLOwDVr1k6nFJ5GUU+OcaBlsAmO7Zb7w4wDX6FMIlzlkQdZwO2VmpIi0UYstY44/uMKw9zMLhOBDIM7AjvH9tiPQsGzfaaNyy/yVzK7xMR2EqzZzxwkjkcTL6onzy5ubKPgRSWl9CT9Oati89qJIjY9/8y7DCTeDEqE9McVoqGc2L6vhjufQ1H5ZdqIRdPJ8ceJQqnNCa6WTwK39DrHx9UyG4jKbyphiYP76cM6eP8w7mhOFHXnhIW6O3SniA3Aog3Oyi1FEQlJQhfi7iz2triQMqcwiLgbexBF3X1D83A1bqkMDHAtaufPahihQxUEnup+D6K4xm7zPkRmK7iWG3fU+Vm5GKMSQldd7AyuxboOHJSLiQHQf9hHIyR0yTtNbACWa+Gv1RSQExfnEPGd4/ms+uaEz8TSuBWszXJdx46Z7Nk22GWKDgS9KsdBwLDczDytNBLqV2x222GWxNW4Kb6iwAV8CdKfP3LsOXpZAZu/g1Sw2xJ5PYsOm52/Ajp+c4E5ymOvAAAAAElFTkSuQmCC" id="image7ce36a921e" transform="scale(1 -1) translate(0 -50.4)" x="302.4" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABGCAYAAACe7Im6AAAJDklEQVR4nM1cTY8kRxF9UZXduzOzKzAslsEW+BOMwBLiyAktshAXxE9ByBwR/2AlLvwCH7gicUA+IBAHEBcEshCfu4C9XoxXtnfxznRPVVZwqK6erMwXVZndPYY49WRFvoh8GREZVV09cueNJxREKjJWM0UAtUjW/B6D6JL5pq6BzH3guhXBrSXVdZ5SA5D5tmgK4sEdBoiuKiWCDXl4rqvp4jy84UOq26lPSHOrALSWC8ebYA01WVAI3wSf6wC/2ZBmR1GIoZv5AUBAOrM3Jqm70A0wxusYxn2Aa3kHuFN1F4snUVRJOsjI6g1pglGT+RZGBdAorMnm90tivqXDPHoGDD8aCwl3K3V0IiOskm5ad2Z+j1FIdoRRTDaQEG6TPTbmVrqgxqhBnSIoHaeLNjAsgrgPJQRxHxjJ8Xz3qLuSKk1FSBwJhBQLw44mplvgwyVFtDsl5BQZM3QrLSCN1BmAE0/tqRE1JVFOfOBppbZzvYMXQCeyxrGscVKtcSINKlG840/wqLuC0+5K6qCxkB43tZf4oGPdbz73OsWak9duf477ENacMK2sUE5CTnvdT7iHeHH5Dl789L1kznt3n8Rv1yf4t7+OVcfrWpL3w8KJH4zkfWRYtxX5AOBOu+VoIDc8n17ex7ee/70J/NiTd3ETwM9ufxZ/725QguzCmplOe8hQTqzUAwB36qcZZNX+xuI/+Mbxe1lO3Hz2z/jRX67j3fbaGFe6tB+xjnSqux9Zj7or/ZpZb7fBduvuos8xdzIi7rnl2zj61D+yHbl59BZebR7DsBEWbu/D/gU3R1a62JYHS0bkpMaZQ4qXn/1TkSOPP3UPx398Afeb6wACUpK2oOA03LPorI06GBLuzqKac0gHQjmp1jjzi40DVvqwviTzkCiUuNZuccPTanCYXTyUI4PEu2WlRQlJu8qpX5q2BnFrn6aVvbO7O/huew1nfmGk6nw/tdU9UDTH5YR2yKsicvrxf735STzxVNrbTMk/1x/7vyInLicM163itMqoB68+eAmvIJ+cn/7t87j79kdx3qXPEnvcmowRHw5YA8NyYuG6hjlsKMvG6d88eBo/v/M8vvrMX7Mc+cn7X8LD5mqPXRANLKIOVv98Rlqt2/k+R6Lx99dH+OG9m8AdTBJ09tZn8N17X8HtD26g8ekmxLipD7UxPu1vjgzlZLIgnwdOM2cZwLmvsfYOt974Ol773Uv49sd/jcejGvSDP3wNv7j9Ah6cH2HdOupErr25Rewiq5m2AgBc440n9Ox5d/iM2dd4dL7E/bMT/Or+M/jIL89w7BqsvMPKL/DwzatouhSbkmQ4d5kk5ZQT+eKPv2day00za6zHyJtfas/ULcCdi2bXGpHTK1rj+eSUOPthkp6j69qWf1VnLrbAKb7Y+Hn/lO7/lnDXTUaOkXHJuBRGWYlu3ib5Sd3dSHadn/qmcXyNLqrA8DzZEugaqpdMdhjVrmvTyLGMKQM2lLkTQlmz7DHiTVXq236lwWEgJ7g4UhPjj5ko0rkoKLAnRbrEVmSPTg/HN7oO27QSE8RGIQNkvpq6Fu4YQydqWqrLbPWDcXRM6QKAk6Dm9GmT97aD/RYGm2/VGmssvcBT2rLHzTEMk3QADu1c+BszC3R1X1zrwmWRvlF14qORAqfpooHEaSnEzd/5QlxL0SBolFbpvOn6E84cEyWpgoHB53MfZAfcPKI5rqtYuzoAWMgFxzFPKePkoKTl+1CS1naBv/jopLUV7MiYcciKrjmFnX3YJcLnfbDTKqkbuxM0i5GJK7C+Is+vPdPrGKO7ikSOGjFecppZ6cSGSwhjPpRuGvWBTKBpdWkO0L59sg/L8oG1/kX+gq/ZmV9FkXCcNJqJsU9q2j6kDLPSMshOaZUT3lNGczCyCiwTgnGQQ4K1IBsZpZV1nB7iiNz1OJ2cX6BryeTtw2Sfc4goYP3VDlEXXyrRLUuti49OWjUB+hphbRkZ2jHVxvYydTdjr9/6Djc6I1945dYsQbORI3FnMROyI4PmrtuE77XrBSJ+3hbvkAfdkuNUI51Bj2Lk302X1JoSYf1dbG/cBGY4NzqthozsAOkUor2+VgKtgh+ylLQFhziSM8QqJ+GfriK/KQqNTh7d2hNTtYq6UUgLaA10C4FfCLTuiWK4W8g96tQ+wROXE2Zv4vaBSOzcQMxZh8UHLarGQ+sKzTUHOa7hlwKtdDuvpC8puhvfQeJyQjvkqh1HjqbNpt1yd0C97rA4beEenEFWDbBwAI6gtQBSoat7yyW4uxwEpbINiqk+R6K0spu49IJ02lf9ViFtB2k9VATiO4hXiFdUEOhww1lUcNMLhzilBgnLiYXrqvCnbBPhnewkAFEFVPsivOzfd9FFDa0Eon3KdbBri4pMRNPuEZ0jQ+RQ3A32KK2KWvzhugLqKvjjBeSqg4qgW26+f++AqtHJ9GFOUV32rfVe5Kjtw0YuOuTQ1mjCDHnan0jdsoaoQkX6E6pTVPGciOkhGrNOJs/Hd5WhnEydxq7y9uuzcZ2Za762KagKdD1BQog2eyfyXol9wu3HUnwQXdi7wHXS2OTILkUxrlEkHexjmvVc+bolIgY5oTlXEXJMAipSQBEvIOo843s3kaS4T0VHchDYb8wUyeggAuhBIC9/+fv2FtAiaO1kQZTR9Clo+gxdXrsM3IyIdtKQ2/JhYnipiJQhtfJx02O6nOxR/G7nGxFCfYgek0pD7h/4C3fcMf6yC9fdMxJLcK1GsiTqHDyJnO2xuZvjo6sh0TvgqokbfN4Td4Qd4I4j5wC7qHHU+f1wzWO+FBcG0XHUBbHicB78m47KOAoKyJFLSsmE9B1wqTbD3YjTpgGEVUp7Mut/TKcOQDgqgZCWYF9crYS2GoM4tGFaGQuJCNJJ50icGrtDSTYJYlW3ZJPIJk+lJgCn7LSynIGxICs0Gdnhk8Ex8Mz8YIs/pGh2GtacCeODA0nHWJI2l0g47WRNwvPKh9M2IGcwHM0diux4pwfd6PZjalfjtqEi/yKIvS0d6CL6IawIqRuTxBr+kltMN/rHO+p5T+DBawnI6UR0dXsTGut6Ox3iKPOGb1RXDVxS/+jaevkv25GL1SErJjAAAAAASUVORK5CYII=" id="image581ff47e8e" transform="scale(1 -1) translate(0 -50.4)" x="363.6" y="-55.44" width="51.12" height="50.4"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAIFklEQVR4nO1cu64kNRA97vbcu6vloU0Q/4CE+ARAxPwGOYiYAIlsf4C/IEXiG5AISAnISJDYvY+dbtsEPd3jxynb1TO7EhJOdqf6+Lh8XFV29/Rc88efHwac2oCyjcQGAKMxhY31XzgIlvSXsAt3yc58kLCDwDsayWt5Pv83ANaF8wcXXRhPQvvIFivpwrnjGl1x/4XDnDjO2JUj7r9wEGwUGS5i37ARRRwtCXbzgWN9cJE9jS57H8rEGRCAUJgxmtI4MiBOIuQC8Mg+uZqLVZhOHFI650tzEjHj0KSzfelvoo6+ACSTD4J968+F2rDJSgvY1gKEMgrT/vEngk3mwNu6APYxHBjXQkjFKm2LnUdbU/AGR1PwGHvliN/EGcikWahNGCmxIyIAwBhKDi84RjkCnwjjkIT0obRP4NEbC2nv/C0B+M2xZHAmoICVV+YyDhaJC9Zf2L9MfXsX1ZzF0TI0WSoNigmNgkObUDlHLRU1C8Y4OkrH2uxjOIszwmOiOUvECYIDLL8FrFSPun2AByurbDFHBEzMB8E3ALCv/YFeSBxPKjyLImlHa6xqsnsxbD+v7O+OSD41+9I96euo2HWaed3klXyQdq7+1GouQtTslB0C188FeRiL6r6GKcUS5yYQgU7jxfjplCollou57KCpfdp42TaeY/k87H1ekBWrNhqP7z/+ieIB4LvfvkywlFcT9qa+I3370c+iLy9+/6Idudll++B4zVk6tlWvtVz4Wn9JfIpVCLr54tIjCy3weeQ8uHICDLgQ8tyUGhNeUzck7J5FixdqJBEIlKLb157fYbDnHNL9kNRee0t4pcNh/4FtjzixL70LZB9PqyuukrgjtYV6cAelGP27nzaK2UK1RN7EKToqVlJqjFuzCAwrHhMaYt3N55rTuwj26MYMUHEyDE1s3I4uDuUdNSUwW+bDCTRXPVkih93ypAfNlNt8+svX1EMqkpBipiboBXY5pS8r3r3Rax/n8+qyZ2SaQZlIGgfflPi6VI5qzjEWR0GswlKrbiIXC6QQfm12dlmeqSKif7BLI+1tCwwAdvZtcSTnNE68TYE1EVwVZ5rG0yD9TvLJ8wFUHIKTmqjjYvX7Fo9lvZdcAgyZ8ULI7P0OaDiMCfDkOfR1xK3zWh+fcxQD0pWjq2mUUdk/Xi+Hq2LlOVs/m9TcPRGOZc4txv460C+8thz0YwHAhpl8NZaRbN07a02g2JqYGiwzXii8wGHh5JpTspuCJFSxEm/OwVOPY9l4y4d84kHyC30iW1QKMu/MYpNhFRzlV9oRR46VUyZ0Ytl4rEBbM5WOcifLi4HWBu6PxMGxNV7ub9Jj698/NyajNS6fMEElA0YmcWuROEoTFVhKye4gL9O/yiHwWuNKHBdI5zDl+E8JDFgzl2jJ394JyxxG4Oi0CT7Ixbyfl9ntMMuDiilLdgrxtMA4NOIDyQJUfdCIJNhjji2tDLnYHMDwy1fhYKAGb58P9U0k7nYuyBcOEIQB9qYM5QjEFtmly+KHZuSwh6/SQpGYDk1s2y5z1Bch5+3xoRXVSeQMrtGpQhT/d28q1TlKj2vHJVXNkvC05uxQuWmT2hUKp24n4hdacy7OOZrOzfFVRwIO10T0NRY4Tav8dafGhJrFVrBrCvalxX3vWHmzwxwiIDthCbzKVcovXVpYRQ5NBArY1WiT78cU9yNMSCNgmc2g7+B5Hk+AXlyPBDuyrbxrxTZb9uxkx0T7otHs8K0cb080brcPrY6a84EG2xYwdGPZWKVv0o5SYu3g8gjQbXsmAMYHGLf8P5jlNT8/lsuliS5NZKy+/frjNwIZ8MlXL07Y/oNlceNp8lvIRqgODrCPHvbeY5g8/GHA9GzE/GQRKRb7mruU6tAKgB5ZGpGc7FbNwUn4jEeP278nHP56BXP/iPDsKYYP3gGeH+BuDbpqU+FhzQfeXUyX1c/oyNLrg5XeR+K7SSgIhilgeJhh/rlDePkKxnmM7z3BMFv4A9/Reietw8pfsQBL6qe88rFlbXYg79Kv7/B0VXiP5fvWgwVubwE7IhgD4wJYVGp3jhJbgntSbE/5SMTZcwo2PsAfRrjn72J49hTuxiLcDMsL1ccgcgTp16XNM5XMWWvSQnEflgt2mPwGLLDCN/BJ4QyAvxkQ3r+FCTcIxiCMZtnBQuVkWimQXZO4ijhSai1Y89nnP4jJ2joTpFjJK0UaaFLmGsW6EdHWTFlFTrbeSDeSBl3b9Okc1belr1hyuSOKz51SoyG8fPw0ou1wdOLgmtOk9MyE1zGBV6hDix/tAnr2t/+sVvpwNtpti1v/FSKnnTb87p5HSXxLECHi32gk0VP6lvrA/G2nUlLmV3y05VszyY8CxUkmv75fAZIgijoycA4aEUnqMh86Fpbh05qTf+XZcjBq4nbMdoH9vBuiwRu33rpVO1lbTNltuXjjySYhhW7/JKh9p+hddia8gLVmliMnboEJ4QS8wmFzBV764E0SWHrlhnBY5OIANCKM5LBA3C3yFXgNUPqs5GUC23A8Zv0UkTCwV+bqDqWF/c2kJY3yBi/rYTHPCbA4LkvxORjApwdI+XUSVlcG0N+PqRaHY+mThh3RXRZkUXUiUoY9v6/HHMn7y4dPtci92E6R11NykVbqAQW8OEG62mn/TWQhMrhv7cWr+sZqjj+yP2KwjtcTAdyRIDkB9NcqYZF0x4qSI0j4jNfCy98HB3q0dHRAQ7BBcC7/i0xn3/Idx1GBFl6yO1ExPRfToRnF//9Bs0r7F+A3O0N4Le12AAAAAElFTkSuQmCC" id="image6f0f0bd935" transform="scale(1 -1) translate(0 -51.12)" x="57.6" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAI8klEQVR4nO1cPY8ktxF97Obu3con2BJgSID/gQLDgX+APwArUeRUiQMHhkMpcWwBzgQoEiDn/gEODdj/wIEAA04dKJdwWp3u1D1NOujuGZL1Hpu8WSkSk92pfnxVLFYV2Rzuuv999mbE1gbYNhIZAIzOGRnrv3JYLAAMhENiCTuzQWEHwTs6ZbUez/cNgE8/hOLhAGAhnUYAS4y5zDnT/8JRYLdZDAXH4JzGFuwDBmqDwoaCd5U7LDHHppHk78NYdLAkaycrH1NsYihNzyyqBTahY+mcp9Fl2s4cWf+XTOWEw9+HW0FSxEEsnJGRk1kpnRkr2NYJqXKULW4cVqxrY978i3izkhtnjBiL8JzPJNHImTMVVjm5dOgsJmRGu0MVB2CdNCN3pp/i5q/t55gMck58mSrYnZQ5JKbYYLCsv+W4/LrjU2yLU2cyKZk+wjGS/gDg78MdVbh2siV2oGWXY3n4X9cfUCkveGnKt0W5vw+PL52IAlaIVyLiODJAZeDKbaOmyXmV2qM4WH/FsY/DL/GSeQsGM4NBGL1gNI7buUrHBXDHLXE0xjEbVjsGYsPaSvwCyxs224zjhF0A4L8Oj8xDHQEsWtgS3546OlLaeZm9TdEKVCPWP13utoesUF1vzPVYZUN7ustxHJQRP0f+9jQgonx2VhLtxnEG2rAuWOzuLIodBRYGC4DggxlHzbHZajUH4RxCEMiu84Of/p32B4A//+cdyxvtFizImbX61D5pgMP7b/1T2vLRf3+dYLm+cnz+q8XWnDNJR+iy9vR011WTKFY6jtum2v3y2MhoDUxk/tmJFeT2mlBrzxf7alJyKF2AdaK269hRexDICSdj8yxVQlxl5eyEODYZsrc5DmbVCXHMeEPyuHRcQK5vyfYnF+xub60t28vCzmG2G2Rs/vlyQ8lY2PY4BgCm4I1MpQPjllsKhj1Is3ScreXCfzVf0ooa2LHslu1+rodyn75257HGykfNNgDw83JZrVzPILZDoT/++118/PO/med/+vS3mL5cI6fHwV2OrBxxlu35ctPtYPeLf713OUMuOtdCtXRk36DUO9G35cR2fSnWvzhd6gIraz1G9ESekisHMe5vz0Hbi+d0skWzZpBS1IUV+noGda2zAO6YlNcvwcmHSJbIIYEtm1xjYx0rDD9jk+dB8DJ9SyJL9SmOfRujeP0p6KLWM8Nq8HyGhb4rI0faxtUdRqqfpjytxPdk1BjtkLb+VTmR9ThKO6/dBh+WynKYddrTQ0Cpge1YJi/7L1XswzvZl1vv/Oslm8PZ92gJWcywMFh3gE3lVNcBRyzHsXPkFHAuZrUJSMZcyH2Yi8jpmW2JZcL2meW6HiKF27EA4GOjc+i7XasRLmriQuwU9ruciK15M2qRNrDlJ/dYlh4Fhwl5ra9Mj4uuQkzTnNiV6DNuIPrSTx5L7XXfyVmQ66ORW45YxSreksPJgk/1UawzkZN+8m4+CnceHRnhYT065uA2sLC4/JppbXRUia1xeHcSYVy0Ws1xpnAwLOdl8ouz22yrO7zkKPNT9/QmEdVksTq5ecw4LhIjxHiZ3O2EAmsitVLvlc1G/6o4e+SHMq16ImcjZKWDEfHoU7zaBhup7duMelQX+5zhxDnooiGKGh2IsqHDQdwGwdxVDjjW1BzzLck+O2ZrCTriyOTKVoKNsLIzB7VBRKTBxvRH1QbF4YdiKa+lDyOUTmiVPwSW9m/bdAJ6HN4V382qFfhoOdZObZQpDrHPovvPztWM1v3kgx/YdVFiwFFTE9WT84wjCmL+2tBjA1lICqzP7vSwRaBVodpI0y1AhUMtRAQrbWgZh9iGuGQb4l2yWumUyviasC1OPUqHo8g9tEFxHI5j/cWPUxtJj8IjeU996qplD6zvvJT37EHEitq3X2nUt3+8Wl+jg9KxeRdUsWBKHX/cEbovO8jvUt/++JxWbURRyCv9G4xpw4oQbtT3MqllXh+uqQe1dGG8PakMxGbbpC6KteBz5JgTNrV8k6VzX2ZdRLaMRpf8LLAPviQnHJ9+8j55uLaf/eHDjPrCuQ/C9vHDSZ+hNkVLBPzziJtnC4YpINwOmJ+MmO8covz2jouNkypLubHjaNk/JZSNEejH6fjwh9eGVeiWiMefT7j97AvE+2dwr/4A409eQ3zjFue7mA9Sc9qxrI0Tx9f2Ur56/6eo3jnZ6tRhiRi/PiF+8RT/+PyveHv+PcbXn2A43VSdqnmt/l4sa9mqLHlzknNakRuwlT1OMsAFiKODe+UOv5l+B3f3KqIfgAgMCxtI7Nw78YLZ894HgN3iJ3ucvOj7IbmV3L5SJQMMQHg0Ivz4R3A/fIJw6xEejRjmiLio/htL04Rsk9ezlJM2TnxSqmk1zDqvaEdSZJebAeH1O7gYEZ1DHNZaxGbr5d/P0knsd1S28DTupdwvf/WXqB7WiZgFHNyaLg9zJtQxhoPI9S6JHMeIxSUMY4RDflE4wRlWB7AzDukc4nSF5WdC7dj8sGsqTrvk7BchVxAda22PoNWOYxtyY67Bcqh3S1EYAkeyqOqbaRJBANQxKLVDfjVNVrRBHa8SrHZO8V0xrSWAuSwDkYaAyZj1AJsYOziC3TjN5Rzhm/VyjtFH/1uAc9aOwdq7N++m/M1TDZiGqboURlOwI10Vt74qRniFbSKqWfOYkq8f+O2e9UeDQUp5bVDlE1krOp3Vw6v8WBx27bvlBJ7fXTuW72mqLuEw7nTHLXnZADds6bgar7hoyJznMSdptRnuynytRFSLPHP2Usf2RMPZ0S32KttUZgDwcZqIWNQeGZok5uWR6sOkYyuWHps02pbXnIQo1owDgOwvVha5P7rayQrf6GS3VLAHvB7p/4/JQp6scVm9SMDOkdBeBxoZR7H0nh24FCR0hdwcaO/hbhxMLsbBOBKZjy++sQakRhwZK7ByD0SXUhExXanWziFtK9MqfEOcIxQ5aizHqiNSY1hPKgksfyfUTqR7PoL3bOd7TplC6eV8pqg3II6j2C3NstXrkkp8kAPFprZlI9jxSXrlvAkHW0UTe7//h2aV9n9VD8LCLGQJ/QAAAABJRU5ErkJggg==" id="imageb009c9a4db" transform="scale(1 -1) translate(0 -51.12)" x="118.8" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAIzklEQVR4nO1cu44kNRQ9dnl2d1jtig1ASPwBAYI/4CGxCSk/gAiI2T8ACYlgJdL9BiJCJPgGkBCkBEhEsGKZfXa1bYKqrrq2z3XZ0z1EOJkZ17nH18f3Xruqq8f89vtrEXOzKNtA+gBgMKboY/YTR4kFAEs4VCxhZz5oWKvwDkbzWp/P/w2A83PcDAYI2UULwBOjAYCPMe0zprBfOTLsvIoh47DG6NiM3cIWPqx+lNiAEmth4GOKlZHknsY5ceLBoCSZjIgjEiscpemZRLWCFXQsndM0WpeNcVw6lYUP7iJcIwQkBmImRkJOViUXM1awrQui+MBrQ8wWZMbyoehiuOfxbDKSgszRNGThOS5EsegvBFXEGEnf4ngmxkjEOPjQIuhYWZCJo+SVgrrdklardsM80THT8zDIKPoWUaS9EFViq/ZAllYltkVUJkgy3oaoo7jsLsK5Ai5Ty9KSy7H6ap2Ag0RpHuU1jtay4S7CjdWIDMAK8TQoEY8NqjhYiBE7hKvUPz6HtpQH0jk4H9fM87AFUVh2sbTfYyiEO3DlwgWUwvmlrq0cXlQB6UeYeaUP8oghsR4lbxBzZByJvSgP7mm4jrzpEcD7i52pgu1L13Ze5nNTxAJq1LoLf6MEV4h6UkfD92H/uwXJF8O9iI4PjogxZruVCcmulGA1hzK8NaHEHsSiWL5j5voy7IQPxTyYsAAwZKdlNwbt1pKdO4ZiFb5481vV/vOfPyw741Bsp+sZIy+cQxFN2lnLwuDeG9+rvgDA17++P/PyKA3Zqdo99mXNWQbs2KlYe7Q/7wp/ilXSWDuv1JosISqv3K2e7K9nFzvqgYI9tGc+vTVhHD0LoPvWJtRjf71rAVweSiGuf9tkO1zT7zBA2PBpjDYR8MCR8q54KV6Y64ecjBdYKZ70udY8LOVgcwMA98yfqWQsdFtXCQB2QS/2tJ9wq7ujyWvOtl9yri3p7h6Pa1pR5zq23bxdzNx80toJtw3bMn7eWkpIUnNGv4aU6chpOz8UuvfjR7j/9jfF9a9+uYvdX1Pk9AjcXleGrigGgBckkksfhB7v/PDZ+gy5I3KYkL0F89hI7eK9RKS65/tVTVbWehzoi7x2ZzXuqxNnWgi32/Oi2etQF1YZL8efYqxjssH5YHSA3NbnX73o07GxjlWcT/Aot+gFS/p95PNgHPnxRePdLMitq6zZa/1WIW6Nip4U7plDcs4Zx/TeSvmcjAunitFmr/JyF5pF0oVr9wsAXPCVz/USQzOTKVDqZLszLfa+im2vb621zOU5mX68VNaM4nO0pV9iQbHGxASXYsuawew5ltsDxZMNGFafDr/kNSeMWeR0RIaOZZ3tK8vHOkUKt2MBwEUpDvV0+lFQVBzgWOYZs+/BUheobyq0Jk4SSixlmK1BEYJQUmni4KnExsvTAwqWpd2KJWkH0liai98dfO12X1zrSKu03xT2cRPLONPxY22DKLBsrKkjjxz5lzNjWwgDRhUjNglXmbjWyG6Z2+sTz+05rmbvzF4J46zR50nz4CY3qtSulv5V7CN4qX1H9ANwRTKSxUp4JZlQLBEv4Yikrz6ekWQKNm7xgvSzFVaOJgDgbHNabUWPZq8IuDEWjyh5+RT1kPWvAGf3dScSe34oKe218TvEYT6o2y7z4dLirM2xT0n080PZxSeh2JNG3lzTORTiPiEVP5g4lmzluur8cHVMuqj2GodSBahnR6axM/lns6DpVx8Qx9cTyqGsfle0tkY7wTrLXhetkGiNOdeb9zlHVGZ8VbUrx7rkrTPtWNAy8dq9ooKlHNomRLZ0/fiwwaEcQXLNnBG7lb4dE6INbMtK9ozH2qYPGkejD86SmlMb8BRbZA/HscVa5WgYyxlZcxpIaofNVuf0gn+F4zWInI/lTO1tAKqu4Zcqziel4ZJR05PGm+NtiHq47IZdL1FUBzg6XVSntb33iLEasMXtw1WEb+u5Qh8rXlka58LLv4q7cn73rQwaxc8ImBgRjUG0k/0SpqfckhVsNMBPD+6h1t769H4xnpH3L8Vute+rOSU54J5FnD3xsLuA4AzGWw7780mknpQ4SXRU2uHY0lqg3bDTlWNOrNi5MPuIGw93OPvjEfDPY5ibL2F4/Q6evXoNwRlMIcUpjhGjhtXaQI4tNV5XfQfIkMPt0jGJan3E8HQPPPwb3/35AHfvfILhzk3Y/Rki+7xQRFLPrnLZHSiB+HIzSXlTkiSt+GT4n8uW7oHgLIbzc3xw+2OYm7cQBwsTgMN9WzoZvttVzzMAFbXn3g8AikAofIgJr7PiOzR9d9azXQDiNQv/ysuwt28iXHMI5w7Gx/WLX1qqNCzGpXxT2jCW9bUmsLMjzytqpLwa4c8swp0bMPH6slsZH8uVynirJ+WiXy5ix7lHtCUQGs9T5t33vozaxToRG52DW1JlG9vYh855VKLXGRE5hpEqL2FwB7TnJIpnBK+KQ4Tvedbd91x8+uHsLnvapa4+OUmeJCKOXBDFj1ZsteYYnxWGwNEsqnpWWX+xh0SbV/Adnz4Y5WvAPYvsjE9J9AmzF4a0RCZ92sQ66lTXc+KeTzWUSHVml955ahOmYaq9FMZWsocXAP0XE/prZYRX8Y3WLg522IkzNX+7Z/rR4FDlLchm3uZF2BCqdvLNeZOrAps97JKn5RmU3LUKGtbvFazMsw1eQ3kFk/Zyz0FAYm/K9+cK3pVbfFaOMXugY9fbAkq41af0L2IfyUuvWNPGS/plVOUWLu52YI3WCDU8SdxrD5FOlI4t/bGBV0spQNacjChWHcnF8OoETy3yNoeYuN/AbvjmcPhGbBGWFmVeH+pFBjYsrKeJRsZB9lljDOAJL2vWlhys7i392jwIVvS5+PyF7kDeOlZcPQPRA6LyVY2uwyTxtyeaCbcLLzJxlP9hZZRJ0WZN+cJUTypNBs1Yfk+oi1gshIJ1RWjJlBGDJpm0iOJFlxiAYCPbzoEklfgkLcUmvjG8SK+UV3CwHVQs+P//0KzS/gUy17kR35P5XAAAAABJRU5ErkJggg==" id="image8e5c15d305" transform="scale(1 -1) translate(0 -51.12)" x="180" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAItElEQVR4nO1cS64ctxU9t4p6n0g2YgQJAmQHGRheQmxPvRCP7R0kgGcGMs0yMk/2ECBAphkkU8uRJdlPXV3FDKqrm7w8h0W+lmAPzIlU7MNzLy/vr6qrn/37P7+NOI0B5RjJHACMZsUcW79ylFgAGAiHxBJ2poPCDoJ3NKW13s/PA0CYY3LFjctHjOWcOEmgxI4wLJRDCVwIrThbI1jlB7HEbt4Uvo9J4ERgIBtZF7ANurnTZml4FpsW2NgTyjOXFfUp+1CmYXzaVni53AgSZ9FIjHEWUM4P3pixgm09kCoHAYIcSqzlxnyEh/jkIiA1yMmjxsSdp4woFvP5+jrWz190SLA7HC1GnSoGXTkuY+PdDBoOWViNGJ3HTNiMFN08867SoBuWKcc5NFZu0HnYRDhqBvUeuhk0vFzuqcB1UZmsBpYYBRZQIdDOoQ1Seqk/lKoODV4eXoucsyqrcgwxWodxlCEHWjkEL6l0XQcnPDTdR5hikGSLW7sJn13qGi1idmV1M6BXa7AFc3TrT0r62jPagsXxXnQodS70OvGmHKkBU45tz6lu4fvltlCeeTL3lhMw7mPPCgiDl9jYzMt0lpVVpQoCDy/m+wTUEQJuXinTg9WhWTmYBqzcA8UmOWdyLu4NtH1eCIhjhs1KtKhi2+ZlOU90SQ1FK2byX45N5jJe1sym2Mt8mBbej5blsSzzAPCnD/9K1wPAH//5WT7hDAqkvQUxftFWnLBFZ75iv/z936QuAPDnf326NoGyXcnnw3fHNayYS/e4Lhsvjve7J7UnT4eEDmM1Xs53u9xZtfphfuI+bM8HqtRv44f50iaofNJzAFq3NkO9mm+7DiBMSZkbLWJxN23DuRy63GSxKPV+bNwrL89tRbuwtREo5c0Oe24XKjea6ZgxUA62NwAIh6Xsc1Ll2aLWwbh7eHu8tSXMtihp9dbwavJ9TttdswqTdLw8cfeEg9okxTbokI7Xx9tirnbgYZovLmVUAaXs/kPEzXNajVs1YhF+UT/sEuON82TaPCY62B/+/sXlGXLHSfYZsgPb2CDWTtx/VsVCY8PD8WJNltZ6NswM1svxUziM7SDC4cgTcq9CXVim5DuSdc0BhHnhZdAsAr6sGzCTssmxUWO9sgLLSvRAZAFAFJ4sOcjwRgvHRSe1njBTIcWNIeT9yJ7q58PhUIaV+oaFKcTnrlsPXH8wOv+16xaWmXiOVKISgsVcuxLa6/LruYolvATXY+DgY9IAGtNmsfwe70QYPYep7/yiwLbJY+sVh1mkaZfNqT2HZRoIkhAwi0ssm2wPHy6rzbvk+k4sAISYGkfdvxmxeEUBjmWasfU9WKoC1U1Ca8ZBWsrViVNf5Btg4dTjYfIGu8MblW5s1G5Xc+O0KqXme7AQhpCZnCaxZqwyOk8B6z/BpopxLEG6/+aC1QmShT1vcmS8JUcmtSPEUmyNI9ixrrh28+TudVvY6TlcXt0gTbwMtHuA5VzITFeUasFpKKwWjazflCK8Wp41YWONt0HfC5HWNww+rLryQ+o9iuNywTm4vD0vtsyr2tuMfQ+8XIThWFciW1LM63CSRavDQNSJlCtfW/nIfGDfkly7CaDSg1CC9m6hxxDSmI37CMNMcgfVSZdH5VA9ClGOxlxF20ZWKYUeSocwHCoAdXF188ahe3lNYbXObfLU+rA9mvXrG78KOi9uXl9RnnPwk2/yVqXHyeB7OofiK2rVFtS8peU+UZZksr6mA1vveVUhO2N5BfXygrlqpULVE+1h+ypQG5aNqg6KozEkw+hyjiLpEbg33xNy15bja+SdS/neCTBrP1ZoTwK9VlYGaTjU9DKYehuACjUO6RD6WA99zOFdIw9IwqqNKIr5t6NMHUs+eJfhCXf78DY2xqYf8yyl5Hh8pytloW707K68p3SaW5eGZxwMcWhoC5L5x5XkfP0//vIlauOjz7/OZQGwSKLhfFd+rDwodEaNxDMsAuEhIryeMR4WLE8GTE9HHO8NS/lTmXcWji33clvbop8955dhPPjvP9oVghmGY8Tt8wk3//0WePEK9uwXGH/3AR5+c4vzG3UVxQve3p6nwSjbGKdyrtZLFR2yEmqUKMLmiPHhCPzvO8zfPMc4vY/xV+/B5hsYe1GVVLyWjT+mpBeQ7Z03WdlyknNY0feAZDm+fGDLmmPs7g7Ds2ewu7v1egaGmRu0r8zz9qHr3i/RlZKdL2PeBA5THlZtsZ+cwAIsNyPmX/8Sw/tPsdwELPcBtkRsd/zVsPKHImTRqrLD7cc4lfm1GlbDxOOKLhLfwi/BMH1wB4u3iLZWKpsj/allz70V67GuMdLZERrLun38yVexBqgJl1Z3RqyGAJF3/b1bxx4qrxUGc6Xcsh8V8EWb8ExWepG87BvNSp1E100fPglv1YnZ8j1UsYD53yZlOecNecIuFdoxSqIgX9+OZQfT59V94cd0CzYniWEj9DejZ6OUp1yMoWyrqVHPE1yWf/C/NqACW+hQYveMxT4Nlr3vHgVJLE7SlHJLJOU3NucnpQNP3KUsAPzdIIGt/U2GYIc8rNTbW9RoilhujiVfgaV9lzr9xqQO0JShvCrgkPTU/O2e9Z89hdTLdh280vUfYage3jyHXq7cwy4Xp4OVProt9vPVu0GHZbwgXrtd+4rCxG0GJPqa+rsbjnc14gUbMCVh5U7fZnS5spqP3qtmjW3lXUMvmZs1tsab3Qq5z0I8HKAGzT/SRdlbqY0hCfCwrD2HUF7mRnEwDqtCCgACjqTP2YipcUQWHsp7hbdl3KZ5kfOMvbnWyBswk4AGTkZguYLgSfzCBtCXXdUmev4eDzNkz7NY/aZ2dhniw5t2BTqUVS0BDx/xU42eUJMeTVqFVs9Z3jjjCCEmNqUU4q+UsFNUfz2pHct5tREL3QQ2lCU5iY9EaBZNZ6PMyZSqHJcNncMswyYcewaZhW4Mn3T+OW/Csekh9P35D5pVxv8Bzi2dx3/WZpcAAAAASUVORK5CYII=" id="image612af7cbf7" transform="scale(1 -1) translate(0 -51.12)" x="241.2" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAMwklEQVR4nM2baZBdRRXHf933viXzkgkhiU4yyYQkEkFBtkILKVIqS0DEsrBQq1QU1AL9IKWyifgJQXa0rLJEAcvSUks+WJQLi2xFwubCKhC2BJgkBIdkksm8N2+53e2Hu7x77+v7liTzXk5VJ/f2Pd3n9L/P0ssbsWV8xBCQIwRpki014NDKByAt7TN5rT1n6WDnlZa+HZHBu+Q1a307Em/Gwclg6hY0vw8Lr6V9Ju8BBJrbMM2XRqKzWL0xlLWkZhwWOQ3Glm+Pvj22eSW3T6zl0fGVzOwukivVOXrZVr4y8iinzSlTWLoZgO1bllAN+ilJzZAQSXnRgHRT8QCQg0e3Zg5gNqi+bRUAbsU07UXSRKokNItknopp8FTtIB4tr6Gi8nxj4YZER+f+8zxe//wPE3WvAy9ccQtnXfPdqG6hnMN1Ow6novOsnbuRYwu7WOSUmFQVykZDIDthvYaBUM14ALhl4wLgpDXRHosCS5xQw/xr5woa2qGywE2wuf+daxUw+nA58Z5buonnHjuFqXqRscIOji3sir5VjLDrMCCqGN+mxQOb1hgAKbSV0aG1vmpy0fNip8xi6bHImUNh6WZq21YyqatMKEnZuBFvSdRT/bYCIYUdnGNWbOlmTPuNNo+PAOCWTR4AxyQVe6uxkMd3r2a0uItrjvpzVL9+8ypOW7Uxejep4FdYupkRYAQ4ee3VPPjIFQCc89gF/Omjv4z4frFxLc9NL+d9Q+9w/JxNVh0GRWXtu4xb1fmWj47QvFwZ4Zl3RnlneF7iW9xqOlEIDMB/3hxLfHt15r089vYhsASOLI636jBAF6sEoUaWdZ6qySVKWReY69YYnb+bJXOmEg3LutC1kNNXXRw9j71nZ+Lbotw0qxbsYHF+D2VdoKwLSR1MnrLJ9zQZ+4uqxqVqXNyqybdkBQfNafOe56oP3cWOLUs55q9XMO/n8ylM1vh046GEK4k2M2w2CeAm/+VkOOms6ylM1nn1fJf7h3/C94+4m63jI6yvjgYN4joMznJCA3ArgVullTlppR8HFi7bxoJPzOWhBy8F4LjaM3stdMNfLgke4FAuB2B0+XZ4dZSKLiAtwX8QFFqrW9OtZpvOXIXN70bPFUuM2mdldI5qSo+szNUPqqQtJ6RwIfizlz7OYneKii5QerfMik0/xlQdFuWmE/wnnHMjj995MZ3oH5vez8onrkHVHI5cvYU73vtRFrtTTHjD7PSSayUp9MAWgBB3K9UEJz5bt952Fi/c9B0gma7//voHE3HG3CmAS6xCEvFoVbKfY79+E0/f9l1OOvN6jr7q6Qh0eQAsBCu6gBQaWdMuYZlROWZUjpp2I2DStLa4Z78o8PRt/tZi/d8uZXt1OHKtis5T0Xlqxo1Kv6lqXCo6jzuTsJzOAXHu6Fv7XZmJmbmMFncFOgzecsI47M4o/8EHJuvQYpaV8VxmdP6AcCmAMNS4dR0CEtudt5m9/21ZAsv2v0I1lXSfwWYrHxxZVTmqKkdduVGpejlOPvFH1oZ3Ta/eLwqcvroZxIcL1SjWtca//ltzXbvUtYtbV3bhE5fVGJu4jlypwT3HH87pq18C4O53j9grgWO33gAX+M/TW8eYeDbHiolrOejgaYpOg7oFBN96+g9OGGrctDmDr9RPj/wjH1vpHyGuGz6PM3gRgKMmr+x++xDfsV8Q4x2FN8eXRCeKZ2+4kKo6cBaBETih5aRPXePnOPdN3RE9ezrr9Lg3KsXOdKsqR025B0xADjGRDeXQUA71oHha4mnJH3acwMa3lvD7V47nqG/dHDXMOWqfhZ/0yev5yc7j2To+wnUvrGO6XqChHGrKpaZc6tpJlH5TGIdlw3OIl3pQHnhjDevuv4gr//0Zjvvqc1S3rYrcZF3pXAQGgQn+tZdTTrwqeltx+7VR/RnXP8ydrx7DiQ99m19vPIGachKlnir9pnCSIssJLSZ8r+4sMvRaHjle5GuLH4luEQDuK/+mKyEPPHpl9Dz/2eZi8/tH3M3MjjnM2VhkZmIomhBP+aXuuYnSbwoxkEpJlJJ4noPnOYTvuAavZFAFw/rKmqiht5czWVvYfH7mzWWQM9TnG3BMDJQQJJko/abQYl3lBcKj7BDcBBQVjTGNdDW/e+3DrH/kUCSG6vYch/3gFuZv0kytkPz2/I/w5TVPWoWsvvZmFj5vmB6VzBw6w9kbLkQKw/hLB5Ev1VFjHnlHRwCE93b7HtX2jRqBPq5KKRaSdBRDpQZKSabensfrTy7AqYM6rsL6b97AyLK3aWxbxafO/BLn8oRVyH2fO4xTL38ZgI/ccxkv3rsGpw7l5Yp5y6eYW6wxXS1QreUCHVqzVcZl6axSI/AO1yhfeqRWqIxouptQAgRoF4wSbGyUGAFeaVRRQ9lnvG/UFwMvU9u2kvpTjt+H48sIAfGURKdcxwZSP8nzInDsPq2UoBoCV9DMjIYfJBc9/wWWPDzFzheH2HnWECeLq3lgww+itus+cAVvfHYxP33549z1yNFMPFdi954h1Kh/k0hB4dUdKkqiAxnJhVZskTkAy4m8aexX19mnyaZU1oxaeTMk9+I6NnkZvFZry+TtTpaLSnG2nak0796A1dpHSy+ZvLZ+/dbpy8V2ANjltTZwRRqcdooAJgGIzSU692Fl6mHmrS6Yat8SQxO8Wf0m+3CFl+Q0HUxOxHvuyZ1aq6xA99pvFsNegZ1kcIWX4rMOogfryrQ4C2svFtcz6DaBPcijS7eKc3SyrE59JNvvnbX0y7pdobI/2uqiQdpquxBqBynO29kd2gLdo3W0A9qVXjumbEG2T730YXXfjAabLvtehiKzQ4fcciMQc6sWlfYC6Y5W0aZfG+Bt+5lFkkGSSgbkHtwKYsqLpEkLSPqdNWhmaJaIHYOh8PrO7eIeL9aq9TUxyG5GYwkNPQXpPlBoMFHMSX61N7KmY2vv2fxW9l4sqw8UuVUcnE4BNSumCJ1MMkb6PBm/i86WFRMyQGwIM3gilXcbUBNbEeMXEfyPALQPjNEpvli7zim9m2HMDkVulV4hW9N0t24W3+aobhaP7d1yUCRDy5FezB86+b4gsgBh/G9GCowDxml+FzqI+BqEMRghmnGlw8AHGWtCsrpV82vqsWUB4hchwDgGLf27TBNaggGhDEKFIBoQwcBjAGfKo81+rg/Umq26cR3bwAxIYxCeb0XCmJTlBO3SsaqDrHbXzLNNrW5ldSlhHYuRREBJD6Rn/A6NBiHQDmg3cDfhA5TeMnW7Sx+Emwkv3FtZ3CpUKDp6jA3OSNBCRIN2a4bcHkV+qoGseeicQ2M4R33YoTEk/T/MMr4fC92cCCOyonFKh30fa88km25lWtctkJzGwFUAjONbRhhfnLqmMFnDfXsSM13GKRaRIwvQuRJe0b+xkBqkMpFQI8HIptwwuLfokHjoH4VxWAoVuka6mCComqbrBHVSBXFFGWTdIKfrmMnd3PvurejJXcg9VZy6jvH5JezDL01ZmTqo5iz2k0L5rmxYAl+4/A8vQ+MuEav3BxbU51xOzX8R4RZBiAhUR5oIGBGAZRz//qqtUQx0+xDEnGhwaRJN84rHHOn5aVkGgAkDeiiHs3ABztwSJp9DzytiBMhG4I6maTVh59KYZPbLzJb9R6mZrRqpbbnstEoDRyUPx1Uph87PQ2jjLwpzEqTAqemWbQb4C0Oh6AqQQaT0yHISK2RozbmklQUw0crXSFB5iSk2/cRf5xhkXTeDbQboPR2894nCUOMKr8OBjhD+7MV32Bo/gwmBcYW/Qpb+ItDfPjSDuQj++s5IYXURe1aKxbeBZKvIcprg2P07iA3p9ZAhysHSgFAiGoifpQwoE1mO0DRBDkVajjTSOgxmneMvM1zRCEZtWw1n2HdymQ+kABbpv9UUqX1H2D4NeGilAybh+ZfLPjiZl3at9dGWImYBwhjQutlGCIwjovb2Y4lkbab7dEoQs0BhkmqNOW2iYQKYcNaDYwnCEoCDFs3nNv1FYhPaxZ4H8DOvEBMXr410m/9LYVc4XCRK/GBtsoExNovKAmQQ65wQHNGwrM9t7hSatyVOJJ6zFpUx3pbebYAP8jwniMMuITg2ZWL+LtrNZnRvldysZvGbdBxRdr7Mutmmplt18TMLAJlxlZC2nCzqFuhO/fSBQm9yTaOR+mJTNh6BY2TJJKKHzNcV4AOgEBxx2vB5iSCROThbSs26mLLyZoFm6SMjfVt168XSe5k4wDX1emfGXgRlWIN1YFlrmAzArKF+Fi3dNalsFZ0g9Ki4vf9mfTSwXlypF6Cz9Ohg3Sm3STZNphWfThHn2DvMnCVb9b65YTYA+2bFvQBrBacTWcHrIabsb6ts8u5fq/w/icuVLPeXAyMAAAAASUVORK5CYII=" id="imagea3508ef7f8" transform="scale(1 -1) translate(0 -51.12)" x="302.4" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAIoUlEQVR4nNVbTYwcxRX+qrp61rv+kQALEcQBCXzAQGRFsgQioEQhyAKUSwQXJMgREBfvAaEIX6LAgZ91boEjFw5JTohEEVIuOEEcESSShYz4tbHsgMBmZ3dmuqty6J/pqnqvumpmdjP7Ljtb/dV7X3316lVNb634/MsbDDqWwbdMCKIVkBQWDJbwwWJJzzQPDisJ35lgsD86R7aLTzvi0F2XVzCeg4+lxKp88IKJj78Ii5PRPhkRaKNnnMESg6BEZLELzDq1bTLngbGRpnHutAPIXGzrwzFjEkQ2vsgthxiRy5qbgzVpIgOA2jSqA6IHm8HAfSQJsTgfkugPzCC4sZ+FBXewflPtg3ECQA31SuVQaBKQwW+nxOJ8LIPgvA94glc+qp9q2+TVJ5coJQoroPF8JItNcUgV2+OQKHZj9Ue1WWdO25Eg1Arl+KSw7EB3UezWR4TYVjzHppnTWAfHBW6DWFhmlvaw2F7muAAq+JoY4frsKj6bHMYbX96NK3+5ER+8tt7i7n34JVx8Yhvrd/4Dt698hU29gsvloR0R+9e3fkDi+uyvn9xucyC4qaEeBAlZCtePH9z/KW646Wv87ZMc797/itfnzNvPAm8Dfz9zEU/e+y4A4M2Pj6M7ERnhlyNZ4f3Z58SOsYZLKLPVSOfewxYktJ/6MNjUxgrA2XY59b2pV2DtjJ5fLqsWK0rLjdiIXL9W5gDEIbDp2FH4xPtPI3/uAI5/+xEMc4ACAPwMuO23GxjePMFvxv/CWjYiCMVlK4ed1awsZsRWw3JQE+K2Pb/juUdPAQBMdrqXxNkXTwIAPvrnU7jz0IWpXzKdE4o6U1BjbagHvROjRlp5gApEnV5tQmdfOAlg3cNR9uH5G3HLgf+SYrMTQxRqTsBUc8sJtSmorXJAgyyS8xMqLq9idMSeiN4609M2jzXlJCS2Gmn3a97090WI0pgoBbbqAs35pYhyy2desbaZjcg653R3lFDgedNZ7y8xJpYw55fOnvl3qca2SndZ+fEscVJmDgB++quXgbfiyKxdO7QIzSaKneXzZPZWOeBPybVfNS7pV1RTkpnXfsf6afx74yS+fmy7V5xfDh7D8KFjyA79gO0iT1omIQ7zWlVOwmKrUWmnOhe4237i8few8czr+OOFn0MEltuRP/8Of/jJn3B2dAnvXDqKMUEopfguaqcC7AMqF6/NHO4oR3U8nF/FffvGeL1nBkff78OJtREkzuMdHEV3IlLqmtiB3Wu7zHvLiJoQy4oL3JA8880RnBtej0vDgzj25Ib1pbOxX9zze1y4rsDT5+/Cd+NVXBnti8pKKl4MNtXcckItYfHjt563olGEqLbKod/GY1P8ElgSmbYsU8VWheb+DsAIxWBTBk+TjOeQGi9WbBenisJfVinZ07yfLqOwyyk2W5DLopM5LWjqmXs5TwuYgl1+sZXmlhXXMeCM97E3xVa6cBgI74PV0URhqch7S2wAUKaQfC52Opk+Qha22+59sMj0Y/u5kS66bTOKrVBS+zETKiF7aJZN//hM84a1i2Lb4gTeeHoAdjZCbQsWu/YRJXaLjeQGQAkqcwKEDJGKHHbnxaZ90GK7WMqvU5CFW5ABmEBHMY8oSy+2/VCJwgd1IYZLuwTye1VsflkRQlXBKWx4lvaq2EqUYYDbZg20D0D4oIVysUsgNgAlixDJcHvcQOP6A7ssNjfh3XOOKL1EnDGdY7C037nFrtujxOZ8EBzIghzTsS+duR20a0stNgCV/NeOpEId72MZxVayCANinUXNEmVLLLYlTohQCvmYGtaX5ssgtrWV9xLiNpGdGvz/WWy7IHOEZlhqu5Vp/3k17paHa0ef3WBjNaZkyb99j50NIzhFfGyI0NxiJ5h3+HXiAVTmBAhxMyqE8chS56601GfAHLdEczciipu9Wy3R8vH+zJxSZyKsTYoAN3ZZdWcuuCQMIEsDUVbZYgRgMkBnAkb6+NTimyR2glHjdmN555yYmes6kQWQDzXUsIQca+hcolyVmOzPUA785ZFafGNq1SzW96oGAJQsjEXIJ+8zMgIwEhAaUCON/GqB/NshxHAEDHIU16zBSAEjJYysRXYnKrik+rEVfna1ZBHyW/1szzk8oc7L64a8AIyulpEcG2TDCeSVIczVHyBWVyFXcsiDOWRhoOt/QRH13WVO7DAHH1s9nv1SgbusqDKi5CSi8EnnkaicCWPQXsJUGUSeA6q+0qINZGFabae7FyG2GzAlq2a0ppywZQSAEoFzTrsWS7/NCEAYAxgDPcggD6wCgxxGSejV6h6OKABZ/z+Tde/aFdvx3bVmRmOXWqw14w6VESUnxNdy5q/wVkoLAMZAGEAPMhQHVwAzAISAzmVLgBI25u3jVJT+zJ7F+C/c03htQbYAZTf1uS3d7qcHstMHgDaQTZ0hxOa3dFqUpC+OEdaWk0AZUaJgXuhwM+fcOzBCdJZa9aEVpTFC7JQNwMNz90sSjCwnTmYr6YgzzRQmne3rChAwnV2sUYkKHCd2lwMrYKhORppVThixlZg0ezlHqH9JtL+SyydN7OpRzJKaL3tkYfrLyAPHToUvtnSMPXRRV3z4+x6EXxoaFtv1G88hVuxp5gRAZJp3BSFmPyY4u3wa32RWxfilKTRidx+zmQ1AoeBebDCEmtn0tmOCETtzc4q9QL/2Dmz7VWJCvNChZmMBoswtNsctlIFz+FXoikN1bFJxAaJ0bcfFdn3PkIEKBfUqMEBIEtU3QZS9JLYyk0lkR6JKMucDkUJoScUGAGXGkw6GD+47JAZVY/2rZSEfzqiWQfCahzLjcRucPPCkBCKw5KDq4D6YuRMtGW6kYFQVpuOx3JqaY+qC3A0uEomHglqDWhKhPW7csoL2VTaa6lDSAUtfTMORK3X8DJalJ1Dll8KCEFPTvktElwpBf0sE7hePMA7iZ2mRGWhjdycD/weWz4eecSIwigAAAABJRU5ErkJggg==" id="image2b64b1f4d5" transform="scale(1 -1) translate(0 -51.12)" x="363.6" y="-149.04" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAADF0lEQVR4nO2bMW4TQRSGd9bjSAiJAoHENSgiUSLBERBVDkBJ4TvQRFyAEqWCM9AiIQpOQBCUFFYUR4BCvBSWnHlvPZ/fGHf5/2p25p834z/v/bNrb9LpjwdDV6Av2pOujklK5rqv8FZxUnWsL+IQb7XG9Sp+/RpvdV3nTlJ95/SZbjwkDiB/vbxjOvq0XLcn3dLzizFTjWYecf08G8OOjblX11yIM9kap1jTzKuPCQ4SByBxAPnb5b3qYO88x9eyGQN/sj5GXmFj+PWJy75SrB/0xtU8oQqJA8inf+5XB/1xSSlZcpHXUg7/wbXzlgUvbg3KHIDEAUgcQP7+667poNvysl59jb85fLtuv/h8VI/R8NgxfpyIcV8/fGfGZl+eVWOQBylzABIHkJ5+eGnyKlGambKKP11zqcaO2ZY1o9bg1x9xcTc3HBIHIHEA+efFbdORjK/UJ3pvIj+I+hh/vR73vJa9kVcpcwASByBxAPl8cct0mHrFuvbXMS7zBnddpbbFqYexnje6PxOqkDiA/PdiWh/1qUv5WY41lOOu643KCrmwBoRQ5gAkDkDiAHK/gLdwXBEOUb+IetMW7gC+MnKObc8eax58LaIXCeKQOICcF/5WF9hlWo94aWOTYnTdhvKgvWAJlrx46ZBVKHMAEgcgcQB52uA5eJRDDL4FiK3dFqdOHMXQj3q7QeIA8vTc9QTTfJyeDdwd1hvF2dPeyhL085Q5AIkDkDiAfHAOT74tjwE7P3bUY+7Fxxq4Q8N/At14SBxAPljU309pSc+P72fr9qPnxxCz5e61Hoa4n05mZujw6Hgjb1tMZQ5A4gAkDiA9fvIq/CsXHol9/TbcxrSD+zva6Ve9gkfpIM+JQ+IA8vTst+3h9z7WzWiJeYzSf28lWAzCn7xlfWUOQOIAJA4gT+YXpmPg92s3t4nXddve2Y3F7Jxf4JEc3xvdAihzABIHkIf5me3po6XjdIXSSdE07/lvlaIl6PcSXV9fdsUhcQASB5Cv5vP6aIPnJDyuC26LN5EHoee4vUX9yX0mZQ5A4gAkDuAfk1SyRDGUC3kAAAAASUVORK5CYII=" id="imaged45c7f7c37" transform="scale(1 -1) translate(0 -51.12)" x="57.6" y="-242.64" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABsklEQVR4nI2UMY7UQBBF36/uFUgzntEEJETADVaCG5CTbIiIOQCChCvABQgJCTfaEGlDAsQ1IAEJobFZN4Hb7mrbsyJozfevX69K457RU10kACSGT0NWNFlLAjOqrNngw5CTEcPhkB9OQEaAh1UQVX7UYU8yLcPSqp8kyOwqm3W8OWxmBcr0ESCWdQf2mdjt7+YAFSytQpga07QEJCt1a/eR+fl89YZ2F6bTNUW3TeC4C7Q74/ryNced0TbDOTYBPX7xLlXb3aLLdqeyIraNluGVxpP1GTx2zQrgP7erwcN1jt02LaZMDaPH7RDfF/9ulyaqhxTIzMflsxf77c0JSP2LRMld15KVzwIxbrocKACt6hlw9PI8U0JKxGb7BylhLmyuYQq7Rl83lzES8d7md9U4Dwy6r/xlPWHqAYj3Nz8XhQ9PPvLyy/PBo4RNiUDR788/8errBYEyUG+/PUtjYxgbvVZPcJt6DVSwQE98eOd7hvQEV5gDgnos+8GD6afhgUR8cPYjNxbTXCC4r6NAxiHegyCIj85+DQ/5LQZp+v8M+d2alLUwjJDvj2FYzgQNXf8AXD2vrUvE87wAAAAASUVORK5CYII=" id="imagec142451bba" transform="matrix(2.550857 0 0 2.550857 118.820571 242.941311)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="20" height="20"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAH9UlEQVR4nNWcv6sdRRTHvzN37nskWEaRWBh5KNGIqBC0shMhf4D/gWAtWIq1TWprEVvRIoWdYp1GE1QMMRjUwh9ESd69b3dnLHZn3+zM9+zO7H2/7oGQ3Nkz55z5zDlnfxJ199cnHSLR8QCABRkDgIVSyRib39ogumS+qCtY5jFwXU3sLhTXNU2CBmgALCIbtjce6TqXgGt6p0MjFo7OB1IYFi4B13SWE12Xwuh1kxhSXeta3RicWTndGRlSqrqfCxB6GEKqur9joFW38JxMrDo/g8UI83t/g8W02xcDPVxHOO5hSDnexmHWrguzM6IVh8Eg6XjMB0Js5EBubbgEsqjrgdIYuJE4v9r5RNcB5qFbdgpuYLw3pixikRbKbMyCHR2aBdsdHp+GPbTlYZtVB0d0PlhoCqrVHQG4xbDNQ7sbTSxYKIMiAtwu2ADLnEBXdEIC6kElu7S9sM2jKHPkIAMrA4AEVBjkFsMelBVfqFC3Y46zgjwa2G8/ezPRz5HPf35JBOVtm7XlDVkLCw13/7xe43HzLy4t/8bzyyV2L97F+rdncL/ex+3qAv6qH0OcmSWwgQ54BuxSWbnl6NoAwDyyO5HjvNS/YP7DO5e/TfR2L97FHoA9/AEAuHHnCm6vnsqC3ccgZsrmULyEmybBNo+andGOzUpFw+HquV+ygri2dws3v7sk2w1kNsAZ8tDujrcRAGZtzaiCNP7y0/ezAwmzU4LNhDdqeSNLJG4nbGPMfkZZbbpL+82SApcv6MZ39CjEb9gYbLNu0swZC2gOKHGXMvxJfWZTWCvhRBRmq9lvUqVxKNKTHVm8jxLY7BL/qEoqjEmKCwDMqsuc6T5TDsUL72scdgmUTbJn3+6M2G1jMwck8DHH3uCNO1dwbe9WViCroHQl2DSrjgGKF9ZOYrtJz5ECUtHEL/95BdcwDeejW29h9SdvyJK/kp40V7LKal0b8WAMJDT244Mn8PEPb+Ddy9+IAXz201V8/ftzqJq0JJntoqzatCE3fN2D65zKps/XRFjR70/uvY5Pv3qN6mrlgHvR/Izrp6kYjqopx+2E+TMH9SJQ4IbGd3kDXe7uWDIlFt9OxmCrF7/4QDzKApJ2n0MRnJ4g7JyrfcmXqZu0rPyz6WYwlh98SUacZdim7spKDjLPQY6NbYNtLMmcMSdQDnG4wlsQvtAi3dOFbWwTqarkH+KCUOB4HHaev5OEDQDGCZnTzmqNJSYpwBLdcX90ejzOFjoYG/eX00aMq9lMITQWaQ8lo9QKALY2XIGu7E80EfmKxaAOMkdM/bGxGCABMAN2a2Mi2wa64/5Uga4XM3xckhM8GZMUzhDsksz2YlRcVmKRZ44BcKTJltqgCicM+xBOCRQAboS82gQK++DhlGAbFZ/KxyYq+s8AlKAg2AjlLMI2Ojq5O6Y54ZiDinVPDvZUDFmwARjVyHqyE3ksPnQasJMYZsJOG3LgWHbCjbHxbYZtdM0m8rRTZCyW8d6Ro8vtnijsTgZltalj2cZ2wk56znTdZsgR7bJsgytMwZ5u1EPpy2oq+Pm7zKdsCnvKHxvPatTBzz5zEptF5NMpxw07219mDGz+oCFPBVSyc9sGmx0yKvq+fxgQO80LMRxXqU3A/v76e9zxhLzw/nV+ILzOiTMHOAxSxU9TNt59QXnT3Z8hybpZWak6VZiVzvHtSjDvxGFnyOSJCOTe6tBxGgwdcICygLKuP+60gtOdDTUTNnUexzb/RZ9vJ2Owja6HDrKhAD2YReWgawfVAG4BNEsFa9o/fl6J3SKAM4XdGcSwk+uckuB1A+jKwexbmP0aqnZwRqE+Z1Cf12gc+gzKs6uKY5grqoYI29s2LL2mAvKHdAMs1i0Y82ANVTVwywVgAbcwcErDGjVZKnN60qbZkzyqIWfhYVmJNc53tC0lB1VZqKoBqhoKgKotVN2W2phtOVtT5aOC4sXHNmY36TnDgDwUvkjVuL6xOaOh3ALO6LYJN20P0nCJZwp78CN4U1DQqEsk587AKAaHQGE13n+pv9Rozu9ANRZuoeGWGtCqBRSYp7BphG2vmgp+E0mSgl3n6IplxcTOAe1beNsu3mkF7Gg4qwHd/bauZWyRDTt0NHhaMFHuc6SHM2Lb6IZ/fzd55mjiSwDVfq3mF+evfRSQDTuJgQfOYJcKaychbAUHo6oITtEuCwd0GngJ7OlT+nwovanByYLDNrqy49cMOnlWHxAW6nb4bQh1zG8yhYwg3zpsUlJAe30mwe6z+81XPyRf9QgW+ecKVFUETstHUB4BmOVvJLNzYjCqCrbZT0oenZZAISURBkOzKscuGQxAh4ePIrMBwKgm0DwuKDl2hXGx/2jZbslFZSIBcIOKPNApgEID8g5OOANn2w1tB3aN8nAKdlMad0cA5UzA7uwY1MIDnZIFdcGnr3nKoYRy2rCNO6iGCsL3p6oIFjkdlGTmWYANwKA6AIT/XCcENfzYRwqIFO4WwzaONWQWDIQFiV86ExslsJXQEIg/GpdkF8iGfVhWGTs8WFDJbhJdcUEsjonMLstqoIc9sWbj/HVO/J0Ocxw6CW9Yp8CG11IdqNQu6Qc9VBupMt0pKCTe+J5bDddsYGMqnUJSn2k/UWKD8wACBJqAglSqmurGwQ91o5eTvV1SkuLaohvPJDCX3q224wFmv3jrgqFxXVgCCoAL/PULsuEuB+XAYrPR9vvMpHbjG1olxwvgf7ABnbMEyf+4AAAAAElFTkSuQmCC" id="imagee880ab7d38" transform="scale(1 -1) translate(0 -51.12)" x="180" y="-242.64" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAALx0lEQVR4nM1caawlRRX+TlXdd9/MgAgGIzNs8xxAohBUlkiC/pBlJBogBhMBDXEJQuIfkZjIsCghJjgT/WPikkgMS1ASDTEGIUSJBEgwJjhhUYSBIczIAMIMb95durvq+KO6qqu6q9+9772593KSynRXnTp16quzdfd9Q2+9tpERkCBCnSSafQKi0QcAMjG/jVck5Epq4T3mxWT/JEktsomV4CaTTExMgwAgOb/JC7QcBLfw7jl+LB2sHk2Axz2I8BBUfdAAOPG416M+CnY82LuA7saXk2N14ppCIW997J09mwAAuuQJAT1i02uta0yC9H+3AADUoOWkpk3DmgUDaHHGydOQc7v+fjOHeqvTpy7f7q/36f6aF9+68N1G334Tt0XDOFC2adMBk+GAySAWzTx6phu17c9eiK1HfRMExsm37cAT998ABoFBeC47sryyjaO7uIV3pz1wk797cNcOnHz/D/zYpY9di55RUVs0HSyaDpa44fkTpyXDWDIMkbPCgDtRu2/3J/HQ278EAPznpu9EE/893LiqBTfdGLvNwnV7/fWrd23BgJVvGYRvA06lg8nSgAVyENSS6a5oYi/hduOQWR/Po3XrqjEFLHE1Lnn6rhRSjxUkM9SimW8Mfm3zE1j6/a0Y9OZw1sJu4Nxq7Ni5t3HKtp/g2Ed7ePmSdcAN7YuccNePsHAn8Obp8/jE1f8EzqjGPv7H3XjhudvRnc/x1VMex6KpwBJoBudp0qKZhyAD+tnzn6kVgYxvfeRvEXOYgh9/eQHnbj70qfzeF84q128C8+WT/tG6xiTowZdOBQCoAXfikREWfYJae7ZKkdejXH+W1tNjG2rUQT0PMQqRgJ7Lj8AmvD6acYXUFvvkDEByuoiensNB3cVB3UXPzKFn5rD57ttb0/Wd+85bVSr/3ML10dhF667yd+dcuR0H9bxfP2ypmDgNcJZMF2JoFFzr6w76uoNXrvp+68THnz55VQv+edf26P7h/l3++ql7rsfAdNDT3agNTQdD06mLmji5g1H9Wmoe5WJHPS3x6oQUcjQLVwqpp7uQZKD62p7MuHGne2AyNUhfdyCoKXsWQA1ZAQyooZ5+eZ6ioYn1kAmgpkU9ba1YDWrgpE4vpAMLk3lWdhY8S1AcuYMSA91BZpRvA93B2VfuaJ94Wm8iCrn1XVLo6w6GWmEWlt3Xc+jrOYjcSK+Ea1dt+1NrSj7zhFdbx+ptudReHxsUHd8yLZFpiYFWqFv2dMDpYGgk1LCoFncu9e1T/9o6ccuGNyei0ECrwKWn/yQeUmYkAAmVm2YMyfcuoLNxV3Li2/mGiSiUmxiQUbFvkjRwGTwrJMJWaInLXrw4Yr7g7B/6653/W937nL/sqorHCw+7Gn9/pXph/vN/fRrDQvmWaxndT5ucW4tcW0BcywqJ55/cHEWEh5+6xd+98c7hq3p8+PH5X/B3Dx38Dbad/lk/ds8Nn0dhhG9OuUxLpCx70uRir9BaoKi1dW+2v3Qvhqs7ycbjw7u/9tfrH9mJugXnWiDXAlkx/fjjLacoJMKmtUD/g8v4+ypjQeiaALD1w9Vbsv2XnYFcS+Raeutx97mePjhuXaG1gAmaLiS6J72Lfa9tBIOwbeelOOO6HSAwth59DeY3ZKtK5e//6R5s/diN9in8iu340L1v+bEPXLMbWgtoLRoHpfX03SovJPJCgk68+/aGKZyz8AruO/dX/j58g/fRB27GM5fclhyr00reBG75bWVZ9Q+ZL37p5tY1JkGn/uEWEDGUKURDoeUKr2ICMWC4dzPMozLpsrP45OisVUAToAlcVO2ZPVW6vmnnJTjv4jv8/aC3uq8Plz52rb++4MxbccWT36jun/0ijCaYQvjGmsCaYPT04dGFgC4E6Phf3BEfVyrgpvRr03nc+a1y026a/M1AW3JI8NIK9HJrKdIEjiZS8nKUwPT88ZWv+mqDpQweizcl182vxb9leS0paIDGWmTMPiAAe5Vy2xjWCjaFCWEUL6CoGLGBVgBSCnE5ZQ2gJPoPHdhpq2xjUaKgZTfaNjmcwi3Ij6NfU0ZTh6mAneBTZGobTXGPWDgNVJ13+VM6lGCP0oHGlKuoiJnqvOkTbe9rzE9NmjDY7XtYWbJRlKojiK1EqmJ8XUnHgrJxWOVzpTtxtWj7Rtv7CMBL37u+ZeJkaPMO+5CsRBEP2A04bSsNfepjeHOgEhSWbPscOwOkrTAHEgtuTTwRaO+BX+E5g7FutYy1MSUGgo2RAJjJbj6YRAaIfjBhKF1bONErCdQTJtL2X5X4xUcSrFYlDWDj1upKglG+PwtDEoXdb4tboWEpYUwL4wsZi3QIMsuyiUqeMKgsjmZjEeMSaZvP0m4VBFAfG3zssEC4MSotx5kiYIExHQAyiFWmksUuho0AaFYAugyuhI4HQoWiIOz6TLC5MiuFGcnz6US2DQJ5fa14cvTP1Eno0q2ocBqnFWKqDXCNR5TuIwIeU7lbJacpvA7q2DXVhIm0XbZhORExAOIodjhXAltAjCKwsm7EohwvLPoir1K8BZDsqmU5QOWvRplsf4TDDGOSi8NVheyo7koEawkuCDurYBeYGSg3zaK8LQAq2AdqpjLlC67iGAe1Zvi0/B4I1D6Vi1pADonrrlZ3CwOIvLSQZTZFDEDbRRuVdu3ivWA9QUBuPqMwWReg0vQpSMMsS1cqs5YaMDp9A9k3IM1gSdDzAvl6Ad0lGFXFH7cWE/lUD9SKRczeekRhDzuqc0JLMcoyWOtgOAvTcwTdsSA5cOb3DdHZdwDIC0BJFEe/DzhmHfSchPt1vswYMgPIMIwCNKgqB7Q9BBf8Z+1ZVYUcWE5c6JVBUgMyZ4iMwYJgJEDGfmQhA8ihgdrfg3n9DZh+HzQ3B6kk5JFdkPtxQAmABZl9MHc5XWgus2DNimeEkjMYIQqrsG+6uiZtTcwWea7fboZ01Ye8AGcZHjG/A2cZKC8sENrOFYWVI3KO5EQyIx3KbFdUik4XHKu7Enlc5wA2JggCQHYzrkHYiTIjgBkyY8Aw0FEQ69fjwuwrQOcwcEeB2IIhJZWAW5fy8aeoihxR1OPebLOXSLmVIyK7EZ/G3R+EGQYVDNU3EDn5TenD5yGPOwZUaEAKmA1dGEmQGYOM8XHLV8iGIfJKLiVqLR+sD+WuxyS3r8pyHBHgdhE9SpQ1icgNoAly6CQR8vfNoTi8+tjHZPvlkCGHCfANAGY0aiy/fhqwaVFV5+Sm/Y8pDcrHAwJTkNpzAxiAJdnU3RUwHSrTv3UnkTPkUAcVsuX1ChiOHkStH8c0q5TuDEaRNkD9lMjGFACAsfmW3MY0W3dgm7UMRPVii0rXMWWFXFheJoAkgY0DmePHB0GADuPMbFO6KLh8n5NXFVjbSZGw362BckOmOnLBNlizIr8poU0MIuCf0/zrougja/y4vtwvN6ZBVJjyfU5uzYap9nUodDVdL+vLdxalEHCB6Ff4AoAQMdh1GYEb+ce51HvUWWQrF5AdONGLP6IYHBNUsH7jpXsUBqQ1UGjrikSAkuCOBKSs5JjKjQBE8cdRVYTOtkYWmQaIoJAHKSP09TBAupTr+oUFwbqYAQpt03gpgwEQkQ/oVkb8isLGKPfWq/YWbMZEhXUDRYW2AbFOZplHdW0absZK+msAloe5GewRAFeTgVqsSbrZFIh0CQ7yojUWVNyJPudCQoClqOKQG9MalLPn8wcQZsI2+TPOVjTUgAAUZ5k9yVbOckwEQciYChwpQUpayyk3TsaCA13xEVETeMNVXzIYz8hyylCjoPXyidO9AhSBH7hULlzNwjbm1CwnspDSyhpkgvHG2jOyHV0mqYuO/Hoam9R/8pNyOaBlYy1ld0JGq+Wm+lMAt+qQmj/+Wsos9VsnJZVeiXItvHW5/nSS8W58kNu/N4930HW9FOdZq3BuEUyt6I+nhE/1Dd5DD3ybDpY5llHP2o3UcT5dPlqxhGDbtTbQ7HIzBi3QNZFXDw01QPYrju86SbDXGstWAPTEwFkLTRXYZSz4/ywULf2254eBAAAAAElFTkSuQmCC" id="image88739b51ab" transform="scale(1 -1) translate(0 -51.12)" x="241.2" y="-242.64" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAIBUlEQVR4nNVbS6skxRL+Ijv79IzOCOKFOyIuBAUVFXEt3I2Xu1ZQ0KUrQTcqLkVXghsF1/4H8S/owo0PxAtyD8L1CboaFU6fflRWuKiq7nxEZGWe6jOeE5vpkxUZ8eUXj8yuzqGff7rBiGRGFA8BAIwwNoOiK9hQdUXLMg5N1wi2Z6To3v2dOB4L/d8jR168LDJwRfecCNSDmOpK5HU2BN2ePDr+Mc2cvUHJWIWuYncqsRKpqu6ErLQnbAUnMl8GjPjRjGRdycbONYfPJML3MCPddGikDbhoPklmRcLtsp3LhqkVx5NFc4ZMgThRl3viY91S4pn1zEvWrOhymun2hI9koyxEXiUsHZ8JWabZ0MiVMdRkqoxBIl2ab0/aRTQxAz5ORYmUsYzzbFSTLWGoIVvEoLZc2GW7CB14uqoTAdCOqKQfXF6y7YrnhSA9pYBAGdDOySUmOygraaG7tPMmvfTgJ7vPpCy4m7Lvht/8cA++WN3rgSwnO5fZzz3wpeo/Jx9991iPQyfbrsd2qzhyGTJy8uB8gU+Xi962cCRQMnBGbVFG1MqK+3VnMtsu23C3krbUHcgJ8otbFgECcpkynZRBll7FaGTbpTvqAY2Q4j32S4mVY3ms99Dnb+OF+z9P7XpSk61a8y2Vk3aRbyMA7LpNT8gaUbltb0y+ffptLL9+RiS7Jls13VqJ24kUGHtaUFZTo7QHFAZCP9DlI3oIGdpJjmx76uaqggRoClGnuxLWtn/p5FquWyMrYSOKM9WuXVlZ7RekfdceF7mEtV1K+TpwIDl1cVkJXx9WHjkyKcPY2UmRAGlk15AyJXtO26OM3Q6b3QjRzDk+a/Qee/l9rJxVyRYDcw6kDFJSMUlZaYDIm+hv36Un5FdefB7fn9yVAqrYAA7ZlMMslu3adWNVBTogmCeu/YD//fHPrO2qrJrakIMslu3abSu8Q1Uc68e9cTk+vYFNK/etmsUfqinH7URsyJsmjlJqKI7y6189i8dv/xHHqxt49MP38c0HryZz/vWfd/HKO8/jyTuOcby6G5/9dh82LvSlkX0emRLL0E5yZNMjH7+pPq0pNblMFKcVi6/Sld0Vkx37so1Ly2p41+yCsXLwNSAvMtm28cpKXGgFIH2h3b+XjWzbCpmTc4LduLedVxFYo/v3km1bF6lS8kFdECpq/7KRDQC2bYzupDeWmBQJ9A+JY7oiRpFsVVVaaDA2nWyLPnPGFqQi3REYPswRUEJ2Z6NGV8emmoh8xWLRRD1HjEjOQ0ygkG2k/DGyIB6LflW2lusO/mz6umQMvDCmztf6TG6shuwDZ3ZEoKVGOhJLE4UxZZyFJlttV1K4xWSH5FSA5wzzNIWUC0S2pXgrz01UeGQ1SnlsvlxEsq2JNneWNEccy0TFuhPJrli8iqOGbACWmhKQBYAEooDLTbZcVjuGQ5GjkXdy0chWcUjnHNNIk+S0044rpY4vAtmJDW0SAZYctMcHBH85yQ7IibWLonRGx6KTYgy3hmylrA7ouNcvirImVb2j3MZYo9bLSjwfnMXxuG5dlOVp55HZQeaMAaqJXPLogpMtPbLUcCYaipfYIMmOic+f7P++95qMcUQefuM91dcgyQkZ2INMfs1Uos+GQKb7TAyAAWrTJldMtuLLxzZV4oqR/HUn5EihppbJAMwMbgEYAjH35ITnKqZysmVHErazMzWQk8tWMXP8SaO9w/kOWJm0Hzpk78j9Tj8m5MYz25omdFBCCtN+bybHsGvGbM2ghsEzgrtCcEcEN+/1WDSjDFQSeEaRjjAx2ck5pwi8YbABwIBdMxY3G8x/X8EsN+DFHNs7r2B95xxsun7k96Cs3T5ykwgsFP8Lt9ZG7JBeCYYsoP4DA7M1Y/7nBrNfb6K9+Tvo+jXM8Q80V2doFrNOiQHjuo85uzU9aWr2JK9qBLtJWZVElJnRzgjUdqVEmwa8XqM9PcXMWtCmgWkYxgEtuuZsXNof9BJOQRyKlEGGdefspuQEgAZSwqbB/W4E7s8yRxbm+jXMANDVq3CL7mKQ2XK3a7VhWYnlE/zh6yrgp5aVE8zE5xySyJFIgQfUYb9PM8NdscBd10F33IbWGrire3Iw9LSA34JsNcqjg51zxjFYs5W2kjRywePoF3i3MHCLxf5EbAhMgNm2YonEdiWQwdsCdUufcs7Rjx2DXWucfP8uu3O4sETYADyjbg4ziHvnDHS159tVMAlXHnQC5cyuEamdxG3E0jYiZ6SkUpzUEdNiV2rU9qdkHukz/oCr0D3A1begnShrtqYnR42SkYHuU5r7HkT7rBh8DOQQlZGtZYRwS2ZKSQF9PxxrI/9+4i05DNK1Hf26QjI0Rnaoq9ktx5AjO8UgzU91LW2j09AwMbwZVOxYLAkfjGA33E4rG7iQ2X5WB7IrYQlDmtmWtk0YzTFSlHG1Txikt4IOZRcYJ1u1K/j3eCAAFs6VgR9ZUJgpFWQfMANLbNcEscucnJMaxwMp50X2BLtnIduiUV7oVJCyq/sDkOLL30225c02VFDun1INICNsB5eNbAAW242nJCyqN5jezZOASl0SIuEi2ZpdiWwVgxYwKiPbG08zpwKcuED15nNog7O6OeLL/E3OdACWN5twJJM9sXPx9FhTUoKumlGFpA+6IjaVdKHvGoLlJvqNojBqDIDE/2JTHmGJiC6jbiHBGl5geHWVl6foWWFmeSRFEjVgNWVS04vO0BqKyPFFJEoDBFwqsmKbfwFaHGczmnLDQQAAAABJRU5ErkJggg==" id="image405a3abfdd" transform="scale(1 -1) translate(0 -51.12)" x="302.4" y="-242.64" width="51.12" height="51.12"/> - + +iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAKWUlEQVR4nNVbTYwcRxX+XnXNzu7OeteO7fWuTGyvHf8l/AQcARLJAQwOcAgcQEJCOJcoEhIHMMQnJIRACQIlKEdAQiBAOSB+hMSFCIijEOBKHGIbkjjrv5V/196d/64qDv0z1d2verp3Z4b1u+x29av3vvrqvVeve2bo4oU5g5R4ROkhiMxIqIusLgAIxoZTl7HOYXDpCoddjxjd+f+yupzQ2xY5HAGeY6IbPGfj7iSQ3licNwGoTACFRjmDrOq6yS0TnRyxLt3AdjFybT3ZMjx8N1kG6Vse8bqcjdi1Sd7jCO/BTOlmhwpEsrLmE2c2Q7hs6ApvlDQ7zpJmHESUIc04dBkbPAZTIsoduimzsm7GeFApsJOijRr5uKEn8PLqIbzZ2I6d48t4+n2/Y+cDwPP/Pop/3l7A/tpVPDx1Ftu9Omrko2U81I1M+nNEahrHkT0XnP7WI28szmcwyLquZgGloma7t4I9soNJ8nChVcXPT38YU69O4tR+jafhJufH5z6Cyp9m8Or9B7Ht6Aoemj6HSZrARb+Ja6oW63FR6oGP3GEJV15kwyInBmkR6MGgJSoAOgCAuq5C3api+ryP9kxy99NSvzOOXe/4aO6o4Hp3U+ADhIbx0DJWOlv+BENKerOGIXZ5iXiQHMg0wHpnG6750/BIo2UqeOKRl3D5g5uxdWwVZ56cx6FdV1iHXz5yCpfevQXbKqt419hNvNLaAg8Gdb05occtPoGBz7iBCsdDIq24UBZk8Pfb+/DS6YOgusQTH/0rvvmeP8b3KQe5eSBZDXf/7Bmg7eHggUv40s5/ZMC4MQw/cuzaG9Ue2e53Whng9RtzmD1VwdTlDm49MrlmADv+XMH4LYWzn5tDfT5Z61w1xiM9ksip62q2IDd08rQSDJIxT2F5J6E7xZ9sRWV1p0Bzm0C1toLIL98C8Ck1zCLdYoJENtRYCMjVZ2j89PAvcOjRoK48/5WjiVQyjo4USKbc3u8/hzdPnggufgg8c/pTGX3uQADcx/wgJTqYbB5kW/Mnjq1kF9xPT72Or67B+VsnTwD4ugWmF4VctAKjOaUiiQuyBUU2C6SVLft3LQ0ETEON5XTQ/MEwTGmobL8nm6riJGSYgLiI5fyNqhls6LFMpMq2KgZy0NKMa53rlOIeWke7WbJlkcORkh577Z2dwO5BgIna9WTbPmpSIok2C7A65E6BgmzLr259aE3Oj9UeB+q965ZKHp3sxoyiwQmlqSoZDJm04gAd/u230PnPNGSDMHf9UuL4zu2QLb0XXzuI+379LEgBaqGJ+9WS058r1UZdA2Xbl07HFI61LmzCgReWQYtLuHJkx5qcf2LvWex7cBni+m2cP74HnbnsU3APg+cYH55E5SXR53Q18w41BUbXFG4fnkF1rgZgdU3O25cXcPvEDGRrE9pbNdp9al3e+DCEKy+y46d3KTtx8+wKrj02Dq0IW6XCmcXek/jDj/0Ar/zhKdbhX946gI/tPQcA+OLbx3D1My0QgNpkGx3lettb7GAYtEQZFAmRgfSV6xV4D1Ct2sHClhsY93ycuTGLL3zvG5jfdwV3HtyBv/3mKQAn2fkP/Og53HtgCcsfmMXVxRYe2r2ICa+LK41p3GwmH2Bdi6cRRU9byYwv6atsWkXvmaNX0h2LwK7yMLkK6MtLqO66J9ehrAP6/EVM3LsFRgVGm6qCrvbQDSPWtXhunIvqQQlXXqRvpRUHiAhodiq4vDoDAPB9D8sHAf/4+9HaSvj9T96Lz973L9bhyoKGd/wImjsIwmvg4spmAEC9PQafAePE0Hdp65eOlVYRBtr7wnfZrXOGs4PAojbK6bqiqth8p10eQrbP0SqlSpl/nIA4olzO+5Nt9U7/J7JValxqX7idWMYSZhkCqZQui3HDkS0RRk6/BTlRhoDSL73yCMhAckarKYxtGJEt4acKI2eQtea4yF1QWpezy79lXG9kBzbKkS2zr0v6gWfGnPNdoZ83lo7W4gsaNNmSfK70cxOZMce4YfK+tF1OYcRkJ8kpAd7kME/rIWUDkS0pfZQXnGjPMs5dysdmy0YkW4rU4W44zT6OeaLSuvmFvi/ZJRbvxFGGbACS/KRCsYW6x9K37may+bSKGU4Kvxv5TkZCdlkbBXsiKfzeRb/a4WpXijreCGRnbLgmESBJgb1dyrE1XqZQb3SyE+S4Jjkdu2RYu0zAuW+fKAGkuOz/zrMZKI60sqTELtsS2xoS2YMWrt9zphXfHxT1FExfL9mlCFynZDIIqcgBii8orRcVeztiXCUn11dBf4OWdL8HAJJ8k5PjDkSU3XlD4bhBfARkCEs5KVNQhx1E6X4PYDpkoAcy8Wkm13CEpBiPANGbJzQADZAOTxWK7LrJzrgYYUoBzsixrtZQPEkAOmJKhNGiAaEMSCNBYuaj4zJ1bchkcTywkQMkQebuqIoMm9wFRLfK1Bl+YDgifJOJbCn85G4aF8hICDCCgkgwACkD2TbwWgakDCAAf1xAVQmqQjBRNJngr6v+cBg438MSUtnIjk8rJynxlveutTTBJ2wGkG2D6k0fYzeboEYbZqwCf+sEWveMQXvRmR7sTKJAs6RQPoYhSpoHAJCk+Fbc9cRnBEAURo428NoGlTttiKUbULeWIaZqkJiFNylBkwQjCKSDghd/WTRFDnsAsKCGJ1GfY7vMpFU/QEaH0UBBwSXfgDo+TLuDF1u/xDHvcVDXh+hqCN8L9FSQfhE5vdPL4ZI51Ybe56R5ACBF111I7QJF4U974gWFJxEZwFQ8iKkaHsWToPFp6Grw0arXNSBDIB0SpKMGKLDtjlbrRfeI+hzhZ53EaZWQOPe5ewaesSq7MVATFWB2BrRlCtrzoCcrgCCIjulFjLEJpvweCkH6Om4NRQTDQxA5keTsXEJFUEJHjXvQVa+nH55moqsB5tMNgul/pDveFgxL7LSKNl4KxX//jj05ogvbkCAYSVBhlwyDsBbpsMaYxFxnmjDfLxnlg6foGmvNAWZJ3RQ5TEpxIKOehTyChoDQBsajuPchZYI00oh/hZpbZ1R250Z5pPN9TkiOc5cEs6DYIgHKwBNhmlh1KGj8QmcquNePcGetc/2meoDC8SDh6xBQWjv8a+d+6ohlSbN0WAJENip6p6KjrXA84gxSuH5PUjfluQApmbH0z56JeimU3nXG7oboc1LlxRAgqesni2E/UvLGY8MWMdyul7DrrD8DljhIrMiWUCq7gH6R0mc8Pu7XYZclZYi1h6JT235t/Mk9X0u+kcrMKrEg7uueA7DLfo10DdGXtZs/X8Jn4n6NwOPGbdBEr9NuMvqK25Wm0+UnxvNKABKOuC9lI0ynAad6THYJuxLdjjWYXRybKk6jDnJGSbrDLq0hNZOR40wd7lvuZeoDQ7pT1wWYIcixcSy2EmuzyOm4nTsAEDl+ZZXjqIjuegmPdLPfVs0jPN3nWQXZ+KlP9Qqki4EjTBndtMOkq+R4EE0lUsihOyiSKdveZuXj9HkOAa/scMbn/ABq1BCjtRA5aWHJcoG6i8n6H98r6I/7jDMqAAAAAElFTkSuQmCC" id="imageab2d6b52cb" transform="scale(1 -1) translate(0 -51.12)" x="363.6" y="-242.64" width="51.12" height="51.12"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_image/interp_alpha.png b/lib/matplotlib/tests/baseline_images/test_image/interp_alpha.png index 5679a2f97df8..804cbc50d5f3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/interp_alpha.png and b/lib/matplotlib/tests/baseline_images/test_image/interp_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.pdf b/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.pdf index 67e39f8cb112..129cbb20816b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.pdf and b/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.svg b/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.svg index 6854b0ed81cf..2f1e9fca18bb 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/interp_nearest_vs_none.svg @@ -1,16 +1,16 @@ - + - 2023-04-16T18:07:28.590874 + 2026-04-03T00:07:29.936854 image/svg+xml - Matplotlib v3.8.0.dev855+gc9636b5044, https://matplotlib.org/ + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ @@ -21,356 +21,302 @@ - - - + - - - - - - - - - - - - +iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAG0lEQVR4nGO8tVTh/4JD5xgY6v8z/P+f7P4fAFlwCfU0woTFAAAAAElFTkSuQmCC" id="image9b2e53ece1" width="2" height="2" transform="matrix(81.163636 0 0 81.163636 57.6 93.364364)" style="image-rendering:crisp-edges;image-rendering:pixelated"/> - + - - - - - - - - - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - + - + - + - + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + + + + + + + + + + + + + - - + - - - - - - - - - - - - +iVBORw0KGgoAAAANSUhEUgAAAAYAAAAHCAYAAAArkDztAAAAKUlEQVR4nGOs/8/wnwEKGlLcYUwGJgYcAKcE462lCnCjFhw6R4FROCUAKbgIgxTcr20AAAAASUVORK5CYII=" id="image37f7dc8809" transform="scale(1 -1) translate(0 -168)" x="264" y="-81.6" width="144" height="168"/> - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + - - + + - + - + + + - + + + + + + + + + + + + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.pdf b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.pdf index b338fce6ee5a..3ff7ac577202 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.pdf and b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.png b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.png index 9d93c8fb00bf..453ea0ab2342 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.png and b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.svg b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.svg index 6c958cc79592..e9bde5b99554 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/log_scale_image.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-10T19:29:53.473547 + image/svg+xml + + + Matplotlib v3.11.0.dev1075+g945334b731, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 576 432 L 576 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,100 +35,100 @@ L 518.4 388.8 L 518.4 43.2 L 72 43.2 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + @@ -126,248 +137,248 @@ L 0 4 - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -375,8 +386,8 @@ L -2 0 - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/mask_image.png b/lib/matplotlib/tests/baseline_images/test_image/mask_image.png index 4779a5ea67e3..934e42483498 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/mask_image.png and b/lib/matplotlib/tests/baseline_images/test_image/mask_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/mask_image.svg b/lib/matplotlib/tests/baseline_images/test_image/mask_image.svg index 0f9181a6c1c3..95b1fb40fea3 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/mask_image.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/mask_image.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-01-30T01:57:16.776745 + image/svg+xml + + + Matplotlib v3.11.0.dev1729+g1f7cad29d, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 576 432 L 576 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,100 +35,100 @@ L 274.909091 317.454545 L 274.909091 114.545455 L 72 114.545455 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + @@ -126,70 +137,70 @@ L 0 4 - +" style="stroke: #000000; stroke-width: 0.5"/> - + - +" style="stroke: #000000; stroke-width: 0.5"/> - + - + - + - + - + - + - + - + - + @@ -202,90 +213,90 @@ L 518.4 317.454545 L 518.4 114.545455 L 315.490909 114.545455 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-linejoin: miter; stroke-linecap: square"/> - + - + - + - + - + - + - + - + - + - + @@ -294,60 +305,60 @@ L 518.4 114.545455 - + - + - + - + - + - + - + - + - + - + @@ -355,11 +366,11 @@ L 518.4 114.545455 - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png b/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png index f416faa96d5f..7cdff0a5487c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png and b/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/nn_pixel_alignment.png b/lib/matplotlib/tests/baseline_images/test_image/nn_pixel_alignment.png new file mode 100644 index 000000000000..3821a8a517fa Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/nn_pixel_alignment.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/no_interpolation_origin.png b/lib/matplotlib/tests/baseline_images/test_image/no_interpolation_origin.png index c4b26de1a284..2ee3019d6294 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/no_interpolation_origin.png and b/lib/matplotlib/tests/baseline_images/test_image/no_interpolation_origin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png b/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png index 0d6060411751..58d1ce37c42f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png and b/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/nonuniform_logscale.png b/lib/matplotlib/tests/baseline_images/test_image/nonuniform_logscale.png index da2c0467864e..25653c4dd277 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/nonuniform_logscale.png and b/lib/matplotlib/tests/baseline_images/test_image/nonuniform_logscale.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf index e40335e22503..739de6ee17c7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf and b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg index a4fda6a4e839..7e8f573a1037 100644 --- a/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg +++ b/lib/matplotlib/tests/baseline_images/test_image/rasterize_10dpi.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-01-31T05:22:32.107077 + image/svg+xml + + + Matplotlib v3.11.0.dev1730+g53f57dd80, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 216 72 L 216 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,11 +35,11 @@ L 76.235294 60.977647 L 76.235294 11.742353 L 27 11.742353 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + @@ -40,12 +51,12 @@ L 135.317647 64.08 L 135.317647 8.64 L 86.082353 8.64 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + @@ -54,23 +65,23 @@ L 194.4 64.08 L 194.4 8.64 L 145.164706 8.64 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" clip-path="url(#p6d7104f26b)" style="fill: none; stroke: #1f77b4; stroke-width: 20; stroke-linecap: square"/> - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png b/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png index 65476dc9a595..e33c7effbbb8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png and b/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/rgba_clean_edges.png b/lib/matplotlib/tests/baseline_images/test_image/rgba_clean_edges.png new file mode 100644 index 000000000000..467a757f933f Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/rgba_clean_edges.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/rotate_image.png b/lib/matplotlib/tests/baseline_images/test_image/rotate_image.png index f0edf0225890..1aa443ee55b6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/rotate_image.png and b/lib/matplotlib/tests/baseline_images/test_image/rotate_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/upsampling.png b/lib/matplotlib/tests/baseline_images/test_image/upsampling.png new file mode 100644 index 000000000000..430aaa11d8cd Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/upsampling.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png new file mode 100644 index 000000000000..df8b768725d7 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_connector_styles.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_transform.png b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_transform.png new file mode 100644 index 000000000000..4990efa5cfc9 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_inset/zoom_inset_transform.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/fancy.pdf b/lib/matplotlib/tests/baseline_images/test_legend/fancy.pdf deleted file mode 100644 index b0fb8a4af7f2..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/fancy.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/fancy.png b/lib/matplotlib/tests/baseline_images/test_legend/fancy.png index fbb827bbefa5..a2041b7dc8ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/fancy.png and b/lib/matplotlib/tests/baseline_images/test_legend/fancy.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/fancy.svg b/lib/matplotlib/tests/baseline_images/test_legend/fancy.svg deleted file mode 100644 index 9e56f5ed36bb..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/fancy.svg +++ /dev/null @@ -1,776 +0,0 @@ - - - - - - - - 2020-08-07T20:03:21.839839 - image/svg+xml - - - Matplotlib v3.3.0rc1.post625.dev0+gf3ab37aad, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.pdf b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.pdf index e16d7457059a..442a2accd056 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.pdf and b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.png b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.png index 8bf8c9ea2ba6..5343917be870 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.png and b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.svg b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.svg index 8b8e885da22b..88e2f0568fd7 100644 --- a/lib/matplotlib/tests/baseline_images/test_legend/framealpha.svg +++ b/lib/matplotlib/tests/baseline_images/test_legend/framealpha.svg @@ -1,486 +1,441 @@ - - + + + + + + 2026-04-03T00:28:44.349245 + image/svg+xml + + + Matplotlib v3.11.0.dev2222+g4230aa142, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - + - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + + + + + + + + + + + + + + + + - +" style="fill: #ffffff; opacity: 0.5; stroke: #cccccc; stroke-linejoin: miter"/> - - + + - - - - + + - + - + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_legend/hatching.pdf b/lib/matplotlib/tests/baseline_images/test_legend/hatching.pdf index e345dbff7ce5..e360d5aca6a5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/hatching.pdf and b/lib/matplotlib/tests/baseline_images/test_legend/hatching.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/hatching.png b/lib/matplotlib/tests/baseline_images/test_legend/hatching.png index 9a309c98bb91..2d5cfc8f1883 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/hatching.png and b/lib/matplotlib/tests/baseline_images/test_legend/hatching.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/hatching.svg b/lib/matplotlib/tests/baseline_images/test_legend/hatching.svg index cc9f09b0431e..29983db3d473 100644 --- a/lib/matplotlib/tests/baseline_images/test_legend/hatching.svg +++ b/lib/matplotlib/tests/baseline_images/test_legend/hatching.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:43.532119 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,109 +35,109 @@ L 414.72 307.584 L 414.72 41.472 L 57.6 41.472 z -" style="fill:#ffffff;"/> - - - - - - +" style="fill: #ffffff"/> - +" clip-path="url(#p601e849151)" style="fill: url(#h6153324e45)"/> - +" clip-path="url(#p601e849151)" style="fill: url(#h11887c8ba3); stroke: #ff7f0e; stroke-linejoin: miter"/> - +" clip-path="url(#p601e849151)" style="fill: url(#h5f4f9f2203); stroke: #000000; stroke-linejoin: miter"/> - +" clip-path="url(#p601e849151)" style="fill: url(#h8d19ea12d1); stroke: #ff7f0e; stroke-linejoin: miter"/> + + + + + + - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + @@ -135,47 +146,47 @@ L 0 3.5 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - + - + - + - + @@ -183,675 +194,656 @@ L -3.5 0 +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="fill: #ffffff; opacity: 0.8; stroke: #cccccc; stroke-linejoin: miter"/> - +" style="fill: url(#h6153324e45)"/> - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + - +" style="fill: url(#h11887c8ba3); stroke: #ff7f0e; stroke-linejoin: miter"/> - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + - +" style="fill: url(#h5f4f9f2203); stroke: #000000; stroke-linejoin: miter"/> - - - - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + - +" style="fill: url(#h8d19ea12d1); stroke: #ff7f0e; stroke-linejoin: miter"/> - - - - - - + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + - +" style="fill: url(#h4c1e6e79d5)"/> - - - - - + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - +" style="fill: url(#h738a389c00); stroke: #2ca02c; stroke-linejoin: miter"/> - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - - - - - - - - - - + + +" style="fill: #000000; stroke: #000000; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/> - - + + +" style="fill: #ff7f0e; stroke: #ff7f0e; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/> - - + + +" style="fill: #000000; stroke: #000000; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/> - - + + +" style="fill: #ff7f0e; stroke: #ff7f0e; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.pdf b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.pdf deleted file mode 100644 index fef8197061cf..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.png index df81e176eeaa..baa71b173451 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.svg b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.svg deleted file mode 100644 index 17a7270dc97b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto1.svg +++ /dev/null @@ -1,561 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.pdf b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.pdf deleted file mode 100644 index 95642d306bdc..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.png index ef28581eaccf..6a6af293c5f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.svg b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.svg deleted file mode 100644 index 4dbb53e56e0b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto2.svg +++ /dev/null @@ -1,1990 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.pdf b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.pdf deleted file mode 100644 index 69c1ffba7a60..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.png index 5463eaacc97c..c71515453efa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.svg b/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.svg deleted file mode 100644 index dcc9d3f83f9a..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/legend_auto3.svg +++ /dev/null @@ -1,594 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.pdf b/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.pdf deleted file mode 100644 index 7db4ea697579..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.png index 4ad5d3f87601..86e0da56012b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.svg b/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.svg deleted file mode 100644 index 142da9bd7fe6..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/legend_expand.svg +++ /dev/null @@ -1,1135 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png index 456c1970e207..e2d3be4089c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_labels_first.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_multiple_keys.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_multiple_keys.png index 9e432c072067..d9429c1c0290 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_multiple_keys.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_multiple_keys.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_stackplot.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_stackplot.png index 646753355838..ee4989c512c4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_stackplot.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_stackplot.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.pdf b/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.pdf deleted file mode 100644 index a16341c99039..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.png b/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.png index 9faf54d00ffa..67ddb90da287 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.png and b/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.svg b/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.svg deleted file mode 100644 index d0ee0f63fdf8..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/legend_various_labels.svg +++ /dev/null @@ -1,596 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter.png b/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter.png index f88c70be879f..d94b074b33c1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter.png and b/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter_transform.png b/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter_transform.png index fc746c4edcc7..f27b0d6546f0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter_transform.png and b/lib/matplotlib/tests/baseline_images/test_legend/not_covering_scatter_transform.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/rcparam_alpha.png b/lib/matplotlib/tests/baseline_images/test_legend/rcparam_alpha.png index e55321cf34e9..31723aa8dea9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/rcparam_alpha.png and b/lib/matplotlib/tests/baseline_images/test_legend/rcparam_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/rgba_alpha.png b/lib/matplotlib/tests/baseline_images/test_legend/rgba_alpha.png index 767106c24679..314d665518d1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/rgba_alpha.png and b/lib/matplotlib/tests/baseline_images/test_legend/rgba_alpha.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.pdf b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.pdf deleted file mode 100644 index c7fae8bd6dde..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.png b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.png index e92d368f33d3..58f146f5019e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.png and b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.svg b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.svg deleted file mode 100644 index ea371456253f..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc1.svg +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.pdf b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.pdf deleted file mode 100644 index 321d75f1ae41..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.png b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.png index 1d9c4cf999ae..5addd799b4b6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.png and b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.svg b/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.svg deleted file mode 100644 index cd5aa2fcef1e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_legend/scatter_rc3.svg +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_legend/shadow_argument_types.png b/lib/matplotlib/tests/baseline_images/test_legend/shadow_argument_types.png index c38699467d55..010bebee5157 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_legend/shadow_argument_types.png and b/lib/matplotlib/tests/baseline_images/test_legend/shadow_argument_types.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.pdf b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.pdf index c644b4cd8c5e..5d13d34f15e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.pdf and b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.png b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.png index ab6317278b53..a04f90f171e4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.png and b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.svg b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.svg index 6fbecd2b9deb..248056f322f7 100644 --- a/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.svg +++ b/lib/matplotlib/tests/baseline_images/test_lines/scaled_lines.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:49.209875 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -24,227 +35,233 @@ L 414.72 307.584 L 414.72 41.472 L 57.6 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - - - - + + + + + - + - - + + - - - +" transform="scale(0.015625)"/> + + - + - - + + - - - - +" transform="scale(0.015625)"/> + + + - + - - - + + + - + - - - - - - + + + + + + - + - - - + + + - + - - - - - - + + + + + + @@ -253,124 +270,125 @@ Q 46.96875 40.921875 40.578125 39.3125 - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - + + - - - - - +" transform="scale(0.015625)"/> + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 1.85,0.8; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 0.5"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 5.755556,2.488889; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 1.555556"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 9.661111,4.177778; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 2.611111"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 13.566667,5.866667; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 3.666667"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 17.472222,7.555556; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 4.722222"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 21.377778,9.244444; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 5.777778"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 25.283333,10.933333; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 6.833333"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 29.188889,12.622222; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 7.888889"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 33.094444,14.311111; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 8.944444"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 37,16; stroke-dashoffset: 0; stroke: #1f77b4; stroke-width: 10"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 0.5,0.825; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 0.5"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 1.555556,2.566667; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 1.555556"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 2.611111,4.308333; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 2.611111"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 3.666667,6.05; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 3.666667"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 4.722222,7.791667; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 4.722222"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 5.777778,9.533333; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 5.777778"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 6.833333,11.275; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 6.833333"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 7.888889,13.016667; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 7.888889"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 8.944444,14.758333; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 8.944444"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 10,16.5; stroke-dashoffset: 0; stroke: #ff7f0e; stroke-width: 10"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 3.2,0.8,0.5,0.8; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 0.5"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 9.955556,2.488889,1.555556,2.488889; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 1.555556"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 16.711111,4.177778,2.611111,4.177778; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 2.611111"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 23.466667,5.866667,3.666667,5.866667; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 3.666667"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 30.222222,7.555556,4.722222,7.555556; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 4.722222"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 36.977778,9.244444,5.777778,9.244444; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 5.777778"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 43.733333,10.933333,6.833333,10.933333; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 6.833333"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 50.488889,12.622222,7.888889,12.622222; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 7.888889"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 57.244444,14.311111,8.944444,14.311111; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 8.944444"/> - +" clip-path="url(#pd5d89dcd2d)" style="fill: none; stroke-dasharray: 64,16,10,16; stroke-dashoffset: 0; stroke: #2ca02c; stroke-width: 10"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_lines/striped_line.png b/lib/matplotlib/tests/baseline_images/test_lines/striped_line.png index 957129cb981f..758ef251d5ee 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_lines/striped_line.png and b/lib/matplotlib/tests/baseline_images/test_lines/striped_line.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png b/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png index feb34b1793db..e7fade353e60 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_00.png index 0ac313befd47..91150d2c2922 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_01.png index c4de985ffb32..e46bb62c3f02 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_02.png index d7463a69cb0a..922a2fc5f9e0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_03.png index e228e39f2819..7c3ee7d3d6bd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_04.png index 0c5fba073181..ed831bb8cf5b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_05.png index 127ef7dd910e..20048247a899 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_06.png index 27ab64d232cb..5769c591714a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_07.png index 4e5e0287ce7a..5ff42c34392e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_08.png index 33426cb185ef..e959a733ab0b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_09.png index a5b9b6018e02..77b8412948b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_10.png index 91ab33a1af88..bb74cd5f165b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_11.png index 66ee00e65f53..cae4e006110f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_12.png index 679209e5ff7c..a701f08389de 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_13.png index 80b36cb78b97..e3faabead9b6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_14.png index 5f8995a1756e..2196f71f8eac 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_15.png index f7933d12b585..c5ca67dd03be 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_16.png index 5199147b0b2a..5e2ff07c0541 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_17.png index e96ab125bc22..11f662f9aae7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_18.png index 1761facc085a..7ee8d38002b7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_19.png index 85e5c76656f6..8045f17cd715 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_20.png index 100f65c37098..2c6429267d13 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_21.png index 0116cf31e5dc..66298251d8b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_22.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_22.png index 166299364c28..c57534104b4b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_22.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_22.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_32.png index a9c287089828..73cf51d1ea20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_33.png index 8230ca5ae81f..22edfa0ed1ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_34.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_34.png index bc6804e51b44..4dfd1beedf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_34.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_34.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_35.png index 6fd2a414526a..15e0af6e9bad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_36.png index 1acf93778357..dab9f5edd1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_37.png index 8c02b7fe9879..cb2c1d4ce441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_38.png index 9c8f9fade93d..810467897d06 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_39.png index d3e823c4257a..c4c809793c31 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_40.png index 590425ecdc52..cadc0a85baf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_41.png index 337b9f3150a6..45af7b72a2ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_42.png index 9d33f53d5558..d862e5563b35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_43.png index e3bec1bcadb3..b7774d9e7788 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_44.png index 5b004bc39b59..dcd6d81702c2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_45.png index 8470d37ca0e6..38a2dc7ce722 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_46.png index 8cc41c6d6840..7f2b33b5c867 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_47.png index 8327a7d31b4a..875a83baed23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_48.png index 64c653e67e5e..9d2ee6e573b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_49.png index e23a422a745c..ed7a48f0ce28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_50.png index 9423513dbdff..5d37ed6eea3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_51.png index 75258500cec0..b35664168529 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_52.png index ebd3476eea81..25e662ea315f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_53.png index d6a6c4e0a81b..64213b476090 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_54.png index e4faab4691fa..240e1c5d380b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_55.png index d5a25192206f..6683bd061e75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_56.png index 8301835aa9e0..da87d3f1534b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_57.png index 3ad8a58ef726..f933883b212d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_58.png index cefd7f3a7fda..282d1f174440 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_59.png index 1dddcca0a353..076e784e13a5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_60.png index 92c6be388eb3..ffd75bee281d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_61.png index eb3ccc2ba586..495ecd6e1c35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_62.png index a646f604a3e3..db904d1e8136 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_63.png index d9e3e47b176e..52ba874bc02d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_64.png index 5985624ec817..41ae5fbe3cce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_cm_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_00.png index 35808bc58cd5..24c7c257e396 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_01.png index f2024b715fa4..41d5413e50b7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_02.png index b2d2a9a0e093..b4140bbcab9e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_03.png index fe437eab37a3..cef5ef0dfa44 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_04.png index 4fd29aff335f..ac3d0b6f9d82 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_05.png index 10cdabdd3c36..1a6b2339e425 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_06.png index 49c13cf87d1f..8a1f18efcf86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_07.png index d46178a6b557..ec7a9ec3ea83 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_08.png index 431c1c5d4100..c7eb8cefd1b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_09.png index 9e027208e495..712a93516944 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_10.png index 68dad026721c..a7fa31fb7c39 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_11.png index 3e9e636c3d8d..9de63f7e1d5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_12.png index 2d1b4d416d8d..975185df0ae6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_13.png index dba0f1dc2cd4..33d80705362f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_14.png index d08b96b464b2..befb19e8cf6d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_15.png index c38343778f3d..45cc5c10dc65 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_16.png index bca817bd8078..bb7d88241b60 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_17.png index b39bec8c56be..ee24c715b6b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_18.png index 07ebc5a895d7..5e63206c92bd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_19.png index fc38134e6242..037d11f4b953 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_20.png index 5dc91d53a229..1386dc1b7fc2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_21.png index bf6947774b83..26bf5e0506aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_22.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_22.png index 524e93129996..74be6d6e1aa4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_22.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_22.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_32.png index a9c287089828..73cf51d1ea20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_33.png index 8230ca5ae81f..22edfa0ed1ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_34.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_34.png index bc6804e51b44..4dfd1beedf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_34.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_34.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_35.png index e01a3737f15a..99c28d9d4334 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_36.png index 1acf93778357..dab9f5edd1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_37.png index 8c02b7fe9879..cb2c1d4ce441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_38.png index 9c8f9fade93d..810467897d06 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_39.png index d3e823c4257a..c4c809793c31 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_40.png index 590425ecdc52..cadc0a85baf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_41.png index 337b9f3150a6..45af7b72a2ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_42.png index 9d33f53d5558..d862e5563b35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_43.png index e3bec1bcadb3..b7774d9e7788 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_44.png index 15c2c46d6894..fe938b8248c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_45.png index 8470d37ca0e6..38a2dc7ce722 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_46.png index 8cc41c6d6840..7f2b33b5c867 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_47.png index 8327a7d31b4a..875a83baed23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_48.png index 64c653e67e5e..9d2ee6e573b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_49.png index e23a422a745c..ed7a48f0ce28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_50.png index 9423513dbdff..5d37ed6eea3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_51.png index 6d01566d986d..f4ca56276119 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_52.png index a0f3a4f11838..53ff19aa151c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_53.png index 05726ab1c850..4bcfb575546b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_54.png index 3371ab56631c..816dad31bf03 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_55.png index 284ed20744bd..a39a32a3a80d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_56.png index 294ac7890bbc..f529ee07d9f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_57.png index e9cfb211ffca..bb4e5be9b78c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_58.png index 0faf2f392326..efc94fbd32d4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_59.png index 8b9bc40cee1d..ad2b436ba0dc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_60.png index 58c3cf96845b..156659640530 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_61.png index 5c9e07694012..13291076962f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_62.png index 0a06cd6a2598..9a0e2d05f308 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_63.png index cca544da1bc2..6e37554e6eb6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_64.png index a5cd8629efea..81697ec50d92 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavusans_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_00.png index dabeca4cccec..ff9796a877e2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_01.png index 0893e9592deb..7679cf235a90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_02.png index 3a94c4c69ca6..3f2bf30948ac 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_03.png index bc77000754c5..2284c9f5536e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_04.png index 3940aae8b813..3c2729bdec62 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_05.png index 7200ae299c51..aeeffbbaf8fe 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_06.png index bc8519068235..db954c33d046 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_07.png index c1efe5f4a7ac..2cb2648033d1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_08.png index 165ee8dc785a..fccf55e4011c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_09.png index 2dfb980b9ef7..29c8af2ca764 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_10.png index ade221179e64..5d6814472853 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_11.png index 5ff69ac5a0dd..5f4047313d88 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_12.png index 892b8e88347e..cd51d1de85ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_13.png index c637d9440e2a..80e5d4493802 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_14.png index b776fb15e890..685a396a8b87 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_15.png index d555f1044327..de289fb56d2a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_16.png index 357e054c6b3c..a22862dcf24c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_17.png index 8590c285a53e..25d0bfe1dd97 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_18.png index 25751a4ef86a..f42a36885574 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_19.png index 20a9d23f451d..750f5275e164 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_20.png index 5dc91d53a229..1386dc1b7fc2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_21.png index bf6947774b83..26bf5e0506aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_22.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_22.png index 524e93129996..74be6d6e1aa4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_22.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_22.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_32.png index a9c287089828..73cf51d1ea20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_33.png index 8230ca5ae81f..22edfa0ed1ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_34.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_34.png index bc6804e51b44..4dfd1beedf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_34.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_34.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_35.png index 6fd2a414526a..15e0af6e9bad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_36.png index 1acf93778357..dab9f5edd1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_37.png index 8c02b7fe9879..cb2c1d4ce441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_38.png index 9c8f9fade93d..810467897d06 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_39.png index d3e823c4257a..c4c809793c31 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_40.png index 590425ecdc52..cadc0a85baf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_41.png index 337b9f3150a6..45af7b72a2ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_42.png index 9d33f53d5558..d862e5563b35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_43.png index e3bec1bcadb3..b7774d9e7788 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_44.png index 15c2c46d6894..fe938b8248c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_45.png index 8470d37ca0e6..38a2dc7ce722 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_46.png index 8cc41c6d6840..7f2b33b5c867 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_47.png index 8327a7d31b4a..875a83baed23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_48.png index 64c653e67e5e..9d2ee6e573b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_49.png index e23a422a745c..ed7a48f0ce28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_50.png index 9423513dbdff..5d37ed6eea3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_51.png index 6d01566d986d..f4ca56276119 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_52.png index a0f3a4f11838..53ff19aa151c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_53.png index 05726ab1c850..4bcfb575546b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_54.png index 3371ab56631c..816dad31bf03 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_55.png index 284ed20744bd..a39a32a3a80d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_56.png index 294ac7890bbc..f529ee07d9f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_57.png index e9cfb211ffca..bb4e5be9b78c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_58.png index 0faf2f392326..efc94fbd32d4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_59.png index 8b9bc40cee1d..ad2b436ba0dc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_60.png index 46ff984ed8c4..a32188889818 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_61.png index 59dfb57d9de2..8bc5e03d80b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_62.png index f899ba21bb2d..6b2cdd1d9131 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_63.png index 4d670f64af3e..9f30c6bd1ccf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_64.png index 621dfad14623..88cb815aa68f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_dejavuserif_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_00.png index 47ca06bcea93..201318c06a4f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_01.png index 0db2078302c2..373db385ccce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_02.png index 96fa961bdf37..2e1fbb39e6c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_03.png index f0453bb9e2c8..acae2cc52a2c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_04.png index d7d702c66aa2..e38923675661 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_05.png index 3ee509585e70..0706652d9ef6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_06.png index 987f70d02c4a..2ed7e2830311 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_07.png index c0859b4e4ce1..476134298d81 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_08.png index 9a877363e441..b20fd61d73eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_09.png index 28e4a8498f10..6b7eb4d37cd9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_10.png index b1fafe4588f6..976f94dc1344 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_11.png index b787af3a3326..8982cb2a28b3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_12.png index 756c5e56097a..cbe666d12f58 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_13.png index d1e8a49df318..4a77cfcfff32 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_14.png index 5f8051886104..a12e86ac4566 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_15.png index 1935554ce8c5..dff487fc2452 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_16.png index ad942a517296..60250abeed83 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_17.png index 12abdae2e203..d1d46b7c5159 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_18.png index 1ac3d5c843a8..7e1fd4045927 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_19.png index 422d5d8a9c1c..ce876adc1684 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_20.png index 6d227c447fdb..d5d0d211c4dc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_21.png index ec2f3c06eea9..759030051a90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_22.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_22.png index 17bb6fa58f3e..07e71421e4db 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_22.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_22.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_32.png index a9c287089828..73cf51d1ea20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_33.png index 8230ca5ae81f..22edfa0ed1ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_34.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_34.png index bc6804e51b44..4dfd1beedf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_34.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_34.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_35.png index 6fd2a414526a..15e0af6e9bad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_36.png index 1acf93778357..dab9f5edd1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_37.png index 8c02b7fe9879..cb2c1d4ce441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_38.png index 9c8f9fade93d..810467897d06 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_39.png index d3e823c4257a..c4c809793c31 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_40.png index 590425ecdc52..cadc0a85baf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_41.png index 337b9f3150a6..45af7b72a2ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_42.png index 9d33f53d5558..d862e5563b35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_43.png index e3bec1bcadb3..b7774d9e7788 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_44.png index 15c2c46d6894..fe938b8248c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_45.png index 8470d37ca0e6..38a2dc7ce722 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_46.png index 8cc41c6d6840..7f2b33b5c867 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_47.png index 8327a7d31b4a..875a83baed23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_48.png index 64c653e67e5e..9d2ee6e573b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_49.png index e23a422a745c..ed7a48f0ce28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_50.png index 9423513dbdff..5d37ed6eea3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_51.png index 03c7e22b0ca6..6f78ea9de0ed 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_52.png index ef8cbc4c2b19..9a7f09e696ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_53.png index aaf1e3dd426c..90a19ded6da5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_54.png index 54595c34978b..dd12803729ce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_55.png index abcf618d25d2..d096098943e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_56.png index 356a88087e8a..bb698a10ead9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_57.png index bd7d2103faa0..186e0e03e39b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_58.png index 512aa9b0a219..e916d96d336a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_59.png index d2b1aa74e81a..c7c750dfcb96 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_60.png index 92c6be388eb3..ffd75bee281d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_61.png index eb3ccc2ba586..495ecd6e1c35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_62.png index a646f604a3e3..db904d1e8136 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_63.png index d9e3e47b176e..52ba874bc02d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_64.png index 5985624ec817..41ae5fbe3cce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stix_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_00.png index 7109d54ec56a..8ca8fde71712 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_01.png index f73e3999768b..3e0ce6302afb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_02.png index 01ccce3df1d3..6accb07688d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_03.png index 9f6a70b1076a..cb6984428bef 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_04.png index 89237266990c..df0c4054d240 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_05.png index fec089302890..15c4f7a6362b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_06.png index 49529796b0ed..a69659364e74 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_07.png index 2438d393a245..1ebe25e1680e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_08.png index dba202a996a1..12de76dff0cf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_09.png index e4f2b6d82cdd..6e3b1c454e72 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_10.png index 422a8ae60405..2763fc7e6966 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_11.png index 4e10c41375e1..de800aa6d23a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_12.png index 10ec61fd88c5..dc9eb4cc225e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_13.png index 72d44f492074..76dfc863390e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_14.png index 3299b4305f24..1004091a5c42 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_15.png index 6ad62be433e3..a4800741d806 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_16.png index f8f1bb1cb7f2..3f6893bb8eb1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_17.png index 239e6b7b7fb0..8e63f34fff59 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_18.png index b6a3d0cb4607..cb52336ff2ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_19.png index 1fd70f56891e..5678a91a9806 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_20.png index 6d227c447fdb..d5d0d211c4dc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_21.png index ec2f3c06eea9..759030051a90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_22.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_22.png index 17bb6fa58f3e..07e71421e4db 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_22.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_22.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_32.png index a9c287089828..73cf51d1ea20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_33.png index 8230ca5ae81f..22edfa0ed1ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_34.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_34.png index bc6804e51b44..4dfd1beedf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_34.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_34.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_35.png index e01a3737f15a..99c28d9d4334 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_36.png index 1acf93778357..dab9f5edd1d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_37.png index 8c02b7fe9879..cb2c1d4ce441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_38.png index 9c8f9fade93d..810467897d06 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_39.png index d3e823c4257a..c4c809793c31 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_40.png index 590425ecdc52..cadc0a85baf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_41.png index 337b9f3150a6..45af7b72a2ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_42.png index 9d33f53d5558..d862e5563b35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_43.png index e3bec1bcadb3..b7774d9e7788 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_44.png index 15c2c46d6894..fe938b8248c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_45.png index 8470d37ca0e6..38a2dc7ce722 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_46.png index 8cc41c6d6840..7f2b33b5c867 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_47.png index 8327a7d31b4a..875a83baed23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_48.png index 64c653e67e5e..9d2ee6e573b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_49.png index e23a422a745c..ed7a48f0ce28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_50.png index 9423513dbdff..5d37ed6eea3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_51.png index 03c7e22b0ca6..6f78ea9de0ed 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_52.png index ef8cbc4c2b19..9a7f09e696ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_53.png index aaf1e3dd426c..90a19ded6da5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_54.png index 54595c34978b..dd12803729ce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_55.png index abcf618d25d2..d096098943e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_56.png index 356a88087e8a..bb698a10ead9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_57.png index bd7d2103faa0..186e0e03e39b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_58.png index 512aa9b0a219..e916d96d336a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_59.png index d2b1aa74e81a..c7c750dfcb96 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_60.png index 92c6be388eb3..ffd75bee281d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_61.png index ee51257365e6..5eb13fe9a26e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_62.png index 1fcf6a6dcb53..992ba3224b23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_63.png index 851cc51fc1ad..852e79665974 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_64.png index 39c976685823..f615a48b3f4c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathfont_stixsans_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_00.svg index 619549bbbe68..518d9b9dc069 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_00.svg @@ -8,10 +8,10 @@ - + - ¡ - - + ¡ + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_01.svg new file mode 100644 index 000000000000..813c69f84e05 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_01.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + ( + ) + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ² + ¡ + ± + ² + ¢ + M + B + B + ³ + . + / + ´ + M + I + M + B + U + I + N + G + ³ + . + / + ´ + µ + Á +  + + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_02.svg new file mode 100644 index 000000000000..06f9a44349c3 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_02.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + [ + ] + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ² + £ + ± + ² + ¤ + M + B + B + h + . + / + i + M + I + M + B + U + I + N + G + h + . + / + i + + Á +  + ¸ + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_03.svg new file mode 100644 index 000000000000..0659dcfd7297 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_03.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + h + i + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ² + ­ + ® + M + B + B + ± + ² + D + E + M + I + M + B + U + I + N + G + . + / + D + E + . + / + ¿ + Á +  + À + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_04.svg new file mode 100644 index 000000000000..b988f429a576 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_04.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + © + ² + ª + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + © + ± + ² + ª + M + B + B + n + . + / + o + M + I + M + B + U + I + N + G + n + . + / + o + ½ + Á +  + ¾ + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_05.svg new file mode 100644 index 000000000000..cd30619f2223 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_05.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + ¥ + ² + ¦ + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ¥ + ± + ² + ¦ + M + B + B + j + . + / + k + M + I + M + B + U + I + N + G + j + . + / + k + ¹ + Á +  + º + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_06.svg new file mode 100644 index 000000000000..1e1423f64b0f --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_cm_06.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + § + ² + ¨ + M + = + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + § + ± + ² + ¨ + M + B + B + l + . + / + m + M + I + M + B + U + I + N + G + l + . + / + m + » + Á +  + ¼ + M + I + B + U + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_00.svg index 72ba7d0a32cc..1d983187af08 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_00.svg @@ -8,9 +8,10 @@ - + - −- + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_01.svg new file mode 100644 index 000000000000..092e2336a1ec --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_01.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + ( + / + ? + \ + ? + ) + ? + ? + ? + ? + ? + ? + ? + ? + M + ( + / + \ + ) + M + B + B + ( + / + \ + ) + M + I + M + B + U + I + N + G + ( + / + \ + ) + ( + / + \ + ) + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_02.svg new file mode 100644 index 000000000000..2fd150fe7bb4 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_02.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + [ + / + ? + \ + ? + ] + ? + ? + ? + ? + ? + ? + ? + ? + M + [ + / + \ + ] + M + B + B + [ + / + \ + ] + M + I + M + B + U + I + N + G + [ + / + \ + ] + [ + / + \ + ] + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_03.svg new file mode 100644 index 000000000000..7d685d8cb92e --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_03.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + / + ? + \ + ? + + ? + ? + ? + ? + ? + ? + ? + ? + M + + / + \ + + M + B + B + + / + \ + + M + I + M + B + U + I + N + G + + / + \ + + + / + \ + + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_04.svg new file mode 100644 index 000000000000..5d70e1c782a9 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_04.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + { + / + ? + \ + ? + } + ? + ? + ? + ? + ? + ? + ? + ? + M + { + / + \ + } + M + B + B + { + / + \ + } + M + I + M + B + U + I + N + G + { + / + \ + } + { + / + \ + } + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_05.svg new file mode 100644 index 000000000000..2a2afeaededf --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_05.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + / + ? + \ + ? + + ? + ? + ? + ? + ? + ? + ? + ? + M + + / + \ + + M + B + B + + / + \ + + M + I + M + B + U + I + N + G + + / + \ + + + / + \ + + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_06.svg new file mode 100644 index 000000000000..47173806e844 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext0_dejavusans_06.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + / + ? + \ + ? + + ? + ? + ? + ? + ? + ? + ? + ? + M + + / + \ + + M + B + B + + / + \ + + M + I + M + B + U + I + N + G + + / + \ + + + / + \ + + M + B + U + I + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_00.png index 4cc6978860e3..216ee4deab24 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_01.png index f303fe49e281..d2859e939cff 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_02.png index b37505cc62b0..660bca74d9bf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_03.png index f28fb31c542f..23410848950a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_04.png index a6861a8f1f08..b5bb3374dce1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_05.png index f7ac08149bcd..f967ad200c8b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_06.png index 85d0adf15112..fa0a4583030a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_07.png index a84cd1d28274..a334fc519aa6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png index 565464062e39..a25f02964a59 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.pdf index 69b52f13b300..0c1116755704 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.png index 03ef7fcdc46a..81ed72b857a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.svg index 6742e2fce3ad..1e9d5f2edba3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_00.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.353424 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,214 +26,224 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.pdf index 7ec963754a71..b64cc37e33d8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.png index 5b2e205ebcda..5eecc2ab25b3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.svg index 0381c1a1f976..4e6556930cac 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_01.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.435615 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,151 +26,156 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + + - - - - - - - +M 4077 2048 +L 307 2048 +L 307 2470 +L 4077 2470 +L 4077 2048 +z +M 4077 768 +L 307 768 +L 307 1190 +L 4077 1190 +L 4077 768 +z +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.png index 6aeebd0e7716..4cc257dcf634 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.svg index 7ebbe3ed270b..f00b12a7d5fa 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_02.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.486537 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,158 +26,168 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.pdf index a48aa96f603a..7fa85a84105a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.png index e826523b4c0d..3bcab329c091 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.svg index 6725ad6a9a21..144b2551f408 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_03.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.028245 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,209 +26,217 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.png index acfd8bd1ef37..84ba64526bcb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.svg index 8862900f307d..c9c15f5b7689 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_04.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.596534 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,130 +26,132 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.pdf index 1c896f8a0080..b16268f87d6f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.png index 599433f3ae51..11a1961f3c30 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.svg index d8d1df253171..be67bcd7c18f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_05.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.647947 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,321 +26,332 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + + - - - + - - - - - - - - - - - - - - - - - - - - +M 481 2094 +Q 428 2094 393 2131 +Q 359 2169 359 2222 +Q 359 2269 393 2309 +Q 428 2350 481 2350 +L 4500 2350 +Q 4547 2350 4581 2309 +Q 4616 2269 4616 2222 +Q 4616 2169 4581 2131 +Q 4547 2094 4500 2094 +L 481 2094 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.pdf index a0f21451b012..4b7d845e1a81 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.png index e3d5b57f7adf..5e4016405c6d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.svg index 595db0c6658a..61ccaeda55c2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_06.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.735829 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,376 +26,390 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.pdf index 0d7cdaaa380c..dfc8ea88725a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.png index 9c642c8ac778..dcc5cbe22e5c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.svg index aa615224351e..eaeb7a230778 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_07.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.805663 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,199 +26,205 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.pdf index a77d282b05d1..d8c0c298447b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.png index e4fc2bfaa1c8..7da15c0d84aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.svg index ea7b427cbf53..4b0c61907c41 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_08.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.871722 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,234 +26,239 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.pdf index 6a2b42c8e703..dfcb45bd1efb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.png index 8c1c9d9f8f91..d8964d0d59b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.svg index d81ae804c81b..04560dc04e79 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_09.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:16.946339 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,136 +26,138 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.pdf index 5c45ec65b84e..fd60aafc00d7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.png index a3f8f96b1eeb..a18a5f2fe2be 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.svg index 03adb112c33c..1ef0f55095ff 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_10.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.055192 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,318 +26,329 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.pdf index f54f869710fa..2ccf2946a1c5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.png index b7b2828cc4b6..033e3840a956 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.svg index 534378a4fe6e..b10ae750f9bb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_11.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:28:08.328699 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,322 +26,333 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - + - + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.pdf index 7c28a29b2204..058bcb83a3c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.png index 5494fb01543c..26138357f2f1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.svg index 6987539b49f5..256df88fb6ea 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_12.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.201747 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,122 +26,125 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.png index eb0726d030b8..c355b8b8e9d1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.svg index a3e3130c1c64..c951ad7ab0ce 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_13.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.264497 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,231 +26,238 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.pdf index 7fb550585822..65675db6c3bd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.png index 025350125c53..ce0b3cd5d12a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.svg index 7010d80e9860..e702c7c6df60 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_14.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.357440 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,116 +26,118 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.pdf index cc1252890f9c..120b95591954 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.png index 6f60b499475b..4a007c4e2058 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.svg index f5002727566e..ed3cde6e1ac6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_15.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.405484 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,116 +26,118 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.pdf index 0506c68545f6..deac3b99fd6b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.png index c80e64a64251..aa775e11a01f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.svg index 2f4280c892ba..3cd0427dbacc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_16.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.501759 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,171 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.pdf index 0506c68545f6..deac3b99fd6b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.png index c80e64a64251..aa775e11a01f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.svg index d3d1185a6595..097eec3d6ce0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_17.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.562003 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,171 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.pdf index e12f32e865fb..cea31b038ac7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.png index fc50fe7349ec..87838c056199 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.svg index 3f4657da04b5..18a9efa2ecf1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_18.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T17:59:47.072291 + 2026-03-12T18:23:41.595413 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.pdf index 8af0bdc88abd..63efcdd07332 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.png index d6049a34bf34..0be7db2fc8e3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.svg index 52666d5df0ff..adf098e9d273 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_19.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:05.701701 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,404 +26,415 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.pdf index 0c57a05a1d3a..4aeab88d27ae 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.png index c1580b8921f2..4dcc7ee0af9b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.svg index 5b560ffaf297..4f3f90f43bb3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_20.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.711144 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,659 +26,681 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - + - + + - + - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.pdf index 43dcf1b1cf86..a9b712c56b14 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.png index 209685e97172..4ff9d29b1f96 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg index 6967f80a1186..617590d8619d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.779182 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,721 +26,752 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.pdf index 097b3edcdab6..71d96179d5e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png index 0317cb99e1c0..8e61bf598e21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg index 9d57faac5f18..ffaf79889a2b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:17.851781 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,297 +26,317 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.pdf index 5e65307cbd28..6762978138a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.png index f5b4782e24b6..dae3dd676803 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.svg index 54945dfbc550..36885d84172b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_24.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.890075 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,114 +26,120 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.pdf index 2403e1785be7..58dede23681e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.png index 92879c99c47f..7d641b275f48 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.svg index 8dd52fd8e09a..bbe35b2282ea 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_25.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.923588 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,157 +26,165 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.pdf index f2bdd2ff8de6..f14c5cc3eb75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.png index c4a7b27248a3..bd27c007f3bc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.svg index 61398684e63e..d96dda20ac27 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_26.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.003751 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,339 +26,357 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - + - - - - + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.pdf index 3377ab51c4dc..20cbe0282b5f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.png index b0baef84e4a3..37445e4e4d37 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.svg index 605c0a77fc1b..bbdbd5b64a41 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.003683 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,358 +26,370 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.pdf index d3eb036d1bc8..d1695c1bcef9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.png index 0875d535b4ec..f3ec7f407bc7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.svg index 90c33c1c7991..a3e6863cbadc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_28.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.038971 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,350 +26,362 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.pdf index 93aa792533b0..8484f89f8094 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.png index 090af7a1ffd0..7993fa95b151 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.svg index 555a70967bb7..006f98cf5ef2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_29.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.187325 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,373 +26,386 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + - - + + - + - - - - - - - - - - - +M 1131 622 +Q 1244 394 1444 245 +Q 1644 97 1881 97 +Q 2103 97 2273 215 +Q 2444 334 2559 534 +Q 2675 734 2729 954 +Q 2784 1175 2784 1381 +Q 2784 1638 2692 1936 +Q 2600 2234 2411 2439 +Q 2222 2644 1953 2644 +Q 1694 2644 1476 2511 +Q 1259 2378 1131 2150 +L 1131 622 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.pdf index 532b5cefcfad..7ce3cc5ff35e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.png index 37bc5f8118e4..a2550a03f307 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.svg index 455a228221b1..50084fc7643e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_31.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:40.726419 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,274 +26,282 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - + + - - - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.pdf index 2effcca7744b..f31172016a75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.png index 7ae2d5ac38b2..70b1a7365864 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.svg index 2ea4525b7595..074533b31854 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_32.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.077728 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,228 +26,234 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.pdf index 34ac765c15d6..b574a00e1dd8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.png index 4a1b526b0768..e353883189ec 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.svg index 99eb31422a01..93f14cd1a753 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_33.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.120866 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,337 +26,345 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - + - - + + - - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.pdf index 1ca5521dd129..9cf6e11d4196 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.png index 143acf785fcd..55887b85e843 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.svg index 2704f7e1b5b4..bef8ea6ee3c6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_35.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.373845 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,231 +26,237 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + + - + - - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.pdf index aa2da267ca7d..0f2468c44408 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.png index 642cb03ed18b..b1fea7d27673 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.svg index b01cbf47dee0..6fd5344a3c73 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_36.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:28:09.583881 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,146 +26,148 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.pdf index 4de2c2cff9cc..6a79ee9d8168 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.png index 9bc3c8a86083..18fd01004391 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.svg index 3972399557bd..f51d2b097574 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_37.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.217122 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,806 +26,838 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.pdf index 86fe09d709ee..769b843a85fc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.png index cedd132b7b45..07acebd2c3ad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.svg index 4afb573565bd..195521f13d4a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_38.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.293675 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,555 +26,564 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + + - - - - - - - + - + - - - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.pdf index 15d48dd37220..3c6d22a72d27 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.png index 03e354807550..9217fd336025 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.svg index c63ca4808ce4..fadd75a0ac56 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_39.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.622647 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,294 +26,306 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.pdf index f7acbc888a26..bd771a073b09 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.png index 4e1ef905dc24..49ebe9769ce7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.svg index 2b9821215581..b9513694b01c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_40.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.684691 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,399 +26,417 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - + - + - + - + - - - - - - - - - - - - - +M 2009 1063 +L 2009 3309 +Q 1025 3172 1025 2188 +Q 1025 1200 2009 1063 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.pdf index 2b9620b353fa..5035d1c74b57 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.png index 427df910c3db..7b1e4567b006 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.svg index c7c47198dfef..fedd7c07a888 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_41.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.739434 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,921 +26,956 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.pdf index eccd81427515..57dc6cd2d08e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.png index e5043a309114..1b8fbf17aa84 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.svg index 1a2a7071ce39..209189d0660c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_42.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.812011 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,172 +26,175 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.pdf index 2ecdc013f074..8a469d461e7e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.png index 7a2023cff775..46b55de81b9e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.svg index 7d88510ba3e6..0946c89c662b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_43.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.852833 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,170 +26,173 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.pdf index 45e9273a6b15..e5a6f52be2e2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.png index 9030fa328aad..6401139b1d08 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.svg index 259ff6e2eb7d..712f6ed9ec6f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_44.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.539389 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,279 +26,285 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - + - - - - - - - - - - - + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.pdf index 838583415be5..56fe6dde7243 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.png index 11a1fdef06d2..0d5bec71ec44 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.svg index 3fd1ab4133a3..a3fcf8cfaed0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_45.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:18.949894 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,279 +26,285 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - + - - - - - - - - - - - + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.pdf index c60e04f00b9d..47896c0a02ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.png index f62abc8591a7..643d07ea5378 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.svg index 544abd4d993b..cebc489345b7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.002815 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,154 +26,160 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.pdf index 3a5ac12ebf60..333ceb589714 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.png index 9a742fcf7bdf..fd66f0126bd7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.svg index b56d1541d6e2..ec2022476426 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_47.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.662951 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,290 +26,300 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.pdf index 60136d38d843..3a234ec23916 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.png index 9a742fcf7bdf..e3b57e5361c5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.svg index b56d1541d6e2..d39df295627c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_48.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.726187 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,290 +26,300 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.pdf index 05756701b4e3..4bf5be17ed0a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.png index b1f264a8432a..5540be29bdf1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.svg index 1fde587ceb85..a4a5efee33f5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.182147 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,221 +26,227 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - + + - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.pdf index f5cd93efcfc4..b4006324f403 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.png index 7dede8e5a8fd..6664efcd164a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.svg index 65dd40269b2d..1a6571f9b12a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_50.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.853424 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,324 +26,327 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.pdf index ef37e7dea821..7e91620a16bb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.png index 7f22b891070e..5e71ee019617 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.svg index 43ed5e505ec7..2b018a08f339 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_51.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.276894 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,171 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.pdf index 3187700c2e2e..602a88fde2ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.png index 99895da0be9e..d01d7f216370 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.svg index b5644d244d5d..d0d54b8cd8a3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_52.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:36.929181 + 2026-03-12T18:23:41.972980 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.pdf index 29dcaa13ee7a..0a8e79106687 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.png index f454d9fd69ee..a89a3d912818 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.svg index c61faa883261..f17efcecbbd0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_53.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.362571 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,246 +26,253 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.pdf index c40bfb701955..779a9b22e009 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.png index 8ee4328529ec..0a34af1bc398 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.svg index 002c7491e91a..8fd8228e84e6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_54.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.076563 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,511 +26,530 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - + + - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.pdf index 4892032f9093..dd6d97b83c02 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.png index f8c951bd67c9..f312adad502d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.svg index 6e97b35e6f84..983da2c05335 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_55.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.495075 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,118 +26,120 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.pdf index f65b1f54f4ef..6105b93c4d21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.png index 41caf551d86c..96ae035f9bee 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.svg index 32d68fbc76e2..a84f04384340 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_56.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.269890 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,255 +26,262 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.pdf index c7af617408c3..42c755201075 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.png index 473f0da5d551..778fc59ddf00 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.svg index cf615910c8d8..854d1773c639 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_57.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.318437 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,235 +26,242 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - - - - - - - - +M 1509 275 +Q 1509 225 1728 225 +L 2472 225 +Q 2828 225 3159 372 +Q 3491 519 3750 775 +Q 3991 1016 4175 1406 +Q 4359 1797 4461 2222 +Q 4563 2647 4563 3003 +Q 4563 3263 4486 3472 +Q 4409 3681 4270 3831 +Q 4131 3981 3922 4064 +Q 3713 4147 3450 4147 +L 2747 4147 +Q 2572 4147 2515 4117 +Q 2459 4088 2413 3928 +L 1538 416 +Q 1509 338 1509 275 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.pdf index 29586956c15a..4595ad7139fb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.png index 9dafb9b347bc..2f66ad0f670c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.svg index 9d8ac3aca8a7..41db2fbd9df2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_58.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.613460 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,171 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.pdf index 4b6d53fa7207..8f1a74a2108c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.png index a42642683bcc..b190a7bdf78a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.svg index 3f68e6f4ef92..d2d9aedb32fc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_59.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.662258 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,171 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.pdf index 8fb302b4c7b2..10633af2df38 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.png index f799992f2885..2dbe39dd6c10 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.svg index d505c7476f0d..8788f49409e8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.445876 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,381 +26,392 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - + - - - + - + - - - - - - - - - - - - +M 1563 1556 +Q 1825 1556 1992 1732 +Q 2159 1909 2234 2171 +Q 2309 2434 2309 2694 +L 2309 2816 +L 2309 2841 +Q 2309 3322 2168 3700 +Q 2028 4078 1631 4078 +Q 1378 4078 1220 3967 +Q 1063 3856 988 3672 +Q 913 3488 892 3278 +Q 872 3069 872 2822 +Q 872 2459 906 2203 +Q 941 1947 1094 1751 +Q 1247 1556 1563 1556 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.pdf index dc4059062f16..790b7217a4e7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.png index 139e9670f16a..b13ffbe696eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.svg index 0b924914f5bf..fa1b42304649 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_61.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.495770 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,347 +26,357 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.pdf index e0a27cbb082a..1ec1626a0ba9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.png index 94cb9cec5ec1..f538072bb196 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.svg index 01868ac6e84b..9daf874855d7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_62.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.535922 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,142 +26,145 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.pdf index 4ac8a338c17b..0afefbaae9e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.png index 113b8c049056..49fbb169d1ef 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.svg index 3b6f49d1115c..148f2e2bb74c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_63.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.865119 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,154 +26,161 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.pdf index 0412c6fe57d7..5b11d6c34d21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.png index c7355b27ad22..922490e26005 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.svg index a42fa59d5524..2717360cc348 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_64.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.916994 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,206 +26,215 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.png index 2bd94196b30a..8f72c1e99b90 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.svg index b6bc6f0cf701..24a8b7d175dd 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_65.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:19.970958 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,149 +26,156 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.pdf index c463850d8d0f..0b9a6106e5c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.png index 2cf6829952b2..a2a946fa6edc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.svg index ea21bf1f35e2..f16dcdccad7c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_68.svg @@ -1,216 +1,216 @@ - - - - - - - - 2022-10-24T12:21:21.236679 - image/svg+xml - - - Matplotlib v3.6.0.dev4028+gdd27a7f3c3.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + 2026-03-12T19:45:20.037345 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.pdf index f3b3655654af..ff0416b3be04 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.png index 8bc7adc29c28..e7c45a963c5b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.svg index 21c6e291cdbd..c13cedc3b03e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:33.473355 + 2026-03-12T19:45:20.140719 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + +" transform="scale(0.015625)"/> - - - + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.pdf index 2b83c3c73f5c..1e4a964cc15d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.png index 24e94be731ae..732cf0628cc4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.svg index 17fd88e436bd..c41d6e6bdc4d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_70.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.241705 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,82 +26,83 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.pdf index e48ff44aec43..d76aa0bebe6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.png index 68061447e56f..b87943be2d69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.svg index 2b6c12ec67df..d8d57d568c7f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_71.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.304341 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,136 +26,144 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - + - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.pdf index 5284c8c9f2d6..6ecea8daa0d7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.png index a40ad0c281f6..2c8eefa4111f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.svg index 7f218efb93a6..a8c9d56a1f17 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_72.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.352411 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.pdf index a68c42e9e815..2da8e346731e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.png index ab843c623b6a..121e0935b41e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.svg index a0aea5dee955..510fe456ebe1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_73.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.784846 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,711 +26,732 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - - - + - + - - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 1594 97 +Q 1816 97 2006 280 +Q 2197 463 2316 691 +Q 2403 863 2479 1111 +Q 2556 1359 2612 1640 +Q 2669 1922 2669 2088 +Q 2669 2234 2630 2362 +Q 2591 2491 2502 2577 +Q 2413 2663 2266 2663 +Q 2031 2663 1823 2495 +Q 1616 2328 1447 2081 +L 1447 2059 +L 1094 647 +Q 1138 419 1263 258 +Q 1388 97 1594 97 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.pdf index 86ab2cb347ff..2da8e346731e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.png index ab843c623b6a..121e0935b41e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.svg index 90f2ebf393dc..1272468bba43 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_74.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.835481 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,711 +26,732 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - - - + - + - - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 1594 97 +Q 1816 97 2006 280 +Q 2197 463 2316 691 +Q 2403 863 2479 1111 +Q 2556 1359 2612 1640 +Q 2669 1922 2669 2088 +Q 2669 2234 2630 2362 +Q 2591 2491 2502 2577 +Q 2413 2663 2266 2663 +Q 2031 2663 1823 2495 +Q 1616 2328 1447 2081 +L 1447 2059 +L 1094 647 +Q 1138 419 1263 258 +Q 1388 97 1594 97 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.pdf index 766c4d194b6d..da91d29c79b7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.png index 4bb8db402403..cf3c116e4735 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.svg index 59cc5aaa18f3..53aa84c297d9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_75.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.896023 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,301 +26,307 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.pdf index b8894b7160fd..4978f666a6f5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.png index d6c9b050cb1f..4c609ebe0349 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.svg index 756e0dcf9cf8..a436d7bfbc5f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_76.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.629496 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,269 +26,281 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - + - - - + + - - - - - - - - - - - - +M 3481 434 +Q 3481 -459 3084 -895 +Q 2688 -1331 1869 -1331 +Q 1566 -1331 1297 -1286 +Q 1028 -1241 775 -1147 +L 775 -588 +Q 1028 -725 1275 -790 +Q 1522 -856 1778 -856 +Q 2344 -856 2625 -561 +Q 2906 -266 2906 331 +L 2906 616 +Q 2728 306 2450 153 +Q 2172 0 1784 0 +Q 1141 0 747 490 +Q 353 981 353 1791 +Q 353 2603 747 3093 +Q 1141 3584 1784 3584 +Q 2172 3584 2450 3431 +Q 2728 3278 2906 2969 +L 2906 3500 +L 3481 3500 +L 3481 434 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.pdf index b1e38648be64..f0c1af4594ce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.png index 2b486f43a174..2cf5e03989ef 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.svg index 4aba11d8621d..bba0c8507437 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_78.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.984188 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,270 +26,280 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.pdf index b069deceda9b..7c9621a03438 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.png index 909bbfbd16bb..d7a00ef0e637 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.svg index 004cb96121cb..de21b07e73e3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_79.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.040441 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,219 +26,225 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - - - - - - - - - + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.pdf index 8e44f24eba2f..ca288f6c4028 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.png index 09147448dcb8..0db9f87782b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.svg index 61ee84791179..69b06d787187 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_80.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.761847 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,543 +26,566 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + + - + - + + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.pdf index 57557e26aa53..c9fb0c3689fd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.png index 157416743e58..b30b5849063d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.svg index a8401a2ab96f..c0362abf99db 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_81.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.141829 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,207 +26,214 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.pdf index 7fcd5d932ab1..65309c1c4faa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.png index 55c704c48547..754033673feb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.svg index 19f8a85b606a..071d16b88333 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_82.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.185762 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,214 +26,217 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.pdf index 31ec241a04fc..ac869cb955c1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.png index a4ce71fb244b..10720d67b3b2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.svg index 01650aa1cfc5..75d1f381ef68 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_83.svg @@ -6,11 +6,11 @@ - 2024-05-08T19:52:27.776189 + 2026-03-12T19:45:20.937889 image/svg+xml - Matplotlib v3.10.0.dev150+gec4808956b.d20240508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - - - - - - - - - + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.pdf index 6f485ad4e295..cddbb9db03ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.png index f9c31185e2c5..e1b00e3cdd5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.svg index 7917410174d9..0b16c4d7a14f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_00.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:30.907211 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,157 +26,168 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - - + - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.pdf index 93bd71cb8748..fee67fd1dbf1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.png index ccb9d85ee639..17c7966416e1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.svg index 7279119df8df..63936a92bed8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_01.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:30.977578 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,62 +26,67 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.pdf index 0a0e13b52a8f..d83fe88bc1af 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.png index 441baa0c49c1..63246ec932ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.svg index 6256e8eb0e8e..553b54ce29c4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_02.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.044123 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,138 +26,147 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + + - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.pdf index 086b5ef9ce08..027564cc57ae 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.png index bf5e7d0293d1..7ea40339639f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.svg index e345d442bb39..fb058a7c8fe4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_03.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.288939 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,128 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.pdf index 9bd092eb47cd..178bf86c5caa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.png index 523f0dd2885f..debe04d51aaf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.svg index 86b98a452211..a1256eb81671 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_04.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.168141 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,45 +26,47 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - +" transform="scale(0.015625)"/> + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.pdf index 4f7bb35aaa62..429fe0f1b8c8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.png index 389497540089..1800ac42f351 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.svg index 1e21c64e72dc..b96dbd21be30 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_05.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.245645 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,170 +26,181 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + + - + - + - - + - + - - - - - - - - - - - - - - - - - - - - +M 4084 744 +Q 3897 503 3655 389 +Q 3413 275 3091 275 +Q 2553 275 2217 664 +Q 1881 1053 1881 1678 +Q 1881 2303 2218 2693 +Q 2556 3084 3091 3084 +Q 3413 3084 3656 2967 +Q 3900 2850 4084 2613 +L 4084 3022 +L 4531 3022 +L 4531 722 +Q 4988 791 5245 1139 +Q 5503 1488 5503 2041 +Q 5503 2375 5404 2669 +Q 5306 2963 5106 3213 +Q 4781 3622 4314 3839 +Q 3847 4056 3297 4056 +Q 2913 4056 2559 3954 +Q 2206 3853 1906 3653 +Q 1416 3334 1139 2817 +Q 863 2300 863 1697 +Q 863 1200 1042 765 +Q 1222 331 1563 0 +Q 1891 -325 2322 -495 +Q 2753 -666 3244 -666 +Q 3647 -666 4036 -530 +Q 4425 -394 4750 -141 +L 5031 -488 +Q 4641 -791 4180 -952 +Q 3719 -1113 3244 -1113 +Q 2666 -1113 2153 -908 +Q 1641 -703 1241 -313 +Q 841 78 631 592 +Q 422 1106 422 1697 +Q 422 2266 634 2781 +Q 847 3297 1241 3688 +Q 1644 4084 2172 4295 +Q 2700 4506 3291 4506 +Q 3953 4506 4520 4234 +Q 5088 3963 5472 3463 +Q 5706 3156 5829 2797 +Q 5953 2438 5953 2053 +Q 5953 1231 5456 756 +Q 4959 281 4084 263 +L 4084 744 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.png index eef5a35dd2a6..aa80d91a5b5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.svg index 2e86884cbfae..a74bda85146d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_06.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.367336 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,197 +26,212 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.pdf index 8c406aa36b5c..ac96d75d1b7b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.png index 81a284799f41..91be4b648b2d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.svg index 71a3e5e8454e..342e8da16c1f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_07.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.436164 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,84 +26,90 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.pdf index 6f39d030fd1d..9b24ff5dcb24 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.png index 2c4e9f77712d..7c9f8f5d033b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.svg index 83042ffb18bb..95b62572d338 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_08.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.498339 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,109 +26,113 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.pdf index 775fea9d1e8e..f829d2ee0127 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.png index 87db29e0d656..5196d8843cab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.svg index ba07d2851c47..23a318762306 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_09.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.558507 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,51 +26,53 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.pdf index 3fdcbd722352..c0227124c950 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.png index f3d4e9d4cf7c..e31134064503 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.svg index 25bbe8c0c5e1..c692c5f62aee 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_10.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.636698 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,227 +26,238 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.pdf index e1699fe51023..5768c336eff6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.png index 50c2340c2bcc..3e36f7085424 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.svg index cb103ab01b1e..f22e10a6ac32 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_11.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.620419 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,186 +26,197 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - - - + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.png index 9f1f884bd558..d3a96c5e13b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.svg index 6f513d5ead3d..e89daeab1efe 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_12.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.723262 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,54 +26,57 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.png index 391099baa950..a0e0cce8abc6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.svg index 3bcd182ac679..f7976298d802 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_13.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.796593 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,116 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.pdf index e3d880b41225..4241ee766e29 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.png index d4a5673b8457..9c641823feda 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.svg index 85aa1db9f499..aabe2587b58b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_14.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.863561 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,52 +26,54 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.pdf index 1705aad603b6..f994789c0b3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.png index bbbacb4f1d36..d1ce614c531c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.svg index a9af26b281d3..b94f49ae10ba 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_15.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:31.932310 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,52 +26,54 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.pdf index ecd79d54c3d7..0a962e4adfd4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.png index dc5a987644a2..2ca13f3592d2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.svg index 952c0be7cf8b..a3c306480305 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_16.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:43.428224 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,72 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.pdf index ecd79d54c3d7..0a962e4adfd4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.png index dc5a987644a2..2ca13f3592d2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.svg index 8d54355ee65a..7cb4809e9edb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_17.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:43.471133 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,72 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.pdf index 0d4bc9fa69a3..ae8a2a492d3c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.png index ebdbbbed07ce..d939b9991402 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.svg index ab482549db6c..b5549fccca3f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_18.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T17:59:55.050120 + 2026-02-06T03:27:43.505206 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.pdf index dd2f41a99e03..0b70a837c67b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.png index 639a3ac77b9d..b8fdb0140624 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.svg index e84e22a05cca..155c0a15f247 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_19.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.954312 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,211 +26,222 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.pdf index a4a074f7a643..7882dad19384 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.png index fa04e1684cd3..353e0f5f9f00 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.svg index 69fc4afc4dc9..8ddcea5daaa3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_20.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.181253 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,331 +26,353 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - + + - - + - + - + - + - - + + - - - + - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.pdf index 0835408b29dd..54922bdbf740 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.png index 7e9be7beea09..7e3f865003d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg index 90f9b2cec969..c214972f4195 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:43.680416 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,437 +26,467 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.pdf index df50080b57d0..a4b8ff8f8321 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png index d6802f84bfda..ea3eb7e6e30d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg index 77ded780c3f1..3c2deb90b227 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.299025 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,268 +26,288 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.pdf index 4605a8794fd7..43eaaf3c1c23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.png index 1917124f9952..930e8d94676d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.svg index fde9b084377c..a60a0cea8748 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_24.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:43.779786 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,56 +26,62 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.pdf index b1dc103a05ed..596f0d74a535 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.png index 82dedc3970ce..0b7502f21524 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.svg index b50d99dca35b..8c30d5b915d1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_25.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:43.814080 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,85 +26,93 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.pdf index fc2a8f9f7b7c..86427887d955 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.png index 394104fa40f3..6c5ab90ca378 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.svg index 5783739f94f4..d4c54cc7aa6f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_26.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.453200 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,250 +26,272 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + + - - + - - + - - + - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.png index 9a08e24fb713..cb46a36fbe0e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg index 7a7b7ec42c25..e0bd8659f019 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.554027 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,190 +26,202 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - + - - - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.pdf index f578944a321d..fb14c0531bc9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.png index f4c1d31b533a..505be62225a9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.svg index 1908e303136e..693950295fec 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_28.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.297097 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,180 +26,192 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - + - - - - - - - - - - - - + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.pdf index 2d33dd48490b..4272b7e4b582 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.png index 065feffb01fa..a70ca2bf4583 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.svg index 671945a5274a..28fe52882681 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_29.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:43.012123 + 2025-11-06T05:24:32.461123 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev1524+g17428e3e2f.d20251106, https://matplotlib.org/ - + @@ -26,20 +26,20 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.png index 11a901df8f39..e956a2898404 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.svg index 9f2e2e7f77d1..3111d678d058 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_31.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.705054 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,136 +26,144 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - + - + - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.pdf index 1ae2e93b151b..2bf2d681d879 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.png index f4450648ac63..78bce2da2736 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.svg index a0bf70e5a42b..27bba7318e98 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_32.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.427787 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,113 +26,119 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.pdf index b094d3c8288c..bf4c2cd199e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.png index e88c1cb4a606..4e43d4058105 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.svg index 453d07a4c1b2..ff8c2a37cb86 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_33.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.474421 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,161 +26,169 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - + - + - - + - - - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.pdf index 20fe0b366018..4022b3b678eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.png index ec55f8770950..8c4b0682f4cf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.svg index 92c2cc9f945a..8477d83478eb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_35.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:32.880298 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,116 +26,122 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + + - - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.pdf index 73256af10a47..ab92fdf557e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.png index d40d36b49cea..7079b5547cb6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.svg index a043b22b5c59..86a32825b3de 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_36.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:44.202630 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,53 +26,55 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.pdf index 59a84de62094..975ee58d62b7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.png index c358b6fcdf16..436d973b22ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.svg index cc092a3919a2..508f4b12731a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_37.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.196210 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,495 +26,525 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.pdf index 2533a3903e10..fb9a31d34749 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.png index 7a85988a7552..ffd2c46771a5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.svg index 534c95b7f76f..ef021e3cfc47 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_38.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:44.417859 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,292 +26,307 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - + - - + - + - - + - + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.pdf index 976b99cb88d1..5c70006488a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.png index c90a4a045ed9..f8062586d021 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.svg index cc4f7e858da5..5ea3c107f591 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_39.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.112144 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,217 +26,229 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - + - - - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.pdf index 00a6500db2e5..b3611f378566 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.png index c7234f2534cd..6006f45ddb9d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.svg index b96a9cfabfd6..497af9c69396 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_40.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.175177 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,202 +26,220 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.pdf index b5673b38f872..bd49ac5bbe98 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.png index 272c65bc9b52..77e5b2024d0c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.svg index 102ab3cbfb34..3f58c207708b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_41.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.234601 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,501 +26,531 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.png index 834be8ef63fd..adf1fcd2fda2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.svg index b831ee81725e..50bcce6e3192 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_42.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.315970 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,70 +26,73 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.png index 1532a4df4fb4..0df0cf0814c9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.svg index 36b72fd67053..da7bf90b4a9d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_43.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.401621 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,82 +26,85 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.pdf index 58f560a38528..7df1eddeec51 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.png index e2f60370eec1..47b8a5b1dd12 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.svg index 33be61feb558..776a7620abb3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_44.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:44.678330 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,118 +26,124 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.pdf index 4ad2b279e69a..8a4ebbcb65c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.png index bce277b9a6c6..26c0da632ace 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.svg index fc1cb71b7a0c..d063832cb85b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_45.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.494464 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,118 +26,124 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.pdf index 36a0ff52c745..1c2aaae4ea5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.png index 75a699ce5819..ff2aff82e516 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg index 0846b552246a..d57cc59e2fea 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.558532 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,116 +26,122 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.pdf index 8636fceaca2e..47aa2b36fb4f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.png index bf70d3e6b621..3feb6c7acf86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.svg index 09a39eb41397..a1d335aa315f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_47.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.671724 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,209 +26,219 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.pdf index b28deefd1bef..696da091a36b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.png index bf70d3e6b621..1cfb41f2ece5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.svg index 09a39eb41397..1f178ac96d90 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_48.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.744990 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,209 +26,219 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.pdf index 7246b36e03ce..1b6721707126 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.png index 689b6113f446..da791933e94d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg index 24db824fd37c..8ab82619f2ad 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:44.940192 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,103 +26,109 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.pdf index 430c41f50d2e..b7bb643ac250 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.png index 32ec8a5e3adb..73f4d6e17096 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.svg index 2356007d7be9..a5d446c4af08 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_50.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.861980 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,167 +26,176 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.pdf index 885a10d9e316..02868b361561 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.png index a97043e9c1b9..6a9b680c86cf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.svg index 4ce0f2c632e5..cf81de016e3a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_51.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:33.942782 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,72 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.pdf index 654a40855e1b..b89de97adf5c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.png index 0f639fc84c3c..cbb4ae410be2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.svg index aa4045b3263a..1d36984d47f5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_52.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:43.790396 + 2026-02-06T03:27:45.094929 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.pdf index 26aa8358c743..ff37316820e3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.png index c032ff5fd771..a3b4870454a6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.svg index bdc49456e43c..b5549aaeac71 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_53.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.102831 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,168 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.pdf index 994bca3b1574..8ca1b3e84816 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.png index a0aa9554c8ed..08532af985a4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.svg index b150251461d3..73ae09135a64 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_54.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.074987 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,265 +26,284 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - + - - + - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.pdf index cd1ef97bf1fb..d9289748d57a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.png index 49cca7cdad6f..4522663c38ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.svg index ebb716775f8e..03aec7bef335 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_55.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.381199 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,54 +26,56 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.pdf index 2d9b133174a1..8925b299ae2c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.png index d74d688ff605..90dcaaa0540d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.svg index 9fe8b9f61490..c1ba69775adb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_56.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.199398 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,137 +26,144 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.pdf index 93bc08b756fc..5ac544137c15 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.png index 0bd8fc8552c2..454ef847991a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.svg index fe18b174bebe..4b8edf732bb8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_57.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:45.380556 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,111 +26,118 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.pdf index 26849c8415b6..fd63922f3f29 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.png index dc57f69b1a9b..944145cff410 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.svg index f9a60f2e9a9f..5a34f31986fa 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_58.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.574136 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,72 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.pdf index e20b385c5c79..eb35334267a1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.png index 0d45b23f746d..db27e6f868f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.svg index c44fb4c3bac2..a6cb2791b213 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_59.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.643944 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,72 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.pdf index a00bd4357c3a..06804ff29439 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.png index 52f7f5813ad5..c3ca67928045 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg index 189491319c10..a02ccef313f4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:45.503956 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,209 +26,220 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - + - - - + - - + - - - - - - - - - - - - - +M 1959 2075 +Q 2384 2075 2632 2365 +Q 2881 2656 2881 3163 +Q 2881 3666 2632 3958 +Q 2384 4250 1959 4250 +Q 1534 4250 1286 3958 +Q 1038 3666 1038 3163 +Q 1038 2656 1286 2365 +Q 1534 2075 1959 2075 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.pdf index a5f6024cf5af..3f07979d5f3d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.png index 68296231e38e..c559a9c73ff9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.svg index ae4164d53a41..858fe07d74c9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_61.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:45.551214 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,181 +26,191 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - + - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.pdf index e0568f726b3f..3698c773e66e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.png index ea6c50664fc9..acd0123a4aa3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.svg index 7df98284b67d..1ec0117196bc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_62.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:45.605807 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,78 +26,81 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.pdf index 551efc43c236..5cdc809517f9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.png index 0e9e31ecbc5c..5d3bd5df66bb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.svg index 94da9834bf1f..f2d257fff69e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_63.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.843935 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,88 +26,93 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.png index 0060975a67b7..1e0c9fbb4b6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.svg index 37d50ef23c96..03c4cb56c2c3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_64.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.927798 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,142 +26,151 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - + - - - - - - - - - +M 678 1631 +L 4684 1631 +L 4684 1100 +L 678 1100 +L 678 1631 +z +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.pdf index 3f351f47dc2e..120338b4d0b9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.png index e06e43237601..daaeb48b4bc7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.svg index 9da42f7afefd..3fb441b44b89 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_65.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:34.984126 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,98 +26,104 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +M 1997 2859 +L 3584 1234 +Q 3769 1513 3872 1830 +Q 3975 2147 3994 2503 +L 4575 2503 +Q 4538 2091 4375 1687 +Q 4213 1284 3922 891 +L 4794 0 +L 4006 0 +L 3559 459 +Q 3234 181 2878 45 +Q 2522 -91 2113 -91 +Q 1359 -91 881 339 +Q 403 769 403 1441 +Q 403 1841 612 2192 +Q 822 2544 1241 2853 +Q 1091 3050 1012 3245 +Q 934 3441 934 3628 +Q 934 4134 1281 4442 +Q 1628 4750 2203 4750 +Q 2463 4750 2720 4694 +Q 2978 4638 3244 4525 +L 3244 3956 +Q 2972 4103 2725 4179 +Q 2478 4256 2266 4256 +Q 1938 4256 1733 4082 +Q 1528 3909 1528 3634 +Q 1528 3475 1620 3314 +Q 1713 3153 1997 2859 +z +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.pdf index a33f6b7d8385..49511753595a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.png index 5cc8c6ef7b31..703233f71967 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.svg index 94bd4b633188..eb999887a311 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_68.svg @@ -1,184 +1,184 @@ - - - - - - - - 2022-10-24T13:47:25.928529 - image/svg+xml - - - Matplotlib v3.6.0.dev4028+g98b55b4eed.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + 2026-03-01T22:41:09.555342 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.pdf index 52d71b790849..e78cde0fff18 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.png index 0d96251d677e..2eda2926bd3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.svg index 5ac977a7a4e3..c2323ebc1e28 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_69.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:35.127335 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,84 +26,88 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.png index 6c0c5814f1c4..6e1851eb44b9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.svg index bacb38b65994..5e082b25b331 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_70.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:35.217120 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,40 +26,41 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.pdf index 4e348e9ba03e..6c942b6ed538 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.png index 53c8295911ae..98ef98370d21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.svg index 33dc31b427a0..ac4ff753e3ad 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_71.svg @@ -6,11 +6,11 @@ - 2021-08-10T16:39:40.187285 + 2026-03-12T19:45:35.284542 image/svg+xml - Matplotlib v3.4.2.post1645+ge771650c3a.d20210810, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - + - - - - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.pdf index d80d8a2bb2f6..457c86f68a2e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.png index 24e9d7955a6b..e0a04c596d67 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.svg index 5825b79e364a..7d1f7d02ce7a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_72.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:35.353838 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,93 +26,105 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + + + - + - - - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.pdf index 452cf8841884..c0050faff8d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.png index 39c9a25d25c2..0999d4a1c407 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.svg index 50e79f2aff0d..dd5aca067b8e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_73.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:45.941791 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,324 +26,345 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - + - + - - + - + + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 909 3500 +L 1484 3500 +L 800 0 +L 225 0 +L 909 3500 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.pdf index e5b9d9f0ca00..c0050faff8d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.png index 39c9a25d25c2..0999d4a1c407 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.svg index b7c59907144f..bdd16a8928cf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_74.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:46.015591 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,324 +26,345 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - + - + - - + - + + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 909 3500 +L 1484 3500 +L 800 0 +L 225 0 +L 909 3500 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.pdf index ddb7ef07712d..19858f2ca3b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.png index d370e8ba8cba..e18f4b8c8b9d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.svg index 7d466c1d29f6..331fad2ceaea 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_75.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:46.092688 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,129 +26,135 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.png index 8a21707737c8..18bc4e341133 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.svg index c3c2286094d6..b94caa0b1c03 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_76.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:35.653869 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,224 +26,236 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - - - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.pdf index dc0231fa868c..675d81771185 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.png index 5d433a750c41..e8df5381165a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.svg index 62c5aed4788e..b0996eb5d481 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_78.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:46.208916 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,175 +26,185 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.pdf index 837edec6e171..dd4e5687159a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.png index 41aa5ac3ee7f..e05f7d8681e4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.svg index f1e434b971d6..d121cb8392e0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_79.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.758679 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,117 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - - - - - - - + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.png index e172fd1b879c..0723f0a4973d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.svg index 416380a2707f..ada051d59e1e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_80.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:35.860893 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,364 +26,387 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + + - + - - - + + - + - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.pdf index bfecc8a5d8ec..2e4af8dceb3b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.png index 464ddbbd45fd..10ce3a709b86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.svg index e1f7760d1467..2f0a68d1c45d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_81.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:46.350914 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,128 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - + - - - - - - - +M 1381 2969 +Q 1594 3256 1914 3420 +Q 2234 3584 2584 3584 +Q 3122 3584 3439 3221 +Q 3756 2859 3756 2241 +Q 3756 1734 3570 1259 +Q 3384 784 3041 416 +Q 2816 172 2522 40 +Q 2228 -91 1906 -91 +Q 1566 -91 1316 65 +Q 1066 222 909 531 +L 806 0 +L 231 0 +L 1178 4863 +L 1753 4863 +L 1381 2969 +z +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.pdf index 4b1874debd8a..f0a8ffafae8a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.png index 80bde0ec5b5c..4da2d0c923d2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.svg index 2c4c3a5baf67..2e8e43046cd6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_82.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:08.903086 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,124 +26,128 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.pdf index e09af853ea1f..bd803cc4faef 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.png index 8a03c6e92bc6..a5131a0b972a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.svg index b0a6fe95cfa3..c32dfdc8a7e6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_83.svg @@ -6,11 +6,11 @@ - 2024-05-08T19:52:35.349617 + 2026-03-12T19:45:36.098751 image/svg+xml - Matplotlib v3.10.0.dev150+gec4808956b.d20240508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - - - - - - - - - + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.pdf index 7d932d8b00b4..1e1daa4d69e2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.png index 3e32c790b6a0..46bff17e9a01 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.svg index 44d20f51cd05..b5e1d1e0aa12 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_00.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.166686 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,162 +26,172 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.pdf index c6f0a4149d1a..48124bd6baf3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.png index 4419bfecabe6..15dcf4b18dd4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.svg index 732b26a74a2a..4f089111ec4c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_01.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.242039 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,77 +26,82 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.pdf index 6d22a5820fca..c2ef41fd1553 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.png index 71a1df68044b..1554613d0f53 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.svg index d90cc1ef0e25..2a395fc1a926 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_02.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.288689 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,147 +26,157 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.pdf index 9b2dbd7c8dc9..a88a4913915b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.png index f412765485de..0e5ece84f1ad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.svg index be73fc0e656d..a6af5f86c843 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_03.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.097637 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,133 +26,141 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.png index 3bf6b6d627bc..3b24e9ae183f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.svg index 59324d5b5335..a4f1c1aa63f8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_04.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.401195 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,56 +26,58 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - +" transform="scale(0.015625)"/> + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.pdf index 8177f6f6215a..30579f332db8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.png index f894c07b14b5..22a7262cae6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.svg index a7d928c83fc4..6e178ada9260 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_05.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.463090 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,191 +26,202 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - + - - - + - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.pdf index 0c1656410821..8fc84c16dae0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.png index 1deecb5bfe06..a72575f10796 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.svg index b362b6d7cf03..66e9acf7c023 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_06.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.533998 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,210 +26,225 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.pdf index 7a34825c03c6..3579ed64b1b1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.png index baefad9dfcc0..82a65374b39a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.svg index bcf053235632..15b8226375af 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_07.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.602737 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,95 +26,101 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.pdf index 2ad46e00c577..06b473a6de29 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.png index f060f7dcfb57..7a708c9848bf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.svg index 39057ee12e5d..20d2ffc1b05f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_08.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.679046 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,159 +26,164 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.png index 27f6371d1fcc..8400423d45ef 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.svg index a2d03e0c3785..532979de50cb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_09.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.762667 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,62 +26,64 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.png index 9b7c8004e975..56d567c82c10 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.svg index 221396d5fb25..9e6611d68129 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_10.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.828195 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,245 +26,256 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - - - - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.pdf index 9d7a3f1ec797..372c131fb37e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.png index 89fbbaf90126..eb877e64a375 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.svg index 15b63cb56dc4..deff728a5515 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_11.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.416361 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,201 +26,212 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - + + - - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.png index 9bd433f3b179..fcac2f5fd6ff 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.svg index 3f13f31357f7..aac33a6443ce 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_12.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:36.966905 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,58 +26,61 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.png index 6178b4eccf7d..ca2e875d79dd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.svg index f5955c84dd9b..3270fc15f0ec 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_13.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.070554 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,142 +26,149 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.pdf index f94d345b53ea..473d096c04da 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.png index 8f8ffcc97840..f606c5bc0e32 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.svg index a786bf55a440..7e505c6c0b7a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_14.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.129450 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,57 +26,59 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.pdf index 8836ef805459..44c76b46814e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.png index da239680da6f..6d324ec511fa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.svg index 41f940b81c8e..6a58b19a88a4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_15.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.192257 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,57 +26,59 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.pdf index 3880a069a8d9..7eeeef9a4100 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.png index 7a3e7fee6874..d470b47f7b75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.svg index f0cfad12e26e..78ec6de323dc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_16.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.059198 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,84 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.pdf index f6353136ca52..7eeeef9a4100 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.png index 7a3e7fee6874..d470b47f7b75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.svg index 830da4fef055..1e9fc844d449 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_17.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.095616 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,84 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.pdf index 5cee7d7fd3ea..2f44bf167851 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.png index 0d5711683aeb..e3e0d8108c52 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.svg index 6693e3eec2f9..ba5433325497 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_18.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T17:59:57.353384 + 2026-03-12T18:23:44.761692 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.pdf index a96393b93c5f..5f9cb1265658 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.png index 6b0139af744d..23f361e967eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.svg index 7c2a54665556..434453e8035d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_19.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.279557 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,227 +26,238 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - + - + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.pdf index 84bbbbb7ddb4..6f282bd389ed 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.png index 469d5c1dc44a..c30474d51bf9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.svg index 124763285b22..567a43fc3830 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_20.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.536844 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,386 +26,408 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - - + - + - - + - - + - - - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.pdf index 861b2f525bee..ea8f9a00581e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.png index 3bae783c5876..6d555cf1a97a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg index e0721c9e47a4..93d41406bf08 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.906466 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,493 +26,524 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.pdf index 6e1810b7b1f9..defdd37390ec 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png index b405dd438309..6246da33df66 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.svg index 13ec8213ff31..6c3905aa8771 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.727953 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,278 +26,298 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - + - - - + - - + - - - - - - - - - - - - - - - - - - - - - +M 3022 2063 +Q 3016 2534 2758 2815 +Q 2500 3097 2075 3097 +Q 1594 3097 1305 2825 +Q 1016 2553 972 2059 +L 3022 2063 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.pdf index a91c1afb3b9d..90b455091ed8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.png index ceff5c2a4265..3ce5f1540491 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.svg index 723e5b5896bf..8c1ff3187d7c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_24.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:47.407411 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,76 +26,82 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.pdf index 031f24231db0..3b8a5dcd0b85 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.png index d519e4ac06c4..3fb61f10a7af 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.svg index e34cba1d2052..1f682b3b447d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_25.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:47.466174 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,105 +26,113 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.pdf index cd85f44d4763..d9544dbf6e48 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.png index 19eb0c32e26c..a059ce7b6555 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.svg index 9a8cd79bbecd..b54796907fd5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_26.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:37.906996 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,249 +26,267 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - - - + - + - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.png index 46c9e9d127a3..6a43478d77a6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.svg index 71cbeccefb71..ee209bc5c479 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.002359 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,217 +26,229 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - + - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.pdf index 34cf00ec48d7..c2dd0e1a0c6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.png index 0481c4e28cf7..01f131caf56e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.svg index c81d05b72846..6d1ad4bbcdd5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_28.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.102048 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,203 +26,215 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + + - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.pdf index ec5a585b2aee..aab73b24bd03 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.png index ab486c2647c0..257d53d559e2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.svg index 4ae8a577e70a..3fd2430bec77 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_29.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-11-06T05:24:32.504363 + image/svg+xml + + + Matplotlib v3.11.0.dev1524+g17428e3e2f.d20251106, https://matplotlib.org/ + + + + + - + @@ -15,251 +26,264 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + - - + - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.pdf index d9af3d4188d4..264fd511a8ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.png index 6e0fb8c17492..11602fa13113 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.svg index 08a0c804eed2..6a1fe09b88a5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_31.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.212209 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,166 +26,174 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - + - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.pdf index 9fa906ee51c3..41049665366f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.png index 6e477d7bac49..82033764378c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.svg index 97055f95131f..403759d080b2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_32.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.782099 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,125 +26,131 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.pdf index 4b370974725e..d870ed0cdea2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.png index bf06d5986ea5..6d0ff3f35a2d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.svg index f360faf9ea6b..5a13519d002e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_33.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.249177 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,194 +26,202 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + + - + - + - - - - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.pdf index 0301874f639e..ce98d778daf1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.png index 8a5bbdbda3a7..13cc97b8a09f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.svg index 5645a79e3ea8..48670fdb55c6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_35.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.390972 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,124 +26,130 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.pdf index ffe0b204d140..0d56b0304a2e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.png index 0ec22c8f6d0b..7f297d2d1405 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.svg index 23df184850f1..feb67cf684fc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_36.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:47.901504 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,83 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.pdf index 791d8ff891b8..9922c68169cf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.png index dba4a7e809d0..971305db6f12 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.svg index 86d7501cbc6e..d390af47b6b7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_37.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.341770 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,545 +26,576 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.pdf index 90e267267c81..ef7807608d20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.png index 5ef7f9e77949..e7e8c894e7e3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.svg index 307b6d750da1..70294951c2a9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_38.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:48.405077 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,326 +26,341 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - - + - - - - + - - - + - + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.pdf index 99968e124685..cfa494e86cd8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.png index 432362cbc42f..a6dccc0bf676 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.svg index 176fd6f96495..283a65b0e197 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_39.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.704084 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,208 +26,220 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - - + - - + - - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.pdf index 12272468a4a8..8890bfefd980 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.png index 1599ef81451d..538f14c4b1b4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.svg index daa56919630c..7a167615f941 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_40.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.803339 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,315 +26,332 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - - - - + + - + - + + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.pdf index 90db609b7f01..f34851b7b303 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.png index 5d93feb17300..465e9cfa7852 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.svg index f8c0c16678df..7505bcb464b3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_41.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.881017 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,604 +26,635 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.png index b123bac2d5f5..9da375759fa7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.svg index 50718cbdfff6..aa07f3cae37f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_42.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:38.960348 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,82 +26,85 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.png index 7b7e17ba891e..42190c1a8ffe 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.svg index 39841770141d..9389d6f7d785 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_43.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.027536 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,97 +26,100 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.pdf index 9677cf6fbf98..30b9ffe095aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.png index b8606cbc7b45..932d35bda07c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.svg index 1761dfac793b..71f1a5cfe566 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_44.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.655056 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,138 +26,144 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - + - + - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.pdf index d5821353be0e..80f063dccf3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.png index 95eaddbb7ef6..a3bc9c850626 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.svg index 2f6bc1f194fd..507bc39bbcc6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_45.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.160784 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,138 +26,144 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - + - + - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.pdf index 5db595e4b298..f33d669d5b5c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.png index 04f87903d9b1..632e2a612c37 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.svg index 6336b8cbbf14..5e79df4de605 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.253949 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,106 +26,112 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.pdf index 0a9a791c2b2a..0901edeacebd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.png index 534b1528607f..ad7eeff5468a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.svg index e096ee922cbf..3fdb68ffb460 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_47.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:09.623109 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,205 +26,215 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.pdf index 0a9a791c2b2a..e9da904ccbbf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.png index 534b1528607f..00ef107b3347 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.svg index e096ee922cbf..06393639c2b5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_48.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.594908 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,205 +26,215 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.pdf index 129b3c2d38b6..9ba210a65bdc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.png index dc28a721663b..09edf2938521 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.svg index ae3f130d32eb..c335690bc9cb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:48.971539 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,114 +26,120 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.pdf index d58d9badba4c..d6326759b9ac 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.png index 71ec832f3db2..d9214331adb8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.svg index e1c87eddeb22..52236c500f16 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_50.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.700424 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,179 +26,188 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.pdf index 0b92901440bb..4c2ddb1c718a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.png index a401f98eb11c..61207d49fd40 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.svg index 6dea9aeac9d9..741e23d9d1fd 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_51.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.543673 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,84 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.pdf index 22e0f6e6d5e5..e5229213cf55 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.png index ec2add682e09..38a495f30837 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.svg index a9ad2cabdfe0..2b82f8e905a0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_52.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:46.112261 + 2026-03-12T18:23:44.802692 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.pdf index 23899ccccc63..efd5c0acced0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.png index 30c6712213cb..f4fb6f0aeafb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.svg index 1fdbec59115a..43c89409c7d1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_53.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.663153 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,172 +26,178 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.pdf index 8275564b9950..0b0a345c21c4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.png index 8f12ba3b8671..ca01422d80ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.svg index 2709ae43354d..f0a3641f29e8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_54.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.902951 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,297 +26,316 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - + - + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.pdf index fa4682d407a9..a0f9d1c0545c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.png index 0afc845bcf60..9f52dcd86872 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.svg index 34c185304053..580e0595b5f0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_55.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:39.828128 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,59 +26,61 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.pdf index 560df6008e28..226afcab6fbf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.png index 0bc6d0250527..463b35b59841 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.svg index 36e853c0e446..c108d51a23bf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_56.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:10.115407 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,156 +26,163 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - + - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.pdf index a6b050f5648d..41ca025e4911 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.png index b0b5310192dc..05ba1c642ea2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.svg index bc42d8384bfc..0e3d39b1f861 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_57.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:27:49.427510 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,153 +26,160 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - - + - - - - - - - - - - +M 2784 1497 +L 2784 1825 +Q 2784 2422 2554 2737 +Q 2325 3053 1888 3053 +Q 1444 3053 1217 2703 +Q 991 2353 991 1663 +Q 991 975 1217 622 +Q 1444 269 1888 269 +Q 2325 269 2554 583 +Q 2784 897 2784 1497 +z +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.pdf index 4f59cb0e7906..9e34c1ae65e9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.png index 903947bfaa41..54229de1aa67 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.svg index adaff66dea96..54992f3ed508 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_58.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.006036 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,84 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.pdf index c31fe8dedf16..39274f581c50 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.png index 5a222388f4c1..972a6b68af4d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.svg index cab9236546fd..b5e5cbf18e83 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_59.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.071622 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,81 +26,84 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.pdf index 9a276a8927b7..e90dbadf0230 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.png index 706a0d76ea48..fbd56b9a151b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg index a4fb4be582a4..170433d82d38 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.143308 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,223 +26,234 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - + - - + - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.pdf index 755c3f725800..2d4da3882b69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.png index 02b3a3297cdd..25c8ab1d1f77 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.svg index 451ef45d9481..187e027b2f4f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_61.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.197049 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,197 +26,207 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - + - - + - + - + - - - - - - - - - +M 822 938 +Q 822 269 1469 269 +Q 1863 269 2130 583 +Q 2397 897 2516 1497 +L 2806 3003 +L 2806 3003 +Q 2806 3094 2556 3094 +Q 1956 3094 1491 2625 +Q 1028 2153 863 1297 +Q 822 1097 822 938 +z +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.pdf index 8cd7c05c426f..ee8baefd9a8e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.png index 9ef9a1471ca1..6cea80336795 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.svg index 592cc403e46d..72072be26fce 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_62.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.248902 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,87 +26,90 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.pdf index 6d65ecbda7fa..c1a81c5f7897 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.png index 6e2e91208c19..0be5a5597fb7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.svg index d959051d17ec..5934f9d0a625 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_63.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.287591 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,91 +26,96 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.png index ffc3b9efec98..d4ea1d516903 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.svg index 3445ca2dd710..90f1bb73fb70 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_64.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.341857 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,132 +26,141 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.png index c8e5b6d1cc35..996d2c9284cd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.svg index 819ee1329e7f..db252bdc05b2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_65.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.390888 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,106 +26,112 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - +M 884 4666 +L 1691 4666 +L 1478 2100 +L 1478 1313 +L 1094 1313 +L 1094 2100 +L 884 4666 +z +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.pdf index 64e9b2fe1a97..32506970502f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.png index 9336942936a6..3338fa2a4967 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.svg index f8773bd214fc..8aa7e1047c04 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_68.svg @@ -1,173 +1,173 @@ - - - - - - - - 2022-10-24T13:47:45.388857 - image/svg+xml - - - Matplotlib v3.6.0.dev4028+g98b55b4eed.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + 2026-03-01T22:41:10.441309 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.pdf index 8875d72178a2..ad0f71db21d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.png index 07484920330f..a91dcd79b67b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.svg index 9a3e21d549e9..f00675c6a3bd 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_69.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:42.291943 + 2026-03-12T19:45:40.509413 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + +" transform="scale(0.015625)"/> - - - + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.png index ac5dc7a54b8a..19abc015e82d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.svg index c9985b1a95af..2e47a0bc175f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_70.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.572323 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,52 +26,53 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.pdf index 6a4bc73dbca7..64e9d66b2d2e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.png index 7bc5239b25de..ff17cdfdc63c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.svg index c04252cae772..7aa0e1b9e0d1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_71.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.640954 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,117 +26,125 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - + - + - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.pdf index c9975f274ccf..b236ab6c24ff 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.png index 05db865e4d57..db76a7451c0d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.svg index 79edc9d8ebd7..081f2335f169 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_72.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.695091 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.pdf index 21a18fdde839..f5ca26fd36a7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.png index 93a7c82947c9..4b2efcc5cc7e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.svg index a747b5b211ed..a08ce03f6cdc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_73.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.545213 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,394 +26,415 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - + - - + - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 963 331 +L 1506 331 +L 1444 0 +L 325 0 +L 903 2988 +L 353 2988 +L 419 3322 +L 1544 3322 +L 963 331 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.pdf index a40bedff8cbb..f5ca26fd36a7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.png index 93a7c82947c9..4b2efcc5cc7e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.svg index 47f55df8c7cb..f851229b04b9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_74.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.592970 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,394 +26,415 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - + - - + - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 963 331 +L 1506 331 +L 1444 0 +L 325 0 +L 903 2988 +L 353 2988 +L 419 3322 +L 1544 3322 +L 963 331 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.pdf index 67c52f03bd4b..aeff66cae14c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.png index 831e1c767c73..ce1c4fca3131 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.svg index 1b540fe41060..a773b76ea91e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_75.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.670589 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,173 +26,179 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.png index f3f6835404bb..9b4760229af2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.svg index dc3c66e0afd9..0d507998e882 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_76.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:40.929139 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,227 +26,239 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.pdf index aa877d848314..015e1cff67ee 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.png index 54b4665063f5..14ba098da97b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.svg index 72685a9e4856..5840578ddefc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_78.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.015990 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,179 +26,189 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 750 794 +L 1409 794 +L 1409 256 +L 897 -744 +L 494 -744 +L 750 256 +L 750 794 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.pdf index b42f9a4be097..24735ecb00e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.png index 63eff7504f52..b21e1405ab4c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.svg index 5f30afaf3dd0..5e1691850a12 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_79.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.071114 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,140 +26,146 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - - - - - - - + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.pdf index 1836c097a257..21999478e67f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.png index d2b3cecb441a..a12653662cb0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.svg index 4ee11299dd0e..2879f1e01f54 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_80.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:41.104915 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,382 +26,405 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + + - - + - + + - + - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.pdf index 0dcae701b9cc..c915b6d8325a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.png index f9726f585d06..f35cd504ded3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.svg index 3e5b53c753e4..654467ad9f33 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_81.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.163364 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,126 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.pdf index 6170feca8d38..d7edf0bfeec0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.png index a6aa19c18065..ecbf4e7b7a19 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.svg index c797a241e0f6..1a95b5f11dd7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_82.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:45.209401 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,137 +26,141 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.pdf index db06e90e5490..ef60c97ba001 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.png index d5c323fa9bd2..dc667fa0cd4d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.svg index 1c3ac31ac5eb..41f241300b63 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_83.svg @@ -6,11 +6,11 @@ - 2024-05-08T19:52:37.707152 + 2026-03-12T19:45:41.245728 image/svg+xml - Matplotlib v3.10.0.dev150+gec4808956b.d20240508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - - - - - - - - - + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.pdf index 603fba7f1e65..98095a0152ac 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.png index 820e37e2aa0e..3c3d5368e2d0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.svg index 2702b0509a91..0436c89157e7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_00.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:20.984463 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,168 +26,178 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - + - - - - - - - - - - - - - - +M 2483 1958 +Q 2483 2509 2035 2509 +Q 1594 2509 1158 1773 +Q 954 1427 826 1008 +Q 698 589 698 294 +Q 698 77 992 77 +Q 1414 77 1779 454 +Q 2074 762 2278 1187 +Q 2483 1613 2483 1958 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.png index 18cb14a6b565..4ad548790a92 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.svg index 8ef02948edb8..0af0829bda8d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_01.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.049210 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,117 +26,122 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + + - - - - - +M 4077 2048 +L 307 2048 +L 307 2470 +L 4077 2470 +L 4077 2048 +z +M 4077 768 +L 307 768 +L 307 1190 +L 4077 1190 +L 4077 768 +z +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.pdf index 6d9efeec9fdf..be15f45ffa51 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.png index 6bbe6cdac52d..ef95af865027 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.svg index 595025c3301a..32092496f644 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_02.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.094737 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,139 +26,149 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - - + - - - - - - - - - - - - - +M 2034 4750 +Q 2819 4750 3233 4129 +Q 3647 3509 3647 2328 +Q 3647 1150 3233 529 +Q 2819 -91 2034 -91 +Q 1250 -91 836 529 +Q 422 1150 422 2328 +Q 422 3509 836 4129 +Q 1250 4750 2034 4750 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.pdf index 3264a12ce946..f76260f6fac2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.png index 51d3d4675329..760a55cd9d5d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.svg index 5ebbd64a05fe..5c74dbab2cdb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_03.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.348451 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,153 +26,161 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.png index 04771da480e9..cbc84a278ce3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.svg index 1ff13835825c..b30f7b203f17 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_04.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.194946 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,96 +26,98 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.pdf index e8423c038ac4..bd78e4e8bc6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.png index 4933861519f7..4433f1940134 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.svg index c13a83ae034c..471b6767198c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_05.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:34.129013 + 2026-03-12T19:45:21.234377 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.pdf index c6fdb0909d9f..6fe937ff6a91 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.png index 4534afe8cb8b..298d16f1b691 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.svg index 3107cfa7b53f..989d0dfc5f91 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_06.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.295418 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,304 +26,318 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - + - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.pdf index cb6a7d80f9c1..d0f80a99a8a8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.png index b657faf689f5..f001f43c3c6d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.svg index 0ef036971153..b48380ed1c1a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_07.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.355683 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,140 +26,146 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - + + - - - - - - - - - - - +M 2502 2579 +L 1082 2579 +L 1792 659 +L 2502 2579 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.pdf index 5e74f3896581..c932ff17f08b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.png index b9b264172f58..d8f9a58f06e5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.svg index d554a50009af..162ba7a86e91 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_08.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.399349 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,212 +26,217 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.pdf index 3bdc9f28b43e..5b3884347921 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.png index a79e66960e34..1bcbd6194292 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.svg index c61d5ddad862..16acd787f72c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_09.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.456219 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,102 +26,104 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.png index 7b4cf277ad71..4b55d1cd1c98 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.svg index c33d0698fd5b..b2cfb68bd25f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_10.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.497422 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,244 +26,255 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - + - - - - - - - - - - +M 979 2035 +L 979 474 +Q 979 326 1161 233 +Q 1344 141 1600 141 +Q 1978 141 2195 416 +Q 2432 710 2432 1261 +Q 2432 1888 2182 2240 +Q 1971 2541 1613 2541 +Q 1357 2541 1168 2390 +Q 979 2240 979 2035 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.pdf index f37c32fee257..5397c88df8f8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.png index c75fe1448fc8..ae442a82d87d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.svg index 90b80110001d..de592af14883 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_11.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:06.888772 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,204 +26,215 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - + + - - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.png index ca765c856b98..ea166d922e71 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.svg index 3dcb6434e2cd..797ba5b1e80e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_12.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.588161 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,84 +26,87 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.png index 73fb2078c61f..54c035263024 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.svg index fa074960280b..95046b4144a9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_13.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.647240 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,181 +26,188 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - + + - + - - - - - - - - +M 1619 0 +L 102 0 +L 102 96 +Q 435 115 521 211 +Q 608 307 608 666 +L 608 2118 +Q 608 2342 563 2432 +Q 518 2522 397 2522 +Q 243 2522 128 2490 +L 128 2592 +L 1120 2944 +L 1146 2918 +L 1146 672 +Q 1146 314 1226 218 +Q 1306 122 1619 96 +L 1619 0 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.pdf index b61916b0f538..d1eec62c0a1c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.png index d182bedf8065..3bf942507d35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.svg index ee075ad6c50a..2b60ce5b743b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_14.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.704144 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,84 +26,86 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.pdf index b9dd76ccbee2..1037902fc134 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.png index 5d21ad018141..4e3b8cd2f403 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.svg index 7309a0c57ece..be0b86c7384d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_15.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:21.770334 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,84 +26,86 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.pdf index bbcc405248e2..442c78a370c2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.png index a0cdf3050742..8535558fd977 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.svg index 878b71eea187..af27c0d4d72b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_16.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.819221 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.pdf index bbcc405248e2..442c78a370c2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.png index a0cdf3050742..8535558fd977 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.svg index 774e860890d0..4166c8caa3a2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_17.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.851960 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.pdf index 363ddaae0c8a..a066038b18a0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.png index a75c41e02bb7..0075ebdc938a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.svg index f211b9f942e5..ddb0270a7f92 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_18.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:38.196787 + 2026-03-12T18:23:42.887624 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.pdf index e2de6053602e..fa84893f33bf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.png index b3928fb2ecc2..8c81db3a3427 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.svg index 7574a0aebf55..4cabdf948241 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_19.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:07.327883 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,264 +26,275 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.pdf index 4ba2e950f2db..bd8904ae911f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.png index fa6eaa86640f..e054a651454f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.svg index 5d9fba94e7e0..d6f94e1a6d6a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_20.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.029095 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,472 +26,494 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - + - + - + - - + - + - - - + - - + - + - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.pdf index 9b6ad78bc600..e8b32b3c12c1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.png index 52000b529874..da5b8cd6b1ee 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg index 4623754e2963..46873bff2161 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.070585 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,531 +26,562 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.pdf index 7068a20b305e..a59ffeecff8b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png index 1923648b80a3..56440aea8006 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.svg index bc6756feadc8..ea7263b59363 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.147293 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,284 +26,304 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - + - - + - - - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.pdf index e387cae1d583..e0efca0f30a2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.png index c706e316f588..dbe02cc6e55b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.svg index a0c30f3a113c..904a7be34cdf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_24.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.146734 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,85 +26,91 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - +M 1574 2803 +L 909 179 +Q 710 -595 419 -960 +Q 128 -1325 -288 -1325 +Q -512 -1325 -653 -1216 +Q -794 -1107 -794 -934 +Q -794 -819 -717 -736 +Q -640 -653 -531 -653 +Q -282 -653 -282 -890 +Q -282 -966 -320 -1014 +Q -358 -1062 -358 -1107 +Q -358 -1184 -243 -1184 +Q -51 -1184 80 -934 +Q 211 -685 378 -19 +L 838 1850 +Q 941 2266 941 2362 +Q 941 2470 873 2515 +Q 806 2560 634 2560 +L 467 2560 +L 467 2662 +Q 755 2682 1549 2822 +L 1574 2803 +z +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.pdf index ba4618cd1a29..d17045b3df9e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.png index 54e0971a9fd0..3909e5734a69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.svg index 70a60a773111..4b8245467fed 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_25.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.178911 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,118 +26,126 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - + - - - - - - - +M 1421 730 +L 1504 659 +Q 1235 243 1049 86 +Q 864 -70 627 -70 +Q 314 -70 314 282 +Q 314 467 454 992 +L 762 2125 +Q 819 2330 819 2406 +Q 819 2502 745 2528 +Q 672 2554 410 2560 +L 410 2662 +Q 704 2688 1434 2822 +L 1459 2803 +L 858 608 +Q 794 390 794 326 +Q 794 230 883 230 +Q 1043 230 1421 730 +z +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.pdf index 3fa086d055cb..cae8b8afbb50 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.png index 3d15fb892fd4..1970fe9a0e7a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.svg index adc8bed4cb06..e98ee54ed260 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_26.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:34.626436 + 2026-03-12T19:45:22.253375 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.pdf index 112d6d6f3d1a..2128e70a3d08 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.png index ff5fc89fe10d..6c12223e0ac0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.svg index 4555ac1aefcb..b963d9de0dc9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.329196 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,263 +26,275 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - + + - - - - - - - - - - - - - - +M 1837 813 +L 1837 1715 +Q 1248 1510 1018 1293 +Q 800 1094 800 800 +Q 800 557 921 432 +Q 1043 307 1242 307 +Q 1453 307 1613 410 +Q 1747 506 1792 589 +Q 1837 672 1837 813 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.pdf index b9cabb0f060a..5905f8b92e3f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.png index d632a2278248..f85068097c86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.svg index 70b3efd4a1e8..21455f2b159f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_28.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.309363 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,252 +26,264 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.pdf index 5d80d6c689cf..db026d6d0c51 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.png index e4de05e4bffc..e896de0f440c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.svg index 7255d93074ff..e9026530a731 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_29.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.414040 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,322 +26,335 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - + - - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.pdf index 945fc7cb66fa..a641f635743c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.png index a8917d146f31..3d42196224e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.svg index b0828cb09b48..7ff7342be431 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_31.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.470536 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,206 +26,214 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - + - + - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.pdf index 2d5a06af33eb..d9b64e817d4c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.png index 2343e2fca2c9..fd6a8f541f24 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.svg index 85d5c68384db..e0fc61801301 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_32.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.420583 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,165 +26,171 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.pdf index abacc5d80d04..55b48114fe7d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.png index 63a9b9ac9462..464ec65fec8b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.svg index e9a87b7138ce..22e7f44dcc36 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_33.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.464061 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,210 +26,218 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - + + - - - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.pdf index ffc25c3e97b2..37f7d8a47c9c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.png index adf82c32694f..fe82d7cb3191 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.svg index c75b672bcad5..6abaf9885bc4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_35.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:07.934040 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,150 +26,156 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + + - + - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.pdf index 15a47c2ade35..5f8027f47c5a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.png index e120da0f210b..888f7db6368e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.svg index 4157b1c1cdff..5e14081e718d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_36.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-02-06T03:28:13.635555 + image/svg+xml + + + Matplotlib v3.11.0.dev1856+ga22069c80c, https://matplotlib.org/ + + + + + - + @@ -15,101 +26,103 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.pdf index 12ffcf34d616..3576ea446a4d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.png index 8c5159af3f92..a7f624ce14f3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.svg index 776c6e5337fb..725a7cae4638 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_37.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.643873 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,618 +26,649 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.pdf index bf15da9b4fec..99b5776ebfb3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.png index 91f5cd31f983..ddde439a05d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.svg index a2b7e36eb21f..dd21fc9bd3a8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_38.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.734722 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,376 +26,391 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - + - - - + - - + - - - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.pdf index c5bf91b5f021..6f8a21bf1c03 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.png index e5867f4839fb..d9f334bc7855 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.svg index dabe0a6e9718..16eb9a38ddfd 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_39.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.824783 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,236 +26,248 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - - + - - - - - - - - - - +M 2483 1958 +Q 2483 2509 2035 2509 +Q 1594 2509 1158 1773 +Q 954 1427 826 1008 +Q 698 589 698 294 +Q 698 77 992 77 +Q 1414 77 1779 454 +Q 2074 762 2278 1187 +Q 2483 1613 2483 1958 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.pdf index 8b58bc70031d..0622ad5585f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.png index fdd2f6394689..d000d4053ab5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.svg index 54bd39ae9336..ed39a952fedc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_40.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.895587 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,372 +26,390 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + + - - - - - + - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.pdf index 4dbfadaa0c9c..1bbc1bb6e617 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.png index 21a304c44aa5..a6b3b627e19b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.svg index 55f3eabadd86..4f0e586bd067 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_41.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:22.960409 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,726 +26,757 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.pdf index 3e7fad2a06bd..3e8d84302441 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.png index b4ff7e515672..0b1f7f0d1a75 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.svg index c57c2c55c064..93bc9ddfc82c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_42.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.036592 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,124 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.pdf index a51b29003927..ebd9008486cc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.png index f1d40f477400..fba4d39d586c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.svg index 293e78b4485c..175cf369c814 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_43.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.082876 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,106 +26,109 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.pdf index 59ce25d7caaf..5f4fcd094c71 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.png index d072de609b99..155d80703112 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.svg index 1c090ba39615..6c7d4d4f5cdf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_44.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.459535 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,192 +26,198 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - + - + - - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.pdf index 22e0050a78db..462ad902cd12 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.png index 2d3c89e0fb99..f26d05780abe 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.svg index f41d192cacbd..776b406aae0f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_45.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.176250 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,192 +26,198 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - + - + - - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.pdf index d160aeb8a343..d03f9e04ffc6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.png index 3fc71b45bbda..cf07e9179019 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.svg index 93d7f2d639ee..38f91cd3eeee 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.239662 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,115 +26,121 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.pdf index 4991a06a72e9..e75b647fcfb6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.png index cce94bd39a10..5305fbd10a69 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.svg index 831c4754e0d3..c4c58bb84128 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_47.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.886317 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,213 +26,223 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.pdf index 4cf305c25d49..916cc5f09f53 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.png index cce94bd39a10..6735b68a5a8f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.svg index 831c4754e0d3..66821b13cf30 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_48.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:41.945224 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,213 +26,223 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.pdf index 38351e4c75db..5f918705bf01 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.png index 845b9d13c43f..8db55da27ad6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.svg index 20fc63945735..46dce0707cf8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.444623 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,135 +26,141 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.pdf index 3db309ae1a3b..02070b8ec4a0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.png index 315c93a2950a..10308957630b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.svg index 9d66a7700c13..2c0b48ce2e07 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_50.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.033336 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,228 +26,237 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.pdf index a8ab5ef2f6ae..43905663d7a8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.png index 0c62894bd5e7..22fcc24722a7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.svg index 9491982c9e5d..0cb70754432b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_51.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.566880 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.pdf index e9d5111e04a3..ec99dbe0c3ec 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.png index dfc27853f20f..eb891d989c92 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.svg index 69716e0c4a45..48502bf1de01 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_52.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:39.161171 + 2026-03-12T18:23:42.140170 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.pdf index 97115f4130bb..045289c7f596 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.png index 7d1656d759af..6d6544a80145 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.svg index 63f9d5b6beb7..cfdafd0b8717 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_53.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:06.590899 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,218 +26,225 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.pdf index 1c7345b41e07..45a850ff02fb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.png index 4cd85e3d72c5..454ac22bd339 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.svg index 406804be6ae5..242772513754 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_54.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.245899 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,344 +26,363 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.pdf index f65236ffb5e5..1213804938fa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.png index f1d0ed669704..3c99312a771f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.svg index d0f2a43f92ac..193edec2a89c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_55.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:23.859380 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,86 +26,88 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.pdf index 7606eaf178f0..192e246be647 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.png index a3e28eeb1102..558d122f004b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.svg index 93b0baf02003..7a2a9a61a3b1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_56.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.376058 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,196 +26,203 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - + - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.pdf index 1ddda2eae3db..3bf12a624528 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.png index a33e613374c2..5a9aa25e9770 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.svg index 9c95dcadb933..c2fad12feb9d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_57.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.429469 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,184 +26,191 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - - - - - - - - +M 2176 653 +L 2176 2125 +Q 2176 2381 1981 2573 +Q 1786 2765 1523 2765 +Q 1158 2765 940 2438 +Q 723 2112 723 1568 +Q 723 979 963 624 +Q 1203 269 1606 269 +Q 1907 269 2080 474 +Q 2176 582 2176 653 +z +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.pdf index 9bcc18d43666..5cd73a9a61eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.png index 44435935e3c7..78ff6ae06fc9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.svg index 0c3dec0e1d18..5b063bdc006e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_58.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.037352 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.pdf index 565df45cfc23..187fb68921d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.png index 217b37f8eb5d..c391516530f5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.svg index 4e77e516f10f..b3e205134e38 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_59.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.097363 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,120 +26,123 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.pdf index 1bac23e7c81a..d03a240ed67c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.png index bbb1aadfe408..b979f2c53d08 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.svg index 3cfa177cab94..3a9c7cc7cdc0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.554686 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,255 +26,266 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - + - + - + - + - - - - - - - - - - - - +M 2317 2272 +L 2317 2522 +Q 2317 4147 1472 4147 +Q 1171 4147 1005 3930 +Q 909 3802 845 3546 +Q 781 3290 781 3034 +Q 781 2464 995 2128 +Q 1210 1792 1568 1792 +Q 1824 1792 2070 1917 +Q 2317 2042 2317 2272 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.pdf index c6ad0489f6e8..1001facc2c23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.png index fb78ec544100..e11672733939 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.svg index 8b9c7d426c87..d4f30b0b8ceb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_61.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.596089 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,257 +26,267 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - + - + - - - - - - - - - +M 2278 2310 +Q 2278 2477 2185 2576 +Q 2093 2675 1978 2675 +Q 1677 2675 1395 2381 +Q 1075 2042 864 1571 +Q 653 1101 653 704 +Q 653 486 752 361 +Q 851 237 1024 237 +Q 1312 237 1606 550 +Q 1901 864 2089 1350 +Q 2278 1837 2278 2310 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.pdf index 74c8d21b926c..64c2b71b4fdb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.png index c4868ba204bd..215410b1b3a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.svg index b28dd244d175..1256f2672d78 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_62.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.663003 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,92 +26,95 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.pdf index 464bc0f3d1c3..e417754764a2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.png index 7d784211b3e6..6f62478497c4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.svg index 912ca68eac80..b14b39809c35 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_63.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.339027 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,115 +26,120 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.pdf index 10a5f9bda9d7..a7a9dbba27f9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.png index 1f60b3a4d5c9..b9ed12718e35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.svg index c3afd90cd485..f3346e88b4d5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_64.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.401897 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,139 +26,148 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - - - - - - - - - +M 4077 768 +L 307 768 +L 307 1190 +L 4077 1190 +L 4077 768 +z +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.pdf index 9b1c286225db..5871f9352d38 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.png index 7cea9ef21fd4..f523c3d942aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.svg index 1ca2f5b4fc56..f937953de551 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_65.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.446599 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,119 +26,126 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +M 1856 288 +Q 1856 147 1760 48 +Q 1664 -51 1523 -51 +Q 1363 -51 1270 48 +Q 1178 147 1178 288 +Q 1178 435 1277 531 +Q 1376 627 1523 627 +Q 1658 627 1757 524 +Q 1856 422 1856 288 +z +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.pdf index c6cf56ddba12..294328f978ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.png index 0aa0ac62b063..a9782477683a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.svg index 078cb0fdb8c4..6e4695f524d9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_68.svg @@ -1,183 +1,183 @@ - - - - - - - - 2022-10-24T13:46:49.965486 - image/svg+xml - - - Matplotlib v3.6.0.dev4028+g98b55b4eed.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + 2026-03-12T19:45:24.498682 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.pdf index 773aaf73079e..34f811bf96e4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.png index 11f627be29db..42bd01c7c2cc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.svg index 9738cc21a69b..ceb428e640c6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_69.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:35.788326 + 2026-03-12T19:45:24.602217 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + +" transform="scale(0.015625)"/> - - - + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.pdf index c6f4bfac8505..39f610484434 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.png index 13823c38b1e0..7d4edd6f0bda 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.svg index 3fb3677fc1a1..451b826b693e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_70.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.654626 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,59 +26,60 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.pdf index 7e67f9c47cd0..97b62612d0b1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.png index 932ca8733f7f..679505e06d29 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.svg index 9e439f7e4254..d289462d1972 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_71.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.730087 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,139 +26,147 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - + + - - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.pdf index 62ef409bd486..6ecea8daa0d7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.png index a40ad0c281f6..2c8eefa4111f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.svg index 7f218efb93a6..6667ca9ace0a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_72.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:24.815240 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.pdf index d85fd8510162..64ee43157f00 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.png index d6806716a4be..b9982f0a87b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.svg index bb38f4590198..c3cba9c92216 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_73.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.951390 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,492 +26,513 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - + - - + - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.pdf index 2886c6866895..64ee43157f00 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.png index d6806716a4be..b9982f0a87b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.svg index a52ae5ec00a8..dec8fdcc2cd2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_74.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.012637 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,492 +26,513 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - + - - + - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.pdf index 7b4143badeb8..79b916240f26 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.png index d55dd5053188..600ef59b66ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.svg index 5bfc6fbdb9b3..0029bfb91dc7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_75.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.092010 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,215 +26,221 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 2202 2803 +L 2138 2803 +Q 1645 2803 1293 2451 +Q 941 2099 941 1606 +Q 941 1254 1129 960 +Q 1318 666 1632 518 +L 2202 2803 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.pdf index 9ac72e73f70e..fd0ab1822db4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.png index bdd18fee315f..3c2cc2a253dd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.svg index 097bfc018f1f..c2064054f4af 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_76.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.144913 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,225 +26,237 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.pdf index cd2be60b4367..c0e1758edd0c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.png index 612b9806f652..d1469fc48e20 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.svg index 43858096fb5c..f5fd92b5a651 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_78.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.220253 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,172 +26,182 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 750 794 +L 1409 794 +L 1409 256 +L 897 -744 +L 494 -744 +L 750 256 +L 750 794 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.pdf index 1bc193adf810..2e7ac6e51ce6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.png index 489d8ddb4987..a08f018e61a8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.svg index 962e93527b4e..e3dde6c72007 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_79.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.282056 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,184 +26,190 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - + - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.pdf index af60e108b0f1..f3138b2d17b9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.png index c0a9a2577e7e..d016fde59c2c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.svg index ec5f382a1cbf..1b71e9af4503 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_80.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.304495 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,381 +26,404 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + + - - + + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.pdf index fa5ae33fc798..c129682c5141 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.png index 7fd3e4d22035..c8d90f9d36c0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.svg index c27df7f2de2a..d2112ab3f5a9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_81.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.482860 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,144 +26,151 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.pdf index 1657aaf5625a..a2d7c8931c41 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.png index 0143688609e4..089a9571284f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.svg index e7574f7f0427..4234944a8735 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_82.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.521391 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,160 +26,161 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.pdf index 6679c1e8af13..57fcd2ed5594 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.png index d6f17be104fa..587c7cde42f0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.svg index 3268d5d3d26d..11e68cbdf0f5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_83.svg @@ -6,11 +6,11 @@ - 2024-05-08T19:52:30.625389 + 2026-03-12T19:45:25.522816 image/svg+xml - Matplotlib v3.10.0.dev150+gec4808956b.d20240508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - - - - - - - - - + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.pdf index c18905e5b2e3..2bd2f060b347 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.png index a188895bedba..487f438e81c1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.svg index 5339d9fcc3a4..6e66767aca4e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_00.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.597882 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,152 +26,162 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.pdf index 39554ec57627..759cfa21c210 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.png index 8e136b1f0b4c..46d9cede4073 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.svg index 79b96dfd43d5..534ddda83ccf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_01.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.671429 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,69 +26,74 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + + - - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.pdf index 522e652d825d..30e3ae87c67e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.png index aaff9429df58..c48c6f398ecf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.svg index b57ff67970f2..c7f2c9ff8f78 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_02.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.716660 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,135 +26,145 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - - - + - - - - - - - - - - - - +M 2034 4750 +Q 2819 4750 3233 4129 +Q 3647 3509 3647 2328 +Q 3647 1150 3233 529 +Q 2819 -91 2034 -91 +Q 1250 -91 836 529 +Q 422 1150 422 2328 +Q 422 3509 836 4129 +Q 1250 4750 2034 4750 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.pdf index 2be9cf1c7552..414ec5ac99f9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.png index 60c9bbb05f2e..8fd32bf9d27a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.svg index 07816b7bd6f1..3efdfee0b543 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_03.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.678850 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,132 +26,140 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.pdf index ec42ffb27c76..6014e23a897c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.png index 07ec22b30dcc..214c971be650 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.svg index c5b0a1d76a1d..4309e628176c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_04.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.839496 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,48 +26,50 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - +" transform="scale(0.015625)"/> + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.pdf index 3d2fd052bb31..7aeb4214022c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.png index 8e5cd1edb750..28cb908216f5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.svg index f4634ce76c62..c089e2d9786c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_05.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:36.331853 + 2026-03-12T19:45:25.898393 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.pdf index 52dc764762b8..fb15877a8da8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.png index 7edcc0fcad1c..5d79f180df99 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.svg index 2127103c8649..eb688642bb80 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_06.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:25.990172 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,251 +26,265 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - + - - + - - - - - - - - - - - - - - - - - +M 3974 4518 +L 1222 -115 +L 922 -115 +L 3443 4096 +Q 3264 3891 3094 3811 +Q 2925 3731 2669 3731 +Q 2445 3731 2182 3821 +Q 2208 3674 2208 3539 +Q 2208 3206 2067 2873 +Q 1926 2541 1696 2323 +Q 1453 2086 1146 2086 +Q 826 2086 608 2339 +Q 390 2592 390 2963 +Q 390 3507 761 3916 +Q 1133 4326 1626 4326 +Q 1792 4326 2010 4147 +Q 2310 3898 2624 3898 +Q 2918 3898 3084 3994 +Q 3251 4090 3693 4518 +L 3974 4518 +z +M 2054 3642 +Q 2054 3782 2025 3833 +Q 1997 3885 1894 3936 +Q 1747 4006 1613 4154 +Q 1414 4058 1331 3984 +Q 1248 3910 1152 3750 +Q 845 3264 845 2707 +Q 845 2509 944 2384 +Q 1043 2259 1210 2259 +Q 1549 2259 1801 2675 +Q 2054 3091 2054 3642 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.pdf index 06350c598354..7954277ea188 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.png index 577e754f75ed..38bc28e4d057 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.svg index be63a751bd1d..c505510a70ae 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_07.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.074445 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,92 +26,98 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - + - - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.pdf index 61f86d6e45d1..a59a823e6079 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.png index 64dfc78c8a17..382fa0be0fe1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.svg index 812400ab8381..e24febb4fff8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_08.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.148544 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,109 +26,113 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.pdf index e15b3f309a3b..74d8efdec6a6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.png index a2e4e381f378..f850abeea68a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.svg index 0f15a43c1f45..4d23bdb99951 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_09.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.217568 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,54 +26,56 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.pdf index c51d2d8bda25..da9b2d841239 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.png index 3a1ad4d5c0f6..9f013133fe3d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.svg index f23a25e25c32..1729ce067373 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_10.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.288634 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,200 +26,211 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.pdf index cce9b2603e92..9c1f2d348b43 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.png index 1599dca2b2ae..3dbf8189ada1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.svg index 7fae93821427..e4de5a9951a0 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_11.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:06.620876 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,179 +26,190 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - + + - + - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.pdf index 211c695e3d49..fda2a560a4af 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.png index 43cbf71a784d..dc86cee5e566 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.svg index 3d78405724f2..e72967ae0dd7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_12.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.422496 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,52 +26,55 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.pdf index 1e3f3f7f8a24..84476e3c0023 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.png index ca4159f321a4..afaa49c287cc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.svg index 0a4ee2c61414..e1cdec457929 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_13.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.485202 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,114 +26,121 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.pdf index 1bbd3f4300cc..4755f6edb6fe 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.png index 2950476da145..9eeca0342eb6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.svg index 1802ccd2426c..c450b9777f33 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_14.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.541691 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,53 +26,55 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - +" transform="scale(0.015625)"/> + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.pdf index 0997cd1c42ac..863cecb9d06e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.png index 767480866740..776749440fb0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.svg index 2618bbfa8a23..bdedea33ad01 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_15.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.583109 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,53 +26,55 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - +" transform="scale(0.015625)"/> + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.pdf index e4ebc875c78f..a3fa5505bc3f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.png index 138d2c5b3d77..5bd4c48bbcb9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.svg index 44dad5a3fee2..edf5d6f6075e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_16.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.299455 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,73 +26,76 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.pdf index e4ebc875c78f..a3fa5505bc3f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.png index 138d2c5b3d77..5bd4c48bbcb9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.svg index a692f202017a..06a7122110aa 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_17.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.340873 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,73 +26,76 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.pdf index b671324c9749..39f6ee1c87ad 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.png index 6fd67255629f..ffbc23fee2c1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.svg index 8850d02e15bf..a3fe61d243e4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_18.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:40.464406 + 2026-03-12T18:23:42.393791 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.pdf index e96021bab2a0..e39e3137a4ce 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.png index 0450ed0c99f3..cac9acaad5b3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.svg index a092d53b1a1c..d45e31164454 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_19.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:06.944275 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,205 +26,216 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - - + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.pdf index 2178ebef20ea..09e7d27d345d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.png index d98751841ba0..fec7a1a13ca9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.svg index bdaa8b47f891..47b5fc2cdcae 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_20.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.773809 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,342 +26,364 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - + - + - + - + - + + - + - + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.pdf index cdee61e24df6..c759a6f31ae3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.png index 8a145bcf1690..8613e7e92f8a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg index d61317816ad6..b87e07559b50 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.551224 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,435 +26,466 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.pdf index 4514c685e0e6..5a23a3ebbaa2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png index a86119004e62..abd30a77ab1c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg index 4e129aa6c87d..a563a936321c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:26.917019 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,269 +26,289 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - + - - + - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.pdf index c3e4a8cd9e93..7268a0f8acfb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.png index 67b95e4a058c..0e6a81ebd162 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.svg index 72cd6eeb89ec..1834e81cb9cf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_24.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.625698 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,56 +26,62 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.pdf index 517a3174cb6e..22e8b4fd7802 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.png index 4380fd6f9fbc..b72fd43b6ff1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.svg index 08e7ce0cf9f7..5ddad5ed6c84 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_25.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.658945 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,82 +26,90 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.pdf index a5c0426fc21b..a8c125906797 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.png index e05ff60e1f60..8a1d6763a5ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.svg index 5a1ed4923774..83c8d692f106 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_26.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:36.792928 + 2026-03-12T19:45:27.040748 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.pdf index 6d15b0447c20..fa4d7379b7a0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.png index 3cdd12e024d3..2fc718d0625f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.svg index b30524e2ea7a..7ee537083172 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.114222 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,183 +26,195 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - + - - - - - - - - - - - - - - +M 2509 1459 +Q 2509 1946 2249 2227 +Q 1990 2509 1574 2509 +Q 1171 2509 928 2201 +Q 685 1894 685 1459 +Q 685 998 941 694 +Q 1197 390 1594 390 +Q 2022 390 2265 732 +Q 2509 1075 2509 1459 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.pdf index 490e728c8601..c9fa5a3a1801 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.png index b6c016a64840..dfaff2afd4ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.svg index 65a11fcbd59d..22319b858a48 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_28.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:36.857412 + 2026-03-12T18:23:42.764865 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.pdf index d16e80a9b54a..f8aff15f72aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.png index 3fdc86c9bdfd..039b32fbc70d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.svg index 06e684cf71b5..756d0a103420 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_29.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.219401 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,213 +26,226 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - + - + - - + - - - - - - - - - - - +M 934 1888 +L 934 787 +Q 1184 390 1594 390 +Q 2010 390 2256 697 +Q 2502 1005 2502 1472 +Q 2502 1914 2278 2211 +Q 2054 2509 1670 2509 +Q 1446 2509 1241 2339 +Q 1037 2170 934 1888 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.pdf index c03e8a6b1904..9d3edeb782e2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.png index 175cd3c5ec15..71f0c9ad6b10 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.svg index 635a911ad6f5..b9ab5a83556d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_31.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.281833 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,134 +26,142 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - + - + - + - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.pdf index efb9af18279e..3868bd61a3eb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.png index a828223e20ed..a484206b9b84 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.svg index b9e6639fa956..e6e162076060 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_32.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.889446 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,115 +26,121 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.pdf index edbe1917346d..642236cc3af0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.png index b091a35d8c79..f85869dd658d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.svg index 19d16f5f47b5..41224f39746e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_33.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:42.927222 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,154 +26,162 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - + + - - - - - - - - - - - + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.pdf index 33de7a49be96..1942bc89e859 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.png index accb168fd70f..9c3835013a8b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.svg index ab6f0cd284a2..e98e9f28fb59 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_35.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:07.604070 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,109 +26,115 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + + - - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.pdf index ca5c7a00989b..9367cca46eb8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.png index e4a9fde26ae9..175de8d80f5f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.svg index 847813d30da2..c4fbf50f703a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_36.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:07.647714 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,53 +26,55 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.pdf index c24e2b91f9ea..34c4950d1bcf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.png index 80fd1e30929e..a2391e49856a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.svg index 95a83126750c..de7aabaffde6 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_37.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:37.056437 + 2026-03-12T18:23:43.016833 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.pdf index 8989506f6c1d..a7f439cfac6d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.png index 74cc89f50b19..0e98109fac5f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.svg index 7de6a32711c7..1009d867730f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_38.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.103937 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,300 +26,315 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - + - + - - + - + - + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.pdf index 4b373e9ac830..355637a60cdf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.png index aa1d8aebaece..8be384cc07d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.svg index 9475fce51bd8..9e0dc2c4c4a9 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_39.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.577160 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,187 +26,199 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - - - - + - - - - - - - - - - +M 1389 1933 +L 1056 602 +Q 1254 390 1587 390 +Q 2048 390 2470 793 +Q 2893 1197 2893 1843 +Q 2893 2150 2729 2329 +Q 2566 2509 2253 2509 +Q 2022 2509 1788 2358 +Q 1555 2208 1389 1933 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.pdf index 419ed96aaf9e..e0910b795d86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.png index 4ee3b254182d..8566368a972b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.svg index ba3091a2d685..99535294b86f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_40.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:37.190526 + 2026-03-12T19:45:27.645488 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.pdf index a0ca7ee5fe7b..ff9ee44e88d6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.png index e8033e7f9301..dcdb945ac863 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.svg index 8b097236395d..eb5e0be64990 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_41.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:37.223168 + 2026-03-12T19:45:27.721075 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.pdf index dd643006b5f8..e61f9f9bfc07 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.png index 2c3aeb5fd7d0..6dd2f3fa5d52 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.svg index 354f3eb546da..26c8b087e116 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_42.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.803298 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,74 +26,77 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.pdf index 4e66e38f694d..d8d349c57add 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.png index c05377d34c3e..9739e14e7541 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.svg index 7d775f77dd0e..ebe6d1f9098e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_43.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.870058 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,80 +26,83 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.pdf index 4fed497db683..d56f4fa43ae7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.png index dc44719e7c22..bcec7b958bc6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.svg index edf05dd4e46e..b47e5cbe2f26 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_44.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.411443 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,127 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.pdf index 3523a8f3b7a5..0444f9d2b40c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.png index 64cb1d8c7a0b..298a17ff83b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.svg index ba1af958a78e..f22001c972bf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_45.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:27.956461 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,127 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - - - - - - - - - - + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.pdf index 048b01614aef..e75fb5a1e095 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.png index 80fb79ed635a..237d4813a850 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.svg index 24ebed72bbfb..0f49fbcad4a8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.003651 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,100 +26,106 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - - - - - - + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.pdf index 922e9806d5e4..d8061e19a5ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.png index 0df830e50ce5..8d7d6a8edaee 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.svg index 3349d2f60e2b..60877da99e86 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_47.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.186929 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,203 +26,213 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.pdf index 01c2ec54f5d5..2233ed77f418 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.png index 0df830e50ce5..574ccb590d41 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.svg index 3349d2f60e2b..9633a1efb73d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_48.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.257680 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,203 +26,213 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + - - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.pdf index 6efe5792d183..f36f0a0430d3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.png index c8a0ad61be8b..67a80bb2b0b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.svg index e83435ef22f5..232b83438cee 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.177840 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,104 +26,110 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - + - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.pdf index 618e7f2442f2..3f1ddbac1321 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.png index 04cb478f442f..697294fd5479 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.svg index 7698c16e33d1..31472bda1ba2 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_50.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.352951 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,162 +26,171 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - + - - - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.pdf index b50e708ceb37..fc31a4ef8f8d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.png index 9435858ff84d..056a2e4b32cc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.svg index f889ecb18648..441de291f4db 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_51.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.291401 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,73 +26,76 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.pdf index 8cdb7a680241..2fab600677c8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.png index 6a4afbc07290..6e861939721b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.svg index b6418ec608fa..960aa9688de4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_52.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:14:41.432583 + 2026-03-12T18:23:43.441037 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + - + - + - + - + - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.pdf index bce5949a9485..4508f45244f1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.png index e270469bb21e..7d734de0cc7e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.svg index d67268e320a7..35459fcae3ef 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_53.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-01T22:41:07.808510 + image/svg+xml + + + Matplotlib v3.11.0.dev1920+g7b52e93248, https://matplotlib.org/ + + + + + - + @@ -15,179 +26,186 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.pdf index dc3d00843436..a17e4856c4d2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.png index 0e52993cf2a2..2f5c25f7f49d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.svg index ee31759d9be7..faadba38438f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_54.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.549562 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,280 +26,299 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - + - - + - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.pdf index ec380a3de731..541fe38a3c35 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.png index 39c3bfabee79..4f2ab0b86bd1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.svg index b173dbd2a387..0e2894ec3eeb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_55.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.560693 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,55 +26,57 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.pdf index 2e07f3b8361a..b2694189770e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.png index 9a795e61b328..c48e33a97ddc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.svg index fa7c7bb4dd82..e254dba365b5 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_56.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.643888 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,152 +26,159 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - + - - + - - - - - - - - - + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.pdf index b141a0989614..ff3d75bb108a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.png index c95c6d092e46..a8baeb2b4503 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.svg index 899ca9493c8a..c3f8419a695b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_57.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.682107 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,127 +26,134 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + - - + - - - - - - - - - - - +M 2253 1011 +L 2253 2106 +Q 1978 2509 1594 2509 +Q 1242 2509 963 2201 +Q 685 1894 685 1421 +Q 685 1146 781 906 +Q 877 666 1043 544 +Q 1248 390 1517 390 +Q 1741 390 1945 560 +Q 2150 730 2253 1011 +z +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.pdf index 79c6b2577e8b..b5289226c08a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.png index 9f24e368356c..98f9fcc3eb4e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.svg index ad724dfd2771..49d45efe3b9a 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_58.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.704217 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,73 +26,76 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.pdf index 5e9f9abfb1d7..8e229bf40d2e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.png index 60c33e172c63..eca1fce253ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.svg index 19b75e3f47b5..c3b922d793cb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_59.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:28.772350 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,73 +26,76 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - - - +" transform="scale(0.015625)"/> + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.pdf index 827113676db3..1c4f0796007e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.png index dcd8f10f3395..131e0d4fca3d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.svg index 21d56809b9aa..d09ba470f84c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.803225 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,205 +26,216 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - + - - + - + - - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.pdf index 379ec0623458..d6c71120e159 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.png index 5f14d8f4eaac..d1db99ce8b88 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.svg index 76362ecb1214..a89a13e13690 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_61.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.842813 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,156 +26,166 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - + - + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.pdf index 623061af973e..04f1eb64e1b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.png index 74d24e776a71..f69264e163df 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.svg index a94dca540149..8f768285a175 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_62.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:43.895414 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,76 +26,79 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.pdf index 942a7b2e5467..bd48a14a6707 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.png index 8c5d0416b0ec..474da01f3ffa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.svg index 183843cc3edb..935a41ecd180 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_63.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.064477 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,99 +26,104 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.pdf index 04cbc790c20b..44a47a31bb21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.png index fe79a4d9b2cb..c0a97458a6b3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.svg index 4b5041064a6b..26db24a3475c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_64.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.117449 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,124 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - - - - - - - - - +M 4077 768 +L 307 768 +L 307 1190 +L 4077 1190 +L 4077 768 +z +" transform="scale(0.015625)"/> + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.pdf index fb95011581af..5871f9352d38 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.png index 7cea9ef21fd4..f523c3d942aa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.svg index 8f6c251485b8..e61651b166bb 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_65.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.168151 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,119 +26,126 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - - - - - +M 1856 288 +Q 1856 147 1760 48 +Q 1664 -51 1523 -51 +Q 1363 -51 1270 48 +Q 1178 147 1178 288 +Q 1178 435 1277 531 +Q 1376 627 1523 627 +Q 1658 627 1757 524 +Q 1856 422 1856 288 +z +" transform="scale(0.015625)"/> + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.pdf index e277346b7f1e..ed375e9e0768 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.png index 1ea8e26f8d17..6a82a8af2071 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.svg index 0a7baa3550a4..58dab99008d7 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_68.svg @@ -1,167 +1,167 @@ - - - - - - - - 2022-10-24T13:47:05.108205 - image/svg+xml - - - Matplotlib v3.6.0.dev4028+g98b55b4eed.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + 2026-03-12T19:45:29.220622 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.pdf index af16ca775a14..2b40f8d2b940 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.png index 6f3ff589e41a..e35fc3d59238 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.svg index 8242b10930c7..719d3f11d4a3 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_69.svg @@ -1,23 +1,23 @@ - + - + - 2020-08-05T13:43:37.975730 + 2026-03-12T19:45:29.303826 image/svg+xml - Matplotlib v3.3.0rc1.post530.dev0+g4269804ce, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,13 +26,13 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - + - - + - + +" transform="scale(0.015625)"/> - - - + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.pdf index ceb6df75c042..2d130284c654 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.png index f3222c890b45..81e74ad66367 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.svg index 686e58f4f545..0867f9b08b0b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_70.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.350373 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,40 +26,41 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.pdf index ad50a45f0428..97b62612d0b1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.png index 932ca8733f7f..679505e06d29 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.svg index 6fba3e060110..c9c52509924e 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_71.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.415822 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,139 +26,147 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + + - + + - - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.pdf index 909aa3b7fb41..6ecea8daa0d7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.png index a40ad0c281f6..2c8eefa4111f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.svg index 7f218efb93a6..1d89e506a872 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_72.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:29.841013 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,121 +26,133 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + + - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.pdf index 1f3908313c9c..088ab957c8e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.png index 2f287c8555a9..5122e9e77185 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.svg index 2df349600b2d..6336fd6933f1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_73.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.192293 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,310 +26,331 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - + - + - + - + - - - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 1389 1933 +L 1056 602 +Q 1254 390 1587 390 +Q 2048 390 2470 793 +Q 2893 1197 2893 1843 +Q 2893 2150 2729 2329 +Q 2566 2509 2253 2509 +Q 2022 2509 1788 2358 +Q 1555 2208 1389 1933 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.pdf index 87e2028af9c8..088ab957c8e6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.png index 2f287c8555a9..5122e9e77185 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.svg index 5c1e52968a72..34caf38131dc 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_74.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.251846 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,310 +26,331 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - + - + - + - + - - - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 1389 1933 +L 1056 602 +Q 1254 390 1587 390 +Q 2048 390 2470 793 +Q 2893 1197 2893 1843 +Q 2893 2150 2729 2329 +Q 2566 2509 2253 2509 +Q 2022 2509 1788 2358 +Q 1555 2208 1389 1933 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.pdf index 987b62656d68..f7a7dfc9f887 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.png index 14f60e5d2f66..e21644bfd6b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.svg index 8bc32e559925..54516f9b375c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_75.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.327703 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,157 +26,163 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.pdf index 8681cac2b494..b049aa4ed1cf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.png index effcf6acfe11..2aeaae088213 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.svg index 8039696442ae..5e5b0ea07420 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_76.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:30.132128 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,219 +26,231 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - - - + - + - - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.pdf index 686dab4dafd9..a5fc3d1d76ea 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.png index 2b66c097353b..a42fb8435a8f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.svg index 1c79857fd936..b60f44145b4d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_78.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.461025 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,179 +26,189 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.pdf index 3f8ae587c4ef..2fb7dc3c2b23 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.png index 91f6e8b74736..ddbc891f5ab9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.svg index 25294c0e11f8..43c852219844 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_79.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.522860 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,144 +26,150 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - + - + - + - - - - - - - - - + + + + + + + + + - diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.pdf index e6484938b079..b0f6c88081cb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.png index 02a1ed4873cc..6ff648306e27 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.svg index 906d64164637..061044a76ca8 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_80.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T19:45:30.390532 + image/svg+xml + + + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ + + + + + - + @@ -15,368 +26,391 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - + + - + + - - + + - + - + - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M 2496 1325 +Q 2496 1786 2233 2045 +Q 1971 2304 1606 2304 +Q 1274 2304 1024 2109 +Q 774 1914 717 1536 +Q 717 1018 973 691 +Q 1229 365 1664 365 +Q 2016 365 2256 637 +Q 2496 909 2496 1325 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.pdf index db909218ff05..46802c7c6245 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.png index d05e3efecdbb..bc3486d66699 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.svg index d8fdb0a86641..6a2399c02376 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_81.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.627753 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,102 +26,109 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - + - + - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.pdf index 085487d51e59..9fe4914fc40f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.png index 7921480555f3..474ab1e76dcc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.svg index 5b7bab5501a8..a6d5cf7c5640 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_82.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-03-12T18:23:44.670579 + image/svg+xml + + + Matplotlib v3.11.0.dev2071+g094de868c8, https://matplotlib.org/ + + + + + - + @@ -15,132 +26,140 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.pdf b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.pdf index 22e75bb9b0a3..0ac6cacd7ce8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.pdf and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.png index c23070cdf8b9..106a5adb8b71 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.png and b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.svg index 97c40174b3ef..0dc90f4241f4 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_83.svg @@ -6,11 +6,11 @@ - 2024-05-08T19:52:33.020611 + 2026-03-12T19:45:30.851295 image/svg+xml - Matplotlib v3.10.0.dev150+gec4808956b.d20240508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -30,9 +30,9 @@ z - + - - - - - - - - - - - + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/bivariate_cmap_shapes.png b/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/bivariate_cmap_shapes.png new file mode 100644 index 000000000000..8553bf61700c Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/bivariate_cmap_shapes.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/multivar_alpha_mixing.png b/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/multivar_alpha_mixing.png new file mode 100644 index 000000000000..415dfda291dc Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_multivariate_colormaps/multivar_alpha_mixing.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_offsetbox/anchoredtext_align.png b/lib/matplotlib/tests/baseline_images/test_offsetbox/anchoredtext_align.png index 42a80e17b068..6aba9c0e604e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_offsetbox/anchoredtext_align.png and b/lib/matplotlib/tests/baseline_images/test_offsetbox/anchoredtext_align.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_offsetbox/paddedbox.png b/lib/matplotlib/tests/baseline_images/test_offsetbox/paddedbox.png index dfb68e2c35a1..75a3bc8b3f09 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_offsetbox/paddedbox.png and b/lib/matplotlib/tests/baseline_images/test_offsetbox/paddedbox.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/annulus.png b/lib/matplotlib/tests/baseline_images/test_patches/annulus.png index 549443f523cf..c5c508d90358 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/annulus.png and b/lib/matplotlib/tests/baseline_images/test_patches/annulus.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.pdf b/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.pdf deleted file mode 100644 index ecff2574e0a5..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.png b/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.png index 1883ccaa82c0..425f2f6fe68d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.png and b/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.svg b/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.svg deleted file mode 100644 index c5f152be9748..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_patches/clip_to_bbox.svg +++ /dev/null @@ -1,498 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_patches/multi_color_hatch.pdf b/lib/matplotlib/tests/baseline_images/test_patches/multi_color_hatch.pdf index e956cbdf248d..a9db7c30998e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/multi_color_hatch.pdf and b/lib/matplotlib/tests/baseline_images/test_patches/multi_color_hatch.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/patch_edgegapcolor.png b/lib/matplotlib/tests/baseline_images/test_patches/patch_edgegapcolor.png new file mode 100644 index 000000000000..a3738e3f7973 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_patches/patch_edgegapcolor.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patches/units_rectangle.png b/lib/matplotlib/tests/baseline_images/test_patches/units_rectangle.png index 651e1e2ef95a..6a77e85c6202 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/units_rectangle.png and b/lib/matplotlib/tests/baseline_images/test_patches/units_rectangle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_path/semi_log_with_zero.png b/lib/matplotlib/tests/baseline_images/test_path/semi_log_with_zero.png index 842b4027dbf3..4d08273968c6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_path/semi_log_with_zero.png and b/lib/matplotlib/tests/baseline_images/test_path/semi_log_with_zero.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.pdf b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.pdf index f7364954ee90..85a9cbb902c9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.pdf and b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.png b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.png index eea0410fd02a..4a736ffd0ba0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.svg b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.svg index 2585026e247b..1c239cfbb616 100644 --- a/lib/matplotlib/tests/baseline_images/test_patheffects/collection.svg +++ b/lib/matplotlib/tests/baseline_images/test_patheffects/collection.svg @@ -6,11 +6,11 @@ - 2023-05-08T08:29:00.655917 + 2026-03-12T19:46:18.815368 image/svg+xml - Matplotlib v3.8.0.dev1016+gecc2e28867.d20230508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -41,19 +41,19 @@ z - - + - + - - + - + - + - - - + + - + - + - - - + + - + - + - - - + + - + - + - - - + + - + - + - - - - + + + - + - - - - + + + + - + - - - - + + + + @@ -321,81 +321,81 @@ z - - + - - + + - + - - - + + + - + - - - + + + - + - - - + + + - + - - - + + + - + +L 215.787383 64.319214 +L 218.184161 63.795361 +L 221.675522 63.299341 +L 221.675522 63.299341 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> +L 196.613154 124.117176 +L 191.819597 121.131597 +L 189.350385 119.424 +L 184.629262 115.698441 +L 182.232483 113.575068 +L 179.835705 111.235911 +L 177.438926 108.591711 +L 175.042148 105.476764 +L 173.590886 103.296 +L 172.132432 100.608 +L 171.019708 97.92 +L 170.248591 95.162288 +L 169.858365 92.544 +L 169.821286 89.856 +L 170.248591 86.775129 +L 170.836039 84.48 +L 171.877922 81.792 +L 173.26639 79.104 +L 175.042148 76.347053 +L 177.039421 73.737357 +L 177.039421 73.737357 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> +L 283.576692 203.728468 +L 283.576692 203.728468 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> +L 314.501078 138.371518 +L 314.501078 138.371518 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> - +L 311.365342 193.020596 +L 311.365342 193.020596 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #000000; stroke-width: 12; stroke-linejoin: miter"/> - + - + +L 215.787383 64.319214 +L 218.184161 63.795361 +L 221.675522 63.299341 +L 221.675522 63.299341 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #472d7b; stroke-width: 5"/> +L 196.613154 124.117176 +L 191.819597 121.131597 +L 189.350385 119.424 +L 184.629262 115.698441 +L 182.232483 113.575068 +L 179.835705 111.235911 +L 177.438926 108.591711 +L 175.042148 105.476764 +L 173.590886 103.296 +L 172.132432 100.608 +L 171.019708 97.92 +L 170.248591 95.162288 +L 169.858365 92.544 +L 169.821286 89.856 +L 170.248591 86.775129 +L 170.836039 84.48 +L 171.877922 81.792 +L 173.26639 79.104 +L 175.042148 76.347053 +L 177.039421 73.737357 +L 177.039421 73.737357 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #3b528b; stroke-width: 5"/> +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #2c728e; stroke-width: 5"/> +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #21918c; stroke-width: 5"/> +L 283.576692 203.728468 +L 283.576692 203.728468 +" clip-path="url(#pa57bc01b0d)" style="fill: none; stroke: #28ae80; stroke-width: 5"/> - + - - - + + + + - - - + - - - + - - - + + + + + - - - - - - - - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + + + - - - - - + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.pdf b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.pdf index 45fd3b5b2b88..8f07a0286e89 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.pdf and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.png b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.png index 3cf4ce936c22..7c1de45bb001 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.svg b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.svg index 0970ea5da48f..add8a1c66f0d 100644 --- a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.svg +++ b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect1.svg @@ -1,466 +1,563 @@ - - + + + + + + 2026-04-02T23:50:08.678879 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - + + - - - - - - - - + + - + - + - - - - - + + - + - - - - - + + - + - - - - - + + - + - - + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - + + + - + - + - + - - - + + + + + - + - + + + + + + + - + - - - - + + + + + + - + - + + + + + + + - + - - - - + + + + + + - + - + + + + + + + - + - - - - + + + + + + - + - + + + + + + + - + + + + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.pdf b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.pdf index ba027d57a34b..004498217ff4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.pdf and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.png b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.png index af91778e7d80..d9f0aebd4c4e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.svg b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.svg index 08e4a9a6f08c..006b5efdd173 100644 --- a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.svg +++ b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect2.svg @@ -6,11 +6,11 @@ - 2023-05-08T08:28:59.785389 + 2026-03-12T19:46:17.436336 image/svg+xml - Matplotlib v3.8.0.dev1016+gecc2e28867.d20230508, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ @@ -37,48 +37,48 @@ L 103.104 41.472 z " style="fill: #ffffff"/> - + +iVBORw0KGgoAAAANSUhEUgAAAXIAAAFxCAYAAABusCOnAAAF0ElEQVR4nO3WQWpTURiG4aa9WtNrUnBgUehOnLlBhy7GFQidavcg0oEghKhxCQYc/LzwPCv4OHBe/s2HL+9PF/zT/fPv0xMS3i5P0xMS7q8O0xMS3iwvpyckXE4PAOD/CDlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPELR8f301vSNhtD9MTEl5tf05PSLjb/piekPD62judw0UOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOECfkAHFCDhAn5ABxQg4QJ+QAcUIOELdcPNxOb0h4Wk/TExK+3Xinc3zd/pmekHC5HqcnJLjIAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKWu8+H6Q0Jx90yPSHheLOZnpBwXK+mJyT8Wv27c7jIAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIE7IAeKEHCBOyAHihBwgTsgB4oQcIG559ulhekPCi/1+ekLC5tY7neO0X6cnJPzeXU9PSHCRA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8QJOUCckAPECTlAnJADxAk5QJyQA8T9BWBuIIhTG3alAAAAAElFTkSuQmCC" id="imageb9ceb3ecce" transform="scale(1 -1) translate(0 -265.68)" x="102.96" y="-41.76" width="266.4" height="265.68"/> - - + - + - + - + - + @@ -87,40 +87,40 @@ L 0 3.5 - - + - + - + - + - + @@ -129,665 +129,665 @@ L -3.5 0 +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#p5b2047cc90)" style="fill: none; stroke: #ffffff; stroke-width: 3"/> - - - - + + + + + - - - + - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.pdf b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.pdf index ad98ff7223cc..8b7e7fe34a1c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.pdf and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.png b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.png index 879f6ab9f047..464c979dc68d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.svg b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.svg index dffe6d6521fd..0d7f44803a2e 100644 --- a/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.svg +++ b/lib/matplotlib/tests/baseline_images/test_patheffects/patheffect3.svg @@ -1,12 +1,23 @@ - - + + + + + + 2026-04-03T01:40:11.711543 + image/svg+xml + + + Matplotlib v3.11.0.dev2224+gb5b8511fb.d20260403, https://matplotlib.org/ + + + + + - + @@ -15,2457 +26,2274 @@ L 576 432 L 576 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - - - - - - - - + + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - - - - + - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - +" style="fill: #000000; stroke: #000000; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/> + + diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/spaces_and_newlines.png b/lib/matplotlib/tests/baseline_images/test_patheffects/spaces_and_newlines.png index 9dd4c1e8d7ff..49ea1bceae2a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/spaces_and_newlines.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/spaces_and_newlines.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/stroked_text.png b/lib/matplotlib/tests/baseline_images/test_patheffects/stroked_text.png index 3119754358de..ba32054f74b0 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/stroked_text.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/stroked_text.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_patheffects/tickedstroke.png b/lib/matplotlib/tests/baseline_images/test_patheffects/tickedstroke.png index 5884d46dc1ce..e6a860f09bb4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patheffects/tickedstroke.png and b/lib/matplotlib/tests/baseline_images/test_patheffects/tickedstroke.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png index e8ba8c51be42..73b3641a012b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png and b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png index e979e7ebb6b8..494341cee745 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.pdf deleted file mode 100644 index f56b04c4d3a6..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.png index e6919fa4e988..e42f92b91e8b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.svg deleted file mode 100644 index cf8b876685f7..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_axes.svg +++ /dev/null @@ -1,1250 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.pdf deleted file mode 100644 index c9fa69334e88..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.png index 6d5aa60ab3d2..fb714f23cfa1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.svg deleted file mode 100644 index a2b3a95d136b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_coords.svg +++ /dev/null @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_errorbar.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_errorbar.png new file mode 100644 index 000000000000..6b587a78ae10 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_polar/polar_errorbar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim.png index 43052ab40df2..ec18e6963fa9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim_rorigin.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim_rorigin.png index 33833d72fed5..3966098fe0ba 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim_rorigin.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_invertedylim_rorigin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png index ab8e20b482a5..bd7a2e500af6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.pdf deleted file mode 100644 index aa98d5bb8cf1..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.png index 98cb695492b3..25ceedf4a320 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.svg deleted file mode 100644 index 47dd9d1a6792..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_negative_rmin.svg +++ /dev/null @@ -1,973 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.pdf deleted file mode 100644 index 8e41309e1736..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.png index c74833e55d27..fb9e0e9cfe0e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.svg deleted file mode 100644 index 07c6695f7790..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_rlabel_position.svg +++ /dev/null @@ -1,687 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.pdf deleted file mode 100644 index 8bcbd4b17bcc..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.png index 3ecf06a2d7bf..af81ffa2b694 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.svg deleted file mode 100644 index 347ae67589e6..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_rmin.svg +++ /dev/null @@ -1,1012 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.pdf deleted file mode 100644 index 962f95a40d4f..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.png index b25c24c33171..a7bae8c8a5a6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.svg deleted file mode 100644 index 2048cb1e7f73..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_rorigin.svg +++ /dev/null @@ -1,1074 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.pdf deleted file mode 100644 index f132d8f33262..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.png index 1d536ba4b40d..ab39839478a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.svg deleted file mode 100644 index 8dfbb7a36482..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_position.svg +++ /dev/null @@ -1,960 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.pdf b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.pdf deleted file mode 100644 index 0a7c328ac737..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.png index d603c3f6e2b1..1c16520ea2e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.svg b/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.svg deleted file mode 100644 index d45bf67a02a9..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_polar/polar_theta_wedge.svg +++ /dev/null @@ -1,6558 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_title_position.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_title_position.png new file mode 100644 index 000000000000..b5036e8c6557 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_polar/polar_title_position.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_animated_test_image.png b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_animated_test_image.png index 247c1e86d0be..a7a5ea635c17 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_animated_test_image.png and b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_animated_test_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_pivot.png b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_pivot.png index 4763bd26d841..84d7d66956e8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_pivot.png and b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_pivot.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_with_key_test_image.png b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_with_key_test_image.png index fbeb7f952b1f..4f091b5ef720 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_with_key_test_image.png and b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_with_key_test_image.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.pdf b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.pdf index 826bbccb24cd..1137366d7776 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.pdf and b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.png b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.png index 2691212622ce..d0fb8869224f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.png and b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.svg b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.svg index fbe862667424..2cbfe69c2370 100644 --- a/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.svg +++ b/lib/matplotlib/tests/baseline_images/test_simplification/clipping_with_nans.svg @@ -1,542 +1,467 @@ - - + + + + + + 2026-04-03T00:06:35.968845 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - + - - - - - + + + + + - - - - - - + - + - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - + - + - - - - - + + + + + - - - - - - + - + - - - - - + + + + + - - - - - - + - + - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - + - + - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - + + - - - +M 3366 4563 +L 3366 3988 +Q 3128 4100 2886 4159 +Q 2644 4219 2406 4219 +Q 1781 4219 1451 3797 +Q 1122 3375 1075 2522 +Q 1259 2794 1537 2939 +Q 1816 3084 2150 3084 +Q 2853 3084 3261 2657 +Q 3669 2231 3669 1497 +Q 3669 778 3244 343 +Q 2819 -91 2113 -91 +Q 1303 -91 875 529 +Q 447 1150 447 2328 +Q 447 3434 972 4092 +Q 1497 4750 2381 4750 +Q 2619 4750 2861 4703 +Q 3103 4656 3366 4563 +z +" transform="scale(0.015625)"/> + + - - - - - - - - - + - + - + - + - - + + - + - - - - - - +" transform="scale(0.015625)"/> + + + + + - - - - - - + - + - + - - - - - + + + + + - - - - - - + - + - + - - - - + + + + - - - - - - + - + - + - - - - + + + + - - - - - - + - + - + - - - - + + + + + + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.pdf b/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.pdf deleted file mode 100644 index affd68412e62..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.svg b/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.svg deleted file mode 100644 index d5410d62d7b2..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_skew/skew_axes.svg +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf deleted file mode 100644 index c16fc9c2d916..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg deleted file mode 100644 index ff18e2b4df0b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg +++ /dev/null @@ -1,5368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_spines/black_axes.pdf b/lib/matplotlib/tests/baseline_images/test_spines/black_axes.pdf deleted file mode 100644 index 96eacb9308d9..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/black_axes.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/black_axes.svg b/lib/matplotlib/tests/baseline_images/test_spines/black_axes.svg deleted file mode 100644 index 8f7ff932859d..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_spines/black_axes.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - 2022-10-24T17:29:00.034663 - image/svg+xml - - - Matplotlib v3.6.0.dev4027+g68c78c9fb1.d20221024, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.pdf b/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.pdf deleted file mode 100644 index 309a299edb40..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.png b/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.png index b5b5301e841a..0a23345be2f7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.png and b/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.svg b/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.svg deleted file mode 100644 index 3893e172bf78..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_spines/spines_axes_positions.svg +++ /dev/null @@ -1,819 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.pdf b/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.pdf deleted file mode 100644 index f596d08ce38d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.svg b/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.svg deleted file mode 100644 index c241158c7fd9..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_spines/spines_capstyle.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.pdf b/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.pdf deleted file mode 100644 index 0819ac3993c4..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.png b/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.png index dfcd600c52e0..41119648636d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.png and b/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.svg b/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.svg deleted file mode 100644 index 86a79b5fe89d..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_spines/spines_data_positions.svg +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.pdf b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.pdf deleted file mode 100644 index 1b29bdcd1fc3..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.svg b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.svg deleted file mode 100644 index a95118c95d97..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_colormap.svg +++ /dev/null @@ -1,2647 +0,0 @@ - - - - - - - - 2021-08-18T03:11:20.912289 - image/svg+xml - - - Matplotlib v3.4.2.post1692+gb0554f4824.d20210818, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_integration.png b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_integration.png new file mode 100644 index 000000000000..73730ea5a653 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_integration.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.pdf b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.pdf deleted file mode 100644 index 1b14d45c3059..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.png b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.png index b928223c3206..13dafd199523 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.png and b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.svg b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.svg deleted file mode 100644 index 2a2bec622027..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_linewidth.svg +++ /dev/null @@ -1,3098 +0,0 @@ - - - - - - - - 2021-08-18T03:24:40.872884 - image/svg+xml - - - Matplotlib v3.4.2.post1692+gb0554f4824.d20210818, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.pdf b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.pdf deleted file mode 100644 index 7d6beca0b3ac..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.svg b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.svg deleted file mode 100644 index 17e4c78c2490..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_masks_and_nans.svg +++ /dev/null @@ -1,3845 +0,0 @@ - - - - - - - - 2021-08-18T03:08:55.311923 - image/svg+xml - - - Matplotlib v3.4.2.post1692+gb0554f4824, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_startpoints.png b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_startpoints.png index 0a91d0ddedd1..7823b33770b5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_startpoints.png and b/lib/matplotlib/tests/baseline_images/test_streamplot/streamplot_startpoints.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.pdf b/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.pdf deleted file mode 100644 index 5fdc39730c4f..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.png b/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.png index 4f540d280965..9182b1fa2f6c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.png and b/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.svg b/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.svg deleted file mode 100644 index 0c231c4d5c25..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_subplots/subplots_offset_text.svg +++ /dev/null @@ -1,1774 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_table/table_auto_column.png b/lib/matplotlib/tests/baseline_images/test_table/table_auto_column.png index 6dc11a67464e..2ff5c6b39812 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_table/table_auto_column.png and b/lib/matplotlib/tests/baseline_images/test_table/table_auto_column.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_table/table_cell_manipulation.png b/lib/matplotlib/tests/baseline_images/test_table/table_cell_manipulation.png index 778e57802982..ee2558b3ebc6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_table/table_cell_manipulation.png and b/lib/matplotlib/tests/baseline_images/test_table/table_cell_manipulation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_table/table_labels.png b/lib/matplotlib/tests/baseline_images/test_table/table_labels.png index f59acb1a4a33..b8490c7f9973 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_table/table_labels.png and b/lib/matplotlib/tests/baseline_images/test_table/table_labels.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_table/table_zorder.png b/lib/matplotlib/tests/baseline_images/test_table/table_zorder.png index 025ea5e0c4d2..e06fbda8785f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_table/table_zorder.png and b/lib/matplotlib/tests/baseline_images/test_table/table_zorder.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/agg_text_clip.png b/lib/matplotlib/tests/baseline_images/test_text/agg_text_clip.png index 25f47cec6128..408e5f2aaa64 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/agg_text_clip.png and b/lib/matplotlib/tests/baseline_images/test_text/agg_text_clip.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_ax_coords.png b/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_ax_coords.png index 06eb1644c892..ec2549e939dc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_ax_coords.png and b/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_ax_coords.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_fig_coords.png b/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_fig_coords.png index b58de77166d3..42d88450b580 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_fig_coords.png and b/lib/matplotlib/tests/baseline_images/test_text/annotation_negative_fig_coords.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/antialiased.png b/lib/matplotlib/tests/baseline_images/test_text/antialiased.png index 06c007591d96..131e5e878b43 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/antialiased.png and b/lib/matplotlib/tests/baseline_images/test_text/antialiased.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/axes_titles.png b/lib/matplotlib/tests/baseline_images/test_text/axes_titles.png index 496d50228e83..631872c9fac2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/axes_titles.png and b/lib/matplotlib/tests/baseline_images/test_text/axes_titles.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/basictext_wrap.png b/lib/matplotlib/tests/baseline_images/test_text/basictext_wrap.png index b041afb8b7b8..da6c0c93df86 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/basictext_wrap.png and b/lib/matplotlib/tests/baseline_images/test_text/basictext_wrap.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/complex.eps b/lib/matplotlib/tests/baseline_images/test_text/complex.eps new file mode 100644 index 000000000000..947226f5201c --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/complex.eps @@ -0,0 +1,864 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 +%%Title: complex.eps +%%Creator: Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ +%%CreationDate: Fri Oct 3 03:14:10 2025 +%%Orientation: portrait +%%BoundingBox: 0 0 432 144 +%%HiResBoundingBox: 0.000000 0.000000 432.000000 144.000000 +%%EndComments +%%BeginProlog +/mpldict 13 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/A /r /a /b /i /c /colon /space] def +/CharStrings 9 dict dup begin +/.notdef 0 def +/A{1401 0 16 0 1384 1493 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +ce} _d +/r{842 0 186 0 842 1147 sc +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/a{1255 0 123 -29 1069 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/b{1300 0 186 -29 1188 1556 sc +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +371 950 m +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 950 l + +ce} _d +/i{569 0 193 0 377 1556 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/c{1126 0 113 -29 999 1147 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +ce} _d +/colon{690 0 240 0 451 1059 sc +240 254 m +451 254 l +451 0 l +240 0 l +240 254 l + +240 1059 m +451 1059 l +451 805 l +240 805 l +240 1059 l + +ce} _d +/space{651 0 0 0 0 0 sc +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/product /uni2210 /summation /integral /uniFEE2 /uni0652 /uniFED7 /uni064E /uni0631] def +/CharStrings 10 dict dup begin +/.notdef 0 def +/product{1550 0 156 -393 1393 1473 sc +156 1473 m +1393 1473 l +1393 -393 l +1153 -393 l +1153 1268 l +395 1268 l +395 -393 l +156 -393 l +156 1473 l + +ce} _d +/uni2210{1550 0 156 -393 1393 1473 sc +156 -393 m +156 1473 l +395 1473 l +395 -188 l +1153 -188 l +1153 1473 l +1393 1473 l +1393 -393 l +156 -393 l + +ce} _d +/summation{1380 0 25 -393 1339 1473 sc +55 1473 m +1313 1473 l +1313 1280 l +354 1280 l +1026 563 l +332 -201 l +1339 -201 l +1339 -393 l +25 -393 l +25 -244 l +750 557 l +55 1296 l +55 1473 l + +ce} _d +/integral{1067 0 117 -435 950 1550 sc +483 1250 m +488 1361 518 1439 572 1483 c +626 1528 678 1550 728 1550 c +779 1550 825 1529 867 1487 c +909 1446 937 1378 950 1285 c +802 1270 l +791 1357 766 1400 728 1400 c +681 1400 654 1342 649 1225 c +584 -135 l +579 -246 549 -324 495 -368 c +441 -413 389 -435 338 -435 c +288 -435 242 -414 200 -372 c +158 -331 130 -263 117 -170 c +265 -155 l +276 -242 301 -285 339 -285 c +386 -285 413 -227 418 -110 c +483 1250 l + +ce} _d +/uniFEE2{1363 0 140 -492 1383 628 sc +610 168 m +691 140 757 126 808 126 c +837 126 859 133 873 148 c +908 186 926 224 926 263 c +926 279 924 294 920 307 c +906 366 879 401 840 412 c +813 420 785 424 754 424 c +715 424 686 415 668 397 c +623 352 600 306 600 261 c +600 234 603 203 610 168 c + +1048 52 m +1038 43 1029 35 1020 30 c +931 -23 862 -50 812 -50 c +691 -50 584 -24 490 28 c +457 47 415 30 365 -22 c +348 -39 340 -74 340 -127 c +340 -492 l +140 -492 l +140 -127 l +140 -12 180 80 260 148 c +303 185 355 203 415 203 c +414 231 413 258 413 285 c +413 384 467 477 575 565 c +626 607 683 628 744 628 c +792 628 844 616 900 592 c +1013 545 1083 461 1110 342 c +1120 297 1138 260 1165 231 c +1190 202 1238 187 1308 187 c +1383 187 l +1383 3 l +1253 3 l +1136 3 1068 19 1048 52 c + +ce} _d +/uni0652{0 0 236 1249 784 1798 sc +662 1524 m +662 1566 647 1602 618 1631 c +589 1660 553 1675 510 1675 c +467 1675 430 1660 401 1631 c +372 1602 358 1567 358 1524 c +358 1481 372 1444 401 1415 c +430 1386 467 1372 510 1372 c +553 1372 589 1387 618 1416 c +647 1445 662 1481 662 1524 c + +704 1718 m +757 1665 784 1601 784 1524 c +784 1447 757 1382 704 1329 c +651 1276 587 1249 510 1249 c +433 1249 368 1276 315 1329 c +262 1382 236 1447 236 1524 c +236 1601 262 1665 315 1718 c +368 1771 433 1798 510 1798 c +587 1798 651 1771 704 1718 c + +ce} _d +/uniFED7{979 0 -20 0 831 1300 sc +598 537 m +625 566 639 606 639 657 c +639 696 617 733 573 767 c +552 784 527 792 498 791 c +463 790 433 776 406 748 c +379 721 366 690 365 656 c +365 603 383 563 420 538 c +447 521 474 513 502 513 c +550 513 582 521 598 537 c + +-20 184 m +221 184 l +277 184 355 192 456 207 c +509 215 555 250 595 312 c +616 344 631 377 641 410 c +599 373 546 354 481 353 c +394 352 325 374 273 420 c +208 478 175 550 175 636 c +175 671 178 703 183 732 c +198 831 256 903 356 949 c +408 973 459 985 510 985 c +570 985 622 968 666 935 c +730 887 777 829 808 761 c +823 727 831 663 831 570 c +831 431 805 314 753 219 c +704 128 638 68 555 39 c +482 13 398 0 301 0 c +-20 0 l +-20 184 l + +550 1300 m +700 1300 l +700 1150 l +550 1150 l +550 1300 l + +300 1300 m +450 1300 l +450 1150 l +300 1150 l +300 1300 l + +ce} _d +/uni064E{0 0 220 1210 804 1450 sc +220 1210 m +220 1320 l +804 1450 l +804 1340 l +220 1210 l + +ce} _d +/uni0631{989 0 -85 -500 866 550 sc +675 158 m +682 197 685 240 685 288 c +685 372 667 459 632 550 c +816 550 l +849 471 866 388 866 300 c +866 245 863 197 858 156 c +827 -77 715 -246 521 -349 c +332 -450 130 -500 -85 -500 c +-85 -316 l +118 -316 287 -273 422 -188 c +567 -96 652 19 675 158 c + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSansDisplay-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-93 -948 3673 2524] def +/FontType 3 def +/Encoding [/product /uni2210 /summation /integral] def +/CharStrings 5 dict dup begin +/.notdef 0 def +/product{2192 0 221 -556 1970 2083 sc +221 2083 m +1970 2083 l +1970 -556 l +1631 -556 l +1631 1793 l +559 1793 l +559 -556 l +221 -556 l +221 2083 l + +ce} _d +/uni2210{2192 0 221 -556 1970 2083 sc +221 -556 m +221 2083 l +559 2083 l +559 -266 l +1631 -266 l +1631 2083 l +1970 2083 l +1970 -556 l +221 -556 l + +ce} _d +/summation{1951 0 35 -556 1894 2083 sc +78 2083 m +1857 2083 l +1857 1810 l +501 1810 l +1451 796 l +470 -284 l +1894 -284 l +1894 -556 l +35 -556 l +35 -345 l +1061 788 l +78 1833 l +78 2083 l + +ce} _d +/integral{1508 0 165 -615 1344 2192 sc +683 1768 m +691 1925 733 2035 809 2098 c +885 2161 959 2192 1030 2192 c +1101 2192 1167 2163 1226 2104 c +1286 2045 1325 1950 1344 1817 c +1134 1796 l +1118 1919 1083 1980 1030 1980 c +963 1980 926 1897 918 1732 c +826 -191 l +819 -348 777 -458 700 -521 c +623 -584 550 -615 479 -615 c +408 -615 342 -586 282 -527 c +223 -468 184 -373 165 -240 c +375 -219 l +391 -342 426 -403 479 -403 c +546 -403 583 -321 591 -156 c +683 1768 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /Cmr10-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-90 -512 2066 1536] def +/FontType 3 def +/Encoding [/A /r /a /b /i /c /colon /space] def +/CharStrings 9 dict dup begin +/.notdef 0 def +/A{1536 0 66 0 1468 1466 sc +66 0 m +66 72 l +188 72 263 112 291 193 c +721 1444 l +725 1459 736 1466 754 1466 c +780 1466 l +798 1466 809 1459 813 1444 c +1262 137 l +1275 108 1299 90 1334 83 c +1370 76 1415 72 1468 72 c +1468 0 l +897 0 l +897 72 l +1010 72 1067 90 1067 127 c +1067 137 l +956 457 l +459 457 l +367 193 l +366 188 365 181 365 172 c +365 138 381 113 413 96 c +446 80 481 72 518 72 c +518 0 l +66 0 l + +483 528 m +932 528 l +707 1182 l +483 528 l + +ce} _d +/r{801 0 53 0 745 905 sc +53 0 m +53 72 l +100 72 138 76 168 83 c +198 90 213 108 213 137 c +213 696 l +213 733 207 759 196 775 c +185 792 170 802 149 805 c +128 809 96 811 53 811 c +53 883 l +346 905 l +346 705 l +368 764 399 812 440 849 c +481 886 530 905 588 905 c +629 905 665 893 697 869 c +729 845 745 813 745 774 c +745 749 736 728 718 709 c +701 691 679 682 653 682 c +628 682 606 691 588 709 c +570 727 561 749 561 774 c +561 811 574 837 600 852 c +588 852 l +533 852 487 832 452 792 c +417 752 393 702 378 643 c +363 584 356 527 356 473 c +356 137 l +356 94 422 72 555 72 c +555 0 l +53 0 l + +ce} _d +/a{1024 0 82 -23 1010 918 sc +82 201 m +82 282 114 348 178 399 c +242 450 319 486 408 507 c +498 528 583 539 664 539 c +664 623 l +664 662 655 700 638 737 c +621 774 596 805 563 828 c +530 852 494 864 455 864 c +364 864 295 844 248 803 c +274 803 295 793 312 773 c +329 754 338 731 338 705 c +338 678 328 654 309 635 c +290 616 267 606 240 606 c +213 606 189 616 170 635 c +151 654 141 678 141 705 c +141 777 174 830 239 865 c +304 900 376 918 455 918 c +510 918 566 906 622 882 c +678 859 724 825 759 781 c +795 737 813 686 813 627 c +813 166 l +813 139 819 115 830 92 c +841 70 859 59 883 59 c +906 59 922 70 933 93 c +944 116 950 140 950 166 c +950 297 l +1010 297 l +1010 166 l +1010 135 1002 106 986 78 c +970 51 948 29 921 12 c +894 -4 865 -12 834 -12 c +794 -12 759 3 730 34 c +701 65 685 102 682 145 c +657 94 619 53 570 22 c +521 -8 468 -23 412 -23 c +360 -23 309 -15 258 0 c +208 15 166 39 132 72 c +99 105 82 148 82 201 c + +248 201 m +248 153 266 113 301 80 c +336 47 378 31 426 31 c +470 31 510 42 546 64 c +582 86 611 116 632 154 c +653 192 664 232 664 274 c +664 487 l +602 487 538 477 473 456 c +408 436 355 404 312 361 c +269 318 248 264 248 201 c + +ce} _d +/b{1137 0 53 -23 1069 1421 sc +213 0 m +213 1212 l +213 1249 207 1275 196 1291 c +185 1308 170 1318 149 1321 c +128 1325 96 1327 53 1327 c +53 1399 l +356 1421 l +356 780 l +379 805 405 827 435 846 c +466 865 498 880 533 890 c +568 900 603 905 639 905 c +700 905 757 893 809 868 c +862 843 907 809 946 766 c +985 723 1015 673 1036 616 c +1058 560 1069 502 1069 442 c +1069 359 1049 282 1008 211 c +968 140 913 83 843 40 c +774 -2 697 -23 614 -23 c +562 -23 512 -10 463 17 c +414 44 374 79 342 123 c +272 0 l +213 0 l + +362 201 m +385 151 417 110 460 78 c +503 47 550 31 602 31 c +673 31 730 51 773 92 c +817 133 848 184 865 246 c +882 308 891 373 891 442 c +891 557 876 645 846 705 c +833 732 814 756 791 779 c +768 802 743 819 714 832 c +686 845 656 852 625 852 c +570 852 520 837 473 808 c +426 779 389 741 362 692 c +362 201 l + +ce} _d +/i{567 0 63 0 510 1370 sc +63 0 m +63 72 l +110 72 148 76 178 83 c +208 90 223 108 223 137 c +223 696 l +223 749 213 781 192 793 c +172 805 132 811 72 811 c +72 883 l +367 905 l +367 137 l +367 108 380 90 406 83 c +432 76 467 72 510 72 c +510 0 l +63 0 l + +150 1257 m +150 1287 161 1313 184 1336 c +207 1359 233 1370 262 1370 c +281 1370 300 1365 318 1355 c +336 1345 350 1331 360 1313 c +370 1295 375 1276 375 1257 c +375 1228 364 1202 341 1179 c +318 1156 292 1145 262 1145 c +233 1145 207 1156 184 1179 c +161 1202 150 1228 150 1257 c + +ce} _d +/c{909 0 68 -23 850 918 sc +510 -23 m +427 -23 352 -2 285 41 c +218 84 165 142 126 213 c +87 285 68 361 68 442 c +68 523 87 600 125 674 c +164 748 217 807 284 851 c +352 896 427 918 510 918 c +590 918 663 902 728 871 c +794 840 827 788 827 717 c +827 690 817 667 798 647 c +779 628 756 618 729 618 c +702 618 678 628 659 647 c +640 667 631 690 631 717 c +631 741 639 762 654 779 c +669 797 688 808 711 813 c +664 843 597 858 512 858 c +447 858 394 836 354 793 c +314 750 286 696 270 632 c +254 568 246 505 246 442 c +246 376 256 312 275 250 c +295 189 327 138 370 97 c +414 57 469 37 535 37 c +600 37 655 57 700 96 c +745 136 776 188 793 252 c +793 260 798 264 809 264 c +834 264 l +838 264 842 262 845 258 c +848 255 850 251 850 246 c +850 240 l +829 159 788 95 727 48 c +666 1 593 -23 510 -23 c + +ce} _d +/colon{567 0 172 0 397 883 sc +172 113 m +172 144 183 170 206 192 c +229 214 255 225 285 225 c +304 225 322 220 340 210 c +358 200 372 186 382 168 c +392 150 397 132 397 113 c +397 83 386 57 364 34 c +342 11 316 0 285 0 c +255 0 229 11 206 34 c +183 57 172 83 172 113 c + +172 770 m +172 789 177 808 187 825 c +197 842 211 856 228 867 c +246 878 265 883 285 883 c +304 883 323 878 340 867 c +358 856 372 842 382 825 c +392 808 397 789 397 770 c +397 739 386 713 364 690 c +343 668 316 657 285 657 c +254 657 228 668 205 690 c +183 713 172 739 172 770 c + +ce} _d +/space{682 0 0 0 0 0 sc +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +0 0 translate +0 0 432 144 rectclip +gsave +0 0 m +432 0 l +432 144 l +0 144 l +cl +1 setgray +fill +grestore +0 setgray +/DejaVuSans-1 32.000 selectfont +gsave + +42.1016 96.6875 translate +0 rotate +0 0 m /product glyphshow +24.2188 0 m /uni2210 glyphshow +48.4375 0 m /summation glyphshow +70 0 m /integral glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +42.1016 96.6875 translate +0 rotate +86.6719 0 m /A glyphshow +108.562 0 m /r glyphshow +121.719 0 m /a glyphshow +141.328 0 m /b glyphshow +161.641 0 m /i glyphshow +170.531 0 m /c glyphshow +188.125 0 m /colon glyphshow +198.906 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +42.1016 96.6875 translate +0 rotate +209.078 0 m /uniFEE2 glyphshow +230.188 2.34375 m /uni0652 glyphshow +230.375 0 m /uniFED7 glyphshow +248.609 -7.03125 m /uni064E glyphshow +245.672 0 m /uni0631 glyphshow +261.125 0 m /product glyphshow +285.344 0 m /uni2210 glyphshow +309.562 0 m /summation glyphshow +331.125 0 m /integral glyphshow +grestore +/DejaVuSansDisplay-1 32.000 selectfont +gsave + +11 23.6797 translate +0 rotate +0 0 m /product glyphshow +34.25 0 m /uni2210 glyphshow +68.5 0 m /summation glyphshow +98.9844 0 m /integral glyphshow +grestore +/Cmr10-0 32.000 selectfont +gsave + +11 23.6797 translate +0 rotate +122.547 0 m /A glyphshow +146.547 0 m /r glyphshow +159.062 0 m /a glyphshow +175.062 0 m /b glyphshow +192.828 0 m /i glyphshow +201.688 0 m /c glyphshow +215.891 0 m /colon glyphshow +224.75 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +11 23.6797 translate +0 rotate +235.406 0 m /uniFEE2 glyphshow +256.516 2.34375 m /uni0652 glyphshow +256.703 0 m /uniFED7 glyphshow +274.938 -7.03125 m /uni064E glyphshow +272 0 m /uni0631 glyphshow +grestore +/DejaVuSansDisplay-1 32.000 selectfont +gsave + +11 23.6797 translate +0 rotate +287.453 0 m /product glyphshow +321.703 0 m /uni2210 glyphshow +355.953 0 m /summation glyphshow +386.438 0 m /integral glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_text/complex.pdf b/lib/matplotlib/tests/baseline_images/test_text/complex.pdf new file mode 100644 index 000000000000..edc34ea6bc36 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/complex.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/complex.png b/lib/matplotlib/tests/baseline_images/test_text/complex.png new file mode 100644 index 000000000000..63efc96ad8de Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/complex.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/complex.svg b/lib/matplotlib/tests/baseline_images/test_text/complex.svg new file mode 100644 index 000000000000..32496372d2a2 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/complex.svg @@ -0,0 +1,693 @@ + + + + + + + + 2025-10-03T03:14:10.620956 + image/svg+xml + + + Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/draw_text_fallback.png b/lib/matplotlib/tests/baseline_images/test_text/draw_text_fallback.png new file mode 100644 index 000000000000..1de47aff1505 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/draw_text_fallback.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/features.eps b/lib/matplotlib/tests/baseline_images/test_text/features.eps new file mode 100644 index 000000000000..6da3f659a60f --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/features.eps @@ -0,0 +1,777 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 +%%Title: features.eps +%%Creator: Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ +%%CreationDate: Fri Oct 3 03:14:13 2025 +%%Orientation: portrait +%%BoundingBox: 0 0 360 108 +%%HiResBoundingBox: 0.000000 0.000000 360.000000 108.000000 +%%EndComments +%%BeginProlog +/mpldict 10 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/D /e /f /a /u /l /t /colon /space /s /i /b /d /c /r /o /n /y] def +/CharStrings 19 dict dup begin +/.notdef 0 def +/D{1577 0 201 0 1456 1493 sc +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/e{1260 0 113 -29 1151 1147 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/f{721 0 47 0 760 1556 sc +760 1556 m +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +711 1120 l +711 977 l +408 977 l +408 0 l +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c +760 1556 l + +ce} _d +/a{1255 0 123 -29 1069 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/u{1298 0 174 -29 1112 1147 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/l{569 0 193 0 377 1556 sc +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/t{803 0 55 0 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/colon{690 0 240 0 451 1059 sc +240 254 m +451 254 l +451 0 l +240 0 l +240 254 l + +240 1059 m +451 1059 l +451 805 l +240 805 l +240 1059 l + +ce} _d +/space{651 0 0 0 0 0 sc +ce} _d +/s{1067 0 111 -29 967 1147 sc +907 1087 m +907 913 l +855 940 801 960 745 973 c +689 986 631 993 571 993 c +480 993 411 979 365 951 c +320 923 297 881 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +737 613 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 843 62 c +761 1 648 -29 504 -29 c +444 -29 381 -23 316 -11 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 151 c +378 134 443 125 508 125 c +595 125 661 140 708 169 c +755 199 778 241 778 295 c +778 345 761 383 727 410 c +694 437 620 462 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 922 156 1004 231 1061 c +306 1118 412 1147 549 1147 c +617 1147 681 1142 741 1132 c +801 1122 856 1107 907 1087 c + +ce} _d +/i{569 0 193 0 377 1556 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/b{1300 0 186 -29 1188 1556 sc +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +371 950 m +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 950 l + +ce} _d +/d{1300 0 113 -29 1114 1556 sc +930 950 m +930 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +/c{1126 0 113 -29 999 1147 sc +999 1077 m +999 905 l +947 934 895 955 842 969 c +790 984 737 991 684 991 c +565 991 472 953 406 877 c +340 802 307 696 307 559 c +307 422 340 316 406 240 c +472 165 565 127 684 127 c +737 127 790 134 842 148 c +895 163 947 184 999 213 c +999 43 l +948 19 894 1 839 -11 c +784 -23 726 -29 664 -29 c +495 -29 361 24 262 130 c +163 236 113 379 113 559 c +113 742 163 885 263 990 c +364 1095 501 1147 676 1147 c +733 1147 788 1141 842 1129 c +896 1118 948 1100 999 1077 c + +ce} _d +/r{842 0 186 0 842 1147 sc +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/o{1253 0 113 -29 1141 1147 sc +627 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 528 127 627 127 c +725 127 803 166 860 243 c +917 320 946 426 946 559 c +946 692 917 797 860 874 c +803 952 725 991 627 991 c + +627 1147 m +787 1147 913 1095 1004 991 c +1095 887 1141 743 1141 559 c +1141 376 1095 232 1004 127 c +913 23 787 -29 627 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c +113 743 158 887 249 991 c +340 1095 466 1147 627 1147 c + +ce} _d +/n{1298 0 186 0 1124 1147 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/y{1212 0 61 -426 1151 1120 sc +659 -104 m +607 -237 556 -324 507 -365 c +458 -406 392 -426 309 -426 c +162 -426 l +162 -272 l +270 -272 l +321 -272 360 -260 388 -236 c +416 -212 447 -155 481 -66 c +514 18 l +61 1120 l +256 1120 l +606 244 l +956 1120 l +1151 1120 l +659 -104 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/fi /uniFB03 /fl /uniFB06] def +/CharStrings 5 dict dup begin +/.notdef 0 def +/fi{1290 0 47 0 1098 1556 sc +1098 1120 m +1098 0 l +913 0 l +913 977 l +408 977 l +408 0 l +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1320 252 1410 309 1468 c +367 1527 456 1556 575 1556 c +760 1556 l +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +1098 1120 l + +913 1554 m +1098 1554 l +1098 1321 l +913 1321 l +913 1554 l + +ce} _d +/uniFB03{1980 0 47 0 1788 1556 sc +760 1556 m +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +913 1120 l +913 1198 l +913 1323 942 1413 1000 1470 c +1019 1489 1041 1504 1067 1517 c +1119 1543 1189 1556 1276 1556 c +1450 1556 l +1450 1403 l +1274 1403 l +1208 1403 1162 1390 1136 1363 c +1111 1336 1098 1288 1098 1219 c +1098 1120 l +1788 1120 l +1788 0 l +1603 0 l +1603 977 l +1098 977 l +1098 0 l +913 0 l +913 977 l +408 977 l +408 0 l +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c +760 1556 l + +1603 1554 m +1788 1554 l +1788 1321 l +1603 1321 l +1603 1554 l + +ce} _d +/fl{1290 0 47 0 1098 1556 sc +586 1556 m +1098 1556 l +1098 0 l +913 0 l +913 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +711 1120 l +711 977 l +408 977 l +408 0 l +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c + +ce} _d +/uniFB06{1763 0 111 -29 1714 1520 sc +849 1087 m +849 913 l +792 942 739 962 690 973 c +634 986 583 993 536 993 c +458 993 398 979 356 951 c +317 925 297 883 297 825 c +297 782 313 749 346 724 c +379 700 444 677 543 655 c +606 641 l +736 612 829 573 884 522 c +939 471 967 400 967 309 c +967 205 926 123 844 62 c +761 1 648 -29 504 -29 c +440 -29 377 -23 316 -12 c +251 0 183 18 111 41 c +111 231 l +179 196 246 169 312 152 c +378 134 443 125 508 125 c +595 125 661 140 708 170 c +755 199 778 241 778 295 c +778 345 761 383 728 410 c +698 433 624 459 506 487 c +442 502 l +328 526 246 563 195 612 c +144 662 119 730 119 817 c +119 928 153 1009 221 1061 c +296 1118 393 1147 514 1147 c +562 1147 612 1143 663 1136 c +660 1155 658 1175 658 1196 c +658 1286 689 1363 750 1426 c +811 1488 895 1519 1003 1520 c +1096 1520 1176 1488 1241 1424 c +1304 1363 1335 1287 1335 1196 c +1335 1120 l +1714 1120 l +1714 977 l +1335 977 l +1335 369 l +1335 278 1347 219 1372 193 c +1397 167 1448 154 1525 154 c +1714 154 l +1714 0 l +1525 0 l +1383 0 1285 27 1231 80 c +1177 133 1150 229 1150 369 c +1150 977 l +1015 977 l +1015 1120 l +1150 1120 l +1150 1198 l +1150 1241 1134 1277 1102 1306 c +1066 1339 1025 1356 980 1355 c +934 1355 893 1339 857 1306 c +822 1274 805 1237 805 1194 c +805 1153 820 1118 849 1087 c + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +0 0 translate +0 0 360 108 rectclip +gsave +0 0 m +360 0 l +360 108 l +0 108 l +cl +1 setgray +fill +grestore +0 setgray +/DejaVuSans-0 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +0 0 m /D glyphshow +24.6406 0 m /e glyphshow +44.3281 0 m /f glyphshow +55.5938 0 m /a glyphshow +75.2031 0 m /u glyphshow +95.4844 0 m /l glyphshow +104.375 0 m /t glyphshow +116.922 0 m /colon glyphshow +127.703 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +137.875 0 m /fi glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +158.031 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +168.203 0 m /uniFB03 glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +199.141 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +209.312 0 m /fl glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +91.1406 75.6 translate +0 rotate +229.469 0 m /space glyphshow +239.641 0 m /s glyphshow +256.312 0 m /t glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +69.0469 43.2 translate +0 rotate +0 0 m /D glyphshow +24.6406 0 m /i glyphshow +33.5312 0 m /s glyphshow +50.2031 0 m /a glyphshow +69.8125 0 m /b glyphshow +90.125 0 m /l glyphshow +99.0156 0 m /e glyphshow +118.703 0 m /d glyphshow +139.016 0 m /colon glyphshow +149.797 0 m /space glyphshow +159.969 0 m /f glyphshow +171.234 0 m /i glyphshow +180.125 0 m /space glyphshow +190.297 0 m /f glyphshow +201.562 0 m /f glyphshow +212.828 0 m /i glyphshow +221.719 0 m /space glyphshow +231.891 0 m /f glyphshow +243.156 0 m /l glyphshow +252.047 0 m /space glyphshow +262.219 0 m /s glyphshow +278.891 0 m /t glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +0 0 m /D glyphshow +24.6406 0 m /i glyphshow +33.5312 0 m /s glyphshow +50.2031 0 m /c glyphshow +67.7969 0 m /r glyphshow +80.25 0 m /e glyphshow +99.9375 0 m /t glyphshow +112.484 0 m /i glyphshow +121.375 0 m /o glyphshow +140.953 0 m /n glyphshow +161.234 0 m /a glyphshow +180.844 0 m /r glyphshow +194 0 m /y glyphshow +210.609 0 m /colon glyphshow +221.391 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +231.562 0 m /fi glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +251.719 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +261.891 0 m /uniFB03 glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +292.828 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +303 0 m /fl glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +323.156 0 m /space glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +-2.54688 10.8 translate +0 rotate +333.328 0 m /uniFB06 glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_text/features.pdf b/lib/matplotlib/tests/baseline_images/test_text/features.pdf new file mode 100644 index 000000000000..3d1e82468060 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/features.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/features.png b/lib/matplotlib/tests/baseline_images/test_text/features.png new file mode 100644 index 000000000000..94ac41d0796f Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/features.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/features.svg b/lib/matplotlib/tests/baseline_images/test_text/features.svg new file mode 100644 index 000000000000..41bf69c9a1bf --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/features.svg @@ -0,0 +1,620 @@ + + + + + + + + 2025-10-03T03:14:13.857547 + image/svg+xml + + + Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/font_scaling.pdf b/lib/matplotlib/tests/baseline_images/test_text/font_scaling.pdf index 72b73eb8f8af..c59c4594960a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/font_scaling.pdf and b/lib/matplotlib/tests/baseline_images/test_text/font_scaling.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/font_styles.pdf b/lib/matplotlib/tests/baseline_images/test_text/font_styles.pdf index ea8304b5460a..5110d915a30f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/font_styles.pdf and b/lib/matplotlib/tests/baseline_images/test_text/font_styles.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/font_styles.png b/lib/matplotlib/tests/baseline_images/test_text/font_styles.png index ae1ab5d3e231..0cd24a4b4734 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/font_styles.png and b/lib/matplotlib/tests/baseline_images/test_text/font_styles.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/font_styles.svg b/lib/matplotlib/tests/baseline_images/test_text/font_styles.svg index e62797eb4c23..d625ab807f02 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/font_styles.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/font_styles.svg @@ -1,825 +1,887 @@ - - + + + + + + 2026-04-03T00:06:36.978036 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - +" style="fill: #ffffff"/> + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/fonttext_wrap.png b/lib/matplotlib/tests/baseline_images/test_text/fonttext_wrap.png index 64c24344f334..27b0221cf603 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/fonttext_wrap.png and b/lib/matplotlib/tests/baseline_images/test_text/fonttext_wrap.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/language.eps b/lib/matplotlib/tests/baseline_images/test_text/language.eps new file mode 100644 index 000000000000..ac4dc4625b9f --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/language.eps @@ -0,0 +1,657 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%LanguageLevel: 3 +%%Title: language.eps +%%Creator: Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ +%%CreationDate: Fri Oct 3 03:14:14 2025 +%%Orientation: portrait +%%BoundingBox: 0 0 360 216 +%%HiResBoundingBox: 0.000000 0.000000 360.000000 216.000000 +%%EndComments +%%BeginProlog +/mpldict 10 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-0 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/D /e /f /a /u /l /t /L /n /g /space /A /B /M /i /x /d] def +/CharStrings 18 dict dup begin +/.notdef 0 def +/D{1577 0 201 0 1456 1493 sc +403 1327 m +403 166 l +647 166 l +853 166 1004 213 1099 306 c +1195 399 1243 547 1243 748 c +1243 948 1195 1094 1099 1187 c +1004 1280 853 1327 647 1327 c +403 1327 l + +201 1493 m +616 1493 l +905 1493 1118 1433 1253 1312 c +1388 1192 1456 1004 1456 748 c +1456 491 1388 302 1252 181 c +1116 60 904 0 616 0 c +201 0 l +201 1493 l + +ce} _d +/e{1260 0 113 -29 1151 1147 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/f{721 0 47 0 760 1556 sc +760 1556 m +760 1403 l +584 1403 l +518 1403 472 1390 446 1363 c +421 1336 408 1288 408 1219 c +408 1120 l +711 1120 l +711 977 l +408 977 l +408 0 l +223 0 l +223 977 l +47 977 l +47 1120 l +223 1120 l +223 1198 l +223 1323 252 1413 310 1470 c +368 1527 460 1556 586 1556 c +760 1556 l + +ce} _d +/a{1255 0 123 -29 1069 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/u{1298 0 174 -29 1112 1147 sc +174 442 m +174 1120 l +358 1120 l +358 449 l +358 343 379 263 420 210 c +461 157 523 131 606 131 c +705 131 784 163 841 226 c +899 289 928 376 928 485 c +928 1120 l +1112 1120 l +1112 0 l +928 0 l +928 172 l +883 104 831 53 772 20 c +713 -13 645 -29 567 -29 c +438 -29 341 11 274 91 c +207 171 174 288 174 442 c + +637 1147 m +637 1147 l + +ce} _d +/l{569 0 193 0 377 1556 sc +193 1556 m +377 1556 l +377 0 l +193 0 l +193 1556 l + +ce} _d +/t{803 0 55 0 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/L{1141 0 201 0 1130 1493 sc +201 1493 m +403 1493 l +403 170 l +1130 170 l +1130 0 l +201 0 l +201 1493 l + +ce} _d +/n{1298 0 186 0 1124 1147 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/g{1300 0 113 -426 1114 1147 sc +930 573 m +930 706 902 810 847 883 c +792 956 715 993 616 993 c +517 993 440 956 385 883 c +330 810 303 706 303 573 c +303 440 330 337 385 264 c +440 191 517 154 616 154 c +715 154 792 191 847 264 c +902 337 930 440 930 573 c + +1114 139 m +1114 -52 1072 -193 987 -286 c +902 -379 773 -426 598 -426 c +533 -426 472 -421 415 -411 c +358 -402 302 -387 248 -367 c +248 -188 l +302 -217 355 -239 408 -253 c +461 -267 514 -274 569 -274 c +690 -274 780 -242 840 -179 c +900 -116 930 -21 930 106 c +930 197 l +892 131 843 82 784 49 c +725 16 654 0 571 0 c +434 0 323 52 239 157 c +155 262 113 400 113 573 c +113 746 155 885 239 990 c +323 1095 434 1147 571 1147 c +654 1147 725 1131 784 1098 c +843 1065 892 1016 930 950 c +930 1120 l +1114 1120 l +1114 139 l + +ce} _d +/space{651 0 0 0 0 0 sc +ce} _d +/A{1401 0 16 0 1384 1493 sc +700 1294 m +426 551 l +975 551 l +700 1294 l + +586 1493 m +815 1493 l +1384 0 l +1174 0 l +1038 383 l +365 383 l +229 0 l +16 0 l +586 1493 l + +ce} _d +/B{1405 0 201 0 1260 1493 sc +403 713 m +403 166 l +727 166 l +836 166 916 188 968 233 c +1021 278 1047 347 1047 440 c +1047 533 1021 602 968 646 c +916 691 836 713 727 713 c +403 713 l + +403 1327 m +403 877 l +702 877 l +801 877 874 895 922 932 c +971 969 995 1026 995 1102 c +995 1177 971 1234 922 1271 c +874 1308 801 1327 702 1327 c +403 1327 l + +201 1493 m +717 1493 l +871 1493 990 1461 1073 1397 c +1156 1333 1198 1242 1198 1124 c +1198 1033 1177 960 1134 906 c +1091 852 1029 818 946 805 c +1045 784 1122 739 1177 671 c +1232 604 1260 519 1260 418 c +1260 285 1215 182 1124 109 c +1033 36 904 0 737 0 c +201 0 l +201 1493 l + +ce} _d +/M{1767 0 201 0 1567 1493 sc +201 1493 m +502 1493 l +883 477 l +1266 1493 l +1567 1493 l +1567 0 l +1370 0 l +1370 1311 l +985 287 l +782 287 l +397 1311 l +397 0 l +201 0 l +201 1493 l + +ce} _d +/i{569 0 193 0 377 1556 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/x{1212 0 59 0 1145 1120 sc +1124 1120 m +719 575 l +1145 0 l +928 0 l +602 440 l +276 0 l +59 0 l +494 586 l +96 1120 l +313 1120 l +610 721 l +907 1120 l +1124 1120 l + +ce} _d +/d{1300 0 113 -29 1114 1556 sc +930 950 m +930 1556 l +1114 1556 l +1114 0 l +930 0 l +930 168 l +891 101 842 52 783 19 c +724 -13 654 -29 571 -29 c +436 -29 325 25 240 133 c +155 241 113 383 113 559 c +113 735 155 877 240 985 c +325 1093 436 1147 571 1147 c +654 1147 724 1131 783 1098 c +842 1066 891 1017 930 950 c + +303 559 m +303 424 331 317 386 240 c +442 163 519 125 616 125 c +713 125 790 163 846 240 c +902 317 930 424 930 559 c +930 694 902 800 846 877 c +790 954 713 993 616 993 c +519 993 442 954 386 877 c +331 800 303 694 303 559 c + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans-1 def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/uni0431 /uniF6C5 /Eng /Eng.alt] def +/CharStrings 5 dict dup begin +/.notdef 0 def +/uni0431{1263 0 112 -29 1151 1591 sc +637 1147 m +797 1147 923 1095 1014 991 c +1105 887 1151 743 1151 559 c +1151 376 1105 232 1014 127 c +923 23 797 -29 637 -29 c +476 -29 352 22 263 123 c +174 224 128 370 123 559 c +117 788 l +114 867 112 921 112 948 c +112 1055 131 1147 170 1226 c +231 1349 313 1438 418 1491 c +523 1544 664 1572 840 1573 c +921 1574 980 1580 1016 1591 c +1067 1445 l +1034 1432 1003 1425 973 1424 c +723 1407 l +639 1401 572 1383 521 1354 c +388 1276 316 1186 303 1084 c +296 1028 l +383 1107 496 1147 637 1147 c + +637 991 m +538 991 460 952 403 875 c +346 798 317 693 317 559 c +317 425 345 319 402 242 c +459 165 538 127 637 127 c +735 127 813 166 870 243 c +927 320 956 426 956 559 c +956 692 927 797 870 874 c +813 952 735 991 637 991 c + +ce} _d +/uniF6C5{1253 0 113 -29 1141 1556 sc +626 991 m +528 991 450 952 393 875 c +336 798 307 693 307 559 c +307 425 335 319 392 242 c +449 165 527 127 626 127 c +725 127 803 166 860 243 c +917 320 946 425 946 558 c +946 691 917 797 860 874 c +803 952 725 991 626 991 c + +113 559 m +113 781 178 944 308 1047 c +255 1078 218 1117 196 1164 c +175 1211 164 1251 164 1284 c +164 1367 196 1433 261 1482 c +326 1531 411 1556 516 1556 c +1024 1556 l +1024 1409 l +552 1409 l +427 1409 364 1363 364 1272 c +364 1223 383 1190 422 1173 c +461 1156 530 1147 627 1147 c +785 1147 910 1095 1002 991 c +1095 887 1141 743 1141 560 c +1141 377 1095 233 1003 128 c +912 23 786 -29 626 -29 c +466 -29 340 23 249 127 c +158 232 113 376 113 559 c + +ce} _d +/Eng{1532 0 201 -426 1305 1520 sc +1104 895 m +1104 1180 1002 1323 797 1323 c +678 1323 582 1280 510 1195 c +439 1110 403 994 403 846 c +403 0 l +201 0 l +201 1493 l +403 1493 l +403 1252 l +455 1341 516 1408 586 1453 c +657 1498 743 1520 845 1520 c +996 1520 1111 1467 1188 1360 c +1266 1254 1305 1098 1305 893 c +1305 -20 l +1305 -162 1278 -265 1224 -330 c +1169 -394 1082 -426 961 -426 c +874 -426 l +874 -270 l +923 -270 l +991 -270 1038 -255 1064 -225 c +1091 -195 1104 -127 1104 -20 c +1104 895 l + +ce} _d +/Eng.alt{1532 0 213 -426 1319 1493 sc +213 1493 m +397 1493 l +1135 344 l +1135 1493 l +1319 1493 l +1319 -20 l +1319 -163 1292 -266 1237 -330 c +1183 -394 1096 -426 975 -426 c +721 -426 l +721 -270 l +954 -270 l +1024 -270 1072 -254 1097 -222 c +1122 -189 1135 -115 1135 0 c +397 1149 l +397 0 l +213 0 l +213 1493 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +0 0 translate +0 0 360 216 rectclip +gsave +0 0 m +360 0 l +360 216 l +0 216 l +cl +1 setgray +fill +grestore +0 setgray +/DejaVuSans-0 32.000 selectfont +gsave + +0 172.8 translate +0 rotate +0 0 m /D glyphshow +24.6406 0 m /e glyphshow +44.3281 0 m /f glyphshow +55.5938 0 m /a glyphshow +75.2031 0 m /u glyphshow +95.4844 0 m /l glyphshow +104.375 0 m /t glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +0 118.8 translate +0 rotate +0 0 m /L glyphshow +17.8281 0 m /a glyphshow +37.4375 0 m /n glyphshow +57.7188 0 m /g glyphshow +78.0312 0 m /space glyphshow +88.2031 0 m /A glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +0 64.8 translate +0 rotate +0 0 m /L glyphshow +17.8281 0 m /a glyphshow +37.4375 0 m /n glyphshow +57.7188 0 m /g glyphshow +78.0312 0 m /space glyphshow +88.2031 0 m /B glyphshow +grestore +/DejaVuSans-0 32.000 selectfont +gsave + +0 10.8 translate +0 rotate +0 0 m /M glyphshow +27.6094 0 m /i glyphshow +36.5 0 m /x glyphshow +54.4531 0 m /e glyphshow +74.1406 0 m /d glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +144 172.8 translate +0 rotate +0 0 m /uni0431 glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +144 118.8 translate +0 rotate +0 0 m /uniF6C5 glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +144 64.8 translate +0 rotate +0 0 m /uni0431 glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +144 10.8 translate +0 rotate +0 0 m /uni0431 glyphshow +19.7344 0 m /uniF6C5 glyphshow +39.3125 0 m /uni0431 glyphshow +59.0469 0 m /uniF6C5 glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +252 172.8 translate +0 rotate +0 0 m /Eng glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +252 118.8 translate +0 rotate +0 0 m /Eng glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +252 64.8 translate +0 rotate +0 0 m /Eng.alt glyphshow +grestore +/DejaVuSans-1 32.000 selectfont +gsave + +252 10.8 translate +0 rotate +0 0 m /Eng glyphshow +23.9375 0 m /Eng.alt glyphshow +47.875 0 m /Eng glyphshow +71.8125 0 m /Eng.alt glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_text/language.pdf b/lib/matplotlib/tests/baseline_images/test_text/language.pdf new file mode 100644 index 000000000000..c199c37318e2 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/language.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/language.png b/lib/matplotlib/tests/baseline_images/test_text/language.png new file mode 100644 index 000000000000..db0c8a5c7ced Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/language.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/language.svg b/lib/matplotlib/tests/baseline_images/test_text/language.svg new file mode 100644 index 000000000000..4f21febb77f2 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_text/language.svg @@ -0,0 +1,580 @@ + + + + + + + + 2025-10-03T03:14:14.008952 + image/svg+xml + + + Matplotlib v3.11.0.dev1446+gbcd613644c.d20251003, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/large_subscript_title.png b/lib/matplotlib/tests/baseline_images/test_text/large_subscript_title.png index 460feef8cb79..b78cbef80378 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/large_subscript_title.png and b/lib/matplotlib/tests/baseline_images/test_text/large_subscript_title.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline.pdf b/lib/matplotlib/tests/baseline_images/test_text/multiline.pdf index 14d313a75590..ed35661d9271 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/multiline.pdf and b/lib/matplotlib/tests/baseline_images/test_text/multiline.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline.png b/lib/matplotlib/tests/baseline_images/test_text/multiline.png index 8a28f05bd6f8..9f266fa44c28 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/multiline.png and b/lib/matplotlib/tests/baseline_images/test_text/multiline.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline.svg b/lib/matplotlib/tests/baseline_images/test_text/multiline.svg index 15bfc30bebdc..135c592ab808 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/multiline.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/multiline.svg @@ -1,504 +1,484 @@ - - + + + + + + 2026-04-03T00:06:37.346401 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - +" style="fill: #ffffff"/> + + - + - + - + - + - - - - + + - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + - - + + - - - +" transform="scale(0.015625)"/> + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - + + + + - + - + - + - - + - - - - - - - - - - - +M 3022 2063 +Q 3016 2534 2758 2815 +Q 2500 3097 2075 3097 +Q 1594 3097 1305 2825 +Q 1016 2553 972 2059 +L 3022 2063 +z +" transform="scale(0.015625)"/> + + + + + + + + + + - - + + + + - - + - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf b/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf index a365b2b29fdf..f58b695daa13 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf and b/lib/matplotlib/tests/baseline_images/test_text/multiline2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.png b/lib/matplotlib/tests/baseline_images/test_text/multiline2.png index 2f33b4d75499..a9728f8eb27b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/multiline2.png and b/lib/matplotlib/tests/baseline_images/test_text/multiline2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg b/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg index a768fd0536cb..b21f9417a4e1 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/multiline2.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:30:15.434491 + 2026-03-12T19:46:19.479065 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -35,217 +35,217 @@ L 414.72 307.584 L 414.72 41.472 L 57.6 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> - +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> +" style="fill: #1f77b4"/> - +" style="stroke: #000000; stroke-width: 0.8"/> - + - + - - + +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - + - +" transform="scale(0.015625)"/> - - - + + + - + - - - - + + + + - + - - - - + + + + @@ -526,35 +526,35 @@ z - +" style="stroke: #000000; stroke-width: 0.8"/> - + - - - - - + + + + + - + - + - +" transform="scale(0.015625)"/> - - - - + + + + - + - - - - - + + + + + - + - + - +" transform="scale(0.015625)"/> - - - - + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - +" clip-path="url(#pa650d0b92a)" style="fill: none; stroke: #2ca02c; stroke-width: 0.3; stroke-linecap: square"/> - +" clip-path="url(#pa650d0b92a)" style="fill: none; stroke: #2ca02c; stroke-width: 0.3; stroke-linecap: square"/> - +" clip-path="url(#pa650d0b92a)" style="fill: none; stroke: #2ca02c; stroke-width: 0.3; stroke-linecap: square"/> - +" clip-path="url(#pa650d0b92a)" style="fill: none; stroke: #2ca02c; stroke-width: 0.3; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - + - - + - + - + +" transform="scale(0.015625)"/> - - - - + + + + - + - - + +" transform="scale(0.015625)"/> - - - - - - - + + + + + + + - - - - - - + + + + + + - + - - + - + +" transform="scale(0.015625)"/> - - - + + + - + - +" transform="scale(0.015625)"/> - - - - - - + + + + + + - + - - + +" transform="scale(0.015625)"/> - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + - - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + - - + +" transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + - - + +" transform="scale(0.015625)"/> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png b/lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png new file mode 100644 index 000000000000..04c0cd8baeac Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf index 1c3b6a27b78d..08228c48f644 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png index b93a3fabe6a1..26cf20e8ec3e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg index 08f4fadf3fb5..5167ed788a4e 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg @@ -1,23 +1,23 @@ - + - + - 2021-02-07T18:30:17.338435 + 2026-03-12T19:46:22.180713 image/svg+xml - Matplotlib v3.3.2.post2013.dev0+g37d022c62, https://matplotlib.org/ + Matplotlib v3.11.0.dev2072+gb1cf5d4866.d20260312, https://matplotlib.org/ - + @@ -26,7 +26,7 @@ L 460.8 345.6 L 460.8 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> @@ -35,58 +35,58 @@ L 414.72 307.584 L 414.72 41.472 L 57.6 41.472 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - +" clip-path="url(#pfe6d54b36a)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> - +" clip-path="url(#pfe6d54b36a)" style="fill: none; stroke: #ff7f0e; stroke-width: 1.5; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> +" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/> - +" style="fill: #f5deb3; opacity: 0.5; stroke: #000000; stroke-linejoin: miter"/> - + - - + - + - - + + - + +" transform="scale(0.015625)"/> - - - - - - + + + + + + - + - - + - + - + - + +" transform="scale(0.015625)"/> - - - - - + + + + + @@ -294,19 +294,19 @@ z +" style="fill: #f5deb3; opacity: 0.5; stroke: #000000; stroke-linejoin: miter"/> - + - - + +" transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - +" style="fill: #f5deb3; opacity: 0.5; stroke: #000000; stroke-linejoin: miter"/> - + - - + - + - + - + - + +" transform="scale(0.015625)"/> - - - - - - - - - - - + + + + + + + + + + + - - - - - - + + + + + + - + - + - - + +" transform="scale(0.015625)"/> - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - +" style="fill: #f5deb3; opacity: 0.5; stroke: #000000; stroke-linejoin: miter"/> - - - - - - - + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + - + - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.pdf index c660f53fd619..8654643311da 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.pdf and b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.png b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.png index 499eb563f696..4ed8dc5f3a61 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.png and b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.svg b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.svg index df97b1b56806..c05061943f23 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/text_bboxclip.svg @@ -1,869 +1,846 @@ - - + + + + + + 2026-04-03T00:06:41.548124 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - - - - - - - - - + - - - - - - - - + + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - + - + - - - - - - - + + + + + + + - - - - - - - - - + - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + - - - - - - + - + - - - - + + + + + + + + + + + + + + + + - +" clip-path="url(#pb041380c4d)" style="fill: #ff0000; stroke: #ff0000; stroke-linejoin: miter"/> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_contains.png b/lib/matplotlib/tests/baseline_images/test_text/text_contains.png index 6b2013fac31f..8736ae10a5a2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_contains.png and b/lib/matplotlib/tests/baseline_images/test_text/text_contains.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_chars_beyond_bmp.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_pdf_chars_beyond_bmp.pdf deleted file mode 100644 index 8890790d2ea2..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_chars_beyond_bmp.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_font42_kerning.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_pdf_font42_kerning.pdf deleted file mode 100644 index a8ce9fca346c..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_font42_kerning.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_kerning.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_pdf_kerning.pdf deleted file mode 100644 index 7db9a1b44fad..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_pdf_kerning.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/titles.pdf b/lib/matplotlib/tests/baseline_images/test_text/titles.pdf index eca6a832de22..8b0f977d8a21 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/titles.pdf and b/lib/matplotlib/tests/baseline_images/test_text/titles.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/titles.png b/lib/matplotlib/tests/baseline_images/test_text/titles.png index caf466e92dea..b181299caca1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_text/titles.png and b/lib/matplotlib/tests/baseline_images/test_text/titles.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/titles.svg b/lib/matplotlib/tests/baseline_images/test_text/titles.svg index f16063a156fd..d76d5429496f 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/titles.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/titles.svg @@ -1,233 +1,255 @@ - - + + + + + + 2026-04-03T00:06:39.913712 + image/svg+xml + + + Matplotlib v3.11.0.dev2221+gef9968a6c.d20260403, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - +" style="fill: #ffffff"/> + + - + - + - + - + - - - - + + - + - - + - - + + + - - - - - - - - - - - - +M 603 4863 +L 1178 4863 +L 1178 4134 +L 603 4134 +L 603 4863 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + - - + + + - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_text/xtick_rotation_mode.png b/lib/matplotlib/tests/baseline_images/test_text/xtick_rotation_mode.png new file mode 100644 index 000000000000..c868837c903d Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/xtick_rotation_mode.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_text/ytick_rotation_mode.png b/lib/matplotlib/tests/baseline_images/test_text/ytick_rotation_mode.png new file mode 100644 index 000000000000..1bb50bc0b373 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/ytick_rotation_mode.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.pdf index c414e59fe1b8..e1901b3b3a7a 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.png index 00c8afb79604..461e51941979 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.svg index 4bc99a39910f..3664df99d1a4 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout1.svg @@ -1,505 +1,200 @@ - - + + + + + + 2025-04-09T03:27:51.168685 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - - - - - - - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - + - - - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - + - - - - - - +"/> - - - - + - - - - - - - - - - +"/> - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - +"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.pdf index e26705d0038a..a70180a9192c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.png index 5197602445ae..b690212612b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.svg index c68cafc5e9c7..2617cd5c2495 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout2.svg @@ -1,1087 +1,668 @@ - - + + + + + + 2025-04-09T03:27:52.212752 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - + - - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - + - - - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - - - - - - - - - - - - + - - - - - + - - - - - - - - - +"/> - - - - - - + - - - - - - - - +"/> - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.pdf index 5d386cc05fe3..c5307c13d72f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.png index d6428c63e39f..fe5d3a483670 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.svg index 88e6a404ac25..a32d43a6f703 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout3.svg @@ -1,891 +1,512 @@ - - + + + + + + 2025-04-09T03:27:51.909780 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - - - - - - - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - - - + - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - - - - - - - - - - - - + - - - - - + - - - - - - - - - +"/> - - - - - - + - - - - - - - - +"/> - - - - + + - - + + - - + + + + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.pdf index 1eaa92bd153e..74a45957ea72 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.png index afcf843968d9..6a0088d6e18b 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.svg index de5e5efd5a1a..28b001a01475 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout4.svg @@ -1,1093 +1,668 @@ - - + + + + + + 2025-04-09T03:27:51.858300 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - + - - - - + - - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - - + - - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - + - - - - - - +"/> - - - - + - - - - - - - - - - +"/> - - - - - - - - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - - - - - - - - - + - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.pdf index 7132b252484f..e034e898d257 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.png index b28d5098b835..15b0c247a793 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.svg index 0443685b49b0..2b3aba91681b 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout5.svg @@ -1,16 +1,16 @@ - + - 2023-04-16T19:42:04.013561 + 2025-04-09T03:27:51.453043 image/svg+xml - Matplotlib v3.8.0.dev855+gc9636b5044.d20230417, https://matplotlib.org/ + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ @@ -21,398 +21,215 @@ - - - + - - - - - - - - - - - - +iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAz0lEQVR4nFWMu00EURAEq3vm3T4HBwxOCOOEyIEELgRCJwACwOQn7e4bjF2dDmPUVa3W6KzXwoFaogiUCRGQiTJ2DjKfTlRuUjaVpkJUeD9RafL3dEelKIsKGFdcFiOgDPn5eLhIXVJXvA+/HrQV/4b1r8NF/hwH5YLYBqggCgyKgVwoiozjN3YRMbCL9CBjEB4be9BiJZ/vP0gPDl4u2TSYYtl5ZfJCvty+0z0zeaZppWumaaF75rB710yeb97o2r50rXQVDegyTWZSoyn4A2BiWT69Db/oAAAAAElFTkSuQmCC" id="imagef5c2a23f35" transform="matrix(30.98 0 0 30.98 75.5 10.8)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="10" height="10"/> - - - - - - - - - + - + - - - - - + - - - +"/> - - - - - - + - + - - - - - + - - - +"/> - - - - - - + - + - - - - - + - - - +"/> - - - - - - + - + - - - - - + - - - +"/> - - - - - - + - + - - - - - + - - - +"/> - - - - - - - - - + - + - + - - - - - + + - - - - - - + - + - - - - - + + - - - - - - + - + - - - - - + + - - - - - - + - + - - - - - + + - - - - - - + - + - - - - - + + + + + + + + + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.pdf index 32c853cad2da..67fc2d901fc9 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.png index cc203bf27864..faf641ba1ef3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.svg index 51e048e2d92b..4b4aa7071372 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout6.svg @@ -1,1229 +1,784 @@ - - + + + + + + 2025-04-09T03:27:52.608841 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - - - - - - - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - - - + - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - - - - - - - - - - - - + - - - - - + - - - - - - - - - +"/> - - - - - - + - - - - - - - - +"/> - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - + - + - - - - - - - - - - - - + + - - - - - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.pdf index b352ed0ebadd..03efb3454886 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.png index fa02aad51ce1..613e695f05a4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.svg index c0aa6d0755a3..da698dd4ffb9 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout7.svg @@ -1,636 +1,208 @@ - - + + + + + + 2025-04-09T03:27:52.779409 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - - - - - - - - - + - - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - - - + - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - + - - - - - - +"/> - - - - + - - - - - - - - - - +"/> - - - - - - - - - - - - - - - - - - - - - - + + - - - - - + + + + + + + + + + + + + - + + + - - - - - - - - - - - - - - +"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.pdf index 73a34ff7e3ac..01d5d00781b8 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.png index 13b7d894a265..e8aaa577dfcd 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.svg index 538d4441b2b8..f5acea9d851b 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout8.svg @@ -1,505 +1,200 @@ - - + + + + + + 2025-04-09T03:27:52.671035 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - +" style="fill: #ffffff"/> - + - + - + - - - - - - - - - - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - + - + - - - - + - - - - - - +"/> - - - - - - - + - - - - - - - - - - - - +"/> - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - + - - - - - - +"/> - - - - + - - - - - - - - - - +"/> - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - +"/> - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.pdf index c694c8431a21..d3a9b1286581 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.pdf and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.png index 2d63e6987a38..cd61aba5d9da 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.png and b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.svg index ea6a6ea62151..0524e0b4a589 100644 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.svg +++ b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout9.svg @@ -1,1017 +1,684 @@ - - + + + + + + 2025-04-09T03:27:52.897268 + image/svg+xml + + + Matplotlib v3.11.0.dev647+g7c466f9a72.d20250409, https://matplotlib.org/ + + + + + - + - +" style="fill: #ffffff"/> - - - - - - - - - - - - - +" style="fill: #ffffff"/> - - - - - - - - - + - + - - - - - - - - - - - + + - - - - - - + - + - - - - - - - - - - + + - - - - - - + - + - - - - - - - - - - + + - - - - - - + - + - - - - - - - - - - + + - - - - - - + - + - - - - - - - - - - + + - - - - - - + - + - - - - - - - - - - + + - - - - - - - - - + - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - + + - - + + - - + + - - + + - - + + + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + - - - - - - + - + - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.pdf deleted file mode 100644 index 7300cf8ed51d..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.png deleted file mode 100644 index a4d5e8f6f22a..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.svg deleted file mode 100644 index e0587bbb902e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes1.svg +++ /dev/null @@ -1,1613 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.pdf deleted file mode 100644 index 85f669de45bc..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.png deleted file mode 100644 index effa2c5d9f64..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.svg deleted file mode 100644 index 88f7e0637e4e..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/tight_layout_offsetboxes2.svg +++ /dev/null @@ -1,1469 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png index b65c21c972af..f7ac255e56a6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png and b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_ttconv/truetype-conversion.pdf b/lib/matplotlib/tests/baseline_images/test_ttconv/truetype-conversion.pdf deleted file mode 100644 index db47fad3a51a..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_ttconv/truetype-conversion.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_units/jpl_bar_units.png b/lib/matplotlib/tests/baseline_images/test_units/jpl_bar_units.png index 8c79da4c7c4e..1585e43078bf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_units/jpl_bar_units.png and b/lib/matplotlib/tests/baseline_images/test_units/jpl_bar_units.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_units/jpl_barh_units.png b/lib/matplotlib/tests/baseline_images/test_units/jpl_barh_units.png index d76f147fe667..f4f409ebf5d5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_units/jpl_barh_units.png and b/lib/matplotlib/tests/baseline_images/test_units/jpl_barh_units.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png b/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png index f15f81fda6f6..dacc0d8b4e84 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png and b/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png b/lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png index 249f15d238dd..df6cbcd103c2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png and b/lib/matplotlib/tests/baseline_images/test_usetex/eqnarray.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_usetex/rotation.png b/lib/matplotlib/tests/baseline_images/test_usetex/rotation.png index 99bab74390b8..3463cc1e774c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_usetex/rotation.png and b/lib/matplotlib/tests/baseline_images/test_usetex/rotation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_usetex/rotation.svg b/lib/matplotlib/tests/baseline_images/test_usetex/rotation.svg index c68bffefde86..48838f41e698 100644 --- a/lib/matplotlib/tests/baseline_images/test_usetex/rotation.svg +++ b/lib/matplotlib/tests/baseline_images/test_usetex/rotation.svg @@ -6,11 +6,11 @@ - 2023-04-27T20:38:40.258942 + 2025-07-11T18:21:45.436534 image/svg+xml - Matplotlib v3.8.0.dev964+g2e2d2d5f57.d20230428, https://matplotlib.org/ + Matplotlib v3.11.0.dev1079+g8ff030e131, https://matplotlib.org/ @@ -33,438 +33,438 @@ z - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - @@ -479,27 +479,27 @@ z - @@ -515,50 +515,50 @@ z - - @@ -575,45 +575,45 @@ z - - @@ -630,20 +630,20 @@ z - @@ -693,27 +693,27 @@ z - @@ -898,27 +898,27 @@ z - @@ -1058,34 +1058,34 @@ z - @@ -1225,23 +1225,23 @@ z - @@ -1380,7 +1380,7 @@ z - + diff --git a/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.pdf b/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.pdf index 4ef375771d38..f2ebbeb528cc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.pdf and b/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.png b/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.png index e4a9183612f5..e4a62a1c87ae 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.png and b/lib/matplotlib/tests/baseline_images/test_usetex/test_usetex.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png index f0d5023008ca..65232c461f4e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png and b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_grid_buttons.png b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_grid_buttons.png new file mode 100644 index 000000000000..632b00b17f7f Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_grid_buttons.png differ diff --git a/lib/matplotlib/tests/conftest.py b/lib/matplotlib/tests/conftest.py index 54a1bc6cae94..3f38f50ccc7f 100644 --- a/lib/matplotlib/tests/conftest.py +++ b/lib/matplotlib/tests/conftest.py @@ -1,2 +1,3 @@ from matplotlib.testing.conftest import ( # noqa - mpl_test_settings, pytest_configure, pytest_unconfigure, pd, xr) + pytest_configure, pytest_unconfigure, + high_memory, mpl_test_settings, pd, text_placeholders, xr) diff --git a/lib/matplotlib/tests/Courier10PitchBT-Bold.pfb b/lib/matplotlib/tests/data/Courier10PitchBT-Bold.pfb similarity index 100% rename from lib/matplotlib/tests/Courier10PitchBT-Bold.pfb rename to lib/matplotlib/tests/data/Courier10PitchBT-Bold.pfb diff --git a/lib/matplotlib/tests/cmr10.pfb b/lib/matplotlib/tests/data/cmr10.pfb similarity index 100% rename from lib/matplotlib/tests/cmr10.pfb rename to lib/matplotlib/tests/data/cmr10.pfb diff --git a/lib/matplotlib/tests/mpltest.ttf b/lib/matplotlib/tests/data/mpltest.ttf similarity index 100% rename from lib/matplotlib/tests/mpltest.ttf rename to lib/matplotlib/tests/data/mpltest.ttf diff --git a/lib/matplotlib/tests/test_inline_01.ipynb b/lib/matplotlib/tests/data/test_inline_01.ipynb similarity index 100% rename from lib/matplotlib/tests/test_inline_01.ipynb rename to lib/matplotlib/tests/data/test_inline_01.ipynb diff --git a/lib/matplotlib/tests/test_nbagg_01.ipynb b/lib/matplotlib/tests/data/test_nbagg_01.ipynb similarity index 100% rename from lib/matplotlib/tests/test_nbagg_01.ipynb rename to lib/matplotlib/tests/data/test_nbagg_01.ipynb diff --git a/lib/matplotlib/tests/data/tinypages/.gitignore b/lib/matplotlib/tests/data/tinypages/.gitignore new file mode 100644 index 000000000000..739e1d9ce65d --- /dev/null +++ b/lib/matplotlib/tests/data/tinypages/.gitignore @@ -0,0 +1,3 @@ +_build/ +doctrees/ +plot_directive/ diff --git a/lib/matplotlib/tests/tinypages/README.md b/lib/matplotlib/tests/data/tinypages/README.md similarity index 100% rename from lib/matplotlib/tests/tinypages/README.md rename to lib/matplotlib/tests/data/tinypages/README.md diff --git a/lib/matplotlib/tests/tinypages/_static/.gitignore b/lib/matplotlib/tests/data/tinypages/_static/.gitignore similarity index 100% rename from lib/matplotlib/tests/tinypages/_static/.gitignore rename to lib/matplotlib/tests/data/tinypages/_static/.gitignore diff --git a/lib/matplotlib/tests/tinypages/_static/README.txt b/lib/matplotlib/tests/data/tinypages/_static/README.txt similarity index 100% rename from lib/matplotlib/tests/tinypages/_static/README.txt rename to lib/matplotlib/tests/data/tinypages/_static/README.txt diff --git a/lib/matplotlib/tests/tinypages/conf.py b/lib/matplotlib/tests/data/tinypages/conf.py similarity index 100% rename from lib/matplotlib/tests/tinypages/conf.py rename to lib/matplotlib/tests/data/tinypages/conf.py diff --git a/lib/matplotlib/tests/tinypages/included_plot_21.rst b/lib/matplotlib/tests/data/tinypages/included_plot_21.rst similarity index 100% rename from lib/matplotlib/tests/tinypages/included_plot_21.rst rename to lib/matplotlib/tests/data/tinypages/included_plot_21.rst diff --git a/lib/matplotlib/tests/tinypages/index.rst b/lib/matplotlib/tests/data/tinypages/index.rst similarity index 100% rename from lib/matplotlib/tests/tinypages/index.rst rename to lib/matplotlib/tests/data/tinypages/index.rst diff --git a/lib/matplotlib/tests/tinypages/nestedpage/index.rst b/lib/matplotlib/tests/data/tinypages/nestedpage/index.rst similarity index 100% rename from lib/matplotlib/tests/tinypages/nestedpage/index.rst rename to lib/matplotlib/tests/data/tinypages/nestedpage/index.rst diff --git a/lib/matplotlib/tests/tinypages/nestedpage2/index.rst b/lib/matplotlib/tests/data/tinypages/nestedpage2/index.rst similarity index 100% rename from lib/matplotlib/tests/tinypages/nestedpage2/index.rst rename to lib/matplotlib/tests/data/tinypages/nestedpage2/index.rst diff --git a/lib/matplotlib/tests/tinypages/range4.py b/lib/matplotlib/tests/data/tinypages/range4.py similarity index 100% rename from lib/matplotlib/tests/tinypages/range4.py rename to lib/matplotlib/tests/data/tinypages/range4.py diff --git a/lib/matplotlib/tests/tinypages/range6.py b/lib/matplotlib/tests/data/tinypages/range6.py similarity index 100% rename from lib/matplotlib/tests/tinypages/range6.py rename to lib/matplotlib/tests/data/tinypages/range6.py diff --git a/lib/matplotlib/tests/tinypages/some_plots.rst b/lib/matplotlib/tests/data/tinypages/some_plots.rst similarity index 87% rename from lib/matplotlib/tests/tinypages/some_plots.rst rename to lib/matplotlib/tests/data/tinypages/some_plots.rst index dd1f79892b0e..17de8f1d742e 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/data/tinypages/some_plots.rst @@ -135,7 +135,12 @@ Plot 16 uses a specific function in a file with plot commands: Plot 17 gets a caption specified by the :caption: option: .. plot:: - :caption: Plot 17 uses the caption option. + :caption: + Plot 17 uses the caption option, + with multi-line input. + :alt: + Plot 17 uses the alt option, + with multi-line input. plt.figure() plt.plot(range(6)) @@ -174,3 +179,22 @@ Plot 21 is generated via an include directive: Plot 22 uses a different specific function in a file with plot commands: .. plot:: range6.py range10 + +Plots 23--25 use filename-prefix. + +.. plot:: + :filename-prefix: custom-basename-6 + + plt.plot(range(6)) + +.. plot:: range4.py + :filename-prefix: custom-basename-4 + +.. plot:: + :filename-prefix: custom-basename-4-6 + + plt.figure() + plt.plot(range(4)) + + plt.figure() + plt.plot(range(6)) diff --git a/lib/matplotlib/tests/meson.build b/lib/matplotlib/tests/meson.build index 148a40f11a2f..48b97a1d4b3d 100644 --- a/lib/matplotlib/tests/meson.build +++ b/lib/matplotlib/tests/meson.build @@ -2,8 +2,8 @@ python_sources = [ '__init__.py', 'conftest.py', 'test_afm.py', - 'test_agg_filter.py', 'test_agg.py', + 'test_agg_filter.py', 'test_animation.py', 'test_api.py', 'test_arrow_patches.py', @@ -13,20 +13,23 @@ python_sources = [ 'test_backend_bases.py', 'test_backend_cairo.py', 'test_backend_gtk3.py', + 'test_backend_inline.py', 'test_backend_macosx.py', 'test_backend_nbagg.py', 'test_backend_pdf.py', 'test_backend_pgf.py', 'test_backend_ps.py', 'test_backend_qt.py', - 'test_backends_interactive.py', + 'test_backend_registry.py', 'test_backend_svg.py', 'test_backend_template.py', 'test_backend_tk.py', 'test_backend_tools.py', 'test_backend_webagg.py', + 'test_backends_interactive.py', 'test_basic.py', 'test_bbox_tight.py', + 'test_bezier.py', 'test_category.py', 'test_cbook.py', 'test_collections.py', @@ -43,8 +46,8 @@ python_sources = [ 'test_doc.py', 'test_dviread.py', 'test_figure.py', - 'test_fontconfig_pattern.py', 'test_font_manager.py', + 'test_fontconfig_pattern.py', 'test_ft2font.py', 'test_getattr.py', 'test_gridspec.py', @@ -54,11 +57,12 @@ python_sources = [ 'test_marker.py', 'test_mathtext.py', 'test_matplotlib.py', + 'test_multivariate_colormaps.py', 'test_mlab.py', 'test_offsetbox.py', 'test_patches.py', - 'test_patheffects.py', 'test_path.py', + 'test_patheffects.py', 'test_pickle.py', 'test_png.py', 'test_polar.py', @@ -78,13 +82,12 @@ python_sources = [ 'test_table.py', 'test_testing.py', 'test_texmanager.py', - 'test_textpath.py', 'test_text.py', + 'test_textpath.py', 'test_ticker.py', 'test_tightlayout.py', 'test_transforms.py', 'test_triangulation.py', - 'test_ttconv.py', 'test_type1font.py', 'test_units.py', 'test_usetex.py', @@ -96,10 +99,6 @@ py3.install_sources(python_sources, install_data( 'README', - 'Courier10PitchBT-Bold.pfb', - 'cmr10.pfb', - 'mpltest.ttf', - 'test_nbagg_01.ipynb', install_tag: 'tests', install_dir: py3.get_install_dir(subdir: 'matplotlib/tests/')) @@ -108,6 +107,6 @@ install_subdir( install_tag: 'tests', install_dir: py3.get_install_dir(subdir: 'matplotlib/tests')) install_subdir( - 'tinypages', + 'data', install_tag: 'tests', - install_dir: py3.get_install_dir(subdir: 'matplotlib/tests')) + install_dir: py3.get_install_dir(subdir: 'matplotlib/tests/')) diff --git a/lib/matplotlib/tests/test__style_helpers.py b/lib/matplotlib/tests/test__style_helpers.py new file mode 100644 index 000000000000..ecaccec224ae --- /dev/null +++ b/lib/matplotlib/tests/test__style_helpers.py @@ -0,0 +1,83 @@ +import pytest + +import matplotlib.colors as mcolors +from matplotlib.lines import _get_dash_pattern +from matplotlib._style_helpers import style_generator + + +@pytest.mark.parametrize('key, value', [('facecolor', ["b", "g", "r"]), + ('edgecolor', ["b", "g", "r"]), + ('hatch', ["/", "\\", "."]), + ('linestyle', ["-", "--", ":"]), + ('linewidth', [1, 1.5, 2])]) +def test_style_generator_list(key, value): + """Test that style parameter lists are distributed to the generator.""" + kw = {'foo': 12, key: value} + new_kw, gen = style_generator(kw) + + assert new_kw == {'foo': 12} + + for v in value * 2: # Result should repeat + style_dict = next(gen) + assert len(style_dict) == 1 + if key.endswith('color'): + assert mcolors.same_color(v, style_dict[key]) + elif key == 'linestyle': + assert _get_dash_pattern(v) == style_dict[key] + else: + assert v == style_dict[key] + + +@pytest.mark.parametrize('key, value', [('facecolor', "b"), + ('edgecolor', "b"), + ('hatch', "/"), + ('linestyle', "-"), + ('linewidth', 1)]) +def test_style_generator_single(key, value): + """Test that single-value style parameters are distributed to the generator.""" + kw = {'foo': 12, key: value} + new_kw, gen = style_generator(kw) + + assert new_kw == {'foo': 12} + for _ in range(2): # Result should repeat + style_dict = next(gen) + if key.endswith('color'): + assert mcolors.same_color(value, style_dict[key]) + elif key == 'linestyle': + assert _get_dash_pattern(value) == style_dict[key] + else: + assert value == style_dict[key] + + +@pytest.mark.parametrize('key', ['facecolor', 'hatch', 'linestyle']) +def test_style_generator_raises_on_empty_style_parameter_list(key): + kw = {key: []} + with pytest.raises(TypeError, match=f'{key} must not be an empty sequence'): + style_generator(kw) + + +def test_style_generator_sequence_type_styles(): + """ + Test that sequence type style values are detected as single value + and passed to all elements of the generator. + """ + kw = {'facecolor': ('r', 0.5), + 'edgecolor': [0.5, 0.5, 0.5], + 'linestyle': (0, (1, 1))} + + _, gen = style_generator(kw) + for _ in range(2): # Result should repeat + style_dict = next(gen) + mcolors.same_color(kw['facecolor'], style_dict['facecolor']) + mcolors.same_color(kw['edgecolor'], style_dict['edgecolor']) + kw['linestyle'] == style_dict['linestyle'] + + +def test_style_generator_none(): + kw = {'facecolor': 'none', + 'edgecolor': 'none'} + _, gen = style_generator(kw) + for _ in range(2): # Result should repeat + style_dict = next(gen) + assert style_dict['facecolor'] == 'none' + assert style_dict['edgecolor'] == 'none' diff --git a/lib/matplotlib/tests/test_afm.py b/lib/matplotlib/tests/test_afm.py index e5c6a83937cd..bc1d587baf6b 100644 --- a/lib/matplotlib/tests/test_afm.py +++ b/lib/matplotlib/tests/test_afm.py @@ -47,20 +47,20 @@ def test_parse_header(): fh = BytesIO(AFM_TEST_DATA) header = _afm._parse_header(fh) assert header == { - b'StartFontMetrics': 2.0, - b'FontName': 'MyFont-Bold', - b'EncodingScheme': 'FontSpecific', - b'FullName': 'My Font Bold', - b'FamilyName': 'Test Fonts', - b'Weight': 'Bold', - b'ItalicAngle': 0.0, - b'IsFixedPitch': False, - b'UnderlinePosition': -100, - b'UnderlineThickness': 56.789, - b'Version': '001.000', - b'Notice': b'Copyright \xa9 2017 No one.', - b'FontBBox': [0, -321, 1234, 369], - b'StartCharMetrics': 3, + 'StartFontMetrics': 2.0, + 'FontName': 'MyFont-Bold', + 'EncodingScheme': 'FontSpecific', + 'FullName': 'My Font Bold', + 'FamilyName': 'Test Fonts', + 'Weight': 'Bold', + 'ItalicAngle': 0.0, + 'IsFixedPitch': False, + 'UnderlinePosition': -100, + 'UnderlineThickness': 56.789, + 'Version': '001.000', + 'Notice': b'Copyright \xa9 2017 No one.', + 'FontBBox': [0, -321, 1234, 369], + 'StartCharMetrics': 3, } @@ -69,20 +69,23 @@ def test_parse_char_metrics(): _afm._parse_header(fh) # position metrics = _afm._parse_char_metrics(fh) assert metrics == ( - {0: (250.0, 'space', [0, 0, 0, 0]), - 42: (1141.0, 'foo', [40, 60, 800, 360]), - 99: (583.0, 'bar', [40, -10, 543, 210]), - }, - {'space': (250.0, 'space', [0, 0, 0, 0]), - 'foo': (1141.0, 'foo', [40, 60, 800, 360]), - 'bar': (583.0, 'bar', [40, -10, 543, 210]), - }) + { + 0: _afm.CharMetrics(250.0, 'space', (0, 0, 0, 0)), + 42: _afm.CharMetrics(1141.0, 'foo', (40, 60, 800, 360)), + 99: _afm.CharMetrics(583.0, 'bar', (40, -10, 543, 210)), + }, + { + 'space': _afm.CharMetrics(250.0, 'space', (0, 0, 0, 0)), + 'foo': _afm.CharMetrics(1141.0, 'foo', (40, 60, 800, 360)), + 'bar': _afm.CharMetrics(583.0, 'bar', (40, -10, 543, 210)), + } + ) def test_get_familyname_guessed(): fh = BytesIO(AFM_TEST_DATA) font = _afm.AFM(fh) - del font._header[b'FamilyName'] # remove FamilyName, so we have to guess + del font._header['FamilyName'] # remove FamilyName, so we have to guess assert font.get_familyname() == 'My Font' @@ -135,3 +138,11 @@ def test_malformed_header(afm_data, caplog): _afm._parse_header(fh) assert len(caplog.records) == 1 + + +def test_afm_kerning(): + fn = fm.findfont("Helvetica", fontext="afm") + with open(fn, 'rb') as fh: + afm = _afm.AFM(fh) + assert afm.get_kern_dist_from_name('A', 'V') == -70.0 + assert afm.get_kern_dist_from_name('V', 'A') == -80.0 diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index 6ca74ed400b1..6eebde1da92b 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -1,8 +1,9 @@ import io +import warnings import numpy as np from numpy.testing import assert_array_almost_equal -from PIL import Image, TiffTags +from PIL import features, Image, TiffTags import pytest @@ -17,6 +18,13 @@ from matplotlib.transforms import IdentityTransform +def require_pillow_feature(name): + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + available = features.check(name.lower()) + return pytest.mark.skipif(not available, reason=f"{name} support not available") + + def test_repeated_save_with_alpha(): # We want an image which has a background color of bluish green, with an # alpha of 0.25. @@ -181,8 +189,8 @@ def process_image(self, padded_src, dpi): shadow.update_from(line) # offset transform - transform = mtransforms.offset_copy(line.get_transform(), ax.figure, - x=4.0, y=-6.0, units='points') + transform = mtransforms.offset_copy( + line.get_transform(), fig, x=4.0, y=-6.0, units='points') shadow.set_transform(transform) # adjust zorder of the shadow lines so that it is drawn below the @@ -199,7 +207,7 @@ def process_image(self, padded_src, dpi): def test_too_large_image(): - fig = plt.figure(figsize=(300, 1000)) + fig = plt.figure(figsize=(300, 2**25)) buff = io.BytesIO() with pytest.raises(ValueError): fig.savefig(buff) @@ -249,6 +257,7 @@ def test_pil_kwargs_tiff(): assert tags["ImageDescription"] == "test image" +@require_pillow_feature('WebP') def test_pil_kwargs_webp(): plt.plot([0, 1, 2], [0, 1, 0]) buf_small = io.BytesIO() @@ -262,6 +271,39 @@ def test_pil_kwargs_webp(): assert buf_large.getbuffer().nbytes > buf_small.getbuffer().nbytes +@require_pillow_feature('AVIF') +def test_pil_kwargs_avif(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf_small = io.BytesIO() + pil_kwargs_low = {"quality": 1} + plt.savefig(buf_small, format="avif", pil_kwargs=pil_kwargs_low) + assert len(pil_kwargs_low) == 1 + buf_large = io.BytesIO() + pil_kwargs_high = {"quality": 100} + plt.savefig(buf_large, format="avif", pil_kwargs=pil_kwargs_high) + assert len(pil_kwargs_high) == 1 + assert buf_large.getbuffer().nbytes > buf_small.getbuffer().nbytes + + +def test_gif_no_alpha(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf = io.BytesIO() + plt.savefig(buf, format="gif", transparent=False) + im = Image.open(buf) + assert im.mode == "P" + assert im.info["transparency"] >= len(im.palette.colors) + + +def test_gif_alpha(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf = io.BytesIO() + plt.savefig(buf, format="gif", transparent=True) + im = Image.open(buf) + assert im.mode == "P" + assert im.info["transparency"] < len(im.palette.colors) + + +@require_pillow_feature('WebP') def test_webp_alpha(): plt.plot([0, 1, 2], [0, 1, 0]) buf = io.BytesIO() @@ -270,6 +312,15 @@ def test_webp_alpha(): assert im.mode == "RGBA" +@require_pillow_feature('AVIF') +def test_avif_alpha(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf = io.BytesIO() + plt.savefig(buf, format="avif", transparent=True) + im = Image.open(buf) + assert im.mode == "RGBA" + + def test_draw_path_collection_error_handling(): fig, ax = plt.subplots() ax.scatter([1], [1]).set_paths(Path([(0, 1), (2, 3)])) @@ -277,7 +328,7 @@ def test_draw_path_collection_error_handling(): fig.canvas.draw() -def test_chunksize_fails(): +def test_chunksize_fails(high_memory): # NOTE: This test covers multiple independent test scenarios in a single # function, because each scenario uses ~2GB of memory and we don't # want parallel test executors to accidentally run multiple of these @@ -331,7 +382,7 @@ def test_chunksize_fails(): def test_non_tuple_rgbaface(): - # This passes rgbaFace as a ndarray to draw_path. + # This passes rgbaFace as an ndarray to draw_path. fig = plt.figure() fig.add_subplot(projection="3d").scatter( [0, 1, 2], [0, 1, 2], path_effects=[patheffects.Stroke(linewidth=4)]) diff --git a/lib/matplotlib/tests/test_agg_filter.py b/lib/matplotlib/tests/test_agg_filter.py index dc8cff6858ae..4c5b55a3d15c 100644 --- a/lib/matplotlib/tests/test_agg_filter.py +++ b/lib/matplotlib/tests/test_agg_filter.py @@ -5,11 +5,8 @@ @image_comparison(baseline_images=['agg_filter_alpha'], - extensions=['png', 'pdf']) + extensions=['gif', 'png', 'pdf'], style='mpl20') def test_agg_filter_alpha(): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - ax = plt.axes() x, y = np.mgrid[0:7, 0:8] data = x**2 - y**2 diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py index d026dae59533..4ca5c1220972 100644 --- a/lib/matplotlib/tests/test_animation.py +++ b/lib/matplotlib/tests/test_animation.py @@ -13,6 +13,7 @@ import matplotlib as mpl from matplotlib import pyplot as plt from matplotlib import animation +from matplotlib.animation import PillowWriter from matplotlib.testing.decorators import check_figures_equal @@ -158,8 +159,7 @@ def isAvailable(cls): def gen_writers(): for writer, output in WRITER_OUTPUT: if not animation.writers.is_available(writer): - mark = pytest.mark.skip( - f"writer '{writer}' not available on this system") + mark = pytest.mark.skip(f"writer '{writer}' not available on this system") yield pytest.param(writer, None, output, marks=[mark]) yield pytest.param(writer, None, Path(output), marks=[mark]) continue @@ -175,7 +175,7 @@ def gen_writers(): # matplotlib.testing.image_comparison @pytest.mark.parametrize('writer, frame_format, output', gen_writers()) @pytest.mark.parametrize('anim', [dict(klass=dict)], indirect=['anim']) -def test_save_animation_smoketest(tmpdir, writer, frame_format, output, anim): +def test_save_animation_smoketest(tmp_path, writer, frame_format, output, anim): if frame_format is not None: plt.rcParams["animation.frame_format"] = frame_format anim = animation.FuncAnimation(**anim) @@ -187,17 +187,14 @@ def test_save_animation_smoketest(tmpdir, writer, frame_format, output, anim): dpi = 100. codec = 'h264' - # Use temporary directory for the file-based writers, which produce a file - # per frame with known names. - with tmpdir.as_cwd(): - anim.save(output, fps=30, writer=writer, bitrate=500, dpi=dpi, - codec=codec) + anim.save(tmp_path / output, fps=30, writer=writer, bitrate=500, dpi=dpi, + codec=codec) del anim @pytest.mark.parametrize('writer, frame_format, output', gen_writers()) -def test_grabframe(tmpdir, writer, frame_format, output): +def test_grabframe(tmp_path, writer, frame_format, output): WriterClass = animation.writers[writer] if frame_format is not None: @@ -214,18 +211,14 @@ def test_grabframe(tmpdir, writer, frame_format, output): codec = 'h264' test_writer = WriterClass() - # Use temporary directory for the file-based writers, which produce a file - # per frame with known names. - with tmpdir.as_cwd(): - with test_writer.saving(fig, output, dpi): - # smoke test it works - test_writer.grab_frame() - for k in {'dpi', 'bbox_inches', 'format'}: - with pytest.raises( - TypeError, - match=f"grab_frame got an unexpected keyword argument {k!r}" - ): - test_writer.grab_frame(**{k: object()}) + with test_writer.saving(fig, tmp_path / output, dpi): + # smoke test it works + test_writer.grab_frame() + for k in {'dpi', 'bbox_inches', 'format'}: + with pytest.raises( + TypeError, + match=f"grab_frame got an unexpected keyword argument {k!r}"): + test_writer.grab_frame(**{k: object()}) @pytest.mark.parametrize('writer', [ @@ -278,6 +271,8 @@ def test_no_length_frames(anim): anim.save('unused.null', writer=NullMovieWriter()) +@pytest.mark.skipif(sys.platform == 'emscripten', + reason='emscripten does not support subprocesses') def test_movie_writer_registry(): assert len(animation.writers._registered) > 0 mpl.rcParams['animation.ffmpeg_path'] = "not_available_ever_xxxx" @@ -295,32 +290,20 @@ def test_movie_writer_registry(): reason="animation writer not installed")), "to_jshtml"]) @pytest.mark.parametrize('anim', [dict(frames=1)], indirect=['anim']) -def test_embed_limit(method_name, caplog, tmpdir, anim): +def test_embed_limit(method_name, caplog, anim): caplog.set_level("WARNING") - with tmpdir.as_cwd(): - with mpl.rc_context({"animation.embed_limit": 1e-6}): # ~1 byte. - getattr(anim, method_name)() + with mpl.rc_context({"animation.embed_limit": 1e-6}): # ~1 byte. + getattr(anim, method_name)() assert len(caplog.records) == 1 record, = caplog.records assert (record.name == "matplotlib.animation" and record.levelname == "WARNING") -@pytest.mark.parametrize( - "method_name", - [pytest.param("to_html5_video", marks=pytest.mark.skipif( - not animation.writers.is_available(mpl.rcParams["animation.writer"]), - reason="animation writer not installed")), - "to_jshtml"]) -@pytest.mark.parametrize('anim', [dict(frames=1)], indirect=['anim']) -def test_cleanup_temporaries(method_name, tmpdir, anim): - with tmpdir.as_cwd(): - getattr(anim, method_name)() - assert list(Path(str(tmpdir)).iterdir()) == [] - - +@pytest.mark.skipif(sys.platform == 'emscripten', + reason='emscripten does not support subprocesses') @pytest.mark.skipif(shutil.which("/bin/sh") is None, reason="requires a POSIX OS") -def test_failing_ffmpeg(tmpdir, monkeypatch, anim): +def test_failing_ffmpeg(tmp_path, monkeypatch, anim): """ Test that we correctly raise a CalledProcessError when ffmpeg fails. @@ -328,13 +311,12 @@ def test_failing_ffmpeg(tmpdir, monkeypatch, anim): succeeds when called with no arguments (so that it gets registered by `isAvailable`), but fails otherwise, and add it to the $PATH. """ - with tmpdir.as_cwd(): - monkeypatch.setenv("PATH", ".:" + os.environ["PATH"]) - exe_path = Path(str(tmpdir), "ffmpeg") - exe_path.write_bytes(b"#!/bin/sh\n[[ $@ -eq 0 ]]\n") - os.chmod(exe_path, 0o755) - with pytest.raises(subprocess.CalledProcessError): - anim.save("test.mpeg") + monkeypatch.setenv("PATH", str(tmp_path), prepend=":") + exe_path = tmp_path / "ffmpeg" + exe_path.write_bytes(b"#!/bin/sh\n[[ $@ -eq 0 ]]\n") + os.chmod(exe_path, 0o755) + with pytest.raises(subprocess.CalledProcessError): + anim.save("test.mpeg") @pytest.mark.parametrize("cache_frame_data", [False, True]) @@ -418,7 +400,7 @@ def animate(i): ) -def test_exhausted_animation(tmpdir): +def test_exhausted_animation(tmp_path): fig, ax = plt.subplots() def update(frame): @@ -429,14 +411,13 @@ def update(frame): cache_frame_data=False ) - with tmpdir.as_cwd(): - anim.save("test.gif", writer='pillow') + anim.save(tmp_path / "test.gif", writer='pillow') with pytest.warns(UserWarning, match="exhausted"): anim._start() -def test_no_frame_warning(tmpdir): +def test_no_frame_warning(): fig, ax = plt.subplots() def update(frame): @@ -451,8 +432,8 @@ def update(frame): anim._start() -@check_figures_equal(extensions=["png"]) -def test_animation_frame(tmpdir, fig_test, fig_ref): +@check_figures_equal() +def test_animation_frame(tmp_path, fig_test, fig_ref): # Test the expected image after iterating through a few frames # we save the animation to get the iteration because we are not # in an interactive framework. @@ -473,8 +454,7 @@ def animate(i): anim = animation.FuncAnimation( fig_test, animate, init_func=init, frames=5, blit=True, repeat=False) - with tmpdir.as_cwd(): - anim.save("test.gif") + anim.save(tmp_path / "test.gif") # Reference figure without animation ax = fig_ref.add_subplot() @@ -545,9 +525,28 @@ def test_disable_cache_warning(anim): def test_movie_writer_invalid_path(anim): if sys.platform == "win32": - match_str = r"\[WinError 3] .*'\\\\foo\\\\bar\\\\aardvark'" + match_str = r"\[WinError 3] .*\\\\foo\\\\bar\\\\aardvark'" + elif sys.platform == "emscripten": + match_str = r"\[Errno 44] .*'/foo" else: match_str = r"\[Errno 2] .*'/foo" with pytest.raises(FileNotFoundError, match=match_str): anim.save("/foo/bar/aardvark/thiscannotreallyexist.mp4", writer=animation.FFMpegFileWriter()) + + +def test_animation_with_transparency(): + """Test animation exhaustion with transparency using PillowWriter directly""" + fig, ax = plt.subplots() + rect = plt.Rectangle((0, 0), 1, 1, color='red', alpha=0.5) + ax.add_patch(rect) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + + writer = PillowWriter(fps=30) + writer.setup(fig, 'unused.gif', dpi=100) + writer.grab_frame(transparent=True) + frame = writer._frames[-1] + # Check that the alpha channel is not 255, so frame has transparency + assert frame.getextrema()[3][0] < 255 + plt.close(fig) diff --git a/lib/matplotlib/tests/test_api.py b/lib/matplotlib/tests/test_api.py index 8b0f1e70114e..4d0241264ddb 100644 --- a/lib/matplotlib/tests/test_api.py +++ b/lib/matplotlib/tests/test_api.py @@ -1,8 +1,9 @@ from __future__ import annotations +from collections.abc import Callable import re import typing -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar import numpy as np import pytest @@ -12,7 +13,7 @@ if typing.TYPE_CHECKING: - from typing_extensions import Self + from typing import Self T = TypeVar('T') @@ -48,6 +49,41 @@ def f(cls: Self) -> None: a.f +def test_warn_deprecated(): + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\.'): + _api.warn_deprecated('3.10', name='foo') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'The foo class was deprecated in Matplotlib 3\.10 and ' + r'will be removed in 3\.12\.'): + _api.warn_deprecated('3.10', name='foo', obj_type='class') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\. Use bar instead\.'): + _api.warn_deprecated('3.10', name='foo', alternative='bar') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\. More information\.'): + _api.warn_deprecated('3.10', name='foo', addendum='More information.') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 4\.0\.'): + _api.warn_deprecated('3.10', name='foo', removal='4.0') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10\.'): + _api.warn_deprecated('3.10', name='foo', removal=False) + with pytest.warns(PendingDeprecationWarning, + match=r'foo will be deprecated in a future version'): + _api.warn_deprecated('3.10', name='foo', pending=True) + with pytest.raises(ValueError, match=r'cannot have a scheduled removal'): + _api.warn_deprecated('3.10', name='foo', pending=True, removal='3.12') + with pytest.warns(mpl.MatplotlibDeprecationWarning, match=r'Complete replacement'): + _api.warn_deprecated('3.10', message='Complete replacement', name='foo', + alternative='bar', addendum='More information.', + obj_type='class', removal='4.0') + + def test_deprecate_privatize_attribute() -> None: class C: def __init__(self) -> None: self._attr = 1 @@ -114,3 +150,8 @@ def f() -> None: def test_empty_check_in_list() -> None: with pytest.raises(TypeError, match="No argument to check!"): _api.check_in_list(["a"]) + + +def test_check_in_list_numpy() -> None: + with pytest.raises(ValueError, match=r"array\(5\) is not a valid value"): + _api.check_in_list(['a', 'b'], value=np.array(5)) diff --git a/lib/matplotlib/tests/test_arrow_patches.py b/lib/matplotlib/tests/test_arrow_patches.py index 254b86cb54d6..08d3d62f0a84 100644 --- a/lib/matplotlib/tests/test_arrow_patches.py +++ b/lib/matplotlib/tests/test_arrow_patches.py @@ -11,8 +11,8 @@ def draw_arrow(ax, t, r): fc="b", ec='k')) -@image_comparison(['fancyarrow_test_image'], - tol=0.012 if platform.machine() == 'arm64' else 0) +@image_comparison(['fancyarrow_test_image.png'], + tol=0 if platform.machine() == 'x86_64' else 0.012) def test_fancyarrow(): # Added 0 to test division by zero error described in issue 3930 r = [0.4, 0.3, 0.2, 0.1, 0] @@ -28,7 +28,7 @@ def test_fancyarrow(): ax.tick_params(labelleft=False, labelbottom=False) -@image_comparison(['boxarrow_test_image.png']) +@image_comparison(['boxarrow_test_image.png'], style='mpl20') def test_boxarrow(): styles = mpatches.BoxStyle.get_styles() @@ -49,6 +49,48 @@ def test_boxarrow(): bbox=dict(boxstyle=stylename, fc="w", ec="k")) +@image_comparison(['boxarrow_adjustment_test_image.png'], style='mpl20') +def test_boxarrow_adjustment(): + + styles = ['larrow', 'rarrow', 'darrow'] + + # Cases [head_width, head_angle] to test for each arrow style + cases = [ + [1.5, 90], + [1.5, 170], # Test dynamic padding + [0.75, 30], + [0.5, -10], # Should just give a rectangle + [2, -90], + [2, -15] # None of arrow body is outside of head + ] + + # Horizontal and vertical spacings of arrow centres + spacing_horizontal = 3.75 + spacing_vertical = 1.6 + + # Numbers of styles and cases + m = len(styles) + n = len(cases) + + figwidth = (m * spacing_horizontal) + figheight = (n * spacing_vertical) + .5 + + fig = plt.figure(figsize=(figwidth / 1.5, figheight / 1.5)) + + fontsize = 0.3 * 72 + + for i, stylename in enumerate(styles): + for j, case in enumerate(cases): + # Draw arrow + fig.text( + ((m - i) * spacing_horizontal - 1.5) / figwidth, + ((n - j) * spacing_vertical - 0.5) / figheight, + stylename, ha='center', va='center', + size=fontsize, transform=fig.transFigure, + bbox=dict(boxstyle=f"{stylename}, head_width={case[0]}, \ + head_angle={case[1]}", fc="w", ec="k")) + + def __prepare_fancyarrow_dpi_cor_test(): """ Convenience function that prepares and returns a FancyArrowPatch. It aims @@ -59,8 +101,8 @@ def __prepare_fancyarrow_dpi_cor_test(): """ fig2 = plt.figure("fancyarrow_dpi_cor_test", figsize=(4, 3), dpi=50) ax = fig2.add_subplot() - ax.set_xlim([0, 1]) - ax.set_ylim([0, 1]) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) ax.add_patch(mpatches.FancyArrowPatch(posA=(0.3, 0.4), posB=(0.8, 0.6), lw=3, arrowstyle='->', mutation_scale=100)) @@ -149,7 +191,7 @@ def test_arrow_styles(): @image_comparison(['connection_styles.png'], style='mpl20', remove_text=True, - tol=0.013 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.013) def test_connection_styles(): styles = mpatches.ConnectionStyle.get_styles() diff --git a/lib/matplotlib/tests/test_artist.py b/lib/matplotlib/tests/test_artist.py index dbb5dd2305e0..976cbb4feae3 100644 --- a/lib/matplotlib/tests/test_artist.py +++ b/lib/matplotlib/tests/test_artist.py @@ -120,15 +120,15 @@ def test_clipping(): patch.set_clip_path(clip_path, ax2.transData) ax2.add_patch(patch) - ax1.set_xlim([-3, 3]) - ax1.set_ylim([-3, 3]) + ax1.set_xlim(-3, 3) + ax1.set_ylim(-3, 3) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_clipping_zoom(fig_test, fig_ref): # This test places the Axes and sets its limits such that the clip path is # outside the figure entirely. This should not break the clip path. - ax_test = fig_test.add_axes([0, 0, 1, 1]) + ax_test = fig_test.add_axes((0, 0, 1, 1)) l, = ax_test.plot([-3, 3], [-3, 3]) # Explicit Path instead of a Rectangle uses clip path processing, instead # of a clip box optimization. @@ -136,7 +136,7 @@ def test_clipping_zoom(fig_test, fig_ref): p = mpatches.PathPatch(p, transform=ax_test.transData) l.set_clip_path(p) - ax_ref = fig_ref.add_axes([0, 0, 1, 1]) + ax_ref = fig_ref.add_axes((0, 0, 1, 1)) ax_ref.plot([-3, 3], [-3, 3]) ax_ref.set(xlim=(0.5, 0.75), ylim=(0.5, 0.75)) @@ -208,7 +208,7 @@ def test_remove(): for art in [im, ln]: assert art.axes is None - assert art.figure is None + assert art.get_figure() is None assert im not in ax._mouseover_set assert fig.stale @@ -217,17 +217,14 @@ def test_remove(): @image_comparison(["default_edges.png"], remove_text=True, style='default') def test_default_edges(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2) ax1.plot(np.arange(10), np.arange(10), 'x', np.arange(10) + 1, np.arange(10), 'o') ax2.bar(np.arange(10), np.arange(10), align='edge') ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth')) - ax3.set_xlim((-1, 1)) - ax3.set_ylim((-1, 1)) + ax3.set_xlim(-1, 1) + ax3.set_ylim(-1, 1) pp1 = mpatches.PathPatch( mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)], [mpath.Path.MOVETO, mpath.Path.CURVE3, @@ -259,6 +256,36 @@ def test_setp(): assert sio.getvalue() == ' zorder: float\n' +def test_artist_set(): + line = mlines.Line2D([], []) + line.set(linewidth=7) + assert line.get_linewidth() == 7 + + # Property aliases should work + line.set(lw=5) + assert line.get_linewidth() == 5 + + +def test_artist_set_invalid_property_raises(): + """ + Test that set() raises AttributeError for invalid property names. + """ + line = mlines.Line2D([0, 1], [0, 1]) + + with pytest.raises(AttributeError, match="unexpected keyword argument"): + line.set(not_a_property=1) + + +def test_artist_set_duplicate_aliases_raises(): + """ + Test that set() raises TypeError when both a property and its alias are provided. + """ + line = mlines.Line2D([0, 1], [0, 1]) + + with pytest.raises(TypeError, match="aliases of one another"): + line.set(lw=2, linewidth=3) + + def test_None_zorder(): fig, ax = plt.subplots() ln, = ax.plot(range(5), zorder=None) @@ -562,3 +589,37 @@ def draw(self, renderer, extra): assert 'aardvark' == art.draw(renderer, 'aardvark') assert 'aardvark' == art.draw(renderer, extra='aardvark') + + +def test_get_figure(): + fig = plt.figure() + sfig1 = fig.subfigures() + sfig2 = sfig1.subfigures() + ax = sfig2.subplots() + + assert fig.get_figure(root=True) is fig + assert fig.get_figure(root=False) is fig + + assert ax.get_figure() is sfig2 + assert ax.get_figure(root=False) is sfig2 + assert ax.get_figure(root=True) is fig + + # SubFigure.get_figure has separate implementation but should give consistent + # results to other artists. + assert sfig2.get_figure(root=False) is sfig1 + assert sfig2.get_figure(root=True) is fig + # Currently different results by default. + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert sfig2.get_figure() is fig + # No deprecation warning if root and parent figure are the same. + assert sfig1.get_figure() is fig + + # An artist not yet attached to anything has no figure. + ln = mlines.Line2D([], []) + assert ln.get_figure(root=True) is None + assert ln.get_figure(root=False) is None + + # figure attribute is root for (Sub)Figures but parent for other artists. + assert ax.figure is sfig2 + assert fig.figure is fig + assert sfig2.figure is fig diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index dd37d3d8ee80..da3811a29db4 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1,13 +1,17 @@ import contextlib -from collections import namedtuple +from collections import namedtuple, deque import datetime from decimal import Decimal from functools import partial +import gc import inspect import io from itertools import product import platform +import re +import sys from types import SimpleNamespace +import unittest.mock import dateutil.tz @@ -21,8 +25,11 @@ from matplotlib import rc_context, patheffects import matplotlib.colors as mcolors import matplotlib.dates as mdates +from matplotlib.container import BarContainer from matplotlib.figure import Figure from matplotlib.axes import Axes +from matplotlib.lines import Line2D +from matplotlib.collections import PathCollection import matplotlib.font_manager as mfont_manager import matplotlib.markers as mmarkers import matplotlib.patches as mpatches @@ -33,20 +40,19 @@ import matplotlib.text as mtext import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms -import mpl_toolkits.axisartist as AA # type: ignore +import mpl_toolkits.axisartist as AA # type: ignore[import] from numpy.testing import ( assert_allclose, assert_array_equal, assert_array_almost_equal) from matplotlib.testing.decorators import ( image_comparison, check_figures_equal, remove_ticks_and_titles) from matplotlib.testing._markers import needs_usetex - # Note: Some test cases are run twice: once normally and once with labeled data # These two must be defined in the same test function or need to have # different baseline images to prevent race conditions when pytest runs # the tests with multiple threads. -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_invisible_axes(fig_test, fig_ref): ax = fig_test.subplots() ax.set_visible(False) @@ -136,23 +142,23 @@ def test_label_shift(): # Test label re-centering on x-axis ax.set_xlabel("Test label", loc="left") ax.set_xlabel("Test label", loc="center") - assert ax.xaxis.get_label().get_horizontalalignment() == "center" + assert ax.xaxis.label.get_horizontalalignment() == "center" ax.set_xlabel("Test label", loc="right") - assert ax.xaxis.get_label().get_horizontalalignment() == "right" + assert ax.xaxis.label.get_horizontalalignment() == "right" ax.set_xlabel("Test label", loc="center") - assert ax.xaxis.get_label().get_horizontalalignment() == "center" + assert ax.xaxis.label.get_horizontalalignment() == "center" # Test label re-centering on y-axis ax.set_ylabel("Test label", loc="top") ax.set_ylabel("Test label", loc="center") - assert ax.yaxis.get_label().get_horizontalalignment() == "center" + assert ax.yaxis.label.get_horizontalalignment() == "center" ax.set_ylabel("Test label", loc="bottom") - assert ax.yaxis.get_label().get_horizontalalignment() == "left" + assert ax.yaxis.label.get_horizontalalignment() == "left" ax.set_ylabel("Test label", loc="center") - assert ax.yaxis.get_label().get_horizontalalignment() == "center" + assert ax.yaxis.label.get_horizontalalignment() == "center" -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_acorr(fig_test, fig_ref): np.random.seed(19680801) Nx = 512 @@ -171,7 +177,7 @@ def test_acorr(fig_test, fig_ref): ax_ref.axhline(y=0, xmin=0, xmax=1) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_acorr_integers(fig_test, fig_ref): np.random.seed(19680801) Nx = 51 @@ -192,7 +198,7 @@ def test_acorr_integers(fig_test, fig_ref): ax_ref.axhline(y=0, xmin=0, xmax=1) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_spy(fig_test, fig_ref): np.random.seed(19680801) a = np.ones(32 * 32) @@ -222,7 +228,7 @@ def test_spy_invalid_kwargs(): ax.spy(np.eye(3, 3), **unsupported_kw) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_matshow(fig_test, fig_ref): mpl.style.use("mpl20") a = np.random.rand(32, 32) @@ -233,18 +239,13 @@ def test_matshow(fig_test, fig_ref): ax_ref.xaxis.set_ticks_position('both') -@image_comparison(['formatter_ticker_001', - 'formatter_ticker_002', - 'formatter_ticker_003', - 'formatter_ticker_004', - 'formatter_ticker_005', - ], - tol=0.031 if platform.machine() == 'arm64' else 0) +@image_comparison([f'formatter_ticker_{i:03d}.png' for i in range(1, 6)], style='mpl20', + tol=0.03 if sys.platform == 'darwin' else 0) def test_formatter_ticker(): import matplotlib.testing.jpl_units as units units.register() - # This should affect the tick size. (Tests issue #543) + # This should not affect the tick size. (Tests issue #543) matplotlib.rcParams['lines.markeredgewidth'] = 30 # This essentially test to see if user specified labels get overwritten @@ -330,7 +331,7 @@ def test_strmethodformatter_auto_formatter(): assert ax.yaxis.get_minor_formatter().fmt == targ_strformatter.fmt -@image_comparison(["twin_axis_locators_formatters"]) +@image_comparison(["twin_axis_locators_formatters.png"], style='mpl20') def test_twin_axis_locators_formatters(): vals = np.linspace(0, 1, num=5, endpoint=True) locs = np.sin(np.pi * vals / 2.0) @@ -340,6 +341,7 @@ def test_twin_axis_locators_formatters(): fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) + ax1.margins(0) ax1.plot([0.1, 100], [0, 1]) ax1.yaxis.set_major_locator(majl) ax1.yaxis.set_minor_locator(minl) @@ -396,7 +398,7 @@ def test_twin_units(twin): @pytest.mark.parametrize('twin', ('x', 'y')) -@check_figures_equal(extensions=['png'], tol=0.19) +@check_figures_equal(tol=0.19) def test_twin_logscale(fig_test, fig_ref, twin): twin_func = f'twin{twin}' # test twinx or twiny set_scale = f'set_{twin}scale' @@ -440,7 +442,7 @@ def test_twin_logscale(fig_test, fig_ref, twin): @image_comparison(['twin_autoscale.png'], - tol=0.009 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.009) def test_twinx_axis_scales(): x = np.array([0, 0.5, 1]) y = 0.5 * x @@ -475,6 +477,42 @@ def test_twin_inherit_autoscale_setting(): assert not ax_y_off.get_autoscaley_on() +@pytest.mark.parametrize("twin", ("x", "y")) +def test_twin_respects_position_after_set_position(twin): + fig, ax = plt.subplots() + + ax.set_position([0.2, 0.2, 0.5, 0.5]) + ax2 = getattr(ax, f"twin{twin}")() + + assert_allclose(ax.get_position(original=True).bounds, + ax2.get_position(original=True).bounds) + + assert_allclose(ax.get_position(original=False).bounds, + ax2.get_position(original=False).bounds) + + +@pytest.mark.parametrize("twin", ("x", "y")) +def test_twin_keeps_layout_participation_for_layout_managed_axes(twin): + fig, ax = plt.subplots() + + ax2 = getattr(ax, f"twin{twin}")() + + assert ax.get_in_layout() + assert ax2.get_in_layout() + + +@pytest.mark.parametrize("twin", ("x", "y")) +def test_twin_stays_aligned_after_constrained_layout(twin): + fig, ax = plt.subplots(constrained_layout=True) + + ax.set_position([0.2, 0.2, 0.5, 0.5]) + ax2 = getattr(ax, f"twin{twin}")() + + fig.canvas.draw() + + assert_allclose(ax.get_position().bounds, ax2.get_position().bounds) + + def test_inverted_cla(): # GitHub PR #5450. Setting autoscale should reset # axes to be non-inverted. @@ -586,7 +624,7 @@ def test_cla_not_redefined_internally(): assert 'cla' not in klass.__dict__ -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_minorticks_on_rcParams_both(fig_test, fig_ref): with matplotlib.rc_context({"xtick.minor.visible": True, "ytick.minor.visible": True}): @@ -597,7 +635,7 @@ def test_minorticks_on_rcParams_both(fig_test, fig_ref): ax_ref.minorticks_on() -@image_comparison(["autoscale_tiny_range"], remove_text=True) +@image_comparison(["autoscale_tiny_range.png"], remove_text=True) def test_autoscale_tiny_range(): # github pull #904 fig, axs = plt.subplots(2, 2) @@ -667,7 +705,7 @@ def test_use_sticky_edges(): assert_allclose(ax.get_ylim(), (-0.5, 1.5)) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_sticky_shared_axes(fig_test, fig_ref): # Check that sticky edges work whether they are set in an Axes that is a # "leader" in a share, or an Axes that is a "follower". @@ -682,6 +720,35 @@ def test_sticky_shared_axes(fig_test, fig_ref): ax0.pcolormesh(Z) +@image_comparison(['sticky_tolerance.png'], remove_text=True, style="mpl20") +def test_sticky_tolerance(): + fig, axs = plt.subplots(2, 2) + + width = .1 + + axs.flat[0].bar(x=0, height=width, bottom=20000.6) + axs.flat[0].bar(x=1, height=width, bottom=20000.1) + + axs.flat[1].bar(x=0, height=-width, bottom=20000.6) + axs.flat[1].bar(x=1, height=-width, bottom=20000.1) + + axs.flat[2].barh(y=0, width=-width, left=-20000.6) + axs.flat[2].barh(y=1, width=-width, left=-20000.1) + + axs.flat[3].barh(y=0, width=width, left=-20000.6) + axs.flat[3].barh(y=1, width=width, left=-20000.1) + + +@image_comparison(['sticky_tolerance_cf.png'], remove_text=True, style="mpl20") +def test_sticky_tolerance_contourf(): + fig, ax = plt.subplots() + + x = y = [14496.71, 14496.75] + data = [[0, 1], [2, 3]] + + ax.contourf(x, y, data) + + def test_nargs_stem(): with pytest.raises(TypeError, match='0 were given'): # stem() takes 1-3 arguments. @@ -704,7 +771,7 @@ def test_nargs_pcolorfast(): ax.pcolorfast([(0, 1), (0, 2)], [[1, 2, 3], [1, 2, 3]]) -@image_comparison(['offset_points'], remove_text=True) +@image_comparison(['offset_points'], remove_text=True, style='mpl20') def test_basic_annotate(): # Setup some data t = np.arange(0.0, 5.0, 0.01) @@ -779,7 +846,7 @@ def test_annotate_signature(): assert p1 == p2 -@image_comparison(['fill_units.png'], savefig_kwarg={'dpi': 60}) +@image_comparison(['fill_units.png'], savefig_kwarg={'dpi': 60}, style='mpl20') def test_fill_units(): import matplotlib.testing.jpl_units as units units.register() @@ -824,7 +891,7 @@ def test_plot_format_kwarg_redundant(): plt.errorbar([0], [0], fmt='none', color='blue') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_errorbar_dashes(fig_test, fig_ref): x = [1, 2, 3, 4] y = np.sin(x) @@ -844,7 +911,7 @@ def test_errorbar_mapview_kwarg(): ax.errorbar(x=D.keys(), y=D.values(), xerr=D.values()) -@image_comparison(['single_point', 'single_point']) +@image_comparison(['single_point', 'single_point'], style='mpl20') def test_single_point(): # Issue #1796: don't let lines.marker affect the grid matplotlib.rcParams['lines.marker'] = 'o' @@ -862,23 +929,7 @@ def test_single_point(): ax2.plot('b', 'b', 'o', data=data) -@image_comparison(['single_date.png'], style='mpl20') -def test_single_date(): - - # use former defaults to match existing baseline image - plt.rcParams['axes.formatter.limits'] = -7, 7 - dt = mdates.date2num(np.datetime64('0000-12-31')) - - time1 = [721964.0] - data1 = [-65.54] - - fig, ax = plt.subplots(2, 1) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - ax[0].plot_date(time1 + dt, data1, 'o', color='r') - ax[1].plot(time1, data1, 'o', color='r') - - -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_shaped_data(fig_test, fig_ref): row = np.arange(10).reshape((1, -1)) col = np.arange(0, 100, 10).reshape((-1, 1)) @@ -903,8 +954,7 @@ def test_structured_data(): axs[1].plot("ones", "twos", "r", data=pts) -@image_comparison(['aitoff_proj'], extensions=["png"], - remove_text=True, style='mpl20') +@image_comparison(['aitoff_proj.png'], remove_text=True, style='mpl20') def test_aitoff_proj(): """ Test aitoff projection ref.: @@ -920,7 +970,7 @@ def test_aitoff_proj(): ax.plot(X.flat, Y.flat, 'o', markersize=4) -@image_comparison(['axvspan_epoch']) +@image_comparison(['axvspan_epoch.png'], style='mpl20') def test_axvspan_epoch(): import matplotlib.testing.jpl_units as units units.register() @@ -935,7 +985,7 @@ def test_axvspan_epoch(): ax.set_xlim(t0 - 5.0*dt, tf + 5.0*dt) -@image_comparison(['axhspan_epoch'], tol=0.02) +@image_comparison(['axhspan_epoch.png'], style='mpl20') def test_axhspan_epoch(): import matplotlib.testing.jpl_units as units units.register() @@ -1015,9 +1065,6 @@ def test_hexbin_pickable(): def test_hexbin_log(): # Issue #1636 (and also test log scaled colorbar) - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - np.random.seed(19680801) n = 100000 x = np.random.standard_normal(n) @@ -1071,7 +1118,7 @@ def test_hexbin_log_clim(): assert h.get_clim() == (2, 100) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_hexbin_mincnt_behavior_upon_C_parameter(fig_test, fig_ref): # see: gh:12926 datapoints = [ @@ -1136,7 +1183,7 @@ def test_inverted_limits(): assert ax.get_ylim() == (10, 1) -@image_comparison(['nonfinite_limits']) +@image_comparison(['nonfinite_limits'], style='mpl20') def test_nonfinite_limits(): x = np.arange(0., np.e, 0.01) # silence divide by zero warning from log(0) @@ -1150,7 +1197,7 @@ def test_nonfinite_limits(): @mpl.style.context('default') @pytest.mark.parametrize('plot_fun', ['scatter', 'plot', 'fill_between']) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_limits_empty_data(plot_fun, fig_test, fig_ref): # Check that plotting empty data doesn't change autoscaling of dates x = np.arange("2010-01-01", "2011-01-01", dtype="datetime64[D]") @@ -1168,7 +1215,7 @@ def test_limits_empty_data(plot_fun, fig_test, fig_ref): def test_imshow(): # use former defaults to match existing baseline image matplotlib.rcParams['image.interpolation'] = 'nearest' - # Create a NxN image + # Create an NxN image N = 100 (x, y) = np.indices((N, N)) x -= N//2 @@ -1185,15 +1232,14 @@ def test_imshow(): ax.imshow("r", data=data) -@image_comparison( - ['imshow_clip'], style='mpl20', - tol=1.24 if platform.machine() in ('aarch64', 'ppc64le', 's390x') else 0) +@image_comparison(['imshow_clip'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 1.24) def test_imshow_clip(): # As originally reported by Gellule Xg # use former defaults to match existing baseline image matplotlib.rcParams['image.interpolation'] = 'nearest' - # Create a NxN image + # Create an NxN image N = 100 (x, y) = np.indices((N, N)) x -= N//2 @@ -1265,8 +1311,8 @@ def test_fill_betweenx_input(y, x1, x2): ax.fill_betweenx(y, x1, x2) -@image_comparison(['fill_between_interpolate'], remove_text=True, - tol=0.012 if platform.machine() == 'arm64' else 0) +@image_comparison(['fill_between_interpolate.png'], remove_text=True, + tol=0 if platform.machine() == 'x86_64' else 0.012) def test_fill_between_interpolate(): x = np.arange(0.0, 2, 0.02) y1 = np.sin(2*np.pi*x) @@ -1290,7 +1336,7 @@ def test_fill_between_interpolate(): interpolate=True) -@image_comparison(['fill_between_interpolate_decreasing'], +@image_comparison(['fill_between_interpolate_decreasing.png'], style='mpl20', remove_text=True) def test_fill_between_interpolate_decreasing(): p = np.array([724.3, 700, 655]) @@ -1311,7 +1357,7 @@ def test_fill_between_interpolate_decreasing(): ax.set_ylim(800, 600) -@image_comparison(['fill_between_interpolate_nan'], remove_text=True) +@image_comparison(['fill_between_interpolate_nan.png'], remove_text=True) def test_fill_between_interpolate_nan(): # Tests fix for issue #18986. x = np.arange(10) @@ -1331,7 +1377,7 @@ def test_fill_between_interpolate_nan(): # test_symlog and test_symlog2 used to have baseline images in all three # formats, but the png and svg baselines got invalidated by the removal of # minor tick overstriking. -@image_comparison(['symlog.pdf']) +@image_comparison(['symlog.pdf'], style='mpl20') def test_symlog(): x = np.array([0, 1, 2, 4, 6, 9, 12, 24]) y = np.array([1000000, 500000, 100000, 100, 5, 0, 0, 0]) @@ -1370,7 +1416,8 @@ def test_pcolorargs_5205(): plt.pcolor(X, Y, list(Z[:-1, :-1])) -@image_comparison(['pcolormesh'], remove_text=True) +@image_comparison(['pcolormesh'], remove_text=True, + tol=0.11 if platform.machine() == 'aarch64' else 0) def test_pcolormesh(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -1394,7 +1441,7 @@ def test_pcolormesh(): ax3.pcolormesh(Qx, Qz, Zm, shading="gouraud") -@image_comparison(['pcolormesh_small'], extensions=["eps"]) +@image_comparison(['pcolormesh_small.eps']) def test_pcolormesh_small(): n = 3 x = np.linspace(-1.5, 1.5, n) @@ -1421,7 +1468,8 @@ def test_pcolormesh_small(): @image_comparison(['pcolormesh_alpha'], extensions=["png", "pdf"], - remove_text=True) + remove_text=True, + tol=0.4 if platform.machine() == "aarch64" else 0) def test_pcolormesh_alpha(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -1456,7 +1504,7 @@ def test_pcolormesh_alpha(): @pytest.mark.parametrize("dims,alpha", [(3, 1), (4, 0.5)]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_pcolormesh_rgba(fig_test, fig_ref, dims, alpha): ax = fig_test.subplots() c = np.ones((5, 6, dims), dtype=float) / 2 @@ -1466,11 +1514,43 @@ def test_pcolormesh_rgba(fig_test, fig_ref, dims, alpha): ax.pcolormesh(c[..., 0], cmap="gray", vmin=0, vmax=1, alpha=alpha) +@check_figures_equal() +def test_pcolormesh_nearest_noargs(fig_test, fig_ref): + x = np.arange(4) + y = np.arange(7) + X, Y = np.meshgrid(x, y) + C = X + Y + + ax = fig_test.subplots() + ax.pcolormesh(C, shading="nearest") + + ax = fig_ref.subplots() + ax.pcolormesh(x, y, C, shading="nearest") + + +@check_figures_equal() +def test_pcolormesh_log_scale(fig_test, fig_ref): + """ + Check that setting a log scale sets good default axis limits + when using pcolormesh. + """ + x = np.linspace(0, 1, 11) + y = np.linspace(1, 2, 5) + X, Y = np.meshgrid(x, y) + C = X + Y + + ax = fig_test.subplots() + ax.pcolormesh(X, Y, C) + ax.set_xscale('log') + + ax = fig_ref.subplots() + ax.pcolormesh(X, Y, C) + ax.set_xlim(1e-2, 1e1) + ax.set_xscale('log') + + @image_comparison(['pcolormesh_datetime_axis.png'], style='mpl20') def test_pcolormesh_datetime_axis(): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - fig = plt.figure() fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15) base = datetime.datetime(2013, 1, 1) @@ -1519,6 +1599,30 @@ def test_pcolor_datetime_axis(): label.set_rotation(30) +@check_figures_equal() +def test_pcolor_log_scale(fig_test, fig_ref): + """ + Check that setting a log scale sets good default axis limits + when using pcolor. + """ + x = np.linspace(0, 1, 11) + # Ensuring second x value always falls slightly above 0.1 prevents flakiness with + # numpy v1 #30882. This can be removed once we require numpy >= 2. + x[1] += 0.00001 + y = np.linspace(1, 2, 5) + X, Y = np.meshgrid(x, y) + C = X[:-1, :-1] + Y[:-1, :-1] + + ax = fig_test.subplots() + ax.pcolor(X, Y, C) + ax.set_xscale('log') + + ax = fig_ref.subplots() + ax.pcolor(X, Y, C) + ax.set_xlim(1e-1, 1e0) + ax.set_xscale('log') + + def test_pcolorargs(): n = 12 x = np.linspace(-1.5, 1.5, n) @@ -1542,7 +1646,9 @@ def test_pcolorargs(): x = np.ma.array(x, mask=(x < 0)) with pytest.raises(ValueError): ax.pcolormesh(x, y, Z[:-1, :-1]) - # Expect a warning with non-increasing coordinates + # If the X or Y coords do not possess monotonicity in their respective + # directions, a warning indicating a bad grid will be triggered. + # The case of specifying coordinates by inputting 1D arrays. x = [359, 0, 1] y = [-10, 10] X, Y = np.meshgrid(x, y) @@ -1550,6 +1656,27 @@ def test_pcolorargs(): with pytest.warns(UserWarning, match='are not monotonically increasing or decreasing'): ax.pcolormesh(X, Y, Z, shading='auto') + # The case of specifying coordinates by inputting 2D arrays. + x = np.linspace(-1, 1, 3) + y = np.linspace(-1, 1, 3) + X, Y = np.meshgrid(x, y) + Z = np.zeros(X.shape) + np.random.seed(19680801) + noise_X = np.random.random(X.shape) + noise_Y = np.random.random(Y.shape) + with pytest.warns(UserWarning, + match='are not monotonically increasing or ' + 'decreasing') as record: + # Small perturbations in coordinates will not disrupt the monotonicity + # of the X-coords and Y-coords in their respective directions. + # Therefore, no warnings will be triggered. + ax.pcolormesh(X+noise_X, Y+noise_Y, Z, shading='auto') + assert len(record) == 0 + # Large perturbations have disrupted the monotonicity of the X-coords + # and Y-coords in their respective directions, thus resulting in two + # bad grid warnings. + ax.pcolormesh(X+10*noise_X, Y+10*noise_Y, Z, shading='auto') + assert len(record) == 2 def test_pcolormesh_underflow_error(): @@ -1588,7 +1715,7 @@ def test_pcolorargs_with_read_only(): plt.pcolor(masked_X, masked_Y, masked_Z) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_pcolornearest(fig_test, fig_ref): ax = fig_test.subplots() x = np.arange(0, 10) @@ -1604,7 +1731,7 @@ def test_pcolornearest(fig_test, fig_ref): ax.pcolormesh(x2, y2, Z, shading='nearest') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_pcolornearestunits(fig_test, fig_ref): ax = fig_test.subplots() x = [datetime.datetime.fromtimestamp(x * 3600) for x in range(10)] @@ -1639,7 +1766,7 @@ def test_samesizepcolorflaterror(): @pytest.mark.parametrize('snap', [False, True]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_pcolorauto(fig_test, fig_ref, snap): ax = fig_test.subplots() x = np.arange(0, 10) @@ -1657,7 +1784,8 @@ def test_pcolorauto(fig_test, fig_ref, snap): ax.pcolormesh(x2, y2, Z, snap=snap) -@image_comparison(['canonical'], tol=0.02 if platform.machine() == 'arm64' else 0) +@image_comparison(['canonical'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.03) def test_canonical(): fig, ax = plt.subplots() ax.plot([1, 2, 3]) @@ -1742,7 +1870,7 @@ def test_marker_as_markerstyle(): ax.errorbar([1, 2, 3], [5, 4, 3], marker=m) -@image_comparison(['markevery'], remove_text=True) +@image_comparison(['markevery.png'], remove_text=True, style='mpl20') def test_markevery(): x = np.linspace(0, 10, 100) y = np.sin(x) * np.sqrt(x/10 + 0.5) @@ -1750,31 +1878,27 @@ def test_markevery(): # check marker only plot fig, ax = plt.subplots() ax.plot(x, y, 'o', label='default') - ax.plot(x, y, 'd', markevery=None, label='mark all') - ax.plot(x, y, 's', markevery=10, label='mark every 10') - ax.plot(x, y, '+', markevery=(5, 20), label='mark every 5 starting at 10') + ax.plot(x, y+1, 'd', markevery=None, label='mark all') + ax.plot(x, y+2, 's', markevery=10, label='mark every 10') + ax.plot(x, y+3, '+', markevery=(5, 20), label='mark every 20 starting at 5') ax.legend() -@image_comparison(['markevery_line'], remove_text=True, tol=0.005) +@image_comparison(['markevery_line.png'], remove_text=True, style='mpl20') def test_markevery_line(): - # TODO: a slight change in rendering between Inkscape versions may explain - # why one had to introduce a small non-zero tolerance for the SVG test - # to pass. One may try to remove this hack once Travis' Inkscape version - # is modern enough. FWIW, no failure with 0.92.3 on my computer (#11358). x = np.linspace(0, 10, 100) y = np.sin(x) * np.sqrt(x/10 + 0.5) # check line/marker combos fig, ax = plt.subplots() ax.plot(x, y, '-o', label='default') - ax.plot(x, y, '-d', markevery=None, label='mark all') - ax.plot(x, y, '-s', markevery=10, label='mark every 10') - ax.plot(x, y, '-+', markevery=(5, 20), label='mark every 5 starting at 10') + ax.plot(x, y+1, '-d', markevery=None, label='mark all') + ax.plot(x, y+2, '-s', markevery=10, label='mark every 10') + ax.plot(x, y+3, '-+', markevery=(5, 20), label='mark every 20 starting at 5') ax.legend() -@image_comparison(['markevery_linear_scales'], remove_text=True, tol=0.001) +@image_comparison(['markevery_linear_scales.png'], remove_text=True, tol=0.001) def test_markevery_linear_scales(): cases = [None, 8, @@ -1799,7 +1923,7 @@ def test_markevery_linear_scales(): plt.plot(x, y, 'o', ls='-', ms=4, markevery=case) -@image_comparison(['markevery_linear_scales_zoomed'], remove_text=True) +@image_comparison(['markevery_linear_scales_zoomed.png'], remove_text=True) def test_markevery_linear_scales_zoomed(): cases = [None, 8, @@ -1826,7 +1950,7 @@ def test_markevery_linear_scales_zoomed(): plt.ylim((1.1, 1.7)) -@image_comparison(['markevery_log_scales'], remove_text=True) +@image_comparison(['markevery_log_scales.png'], remove_text=True) def test_markevery_log_scales(): cases = [None, 8, @@ -1853,7 +1977,7 @@ def test_markevery_log_scales(): plt.plot(x, y, 'o', ls='-', ms=4, markevery=case) -@image_comparison(['markevery_polar'], style='default', remove_text=True) +@image_comparison(['markevery_polar.png'], style='default', remove_text=True) def test_markevery_polar(): cases = [None, 8, @@ -1877,7 +2001,7 @@ def test_markevery_polar(): plt.plot(theta, r, 'o', ls='-', ms=4, markevery=case) -@image_comparison(['markevery_linear_scales_nans'], remove_text=True) +@image_comparison(['markevery_linear_scales_nans.png'], remove_text=True) def test_markevery_linear_scales_nans(): cases = [None, 8, @@ -1912,7 +2036,8 @@ def test_marker_edges(): ax.plot(x+0.2, np.sin(x), 'y.', ms=30.0, mew=2, mec='b') -@image_comparison(['bar_tick_label_single.png', 'bar_tick_label_single.png']) +@image_comparison(['bar_tick_label_single.png', 'bar_tick_label_single.png'], + style='mpl20') def test_bar_tick_label_single(): # From 2516: plot bar with array of string labels for x axis ax = plt.gca() @@ -1935,7 +2060,7 @@ def test_bar_ticklabel_fail(): ax.bar([], []) -@image_comparison(['bar_tick_label_multiple.png']) +@image_comparison(['bar_tick_label_multiple.png'], style='mpl20') def test_bar_tick_label_multiple(): # From 2516: plot bar with array of string labels for x axis ax = plt.gca() @@ -1943,7 +2068,7 @@ def test_bar_tick_label_multiple(): align='center') -@image_comparison(['bar_tick_label_multiple_old_label_alignment.png']) +@image_comparison(['bar_tick_label_multiple_old_label_alignment.png'], style='mpl20') def test_bar_tick_label_multiple_old_alignment(): # Test that the alignment for class is backward compatible matplotlib.rcParams["ytick.alignment"] = "center" @@ -1952,7 +2077,7 @@ def test_bar_tick_label_multiple_old_alignment(): align='center') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_bar_decimal_center(fig_test, fig_ref): ax = fig_test.subplots() x0 = [1.5, 8.4, 5.3, 4.2] @@ -1966,7 +2091,7 @@ def test_bar_decimal_center(fig_test, fig_ref): ax.bar(x0, y0, align='center') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_barh_decimal_center(fig_test, fig_ref): ax = fig_test.subplots() x0 = [1.5, 8.4, 5.3, 4.2] @@ -1980,7 +2105,7 @@ def test_barh_decimal_center(fig_test, fig_ref): ax.barh(x0, y0, height=[0.5, 0.5, 1, 1], align='center') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_bar_decimal_width(fig_test, fig_ref): x = [1.5, 8.4, 5.3, 4.2] y = [1.1, 2.2, 3.3, 4.4] @@ -1994,7 +2119,7 @@ def test_bar_decimal_width(fig_test, fig_ref): ax.bar(x, y, width=w0, align='center') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_barh_decimal_height(fig_test, fig_ref): x = [1.5, 8.4, 5.3, 4.2] y = [1.1, 2.2, 3.3, 4.4] @@ -2024,7 +2149,7 @@ def test_bar_edgecolor_none_alpha(): assert rect.get_edgecolor() == (0, 0, 0, 0) -@image_comparison(['barh_tick_label.png']) +@image_comparison(['barh_tick_label.png'], style='mpl20') def test_barh_tick_label(): # From 2516: plot barh with array of string labels for y axis ax = plt.gca() @@ -2074,6 +2199,119 @@ def test_bar_datetime_start(): assert isinstance(ax.xaxis.get_major_formatter(), mdates.AutoDateFormatter) +@image_comparison(["grouped_bar.png"], style="mpl20") +def test_grouped_bar(): + data = { + 'data1': [1, 2, 3], + 'data2': [1.2, 2.2, 3.2], + 'data3': [1.4, 2.4, 3.4], + } + + fig, ax = plt.subplots() + ax.grouped_bar(data, tick_labels=['A', 'B', 'C'], + group_spacing=0.5, bar_spacing=0.1, + colors=['#1f77b4', '#58a1cf', '#abd0e6']) + ax.set_yticks([]) + + +@check_figures_equal() +def test_grouped_bar_list_of_datasets(fig_test, fig_ref): + categories = ['A', 'B'] + data1 = [1, 1.2] + data2 = [2, 2.4] + data3 = [3, 3.6] + + ax = fig_test.subplots() + ax.grouped_bar([data1, data2, data3], tick_labels=categories, + labels=["data1", "data2", "data3"]) + ax.legend() + + ax = fig_ref.subplots() + label_pos = np.array([0, 1]) + bar_width = 1 / (3 + 1.5) # 3 bars + 1.5 group_spacing + data_shift = -1 * bar_width + np.array([0, bar_width, 2 * bar_width]) + ax.bar(label_pos + data_shift[0], data1, width=bar_width, label="data1") + ax.bar(label_pos + data_shift[1], data2, width=bar_width, label="data2") + ax.bar(label_pos + data_shift[2], data3, width=bar_width, label="data3") + ax.set_xticks(label_pos, categories) + ax.legend() + + +@check_figures_equal() +def test_grouped_bar_dict_of_datasets(fig_test, fig_ref): + categories = ['A', 'B'] + data_dict = dict(data1=[1, 1.2], data2=[2, 2.4], data3=[3, 3.6]) + + ax = fig_test.subplots() + ax.grouped_bar(data_dict, tick_labels=categories) + ax.legend() + + ax = fig_ref.subplots() + ax.grouped_bar(data_dict.values(), tick_labels=categories, labels=data_dict.keys()) + ax.legend() + + +@check_figures_equal() +def test_grouped_bar_array(fig_test, fig_ref): + categories = ['A', 'B'] + array = np.array([[1, 2, 3], [1.2, 2.4, 3.6]]) + labels = ['data1', 'data2', 'data3'] + + ax = fig_test.subplots() + ax.grouped_bar(array, tick_labels=categories, labels=labels) + ax.legend() + + ax = fig_ref.subplots() + list_of_datasets = [column for column in array.T] + ax.grouped_bar(list_of_datasets, tick_labels=categories, labels=labels) + ax.legend() + + +@check_figures_equal() +def test_grouped_bar_dataframe(fig_test, fig_ref, pd): + categories = ['A', 'B'] + labels = ['data1', 'data2', 'data3'] + df = pd.DataFrame([[1, 2, 3], [1.2, 2.4, 3.6]], + index=categories, columns=labels) + + ax = fig_test.subplots() + ax.grouped_bar(df) + ax.legend() + + ax = fig_ref.subplots() + list_of_datasets = [df[col].to_numpy() for col in df.columns] + ax.grouped_bar(list_of_datasets, tick_labels=categories, labels=labels) + ax.legend() + + +def test_grouped_bar_return_value(): + fig, ax = plt.subplots() + ret = ax.grouped_bar([[1, 2, 3], [11, 12, 13]], tick_labels=['A', 'B', 'C']) + + assert len(ret.bar_containers) == 2 + for bc in ret.bar_containers: + assert isinstance(bc, BarContainer) + assert bc in ax.containers + + ret.remove() + for bc in ret.bar_containers: + assert bc not in ax.containers + + +def test_grouped_bar_hatch_sequence(): + """Each dataset should receive its own hatch pattern when a sequence is passed.""" + fig, ax = plt.subplots() + x = np.arange(2) + heights = [np.array([1, 2]), np.array([2, 3]), np.array([3, 4])] + hatches = ['//', 'xx', '..'] + containers = ax.grouped_bar(heights, positions=x, hatch=hatches) + + # Verify each dataset gets the corresponding hatch + for hatch, c in zip(hatches, containers.bar_containers): + for rect in c: + assert rect.get_hatch() == hatch + + def test_boxplot_dates_pandas(pd): # smoke test for boxplot and dates in pandas data = np.random.rand(5, 2) @@ -2117,9 +2355,8 @@ def test_pcolor_regression(pd): time_axis, y_axis = np.meshgrid(times, y_vals) shape = (len(y_vals) - 1, len(times) - 1) - z_data = np.arange(shape[0] * shape[1]) + z_data = np.arange(shape[0] * shape[1]).reshape(shape) - z_data.shape = shape try: register_matplotlib_converters() @@ -2214,7 +2451,7 @@ def test_pandas_minimal_plot(pd): plt.plot(df, df) -@image_comparison(['hist_log'], remove_text=True) +@image_comparison(['hist_log.png'], remove_text=True) def test_hist_log(): data0 = np.linspace(0, 1, 200)**3 data = np.concatenate([1 - data0, 1 + data0]) @@ -2222,7 +2459,7 @@ def test_hist_log(): ax.hist(data, fill=False, log=True) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_hist_log_2(fig_test, fig_ref): axs_test = fig_test.subplots(2, 3) axs_ref = fig_ref.subplots(2, 3) @@ -2248,6 +2485,21 @@ def test_hist_log_barstacked(): assert axs[0].get_ylim() == axs[1].get_ylim() +def test_hist_timedelta_raises(): + import numpy as np + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + + arr_np = np.array([1, 2, 5, 7], dtype="timedelta64[D]") + with pytest.raises(TypeError, match="does not currently support timedelta inputs"): + ax.hist(arr_np) + + arr_py = [datetime.timedelta(seconds=i) for i in range(5)] + with pytest.raises(TypeError, match="does not currently support timedelta inputs"): + ax.hist(arr_py) + + @image_comparison(['hist_bar_empty.png'], remove_text=True) def test_hist_bar_empty(): # From #3886: creating hist from empty dataset raises ValueError @@ -2296,7 +2548,7 @@ def test_hist_step_filled(): assert all(p.get_facecolor() == p.get_edgecolor() for p in patches) -@image_comparison(['hist_density.png']) +@image_comparison(['hist_density.png'], style='mpl20') def test_hist_density(): np.random.seed(19680801) data = np.random.standard_normal(2000) @@ -2372,6 +2624,15 @@ def test_hist_zorder(histtype, zorder): assert patch.get_zorder() == zorder +def test_hist_single_color_multiple_datasets(): + data = [[0, 1, 2], [3, 4, 5]] + _, _, bar_containers = plt.hist(data, color='k') + for p in bar_containers[0].patches: + assert mcolors.same_color(p.get_facecolor(), 'k') + for p in bar_containers[1].patches: + assert mcolors.same_color(p.get_facecolor(), 'k') + + def test_stairs_no_baseline_fill_warns(): fig, ax = plt.subplots() with pytest.warns(UserWarning, match="baseline=None and fill=True"): @@ -2384,7 +2645,7 @@ def test_stairs_no_baseline_fill_warns(): ) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_stairs(fig_test, fig_ref): import matplotlib.lines as mlines y = np.array([6, 14, 32, 37, 48, 32, 21, 4]) # hist @@ -2428,7 +2689,7 @@ def test_stairs(fig_test, fig_ref): ref_axes[5].semilogx() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_stairs_fill(fig_test, fig_ref): h, bins = [1, 2, 3, 4, 2], [0, 1, 2, 3, 4, 5] bs = -2 @@ -2454,7 +2715,7 @@ def test_stairs_fill(fig_test, fig_ref): ref_axes[3].set_xlim(bs, None) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_stairs_update(fig_test, fig_ref): # fixed ylim because stairs() does autoscale, but updating data does not ylim = -3, 4 @@ -2478,7 +2739,7 @@ def test_stairs_update(fig_test, fig_ref): ref_ax.set_ylim(ylim) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_stairs_baseline_None(fig_test, fig_ref): x = np.array([0, 2, 3, 5, 10]) y = np.array([1.148, 1.231, 1.248, 1.25]) @@ -2521,7 +2782,7 @@ def test_stairs_invalid_update2(): h.set_data(edges=np.arange(5)) -@image_comparison(['test_stairs_options.png'], remove_text=True) +@image_comparison(['test_stairs_options.png'], style='mpl20', remove_text=True) def test_stairs_options(): x, y = np.array([1, 2, 3, 4, 5]), np.array([1, 2, 3, 4]).astype(float) yn = y.copy() @@ -2545,7 +2806,7 @@ def test_stairs_options(): ax.legend(loc=0) -@image_comparison(['test_stairs_datetime.png']) +@image_comparison(['test_stairs_datetime.png'], style='mpl20') def test_stairs_datetime(): f, ax = plt.subplots(constrained_layout=True) ax.stairs(np.arange(36), @@ -2554,7 +2815,7 @@ def test_stairs_datetime(): plt.xticks(rotation=30) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_stairs_edge_handling(fig_test, fig_ref): # Test test_ax = fig_test.add_subplot() @@ -2578,13 +2839,12 @@ def test_contour_hatching(): x, y, z = contour_dat() fig, ax = plt.subplots() ax.contourf(x, y, z, 7, hatches=['/', '\\', '//', '-'], - cmap=mpl.colormaps['gray'], - extend='both', alpha=0.5) + cmap=mpl.colormaps['gray'].with_alpha(0.5), + extend='both') -@image_comparison( - ['contour_colorbar'], style='mpl20', - tol=0.54 if platform.machine() in ('aarch64', 'ppc64le', 's390x') else 0) +@image_comparison(['contour_colorbar'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.54) def test_contour_colorbar(): x, y, z = contour_dat() @@ -2606,7 +2866,7 @@ def test_contour_colorbar(): cbar.add_lines(cs2, erase=False) -@image_comparison(['hist2d', 'hist2d'], remove_text=True, style='mpl20') +@image_comparison(['hist2d.png', 'hist2d.png'], remove_text=True, style='mpl20') def test_hist2d(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -2624,7 +2884,7 @@ def test_hist2d(): ax.hist2d("x", "y", bins=10, data=data, rasterized=True) -@image_comparison(['hist2d_transpose'], remove_text=True, style='mpl20') +@image_comparison(['hist2d_transpose.png'], remove_text=True, style='mpl20') def test_hist2d_transpose(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -2645,6 +2905,16 @@ def test_hist2d_density(): obj.hist2d(x, y, density=True) +@mpl.style.context("mpl20") +def test_hist2d_autolimits(): + x, y = np.random.random((2, 100)) + ax = plt.figure().add_subplot() + ax.hist2d(x, y) + assert ax.get_xlim() == (x.min(), x.max()) + assert ax.get_ylim() == (y.min(), y.max()) + assert ax.get_autoscale_on() # Autolimits have not been disabled. + + class TestScatter: @image_comparison(['scatter'], style='mpl20', remove_text=True) def test_scatter_plot(self): @@ -2682,7 +2952,7 @@ def test_scatter_marker(self): edgecolors=['k', 'r', 'g', 'b'], marker=verts) - @image_comparison(['scatter_2D'], remove_text=True, extensions=['png']) + @image_comparison(['scatter_2D.png'], remove_text=True) def test_scatter_2D(self): x = np.arange(3) y = np.arange(2) @@ -2691,7 +2961,7 @@ def test_scatter_2D(self): fig, ax = plt.subplots() ax.scatter(x, y, c=z, s=200, edgecolors='face') - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_decimal(self, fig_test, fig_ref): x0 = np.array([1.5, 8.4, 5.3, 4.2]) y0 = np.array([1.1, 2.2, 3.3, 4.4]) @@ -2755,11 +3025,11 @@ def test_scatter_unfillable(self): def test_scatter_size_arg_size(self): x = np.arange(4) - with pytest.raises(ValueError, match='same size as x and y'): + with pytest.raises(ValueError, match='cannot be broadcast to match x and y'): plt.scatter(x, x, x[1:]) - with pytest.raises(ValueError, match='same size as x and y'): + with pytest.raises(ValueError, match='cannot be broadcast to match x and y'): plt.scatter(x[1:], x[1:], x) - with pytest.raises(ValueError, match='float array-like'): + with pytest.raises(ValueError, match='must be float'): plt.scatter(x, x, 'foo') def test_scatter_edgecolor_RGB(self): @@ -2771,11 +3041,10 @@ def test_scatter_edgecolor_RGB(self): edgecolor=(1, 0, 0, 1)) assert mcolors.same_color(coll.get_edgecolor(), (1, 0, 0, 1)) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_invalid_color(self, fig_test, fig_ref): ax = fig_test.subplots() - cmap = mpl.colormaps["viridis"].resampled(16) - cmap.set_bad("k", 1) + cmap = mpl.colormaps["viridis"].resampled(16).with_extremes(bad="black") # Set a nonuniform size to prevent the last call to `scatter` (plotting # the invalid points separately in fig_ref) from using the marker # stamping fast path, which would result in slightly offset markers. @@ -2787,12 +3056,11 @@ def test_scatter_invalid_color(self, fig_test, fig_ref): ax.scatter([0, 2], [0, 2], c=[1, 2], s=[1, 3], cmap=cmap) ax.scatter([1, 3], [1, 3], s=[2, 4], color="k") - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_no_invalid_color(self, fig_test, fig_ref): # With plotnonfinite=False we plot only 2 points. ax = fig_test.subplots() - cmap = mpl.colormaps["viridis"].resampled(16) - cmap.set_bad("k", 1) + cmap = mpl.colormaps["viridis"].resampled(16).with_extremes(bad="k") ax.scatter(range(4), range(4), c=[1, np.nan, 2, np.nan], s=[1, 2, 3, 4], cmap=cmap, plotnonfinite=False) @@ -2809,14 +3077,14 @@ def test_scatter_norm_vminvmax(self): ax.scatter(x, x, c=x, norm=mcolors.Normalize(-10, 10), vmin=0, vmax=5) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_single_point(self, fig_test, fig_ref): ax = fig_test.subplots() ax.scatter(1, 1, c=1) ax = fig_ref.subplots() ax.scatter([1], [1], c=[1]) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_different_shapes(self, fig_test, fig_ref): x = np.arange(10) ax = fig_test.subplots() @@ -2872,7 +3140,7 @@ def test_scatter_different_shapes(self, fig_test, fig_ref): @pytest.mark.parametrize('c_case, re_key', params_test_scatter_c) def test_scatter_c(self, c_case, re_key): - def get_next_color(): + def get_next_color(): # pragma: no cover return 'blue' # currently unused xsize = 4 @@ -2895,7 +3163,7 @@ def get_next_color(): get_next_color_func=get_next_color) @mpl.style.context('default') - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_scatter_single_color_c(self, fig_test, fig_ref): rgb = [[1, 0.5, 0.05]] rgba = [[1, 0.5, 0.05, .5]] @@ -2947,7 +3215,7 @@ def test_scatter_singular_plural_arguments(self): def _params(c=None, xsize=2, *, edgecolors=None, **kwargs): - return (c, edgecolors, kwargs if kwargs is not None else {}, xsize) + return (c, edgecolors, kwargs, xsize) _result = namedtuple('_result', 'c, colors') @@ -2966,7 +3234,7 @@ def _params(c=None, xsize=2, *, edgecolors=None, **kwargs): _result(c=['b', 'g'], colors=np.array([[0, 0, 1, 1], [0, .5, 0, 1]]))), ]) def test_parse_scatter_color_args(params, expected_result): - def get_next_color(): + def get_next_color(): # pragma: no cover return 'blue' # currently unused c, colors, _edgecolors = mpl.axes.Axes._parse_scatter_color_args( @@ -2993,7 +3261,7 @@ def get_next_color(): (dict(color='r', edgecolor='g'), 'g'), ]) def test_parse_scatter_color_args_edgecolors(kwargs, expected_edgecolors): - def get_next_color(): + def get_next_color(): # pragma: no cover return 'blue' # currently unused c = kwargs.pop('c', None) @@ -3005,7 +3273,7 @@ def get_next_color(): def test_parse_scatter_color_args_error(): - def get_next_color(): + def get_next_color(): # pragma: no cover return 'blue' # currently unused with pytest.raises(ValueError, @@ -3015,6 +3283,55 @@ def get_next_color(): c, None, kwargs={}, xsize=2, get_next_color_func=get_next_color) +# Warning message tested in the next two tests. +WARN_MSG = ( + "You passed both c and facecolor/facecolors for the markers. " + "c has precedence over facecolor/facecolors. This behavior may " + "change in the future." +) +# Test cases shared between direct and integration tests +COLOR_TEST_CASES = [ + ('red', 'blue'), + (['red', 'blue'], ['green', 'yellow']), + ([[1, 0, 0], [0, 1, 0]], [[0, 0, 1], [1, 1, 0]]) +] + + +@pytest.mark.parametrize('c, facecolor', COLOR_TEST_CASES) +def test_parse_c_facecolor_warning_direct(c, facecolor): + """Test the internal _parse_scatter_color_args method directly.""" + def get_next_color(): # pragma: no cover + return 'blue' # currently unused + + # Test with facecolors (plural) + with pytest.warns(UserWarning, match=WARN_MSG): + mpl.axes.Axes._parse_scatter_color_args( + c=c, edgecolors=None, kwargs={'facecolors': facecolor}, + xsize=2, get_next_color_func=get_next_color) + + # Test with facecolor (singular) + with pytest.warns(UserWarning, match=WARN_MSG): + mpl.axes.Axes._parse_scatter_color_args( + c=c, edgecolors=None, kwargs={'facecolor': facecolor}, + xsize=2, get_next_color_func=get_next_color) + + +@pytest.mark.parametrize('c, facecolor', COLOR_TEST_CASES) +def test_scatter_c_facecolor_warning_integration(c, facecolor): + """Test the warning through the actual scatter plot creation.""" + fig, ax = plt.subplots() + x = [0, 1] if isinstance(c, (list, tuple)) else [0] + y = x + + # Test with facecolors (plural) + with pytest.warns(UserWarning, match=WARN_MSG): + ax.scatter(x, y, c=c, facecolors=facecolor) + + # Test with facecolor (singular) + with pytest.warns(UserWarning, match=WARN_MSG): + ax.scatter(x, y, c=c, facecolor=facecolor) + + def test_as_mpl_axes_api(): # tests the _as_mpl_axes api class Polar: @@ -3057,10 +3374,10 @@ def test_log_scales(): ax.set_yscale('log', base=5.5) ax.invert_yaxis() ax.set_xscale('log', base=9.0) - xticks, yticks = [ + xticks, yticks = ( [(t.get_loc(), t.label1.get_text()) for t in axis._update_ticks()] for axis in [ax.xaxis, ax.yaxis] - ] + ) assert xticks == [ (1.0, '$\\mathdefault{9^{0}}$'), (9.0, '$\\mathdefault{9^{1}}$'), @@ -3112,8 +3429,8 @@ def test_log_scales_invalid(): ax.set_ylim(-1, 10) -@image_comparison(['stackplot_test_image', 'stackplot_test_image'], - tol=0.031 if platform.machine() == 'arm64' else 0) +@image_comparison(['stackplot_test_image.png', 'stackplot_test_image.png'], + style='mpl20') def test_stackplot(): fig = plt.figure() x = np.linspace(0, 10, 10) @@ -3122,19 +3439,19 @@ def test_stackplot(): y3 = 3.0 * x + 2 ax = fig.add_subplot(1, 1, 1) ax.stackplot(x, y1, y2, y3) - ax.set_xlim((0, 10)) - ax.set_ylim((0, 70)) + ax.set_xlim(0, 10) + ax.set_ylim(0, 70) # Reuse testcase from above for a test with labeled data and with colours # from the Axes property cycle. data = {"x": x, "y1": y1, "y2": y2, "y3": y3} fig, ax = plt.subplots() ax.stackplot("x", "y1", "y2", "y3", data=data, colors=["C0", "C1", "C2"]) - ax.set_xlim((0, 10)) - ax.set_ylim((0, 70)) + ax.set_xlim(0, 10) + ax.set_ylim(0, 70) -@image_comparison(['stackplot_test_baseline'], remove_text=True) +@image_comparison(['stackplot_test_baseline.png'], remove_text=True) def test_stackplot_baseline(): np.random.seed(0) @@ -3168,16 +3485,50 @@ def test_stackplot_hatching(fig_ref, fig_test): # stackplot with different hatching styles (issue #27146) ax_test = fig_test.subplots() ax_test.stackplot(x, y1, y2, y3, hatch=["x", "//", "\\\\"], colors=["white"]) - ax_test.set_xlim((0, 10)) - ax_test.set_ylim((0, 70)) + ax_test.set_xlim(0, 10) + ax_test.set_ylim(0, 70) # compare with result from hatching each layer individually stack_baseline = np.zeros(len(x)) ax_ref = fig_ref.subplots() ax_ref.fill_between(x, stack_baseline, y1, hatch="x", facecolor="white") ax_ref.fill_between(x, y1, y1+y2, hatch="//", facecolor="white") ax_ref.fill_between(x, y1+y2, y1+y2+y3, hatch="\\\\", facecolor="white") - ax_ref.set_xlim((0, 10)) - ax_ref.set_ylim((0, 70)) + ax_ref.set_xlim(0, 10) + ax_ref.set_ylim(0, 70) + + +def test_stackplot_facecolor(): + # Test that facecolors are properly passed and take precedence over colors parameter + x = np.linspace(0, 10, 10) + y1 = 1.0 * x + y2 = 2.0 * x + 1 + + facecolors = ['r', 'b'] + + fig, ax = plt.subplots() + + colls = ax.stackplot(x, y1, y2, facecolor=facecolors, colors=['c', 'm']) + for coll, fcolor in zip(colls, facecolors): + assert mcolors.same_color(coll.get_facecolor(), fcolor) + + # Plural alias should also work + colls = ax.stackplot(x, y1, y2, facecolors=facecolors, colors=['c', 'm']) + for coll, fcolor in zip(colls, facecolors): + assert mcolors.same_color(coll.get_facecolor(), fcolor) + + +def test_stackplot_subfig_legend(): + # Smoke test for https://github.com/matplotlib/matplotlib/issues/30158 + + fig = plt.figure() + subfigs = fig.subfigures(nrows=1, ncols=2) + + for _fig in subfigs: + ax = _fig.subplots(nrows=1, ncols=1) + ax.stackplot([3, 4], [[1, 2]], labels=['a']) + + fig.legend() + fig.draw_without_rendering() def _bxp_test_helper( @@ -3240,10 +3591,7 @@ def test_bxp_horizontal(): _bxp_test_helper(bxp_kwargs=dict(orientation='horizontal')) -@image_comparison(['bxp_with_ylabels.png'], - savefig_kwarg={'dpi': 40}, - style='default', - tol=0.1) +@image_comparison(['bxp_with_ylabels.png'], savefig_kwarg={'dpi': 40}, style='default') def test_bxp_with_ylabels(): def transform(stats): for s, label in zip(stats, list('ABCD')): @@ -3444,7 +3792,7 @@ def test_bxp_bad_capwidths(): _bxp_test_helper(bxp_kwargs=dict(capwidths=[1])) -@image_comparison(['boxplot', 'boxplot'], tol=1.28, style='default') +@image_comparison(['boxplot.png', 'boxplot.png'], tol=0.43, style='default') def test_boxplot(): # Randomness used for bootstrapping. np.random.seed(937) @@ -3454,16 +3802,16 @@ def test_boxplot(): fig, ax = plt.subplots() ax.boxplot([x, x], bootstrap=10000, notch=1) - ax.set_ylim((-30, 30)) + ax.set_ylim(-30, 30) # Reuse testcase from above for a labeled data test data = {"x": [x, x]} fig, ax = plt.subplots() ax.boxplot("x", bootstrap=10000, notch=1, data=data) - ax.set_ylim((-30, 30)) + ax.set_ylim(-30, 30) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_boxplot_masked(fig_test, fig_ref): # Check that masked values are ignored when plotting a boxplot x_orig = np.linspace(-1, 1, 200) @@ -3498,10 +3846,10 @@ def test_boxplot_sym2(): fig, [ax1, ax2] = plt.subplots(1, 2) ax1.boxplot([x, x], bootstrap=10000, sym='^') - ax1.set_ylim((-30, 30)) + ax1.set_ylim(-30, 30) ax2.boxplot([x, x], bootstrap=10000, sym='g') - ax2.set_ylim((-30, 30)) + ax2.set_ylim(-30, 30) @image_comparison(['boxplot_sym.png'], @@ -3514,7 +3862,7 @@ def test_boxplot_sym(): fig, ax = plt.subplots() ax.boxplot([x, x], sym='gs') - ax.set_ylim((-30, 30)) + ax.set_ylim(-30, 30) @image_comparison(['boxplot_autorange_false_whiskers.png', @@ -3529,11 +3877,11 @@ def test_boxplot_autorange_whiskers(): fig1, ax1 = plt.subplots() ax1.boxplot([x, x], bootstrap=10000, notch=1) - ax1.set_ylim((-5, 5)) + ax1.set_ylim(-5, 5) fig2, ax2 = plt.subplots() ax2.boxplot([x, x], bootstrap=10000, notch=1, autorange=True) - ax2.set_ylim((-5, 5)) + ax2.set_ylim(-5, 5) def _rc_test_bxp_helper(ax, rc_dict): @@ -3544,7 +3892,7 @@ def _rc_test_bxp_helper(ax, rc_dict): return ax -@image_comparison(['boxplot_rc_parameters'], +@image_comparison(['boxplot_rc_parameters.png'], savefig_kwarg={'dpi': 100}, remove_text=True, tol=1, style='default') def test_boxplot_rc_parameters(): @@ -3623,7 +3971,7 @@ def test_boxplot_with_CIarray(): # another with manual values ax.boxplot([x, x], bootstrap=10000, usermedians=[None, 1.0], conf_intervals=CIs, notch=1) - ax.set_ylim((-30, 30)) + ax.set_ylim(-30, 30) @image_comparison(['boxplot_no_inverted_whisker.png'], @@ -3695,7 +4043,7 @@ def test_boxplot_mod_artist_after_plotting(): @image_comparison(['violinplot_vert_baseline.png', - 'violinplot_vert_baseline.png']) + 'violinplot_vert_baseline.png'], style='mpl20') def test_vert_violinplot_baseline(): # First 9 digits of frac(sqrt(2)) np.random.seed(414213562) @@ -3711,7 +4059,7 @@ def test_vert_violinplot_baseline(): showmedians=False, data=data) -@image_comparison(['violinplot_vert_showmeans.png']) +@image_comparison(['violinplot_vert_showmeans.png'], style='mpl20') def test_vert_violinplot_showmeans(): ax = plt.axes() # First 9 digits of frac(sqrt(3)) @@ -3721,7 +4069,7 @@ def test_vert_violinplot_showmeans(): showmedians=False) -@image_comparison(['violinplot_vert_showextrema.png']) +@image_comparison(['violinplot_vert_showextrema.png'], style='mpl20') def test_vert_violinplot_showextrema(): ax = plt.axes() # First 9 digits of frac(sqrt(5)) @@ -3731,7 +4079,7 @@ def test_vert_violinplot_showextrema(): showmedians=False) -@image_comparison(['violinplot_vert_showmedians.png']) +@image_comparison(['violinplot_vert_showmedians.png'], style='mpl20') def test_vert_violinplot_showmedians(): ax = plt.axes() # First 9 digits of frac(sqrt(7)) @@ -3741,7 +4089,7 @@ def test_vert_violinplot_showmedians(): showmedians=True) -@image_comparison(['violinplot_vert_showall.png']) +@image_comparison(['violinplot_vert_showall.png'], style='mpl20') def test_vert_violinplot_showall(): ax = plt.axes() # First 9 digits of frac(sqrt(11)) @@ -3752,7 +4100,7 @@ def test_vert_violinplot_showall(): quantiles=[[0.1, 0.9], [0.2, 0.8], [0.3, 0.7], [0.4, 0.6]]) -@image_comparison(['violinplot_vert_custompoints_10.png']) +@image_comparison(['violinplot_vert_custompoints_10.png'], style='mpl20') def test_vert_violinplot_custompoints_10(): ax = plt.axes() # First 9 digits of frac(sqrt(13)) @@ -3762,7 +4110,7 @@ def test_vert_violinplot_custompoints_10(): showmedians=False, points=10) -@image_comparison(['violinplot_vert_custompoints_200.png']) +@image_comparison(['violinplot_vert_custompoints_200.png'], style='mpl20') def test_vert_violinplot_custompoints_200(): ax = plt.axes() # First 9 digits of frac(sqrt(17)) @@ -3772,7 +4120,7 @@ def test_vert_violinplot_custompoints_200(): showmedians=False, points=200) -@image_comparison(['violinplot_horiz_baseline.png']) +@image_comparison(['violinplot_horiz_baseline.png'], style='mpl20') def test_horiz_violinplot_baseline(): ax = plt.axes() # First 9 digits of frac(sqrt(19)) @@ -3782,7 +4130,7 @@ def test_horiz_violinplot_baseline(): showextrema=False, showmedians=False) -@image_comparison(['violinplot_horiz_showmedians.png']) +@image_comparison(['violinplot_horiz_showmedians.png'], style='mpl20') def test_horiz_violinplot_showmedians(): ax = plt.axes() # First 9 digits of frac(sqrt(23)) @@ -3792,7 +4140,7 @@ def test_horiz_violinplot_showmedians(): showextrema=False, showmedians=True) -@image_comparison(['violinplot_horiz_showmeans.png']) +@image_comparison(['violinplot_horiz_showmeans.png'], style='mpl20') def test_horiz_violinplot_showmeans(): ax = plt.axes() # First 9 digits of frac(sqrt(29)) @@ -3802,7 +4150,7 @@ def test_horiz_violinplot_showmeans(): showextrema=False, showmedians=False) -@image_comparison(['violinplot_horiz_showextrema.png']) +@image_comparison(['violinplot_horiz_showextrema.png'], style='mpl20') def test_horiz_violinplot_showextrema(): ax = plt.axes() # First 9 digits of frac(sqrt(31)) @@ -3812,7 +4160,7 @@ def test_horiz_violinplot_showextrema(): showextrema=True, showmedians=False) -@image_comparison(['violinplot_horiz_showall.png']) +@image_comparison(['violinplot_horiz_showall.png'], style='mpl20') def test_horiz_violinplot_showall(): ax = plt.axes() # First 9 digits of frac(sqrt(37)) @@ -3823,7 +4171,7 @@ def test_horiz_violinplot_showall(): quantiles=[[0.1, 0.9], [0.2, 0.8], [0.3, 0.7], [0.4, 0.6]]) -@image_comparison(['violinplot_horiz_custompoints_10.png']) +@image_comparison(['violinplot_horiz_custompoints_10.png'], style='mpl20') def test_horiz_violinplot_custompoints_10(): ax = plt.axes() # First 9 digits of frac(sqrt(41)) @@ -3833,7 +4181,7 @@ def test_horiz_violinplot_custompoints_10(): showextrema=False, showmedians=False, points=10) -@image_comparison(['violinplot_horiz_custompoints_200.png']) +@image_comparison(['violinplot_horiz_custompoints_200.png'], style='mpl20') def test_horiz_violinplot_custompoints_200(): ax = plt.axes() # First 9 digits of frac(sqrt(43)) @@ -3858,6 +4206,85 @@ def test_violinplot_sides(): showextrema=True, showmedians=True, side=side) +def violin_plot_stats(): + datetimes = [ + datetime.datetime(2023, 2, 10), + datetime.datetime(2023, 5, 18), + datetime.datetime(2023, 6, 6) + ] + return [{ + 'coords': datetimes, + 'vals': [1.2, 2.8, 1.5], + 'mean': 1.84, + 'median': 1.5, + 'min': 1.2, + 'max': 2.8, + 'quantiles': [1.2, 1.5, 2.8] + }, { + 'coords': datetimes, + 'vals': [0.8, 1.1, 0.9], + 'mean': 0.94, + 'median': 0.9, + 'min': 0.8, + 'max': 1.1, + 'quantiles': [0.8, 0.9, 1.1] + }] + + +def test_datetime_positions_with_datetime64(): + """Test that datetime positions with float widths raise TypeError.""" + fig, ax = plt.subplots() + positions = [np.datetime64('2020-01-01'), np.datetime64('2021-01-01')] + widths = [0.5, 1.0] + with pytest.raises(TypeError, + match=("np.datetime64 'position' values require " + "np.timedelta64 'widths'")): + ax.violin(violin_plot_stats(), positions=positions, widths=widths) + + +def test_datetime_positions_with_float_widths_raises(): + """Test that datetime positions with float widths raise TypeError.""" + fig, ax = plt.subplots() + positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)] + widths = [0.5, 1.0] + with pytest.raises(TypeError, + match=("datetime/date 'position' values require " + "timedelta 'widths'")): + ax.violin(violin_plot_stats(), positions=positions, widths=widths) + + +def test_datetime_positions_with_scalar_float_width_raises(): + """Test that datetime positions with scalar float width raise TypeError.""" + fig, ax = plt.subplots() + positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)] + widths = 0.75 + with pytest.raises(TypeError, + match=("datetime/date 'position' values require " + "timedelta 'widths'")): + ax.violin(violin_plot_stats(), positions=positions, widths=widths) + + +def test_numeric_positions_with_float_widths_ok(): + """Test that numeric positions with float widths work.""" + fig, ax = plt.subplots() + positions = [1.0, 2.0] + widths = [0.5, 1.0] + ax.violin(violin_plot_stats(), positions=positions, widths=widths) + + +def test_mixed_positions_datetime_and_numeric_raises(): + """Test that mixed datetime and numeric positions + with float widths raise TypeError. + """ + fig, ax = plt.subplots() + positions = [datetime.datetime(2020, 1, 1), 2.0] + widths = [0.5, 1.0] + with pytest.raises(TypeError, + match=("datetime/date 'position' values require " + "timedelta 'widths'")): + ax.violin(violin_plot_stats(), positions=positions, widths=widths) + + def test_violinplot_bad_positions(): ax = plt.axes() # First 9 digits of frac(sqrt(47)) @@ -3902,7 +4329,111 @@ def test_violinplot_outofrange_quantiles(): ax.violinplot(data, quantiles=[[-0.05, 0.2, 0.3, 0.75]]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() +def test_violinplot_color_specification(fig_test, fig_ref): + # Ensures that setting colors in violinplot constructor works + # the same way as setting the color of each object manually + np.random.seed(19680801) + data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 4)] + kwargs = {'showmeans': True, + 'showextrema': True, + 'showmedians': True + } + + def color_violins(parts, facecolor=None, linecolor=None): + """Helper to color parts manually.""" + if facecolor is not None: + for pc in parts['bodies']: + pc.set_facecolor(facecolor) + # disable alpha Artist property to counter the legacy behavior + # that applies an alpha of 0.3 to the bodies if no facecolor + # was set + pc.set_alpha(None) + if linecolor is not None: + for partname in ('cbars', 'cmins', 'cmaxes', 'cmeans', 'cmedians'): + if partname in parts: + lc = parts[partname] + lc.set_edgecolor(linecolor) + + # Reference image + ax = fig_ref.subplots(1, 3) + parts0 = ax[0].violinplot(data, **kwargs) + parts1 = ax[1].violinplot(data, **kwargs) + parts2 = ax[2].violinplot(data, **kwargs) + + color_violins(parts0, facecolor=('r', 0.5), linecolor=('r', 0.2)) + color_violins(parts1, facecolor='r') + color_violins(parts2, linecolor='r') + + # Test image + ax = fig_test.subplots(1, 3) + ax[0].violinplot(data, facecolor=('r', 0.5), linecolor=('r', 0.2), **kwargs) + ax[1].violinplot(data, facecolor='r', **kwargs) + ax[2].violinplot(data, linecolor='r', **kwargs) + + +def test_violinplot_color_sequence(): + # Ensures that setting a sequence of colors works the same as setting + # each color independently + np.random.seed(19680801) + data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 5)] + kwargs = {'showmeans': True, 'showextrema': True, 'showmedians': True} + + def assert_colors_equal(colors1, colors2): + assert all(mcolors.same_color(c1, c2) + for c1, c2 in zip(colors1, colors2)) + + # Color sequence + N = len(data) + positions = range(N) + facecolors = ['k', 'r', ('b', 0.5), ('g', 0.2)] + linecolors = [('y', 0.4), 'b', 'm', ('k', 0.8)] + + # Test image + fig_test = plt.figure() + ax = fig_test.gca() + parts_test = ax.violinplot(data, + positions=positions, + facecolor=facecolors, + linecolor=linecolors, + **kwargs) + + body_colors = [p.get_facecolor() for p in parts_test["bodies"]] + assert_colors_equal(body_colors, mcolors.to_rgba_array(facecolors)) + + for part in ["cbars", "cmins", "cmaxes", "cmeans", "cmedians"]: + colors_test = parts_test[part].get_edgecolor() + assert_colors_equal(colors_test, mcolors.to_rgba_array(linecolors)) + + +def test_violinplot_alpha(): + matplotlib.style.use('default') + data = [(np.random.normal(0, 1, 100))] + + fig, ax = plt.subplots() + parts = ax.violinplot(data, positions=[1]) + + # Case 1: If facecolor is unspecified, it's the first color from the color cycle + # with Artist-level alpha=0.3 + facecolor = ('y' if mpl.rcParams['_internal.classic_mode'] + else plt.rcParams['axes.prop_cycle'].by_key()['color'][0]) + assert mcolors.same_color(parts['bodies'][0].get_facecolor(), (facecolor, 0.3)) + assert parts['bodies'][0].get_alpha() == 0.3 + # setting a new facecolor maintains the alpha + parts['bodies'][0].set_facecolor('red') + assert mcolors.same_color(parts['bodies'][0].get_facecolor(), ('red', 0.3)) + + # Case 2: If facecolor is explicitly given, it's alpha does not become an + # Artist property + parts = ax.violinplot(data, positions=[1], facecolor=('blue', 0.3)) + assert mcolors.same_color(parts['bodies'][0].get_facecolor(), ('blue', 0.3)) + assert parts['bodies'][0].get_alpha() is None + # so setting a new color does not maintain the alpha + parts['bodies'][0].set_facecolor('red') + assert mcolors.same_color(parts['bodies'][0].get_facecolor(), 'red') + + +@check_figures_equal() def test_violinplot_single_list_quantiles(fig_test, fig_ref): # Ensures quantile list for 1D can be passed in as single list # First 9 digits of frac(sqrt(83)) @@ -3918,7 +4449,7 @@ def test_violinplot_single_list_quantiles(fig_test, fig_ref): ax.violinplot(data, quantiles=[[0.1, 0.3, 0.9]]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_violinplot_pandas_series(fig_test, fig_ref, pd): np.random.seed(110433579) s1 = pd.Series(np.random.normal(size=7), index=[9, 8, 7, 6, 5, 4, 3]) @@ -3959,7 +4490,8 @@ def test_tick_space_size_0(): plt.savefig(b, dpi=80, format='raw') -@image_comparison(['errorbar_basic', 'errorbar_mixed', 'errorbar_basic']) +@image_comparison(['errorbar_basic.png', 'errorbar_mixed.png', 'errorbar_basic.png'], + style='mpl20') def test_errorbar(): # longdouble due to floating point rounding issues with certain # computer chipsets @@ -4014,8 +4546,7 @@ def test_errorbar(): ax.set_title("Simplest errorbars, 0.2 in x, 0.4 in y") -@image_comparison(['mixed_errorbar_polar_caps'], extensions=['png'], - remove_text=True) +@image_comparison(['mixed_errorbar_polar_caps.png'], remove_text=True) def test_mixed_errorbar_polar_caps(): """ Mix several polar errorbar use cases in a single test figure. @@ -4097,7 +4628,7 @@ def test_errorbar_shape(): ax.errorbar(x, y, yerr=yerr, xerr=xerr, fmt='o') -@image_comparison(['errorbar_limits']) +@image_comparison(['errorbar_limits.png'], style='mpl20') def test_errorbar_limits(): x = np.arange(0.5, 5.5, 0.5) y = np.exp(-x) @@ -4123,7 +4654,7 @@ def test_errorbar_limits(): color='red') # including upper and lower limits - ax.errorbar(x, y+1.5, marker='o', ms=8, xerr=xerr, yerr=yerr, + ax.errorbar(x, y+1.5, marker='o', ms=6, xerr=xerr, yerr=yerr, lolims=lolims, uplims=uplims, ls=ls, color='magenta') # including xlower and xupper limits @@ -4136,11 +4667,11 @@ def test_errorbar_limits(): uplims = np.zeros_like(x) lolims[[6]] = True uplims[[3]] = True - ax.errorbar(x, y+2.1, marker='o', ms=8, xerr=xerr, yerr=yerr, + ax.errorbar(x, y+2.1, marker='o', ms=6, xerr=xerr, yerr=yerr, xlolims=xlolims, xuplims=xuplims, uplims=uplims, lolims=lolims, ls='none', mec='blue', capsize=0, color='cyan') - ax.set_xlim((0, 5.5)) + ax.set_xlim(0, 5.5) ax.set_title('Errorbar upper and lower limits') @@ -4155,6 +4686,24 @@ def test_errorbar_nonefmt(): assert np.all(errbar.get_color() == mcolors.to_rgba('C0')) +def test_errorbar_remove(): + x = np.arange(5) + y = np.arange(5) + + fig, ax = plt.subplots() + ec = ax.errorbar(x, y, xerr=1, yerr=1) + + assert len(ax.containers) == 1 + assert len(ax.lines) == 5 + assert len(ax.collections) == 2 + + ec.remove() + + assert not ax.containers + assert not ax.lines + assert not ax.collections + + def test_errorbar_line_specific_kwargs(): # Check that passing line-specific keyword arguments will not result in # errors. @@ -4172,7 +4721,7 @@ def test_errorbar_line_specific_kwargs(): assert plotline.get_drawstyle() == 'steps-mid' -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_errorbar_with_prop_cycle(fig_test, fig_ref): ax = fig_ref.subplots() ax.errorbar(x=[2, 4, 10], y=[0, 1, 2], yerr=0.5, @@ -4296,19 +4845,41 @@ def test_errorbar_linewidth_type(elinewidth): plt.errorbar([1, 2, 3], [1, 2, 3], yerr=[1, 2, 3], elinewidth=elinewidth) -@check_figures_equal(extensions=["png"]) +def test_errorbar_linestyle_type(): + eb = plt.errorbar([1, 2, 3], [1, 2, 3], + yerr=[1, 2, 3], elinestyle='--') + errorlines = eb[-1][0] + errorlinestyle = errorlines.get_linestyle() + assert errorlinestyle == [(0, (6, 6))] + + +@check_figures_equal() def test_errorbar_nan(fig_test, fig_ref): ax = fig_test.add_subplot() xs = range(5) ys = np.array([1, 2, np.nan, np.nan, 3]) es = np.array([4, 5, np.nan, np.nan, 6]) - ax.errorbar(xs, ys, es) + ax.errorbar(xs, ys, yerr=es) + ax = fig_ref.add_subplot() + ax.errorbar([0, 1], [1, 2], yerr=[4, 5]) + ax.errorbar([4], [3], yerr=[6], fmt="C0") + + +@check_figures_equal() +def test_errorbar_masked_negative(fig_test, fig_ref): + ax = fig_test.add_subplot() + xs = range(5) + mask = np.array([False, False, True, True, False]) + ys = np.ma.array([1, 2, 2, 2, 3], mask=mask) + es = np.ma.array([4, 5, -1, -10, 6], mask=mask) + ax.errorbar(xs, ys, yerr=es) ax = fig_ref.add_subplot() - ax.errorbar([0, 1], [1, 2], [4, 5]) - ax.errorbar([4], [3], [6], fmt="C0") + ax.errorbar([0, 1], [1, 2], yerr=[4, 5]) + ax.errorbar([4], [3], yerr=[6], fmt="C0") -@image_comparison(['hist_stacked_stepfilled', 'hist_stacked_stepfilled']) +@image_comparison(['hist_stacked_stepfilled.png', 'hist_stacked_stepfilled.png'], + style='mpl20') def test_hist_stacked_stepfilled(): # make some data d1 = np.linspace(1, 3, 20) @@ -4322,7 +4893,7 @@ def test_hist_stacked_stepfilled(): ax.hist("x", histtype="stepfilled", stacked=True, data=data) -@image_comparison(['hist_offset']) +@image_comparison(['hist_offset.png'], style='mpl20') def test_hist_offset(): # make some data d1 = np.linspace(0, 10, 50) @@ -4342,7 +4913,7 @@ def test_hist_step(): ax.set_xlim(-1, 5) -@image_comparison(['hist_step_horiz.png']) +@image_comparison(['hist_step_horiz.png'], style='mpl20') def test_hist_step_horiz(): # make some data d1 = np.linspace(0, 10, 50) @@ -4351,7 +4922,7 @@ def test_hist_step_horiz(): ax.hist((d1, d2), histtype="step", orientation="horizontal") -@image_comparison(['hist_stacked_weights']) +@image_comparison(['hist_stacked_weights.png'], style='mpl20') def test_hist_stacked_weighted(): # make some data d1 = np.linspace(0, 10, 50) @@ -4363,14 +4934,12 @@ def test_hist_stacked_weighted(): @image_comparison(['stem.png'], style='mpl20', remove_text=True) -def test_stem(): +def test_stem(text_placeholders): x = np.linspace(0.1, 2 * np.pi, 100) fig, ax = plt.subplots() - # Label is a single space to force a legend to be drawn, but to avoid any - # text being drawn ax.stem(x, np.cos(x), - linefmt='C2-.', markerfmt='k+', basefmt='C1-.', label=' ') + linefmt='C2-.', markerfmt='k+', basefmt='C1-.', label='stem') ax.legend() @@ -4395,6 +4964,11 @@ def _assert_equal(stem_container, expected): _assert_equal(ax.stem(y, linefmt='r--'), expected=([0, 1, 2], y)) _assert_equal(ax.stem(y, 'r--'), expected=([0, 1, 2], y)) + with pytest.raises(ValueError): + ax.stem([[y]]) + with pytest.raises(ValueError): + ax.stem([[x]], y) + def test_stem_markerfmt(): """Test that stem(..., markerfmt=...) produces the intended markers.""" @@ -4479,7 +5053,18 @@ def test_stem_orientation(): orientation='horizontal') -@image_comparison(['hist_stacked_stepfilled_alpha']) +def test_stem_polar_baseline(): + """Test that the baseline is interpolated so that it will follow the radius.""" + fig = plt.figure() + ax = fig.add_subplot(projection='polar') + x = np.linspace(1.57, 3.14, 10) + y = np.linspace(0, 1, 10) + bottom = 0.5 + container = ax.stem(x, y, bottom=bottom) + assert container.baseline.get_path()._interpolation_steps > 100 + + +@image_comparison(['hist_stacked_stepfilled_alpha.png'], style='mpl20') def test_hist_stacked_stepfilled_alpha(): # make some data d1 = np.linspace(1, 3, 20) @@ -4488,7 +5073,7 @@ def test_hist_stacked_stepfilled_alpha(): ax.hist((d1, d2), histtype="stepfilled", stacked=True, alpha=0.5) -@image_comparison(['hist_stacked_step']) +@image_comparison(['hist_stacked_step.png'], style='mpl20') def test_hist_stacked_step(): # make some data d1 = np.linspace(1, 3, 20) @@ -4497,7 +5082,7 @@ def test_hist_stacked_step(): ax.hist((d1, d2), histtype="step", stacked=True) -@image_comparison(['hist_stacked_normed']) +@image_comparison(['hist_stacked_normed.png'], style='mpl20') def test_hist_stacked_density(): # make some data d1 = np.linspace(1, 3, 20) @@ -4585,7 +5170,7 @@ def test_hist_stacked_step_bottom_geometry(): assert_array_equal(polygon.get_xy(), xy[1]) -@image_comparison(['hist_stacked_bar']) +@image_comparison(['hist_stacked_bar.png'], style='mpl20') def test_hist_stacked_bar(): # make some data d = [[100, 100, 100, 100, 200, 320, 450, 80, 20, 600, 310, 800], @@ -4596,13 +5181,71 @@ def test_hist_stacked_bar(): colors = [(0.5759849696758961, 1.0, 0.0), (0.0, 1.0, 0.350624650815206), (0.0, 1.0, 0.6549834156005998), (0.0, 0.6569064625276622, 1.0), (0.28302699607823545, 0.0, 1.0), (0.6849123462299822, 0.0, 1.0)] - labels = ['green', 'orange', ' yellow', 'magenta', 'black'] + labels = ['first', 'second', 'third', 'fourth', 'fifth'] fig, ax = plt.subplots() ax.hist(d, bins=10, histtype='barstacked', align='mid', color=colors, label=labels) ax.legend(loc='upper right', bbox_to_anchor=(1.0, 1.0), ncols=1) +@pytest.mark.parametrize('kwargs', ({'facecolor': ["b", "g", "r"]}, + {'edgecolor': ["b", "g", "r"]}, + {'hatch': ["/", "\\", "."]}, + {'linestyle': ["-", "--", ":"]}, + {'linewidth': [1, 1.5, 2]}, + {'color': ["b", "g", "r"]})) +@check_figures_equal() +def test_hist_vectorized_params(fig_test, fig_ref, kwargs): + np.random.seed(19680801) + xs = [np.random.randn(n) for n in [20, 50, 100]] + + (axt1, axt2) = fig_test.subplots(2) + (axr1, axr2) = fig_ref.subplots(2) + + for histtype, axt, axr in [("stepfilled", axt1, axr1), ("step", axt2, axr2)]: + _, bins, _ = axt.hist(xs, bins=10, histtype=histtype, **kwargs) + + kw, values = next(iter(kwargs.items())) + for i, (x, value) in enumerate(zip(xs, values)): + axr.hist(x, bins=bins, histtype=histtype, **{kw: value}, + zorder=(len(xs)-i)/2) + + +@pytest.mark.parametrize('kwargs, patch_face, patch_edge', + # 'C0'(blue) stands for the first color of the + # default color cycle as well as the patch.facecolor rcParam + # When the expected edgecolor is 'k'(black), + # it corresponds to the patch.edgecolor rcParam + [({'histtype': 'stepfilled', 'color': 'r', + 'facecolor': 'y', 'edgecolor': 'g'}, 'y', 'g'), + ({'histtype': 'step', 'color': 'r', + 'facecolor': 'y', 'edgecolor': 'g'}, ('y', 0), 'g'), + ({'histtype': 'stepfilled', 'color': 'r', + 'edgecolor': 'g'}, 'r', 'g'), + ({'histtype': 'step', 'color': 'r', + 'edgecolor': 'g'}, ('r', 0), 'g'), + ({'histtype': 'stepfilled', 'color': 'r', + 'facecolor': 'y'}, 'y', 'k'), + ({'histtype': 'step', 'color': 'r', + 'facecolor': 'y'}, ('y', 0), 'r'), + ({'histtype': 'stepfilled', + 'facecolor': 'y', 'edgecolor': 'g'}, 'y', 'g'), + ({'histtype': 'step', 'facecolor': 'y', + 'edgecolor': 'g'}, ('y', 0), 'g'), + ({'histtype': 'stepfilled', 'color': 'r'}, 'r', 'k'), + ({'histtype': 'step', 'color': 'r'}, ('r', 0), 'r'), + ({'histtype': 'stepfilled', 'facecolor': 'y'}, 'y', 'k'), + ({'histtype': 'step', 'facecolor': 'y'}, ('y', 0), 'C0'), + ({'histtype': 'stepfilled', 'edgecolor': 'g'}, 'C0', 'g'), + ({'histtype': 'step', 'edgecolor': 'g'}, ('C0', 0), 'g'), + ({'histtype': 'stepfilled'}, 'C0', 'k'), + ({'histtype': 'step'}, ('C0', 0), 'C0')]) +def test_hist_color_semantics(kwargs, patch_face, patch_edge): + _, _, patches = plt.figure().subplots().hist([1, 2, 3], **kwargs) + assert all(mcolors.same_color([p.get_facecolor(), p.get_edgecolor()], + [patch_face, patch_edge]) for p in patches) + + def test_hist_barstacked_bottom_unchanged(): b = np.array([10, 20]) plt.hist([[0, 1], [0, 1]], 2, histtype="barstacked", bottom=b) @@ -4614,6 +5257,15 @@ def test_hist_emptydata(): ax.hist([[], range(10), range(10)], histtype="step") +def test_hist_unused_labels(): + # When a list with one dataset and N elements is provided and N labels, ensure + # that the first label is used for the dataset and all other labels are ignored + fig, ax = plt.subplots() + ax.hist([[1, 2, 3]], label=["values", "unused", "also unused"]) + _, labels = ax.get_legend_handles_labels() + assert labels == ["values"] + + def test_hist_labels(): # test singleton labels OK fig, ax = plt.subplots() @@ -4655,7 +5307,7 @@ def test_rgba_markers(): ax.axis([-1, 4, 0, 5]) -@image_comparison(['mollweide_grid'], remove_text=True) +@image_comparison(['mollweide_grid.png'], remove_text=True) def test_mollweide_grid(): # test that both horizontal and vertical gridlines appear on the Mollweide # projection @@ -4738,7 +5390,7 @@ def test_alpha(): markersize=20, lw=10) -@image_comparison(['eventplot', 'eventplot'], remove_text=True) +@image_comparison(['eventplot.png', 'eventplot.png'], remove_text=True) def test_eventplot(): np.random.seed(0) @@ -4890,7 +5542,7 @@ def test_eventplot_orientation(data, orientation): plt.draw() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_eventplot_units_list(fig_test, fig_ref): # test that list of lists converted properly: ts_1 = [datetime.datetime(2021, 1, 1), datetime.datetime(2021, 1, 2), @@ -4921,8 +5573,8 @@ def test_marker_styles(): marker=marker, markersize=10+y/5, label=marker) -@image_comparison(['rc_markerfill.png'], - tol=0.037 if platform.machine() == 'arm64' else 0) +@image_comparison(['rc_markerfill.png'], style='mpl20', + tol=0.033 if sys.platform == 'darwin' else 0) def test_markers_fillstyle_rcparams(): fig, ax = plt.subplots() x = np.arange(7) @@ -4940,12 +5592,12 @@ def test_vertex_markers(): fig, ax = plt.subplots() ax.plot(data, linestyle='', marker=marker_as_tuple, mfc='k') ax.plot(data[::-1], linestyle='', marker=marker_as_list, mfc='b') - ax.set_xlim([-1, 10]) - ax.set_ylim([-1, 10]) + ax.set_xlim(-1, 10) + ax.set_ylim(-1, 10) -@image_comparison(['vline_hline_zorder', 'errorbar_zorder'], - tol=0 if platform.machine() == 'x86_64' else 0.026) +@image_comparison(['vline_hline_zorder.png', 'errorbar_zorder.png'], style='mpl20', + tol=0.02 if sys.platform == 'darwin' else 0) def test_eb_line_zorder(): x = list(range(10)) @@ -5067,8 +5719,8 @@ def test_axline_args(): plt.draw() -@image_comparison(['vlines_basic', 'vlines_with_nan', 'vlines_masked'], - extensions=['png']) +@image_comparison(['vlines_basic.png', 'vlines_with_nan.png', 'vlines_masked.png'], + style='mpl20') def test_vlines(): # normal x1 = [2, 3, 4, 5, 7] @@ -5114,8 +5766,8 @@ def test_vlines_default(): assert mpl.colors.same_color(lines.get_color(), 'red') -@image_comparison(['hlines_basic', 'hlines_with_nan', 'hlines_masked'], - extensions=['png']) +@image_comparison(['hlines_basic.png', 'hlines_with_nan.png', 'hlines_masked.png'], + style='mpl20') def test_hlines(): # normal y1 = [2, 3, 4, 5, 7] @@ -5163,7 +5815,7 @@ def test_hlines_default(): @pytest.mark.parametrize('data', [[1, 2, 3, np.nan, 5], np.ma.masked_equal([1, 2, 3, 4, 5], 4)]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_lines_with_colors(fig_test, fig_ref, data): test_colors = ['red', 'green', 'blue', 'purple', 'orange'] fig_test.add_subplot(2, 1, 1).vlines(data, 0, 1, @@ -5179,8 +5831,7 @@ def test_lines_with_colors(fig_test, fig_ref, data): colors=expect_color, linewidth=5) -@image_comparison(['vlines_hlines_blended_transform'], - extensions=['png'], style='mpl20') +@image_comparison(['vlines_hlines_blended_transform.png'], style='mpl20') def test_vlines_hlines_blended_transform(): t = np.arange(5.0, 10.0, 0.1) s = np.exp(-t) + np.sin(2 * np.pi * t) + 10 @@ -5210,8 +5861,8 @@ def test_step_linestyle(): ax.step(x, y, lw=5, linestyle=ls, where='pre') ax.step(x, y + 1, lw=5, linestyle=ls, where='mid') ax.step(x, y + 2, lw=5, linestyle=ls, where='post') - ax.set_xlim([-1, 5]) - ax.set_ylim([-1, 7]) + ax.set_xlim(-1, 5) + ax.set_ylim(-1, 7) # Reuse testcase from above for a labeled data test data = {"X": x, "Y0": y, "Y1": y+1, "Y2": y+2} @@ -5222,8 +5873,8 @@ def test_step_linestyle(): ax.step("X", "Y0", lw=5, linestyle=ls, where='pre', data=data) ax.step("X", "Y1", lw=5, linestyle=ls, where='mid', data=data) ax.step("X", "Y2", lw=5, linestyle=ls, where='post', data=data) - ax.set_xlim([-1, 5]) - ax.set_ylim([-1, 7]) + ax.set_xlim(-1, 5) + ax.set_ylim(-1, 7) @image_comparison(['mixed_collection'], remove_text=True) @@ -5376,7 +6027,7 @@ def test_specgram_fs_none(): assert xmin == 32 and xmax == 96 -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_specgram_origin_rcparam(fig_test, fig_ref): """Test specgram ignores image.origin rcParam and uses origin 'upper'.""" t = np.arange(500) @@ -5489,7 +6140,7 @@ def test_psd_csd_edge_cases(): axs[1].csd(np.zeros(5), np.zeros(5)) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_twin_remove(fig_test, fig_ref): ax_test = fig_test.add_subplot() ax_twinx = ax_test.twinx() @@ -5505,7 +6156,7 @@ def test_twin_remove(fig_test, fig_ref): @image_comparison(['twin_spines.png'], remove_text=True, - tol=0.022 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.022) def test_twin_spines(): def make_patch_spines_invisible(ax): @@ -5612,6 +6263,21 @@ def test_grid(): assert not ax.xaxis.majorTicks[0].gridline.get_visible() +def test_grid_color_with_alpha(): + """Test that grid(color=(..., alpha)) respects the alpha value.""" + fig, ax = plt.subplots() + ax.grid(True, color=(0.5, 0.6, 0.7, 0.3)) + + # Check that alpha is extracted from color tuple + for tick in ax.xaxis.get_major_ticks(): + assert tick.gridline.get_alpha() == 0.3, \ + f"Expected alpha=0.3, got {tick.gridline.get_alpha()}" + + for tick in ax.yaxis.get_major_ticks(): + assert tick.gridline.get_alpha() == 0.3, \ + f"Expected alpha=0.3, got {tick.gridline.get_alpha()}" + + def test_reset_grid(): fig, ax = plt.subplots() ax.tick_params(reset=True, which='major', labelsize=10) @@ -5625,7 +6291,7 @@ def test_reset_grid(): assert ax.xaxis.majorTicks[0].gridline.get_visible() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_reset_ticks(fig_test, fig_ref): for fig in [fig_ref, fig_test]: ax = fig.add_subplot() @@ -5645,6 +6311,28 @@ def test_reset_ticks(fig_test, fig_ref): ax.yaxis.reset_ticks() +@mpl.style.context('mpl20') +def test_context_ticks(): + with plt.rc_context({ + 'xtick.direction': 'in', 'xtick.major.size': 30, 'xtick.major.width': 5, + 'xtick.color': 'C0', 'xtick.major.pad': 12, + 'xtick.bottom': True, 'xtick.top': True, + 'xtick.labelsize': 14, 'xtick.labelcolor': 'C1'}): + fig, ax = plt.subplots() + # Draw outside the context so that all-but-first tick are generated with the normal + # mpl20 style in place. + fig.draw_without_rendering() + + first_tick = ax.xaxis.majorTicks[0] + for tick in ax.xaxis.majorTicks[1:]: + assert tick._size == first_tick._size + assert tick._width == first_tick._width + assert tick._base_pad == first_tick._base_pad + assert tick._labelrotation == first_tick._labelrotation + assert tick._zorder == first_tick._zorder + assert tick._tickdir == first_tick._tickdir + + def test_vline_limit(): fig = plt.figure() ax = fig.gca() @@ -5822,12 +6510,7 @@ def test_text_labelsize(): ax.tick_params(direction='out') -# Note: The `pie` image tests were affected by Numpy 2.0 changing promotions -# (NEP 50). While the changes were only marginal, tolerances were introduced. -# These tolerances could likely go away when numpy 2.0 is the minimum supported -# numpy and the images are regenerated. - -@image_comparison(['pie_default.png'], tol=0.01) +@image_comparison(['pie_default.png'], style='mpl20') def test_pie_default(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5839,8 +6522,8 @@ def test_pie_default(): autopct='%1.1f%%', shadow=True, startangle=90) -@image_comparison(['pie_linewidth_0', 'pie_linewidth_0', 'pie_linewidth_0'], - extensions=['png'], style='mpl20', tol=0.01) +@image_comparison(['pie_linewidth_0.png', 'pie_linewidth_0.png', 'pie_linewidth_0.png'], + style='mpl20') def test_pie_linewidth_0(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5872,7 +6555,8 @@ def test_pie_linewidth_0(): plt.axis('equal') -@image_comparison(['pie_center_radius.png'], style='mpl20', tol=0.01) +@image_comparison(['pie_center_radius.png'], style='mpl20', + tol=0.01 if sys.platform == 'darwin' else 0) def test_pie_center_radius(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5892,7 +6576,7 @@ def test_pie_center_radius(): plt.axis('equal') -@image_comparison(['pie_linewidth_2.png'], style='mpl20', tol=0.01) +@image_comparison(['pie_linewidth_2.png'], style='mpl20') def test_pie_linewidth_2(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5907,7 +6591,7 @@ def test_pie_linewidth_2(): plt.axis('equal') -@image_comparison(['pie_ccw_true.png'], style='mpl20', tol=0.01) +@image_comparison(['pie_ccw_true.png'], style='mpl20') def test_pie_ccw_true(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5922,7 +6606,7 @@ def test_pie_ccw_true(): plt.axis('equal') -@image_comparison(['pie_frame_grid.png'], style='mpl20', tol=0.002) +@image_comparison(['pie_frame_grid.png'], style='mpl20') def test_pie_frame_grid(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5949,7 +6633,7 @@ def test_pie_frame_grid(): plt.axis('equal') -@image_comparison(['pie_rotatelabels_true.png'], style='mpl20', tol=0.009) +@image_comparison(['pie_rotatelabels_true.png'], style='mpl20') def test_pie_rotatelabels_true(): # The slices will be ordered and plotted counter-clockwise. labels = 'Hogwarts', 'Frogs', 'Dogs', 'Logs' @@ -5964,7 +6648,7 @@ def test_pie_rotatelabels_true(): plt.axis('equal') -@image_comparison(['pie_no_label.png'], tol=0.01) +@image_comparison(['pie_no_label.png'], style='mpl20') def test_pie_nolabel_but_legend(): labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' sizes = [15, 30, 45, 10] @@ -6031,6 +6715,27 @@ def test_pie_get_negative_values(): ax.pie([5, 5, -3], explode=[0, .1, .2]) +def test_pie_invalid_explode(): + # Test ValueError raised when feeding short explode list to axes.pie + fig, ax = plt.subplots() + with pytest.raises(ValueError): + ax.pie([1, 2, 3], explode=[0.1, 0.1]) + + +def test_pie_invalid_labels(): + # Test ValueError raised when feeding short labels list to axes.pie + fig, ax = plt.subplots() + with pytest.raises(ValueError): + ax.pie([1, 2, 3], labels=["One", "Two"]) + + +def test_pie_invalid_radius(): + # Test ValueError raised when feeding negative radius to axes.pie + fig, ax = plt.subplots() + with pytest.raises(ValueError): + ax.pie([1, 2, 3], radius=-5) + + def test_normalize_kwarg_pie(): fig, ax = plt.subplots() x = [0.3, 0.3, 0.1] @@ -6058,8 +6763,59 @@ def test_pie_hatch_multi(fig_test, fig_ref): [w.set_hatch(hp) for w, hp in zip(wedges, hatch)] -@image_comparison(['set_get_ticklabels.png'], - tol=0.025 if platform.machine() == 'arm64' else 0) +def test_pie_label_formatter(): + fig, ax = plt.subplots() + pie = ax.pie([2, 3]) + + texts = ax.pie_label(pie, '{absval:03d}') + assert texts[0].get_text() == '002' + assert texts[1].get_text() == '003' + + texts = ax.pie_label(pie, '{frac:.1%}') + assert texts[0].get_text() == '40.0%' + assert texts[1].get_text() == '60.0%' + + +@pytest.mark.parametrize('distance', [0.6, 1.1]) +@pytest.mark.parametrize('rotate', [False, True]) +def test_pie_label_auto_align(distance, rotate): + fig, ax = plt.subplots() + pie = ax.pie([1, 1], startangle=45) + + texts = ax.pie_label( + pie, ['spam', 'eggs'], distance=distance, rotate=rotate, alignment='auto') + + if distance < 1: + for text in texts: + # labels within the pie should be centered + assert text.get_horizontalalignment() == 'center' + assert text.get_verticalalignment() == 'center' + + else: + # labels outside the pie should be aligned away from it + h_expected = ['right', 'left'] + v_expected = ['bottom', 'top'] + for text, h_align, v_align in zip(texts, h_expected, v_expected): + assert text.get_horizontalalignment() == h_align + if rotate: + assert text.get_verticalalignment() == v_align + else: + assert text.get_verticalalignment() == 'center' + + +def test_pie_label_fail(): + sizes = 15, 30, 45, 10 + labels = 'Frogs', 'Hogs' + fig, ax = plt.subplots() + pie = ax.pie(sizes) + + match = re.escape("The number of labels (2) must match the number of wedges (4)") + with pytest.raises(ValueError, match=match): + ax.pie_label(pie, labels) + + +@image_comparison(['set_get_ticklabels.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.03) def test_set_get_ticklabels(): # test issue 2246 fig, ax = plt.subplots(2) @@ -6096,7 +6852,7 @@ def test_set_ticks_kwargs_raise_error_without_labels(): ax.xaxis.set_ticks(ticks, alpha=0.5) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_set_ticks_with_labels(fig_test, fig_ref): """ Test that these two are identical:: @@ -6160,7 +6916,7 @@ def test_empty_ticks_fixed_loc(): ax.set_xticklabels([]) -@image_comparison(['retain_tick_visibility.png']) +@image_comparison(['retain_tick_visibility.png'], style='mpl20') def test_retain_tick_visibility(): fig, ax = plt.subplots() plt.plot([0, 1, 2], [0, -1, 4]) @@ -6197,12 +6953,12 @@ def formatter_func(x, pos): ax.set_xticks([-1, 0, 1, 2, 3]) ax.set_xlim(-0.5, 2.5) - ax.figure.canvas.draw() + fig.canvas.draw() tick_texts = [tick.get_text() for tick in ax.xaxis.get_ticklabels()] assert tick_texts == ["", "", "unit value", "", ""] -@image_comparison(['o_marker_path_snap.png'], savefig_kwarg={'dpi': 72}) +@image_comparison(['o_marker_path_snap.png'], savefig_kwarg={'dpi': 72}, style='mpl20') def test_o_marker_path_snap(): fig, ax = plt.subplots() ax.margins(.1) @@ -6380,7 +7136,7 @@ def test_move_offsetlabel(): assert ax.xaxis.offsetText.get_verticalalignment() == 'bottom' -@image_comparison(['rc_spines.png'], savefig_kwarg={'dpi': 40}) +@image_comparison(['rc_spines.png'], savefig_kwarg={'dpi': 40}, style='mpl20') def test_rc_spines(): rc_dict = { 'axes.spines.left': False, @@ -6391,7 +7147,7 @@ def test_rc_spines(): plt.subplots() # create a figure and axes with the spine properties -@image_comparison(['rc_grid.png'], savefig_kwarg={'dpi': 40}) +@image_comparison(['rc_grid.png'], savefig_kwarg={'dpi': 40}, style='mpl20') def test_rc_grid(): fig = plt.figure() rc_dict0 = { @@ -6508,6 +7264,27 @@ def test_pcolorfast_bad_dims(): ax.pcolorfast(np.empty(6), np.empty((4, 7)), np.empty((8, 8))) +def test_pcolorfast_regular_xy_incompatible_size(): + """ + Test that the sizes of X, Y, C are compatible for regularly spaced X, Y. + + Note that after the regualar-spacing check, pcolorfast may go into the + fast "image" mode, where the individual X, Y positions are not used anymore. + Therefore, the algorithm had worked with any regularly number of regularly + spaced values, but discarded their values. + """ + fig, ax = plt.subplots() + with pytest.raises( + ValueError, match=r"Length of X \(5\) must be one larger than the " + r"number of columns in C \(20\)"): + ax.pcolorfast(np.arange(5), np.arange(11), np.random.rand(10, 20)) + + with pytest.raises( + ValueError, match=r"Length of Y \(5\) must be one larger than the " + r"number of rows in C \(10\)"): + ax.pcolorfast(np.arange(21), np.arange(5), np.random.rand(10, 20)) + + def test_shared_scale(): fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) @@ -6631,7 +7408,7 @@ def test_loglog(): @image_comparison(["test_loglog_nonpos.png"], remove_text=True, style='mpl20', - tol=0.029 if platform.machine() == 'arm64' else 0) + tol=0.029) def test_loglog_nonpos(): fig, axs = plt.subplots(3, 3) x = np.arange(1, 11) @@ -6651,6 +7428,7 @@ def test_loglog_nonpos(): ax.set_xscale("log", nonpositive=mcx) if mcy: ax.set_yscale("log", nonpositive=mcy) + ax.set_yticks([1e3, 1e7]) # Backcompat tick selection. @mpl.style.context('default') @@ -6719,7 +7497,7 @@ def shared_axes_generator(request): ax = ax_lst[0][0] elif request.param == 'add_axes': fig = plt.figure() - ax = fig.add_axes([.1, .1, .8, .8]) + ax = fig.add_axes((.1, .1, .8, .8)) return fig, ax @@ -6780,8 +7558,8 @@ def test_auto_numticks_log(): fig, ax = plt.subplots() mpl.rcParams['axes.autolimit_mode'] = 'round_numbers' ax.loglog([1e-20, 1e5], [1e-16, 10]) - assert (np.log10(ax.get_xticks()) == np.arange(-26, 18, 4)).all() - assert (np.log10(ax.get_yticks()) == np.arange(-20, 10, 3)).all() + assert_array_equal(np.log10(ax.get_xticks()), np.arange(-26, 11, 4)) + assert_array_equal(np.log10(ax.get_yticks()), np.arange(-20, 5, 3)) def test_broken_barh_empty(): @@ -6798,6 +7576,21 @@ def test_broken_barh_timedelta(): assert pp.get_paths()[0].vertices[2, 0] == mdates.date2num(d0) + 1 / 24 +def test_broken_barh_align(): + fig, ax = plt.subplots() + pc = ax.broken_barh([(0, 10)], (0, 2)) + for path in pc.get_paths(): + assert_array_equal(path.get_extents().intervaly, [0, 2]) + + pc = ax.broken_barh([(0, 10)], (10, 2), align="center") + for path in pc.get_paths(): + assert_array_equal(path.get_extents().intervaly, [9, 11]) + + pc = ax.broken_barh([(0, 10)], (20, 2), align="top") + for path in pc.get_paths(): + assert_array_equal(path.get_extents().intervaly, [18, 20]) + + def test_pandas_pcolormesh(pd): time = pd.date_range('2000-01-01', periods=10) depth = np.arange(20) @@ -6947,63 +7740,6 @@ def test_bar_uint8(): assert patch.xy[0] == x -@image_comparison(['date_timezone_x.png'], tol=1.0) -def test_date_timezone_x(): - # Tests issue 5575 - time_index = [datetime.datetime(2016, 2, 22, hour=x, - tzinfo=dateutil.tz.gettz('Canada/Eastern')) - for x in range(3)] - - # Same Timezone - plt.figure(figsize=(20, 12)) - plt.subplot(2, 1, 1) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date(time_index, [3] * 3, tz='Canada/Eastern') - - # Different Timezone - plt.subplot(2, 1, 2) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date(time_index, [3] * 3, tz='UTC') - - -@image_comparison(['date_timezone_y.png']) -def test_date_timezone_y(): - # Tests issue 5575 - time_index = [datetime.datetime(2016, 2, 22, hour=x, - tzinfo=dateutil.tz.gettz('Canada/Eastern')) - for x in range(3)] - - # Same Timezone - plt.figure(figsize=(20, 12)) - plt.subplot(2, 1, 1) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date([3] * 3, time_index, tz='Canada/Eastern', xdate=False, ydate=True) - - # Different Timezone - plt.subplot(2, 1, 2) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date([3] * 3, time_index, tz='UTC', xdate=False, ydate=True) - - -@image_comparison(['date_timezone_x_and_y.png'], tol=1.0) -def test_date_timezone_x_and_y(): - # Tests issue 5575 - UTC = datetime.timezone.utc - time_index = [datetime.datetime(2016, 2, 22, hour=x, tzinfo=UTC) - for x in range(3)] - - # Same Timezone - plt.figure(figsize=(20, 12)) - plt.subplot(2, 1, 1) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date(time_index, time_index, tz='UTC', ydate=True) - - # Different Timezone - plt.subplot(2, 1, 2) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.plot_date(time_index, time_index, tz='US/Eastern', ydate=True) - - @image_comparison(['axisbelow.png'], remove_text=True) def test_axisbelow(): # Test 'line' setting added in 6287. @@ -7036,7 +7772,7 @@ def test_titletwiny(): bbox_y0_title = title.get_window_extent(renderer).y0 # bottom of title bbox_y1_xlabel2 = xlabel2.get_window_extent(renderer).y1 # top of xlabel2 y_diff = bbox_y0_title - bbox_y1_xlabel2 - assert np.isclose(y_diff, 3) + assert y_diff >= 3 def test_titlesetpos(): @@ -7110,7 +7846,7 @@ def test_title_no_move_off_page(): # make sure that the automatic title repositioning does not get done. mpl.rcParams['axes.titley'] = None fig = plt.figure() - ax = fig.add_axes([0.1, -0.5, 0.8, 0.2]) + ax = fig.add_axes((0.1, -0.5, 0.8, 0.2)) ax.tick_params(axis="x", bottom=True, top=True, labelbottom=True, labeltop=True) tt = ax.set_title('Boo') @@ -7187,15 +7923,19 @@ def test_tick_param_label_rotation(): ax.yaxis.set_tick_params(which='both', rotation=90) for text in ax.get_xticklabels(which='both'): assert text.get_rotation() == 75 + assert text.get_rotation_mode() == 'default' for text in ax.get_yticklabels(which='both'): assert text.get_rotation() == 90 + assert text.get_rotation_mode() == 'default' - ax2.tick_params(axis='x', labelrotation=53) - ax2.tick_params(axis='y', rotation=35) + ax2.tick_params(axis='x', labelrotation=53, labelrotation_mode='xtick') + ax2.tick_params(axis='y', rotation=35, rotation_mode='ytick') for text in ax2.get_xticklabels(which='major'): assert text.get_rotation() == 53 + assert text.get_rotation_mode() == 'xtick' for text in ax2.get_yticklabels(which='major'): assert text.get_rotation() == 35 + assert text.get_rotation_mode() == 'ytick' @mpl.style.context('default') @@ -7331,9 +8071,42 @@ def test_twinx_knows_limits(): assert_array_equal(xtwin.viewLim.intervalx, ax2.viewLim.intervalx) -def test_zero_linewidth(): - # Check that setting a zero linewidth doesn't error - plt.plot([0, 1], [0, 1], ls='--', lw=0) +class SubclassAxes(Axes): + def __init__(self, *args, foo, **kwargs): + super().__init__(*args, **kwargs) + self.foo = foo + + +def test_twinning_with_axes_class(): + """Check that twinx/y(axes_class=...) gives the appropriate class.""" + _, ax = plt.subplots() + twinx = ax.twinx(axes_class=SubclassAxes, foo=1) + assert isinstance(twinx, SubclassAxes) + assert twinx.foo == 1 + twiny = ax.twiny(axes_class=SubclassAxes, foo=2) + assert isinstance(twiny, SubclassAxes) + assert twiny.foo == 2 + + +def test_twinning_default_axes_class(): + """ + Check that the default class for twinx/y() is Axes, + even if the original is an Axes subclass. + """ + _, ax = plt.subplots(subplot_kw=dict(axes_class=SubclassAxes, foo=1)) + twinx = ax.twinx() + assert type(twinx) is Axes + twiny = ax.twiny() + assert type(twiny) is Axes + + +@mpl.style.context('mpl20') +@check_figures_equal() +def test_stairs_fill_zero_linewidth(fig_test, fig_ref): + fig_test.subplots().stairs( + [1, 2, 3, 4], [1, 2, 3, 4, 5], fill=True, ls='--') + fig_ref.subplots().stairs( + [1, 2, 3, 4], [1, 2, 3, 4, 5], fill=True, ls='-') def test_empty_errorbar_legend(): @@ -7343,7 +8116,7 @@ def test_empty_errorbar_legend(): ax.legend() -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_plot_decimal(fig_test, fig_ref): x0 = np.arange(-10, 10, 0.3) y0 = [5.2 * x ** 3 - 2.1 * x ** 2 + 7.34 * x + 4.5 for x in x0] @@ -7355,8 +8128,7 @@ def test_plot_decimal(fig_test, fig_ref): fig_ref.subplots().plot(x0, y0) -# pdf and svg tests fail using travis' old versions of gs and inkscape. -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_markerfacecolor_none_alpha(fig_test, fig_ref): fig_test.subplots().plot(0, "o", mfc="none", alpha=.5) fig_ref.subplots().plot(0, "o", mfc="w", alpha=.5) @@ -7395,12 +8167,12 @@ def test_inset(): rect = [xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]] - rec, connectors = ax.indicate_inset(bounds=rect) - assert connectors is None + inset = ax.indicate_inset(bounds=rect) + assert inset.connectors is None fig.canvas.draw() xx = np.array([[1.5, 2.], [2.15, 2.5]]) - assert np.all(rec.get_bbox().get_points() == xx) + assert np.all(inset.rectangle.get_bbox().get_points() == xx) def test_zoom_inset(): @@ -7420,13 +8192,14 @@ def test_zoom_inset(): axin1 = ax.inset_axes([0.7, 0.7, 0.35, 0.35]) # redraw the data in the inset axes... axin1.pcolormesh(x, y, z[:-1, :-1]) - axin1.set_xlim([1.5, 2.15]) - axin1.set_ylim([2, 2.5]) + axin1.set_xlim(1.5, 2.15) + axin1.set_ylim(2, 2.5) axin1.set_aspect(ax.get_aspect()) - rec, connectors = ax.indicate_inset_zoom(axin1) - assert len(connectors) == 4 + with pytest.warns(mpl.MatplotlibDeprecationWarning): + rec, connectors = ax.indicate_inset_zoom(axin1) fig.canvas.draw() + assert len(connectors) == 4 xx = np.array([[1.5, 2.], [2.15, 2.5]]) assert np.all(rec.get_bbox().get_points() == xx) @@ -7476,8 +8249,8 @@ def test_indicate_inset_inverted(x_inverted, y_inverted): if y_inverted: ax1.invert_yaxis() - rect, bounds = ax1.indicate_inset([2, 2, 5, 4], ax2) - lower_left, upper_left, lower_right, upper_right = bounds + inset = ax1.indicate_inset([2, 2, 5, 4], ax2) + lower_left, upper_left, lower_right, upper_right = inset.connectors sign_x = -1 if x_inverted else 1 sign_y = -1 if y_inverted else 1 @@ -7570,7 +8343,7 @@ def test_scatter_empty_data(): @image_comparison(['annotate_across_transforms.png'], style='mpl20', remove_text=True, - tol=0.025 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.025) def test_annotate_across_transforms(): x = np.linspace(0, 10, 200) y = np.exp(-x) * np.sin(x) @@ -7601,7 +8374,7 @@ def inverted(self): @image_comparison(['secondary_xy.png'], style='mpl20', - tol=0.027 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.024) def test_secondary_xy(): fig, axs = plt.subplots(1, 2, figsize=(10, 5), constrained_layout=True) @@ -7685,6 +8458,18 @@ def test_secondary_formatter(): secax.xaxis.get_major_formatter(), mticker.ScalarFormatter) +def test_secondary_init_xticks(): + fig, ax = plt.subplots() + secax = ax.secondary_xaxis(1, xticks=[0, 1]) + assert isinstance(secax.xaxis.get_major_locator(), mticker.FixedLocator) + with pytest.raises(TypeError): + secax.set_yticks([0, 1]) + secax = ax.secondary_yaxis(1, yticks=[0, 1]) + assert isinstance(secax.yaxis.get_major_locator(), mticker.FixedLocator) + with pytest.raises(TypeError): + secax.set_xticks([0, 1]) + + def test_secondary_repr(): fig, ax = plt.subplots() secax = ax.secondary_xaxis("top") @@ -7715,10 +8500,9 @@ def color_boxes(fig, ax): """ fig.canvas.draw() - renderer = fig.canvas.get_renderer() bbaxis = [] for nn, axx in enumerate([ax.xaxis, ax.yaxis]): - bb = axx.get_tightbbox(renderer) + bb = axx.get_tightbbox() if bb: axisr = mpatches.Rectangle( (bb.x0, bb.y0), width=bb.width, height=bb.height, @@ -7729,7 +8513,7 @@ def color_boxes(fig, ax): bbspines = [] for nn, a in enumerate(['bottom', 'top', 'left', 'right']): - bb = ax.spines[a].get_window_extent(renderer) + bb = ax.spines[a].get_window_extent() spiner = mpatches.Rectangle( (bb.x0, bb.y0), width=bb.width, height=bb.height, linewidth=0.7, edgecolor="green", facecolor="none", transform=None, @@ -7745,7 +8529,7 @@ def color_boxes(fig, ax): fig.add_artist(rect2) bbax = bb - bb2 = ax.get_tightbbox(renderer) + bb2 = ax.get_tightbbox() rect2 = mpatches.Rectangle( (bb2.x0, bb2.y0), width=bb2.width, height=bb2.height, linewidth=3, edgecolor="red", facecolor="none", transform=None, @@ -7764,12 +8548,12 @@ def test_normal_axes(): # test the axis bboxes target = [ - [123.375, 75.88888888888886, 983.25, 33.0], - [85.51388888888889, 99.99999999999997, 53.375, 993.0] + [124.0, 75.56, 982.0, 33.33], + [86.89, 99.33, 52.0, 993.33], ] for nn, b in enumerate(bbaxis): targetbb = mtransforms.Bbox.from_bounds(*target[nn]) - assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2) + assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=1) target = [ [150.0, 119.999, 930.0, 11.111], @@ -7785,9 +8569,9 @@ def test_normal_axes(): targetbb = mtransforms.Bbox.from_bounds(*target) assert_array_almost_equal(bbax.bounds, targetbb.bounds, decimal=2) - target = [85.5138, 75.88888, 1021.11, 1017.11] + target = [86.89, 75.56, 1019.11, 1017.11] targetbb = mtransforms.Bbox.from_bounds(*target) - assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=2) + assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=1) # test that get_position roundtrips to get_window_extent axbb = ax.get_position().transformed(fig.transFigure).bounds @@ -7904,7 +8688,7 @@ def test_minor_accountedfor(): bbspines[n * 2].bounds, targetbb.bounds, atol=1e-2) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_axis_bool_arguments(fig_test, fig_ref): # Test if False and "off" give the same fig_test.add_subplot(211).axis(False) @@ -8016,7 +8800,7 @@ def test_aspect_nonlinear_adjustable_box(): def test_aspect_nonlinear_adjustable_datalim(): fig = plt.figure(figsize=(10, 10)) # Square. - ax = fig.add_axes([.1, .1, .8, .8]) # Square. + ax = fig.add_axes((.1, .1, .8, .8)) # Square. ax.plot([.4, .6], [.4, .6]) # Set minpos to keep logit happy. ax.set(xscale="log", xlim=(1, 100), yscale="logit", ylim=(1 / 101, 1 / 11), @@ -8156,7 +8940,7 @@ def test_unautoscale(axis, auto): assert_array_equal(get_lim(), (-0.5, 0.5)) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_polar_interpolation_steps_variable_r(fig_test, fig_ref): l, = fig_test.add_subplot(projection="polar").plot([0, np.pi/2], [1, 2]) l.get_path()._interpolation_steps = 100 @@ -8231,16 +9015,16 @@ def test_relative_ticklabel_sizes(size): def test_multiplot_autoscale(): fig = plt.figure() ax1, ax2 = fig.subplots(2, 1, sharex='all') - ax1.scatter([1, 2, 3, 4], [2, 3, 2, 3]) + ax1.plot([18000, 18250, 18500, 18750], [2, 3, 2, 3]) ax2.axhspan(-5, 5) xlim = ax1.get_xlim() - assert np.allclose(xlim, [0.5, 4.5]) + assert np.allclose(xlim, [18000, 18800]) def test_sharing_does_not_link_positions(): fig = plt.figure() ax0 = fig.add_subplot(221) - ax1 = fig.add_axes([.6, .6, .3, .3], sharex=ax0) + ax1 = fig.add_axes((.6, .6, .3, .3), sharex=ax0) init_pos = ax1.get_position() fig.subplots_adjust(left=0) assert (ax1.get_position().get_points() == init_pos.get_points()).all() @@ -8266,7 +9050,7 @@ def test_2dcolor_plot(fig_test, fig_ref): axs[4].bar(np.arange(10), np.arange(10), color=color.reshape((1, -1))) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_shared_axes_clear(fig_test, fig_ref): x = np.arange(0.0, 2*np.pi, 0.01) y = np.sin(x) @@ -8302,7 +9086,7 @@ def test_ylabel_ha_with_position(ha): ax = fig.subplots() ax.set_ylabel("test", y=1, ha=ha) ax.yaxis.set_label_position("right") - assert ax.yaxis.get_label().get_ha() == ha + assert ax.yaxis.label.get_ha() == ha def test_bar_label_location_vertical(): @@ -8401,7 +9185,7 @@ def test_bar_label_location_center(): assert labels[1].get_verticalalignment() == 'center' -@image_comparison(['test_centered_bar_label_nonlinear.svg']) +@image_comparison(['test_centered_bar_label_nonlinear.svg'], style='mpl20') def test_centered_bar_label_nonlinear(): _, ax = plt.subplots() bar_container = ax.barh(['c', 'b', 'a'], [1_000, 5_000, 7_000]) @@ -8482,6 +9266,23 @@ def test_bar_label_nan_ydata_inverted(): assert labels[0].get_verticalalignment() == 'bottom' +def test_bar_label_padding(): + """Test that bar_label accepts both float and array-like padding.""" + ax = plt.gca() + xs, heights = [1, 2], [3, 4] + rects = ax.bar(xs, heights) + labels1 = ax.bar_label(rects, padding=5) # test float value + assert labels1[0].xyann[1] == 5 + assert labels1[1].xyann[1] == 5 + + labels2 = ax.bar_label(rects, padding=[2, 8]) # test array-like values + assert labels2[0].xyann[1] == 2 + assert labels2[1].xyann[1] == 8 + + with pytest.raises(ValueError, match="padding must be of length"): + ax.bar_label(rects, padding=[1, 2, 3]) + + def test_nan_barlabels(): fig, ax = plt.subplots() bars = ax.bar([1, 2, 3], [np.nan, 1, 2], yerr=[0.2, 0.4, 0.6]) @@ -8513,7 +9314,7 @@ def test_patch_bounds(): # PR 19078 @mpl.style.context('default') def test_warn_ignored_scatter_kwargs(): with pytest.warns(UserWarning, - match=r"You passed a edgecolor/edgecolors"): + match=r"You passed an edgecolor/edgecolors"): plt.scatter([0], [0], marker="+", s=500, facecolor="r", edgecolor="b") @@ -8773,7 +9574,7 @@ def test_bar_leading_nan(): assert np.isfinite(b.get_width()) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_bar_all_nan(fig_test, fig_ref): mpl.style.use("mpl20") ax_test = fig_test.subplots() @@ -8827,11 +9628,11 @@ def test_cla_clears_children_axes_and_fig(): img = ax.imshow([[1]]) for art in lines + [img]: assert art.axes is ax - assert art.figure is fig + assert art.get_figure() is fig ax.clear() for art in lines + [img]: assert art.axes is None - assert art.figure is None + assert art.get_figure() is None def test_child_axes_removal(): @@ -8844,8 +9645,8 @@ def test_child_axes_removal(): def test_scatter_color_repr_error(): - def get_next_color(): - return 'blue' # pragma: no cover + def get_next_color(): # pragma: no cover + return 'blue' # currently unused msg = ( r"'c' argument must be a color, a sequence of colors" r", or a sequence of numbers, not 'red\\n'" @@ -8865,7 +9666,7 @@ def test_zorder_and_explicit_rasterization(): @image_comparison(["preset_clip_paths.png"], remove_text=True, style="mpl20", - tol=0.027 if platform.machine() == "arm64" else 0) + tol=0.01 if sys.platform == 'darwin' else 0) def test_preset_clip_paths(): fig, ax = plt.subplots() @@ -8914,7 +9715,7 @@ def test_rc_axes_label_formatting(): assert ax.xaxis.label.get_fontweight() == 'bold' -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_ecdf(fig_test, fig_ref): data = np.array([0, -np.inf, -np.inf, np.inf, 1, 1, 2]) weights = range(len(data)) @@ -9005,7 +9806,7 @@ def test_axhvlinespan_interpolation(): ax.axhspan(.6, .7, .8, .9, fc="C2", alpha=.5) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() @pytest.mark.parametrize("which", ("x", "y")) def test_axes_clear_behavior(fig_ref, fig_test, which): """Test that the given tick params are not reset by ax.clear().""" @@ -9048,6 +9849,49 @@ def test_axes_clear_behavior(fig_ref, fig_test, which): ax_test.grid(True) +@pytest.mark.skipif( + sys.version_info[:3] == (3, 13, 0) and sys.version_info.releaselevel != "final", + reason="https://github.com/python/cpython/issues/124538", +) +def test_axes_clear_reference_cycle(): + def assert_not_in_reference_cycle(start): + # Breadth first search. Return True if we encounter the starting node + to_visit = deque([start]) + explored = set() + while len(to_visit) > 0: + parent = to_visit.popleft() + for child in gc.get_referents(parent): + if id(child) in explored: + continue + assert child is not start + explored.add(id(child)) + to_visit.append(child) + + fig = Figure() + ax = fig.add_subplot() + points = np.random.rand(1000) + ax.plot(points, points) + ax.scatter(points, points) + ax_children = ax.get_children() + fig.clear() # This should break the reference cycle + + # Care most about the objects that scale with number of points + big_artists = [ + a for a in ax_children + if isinstance(a, (Line2D, PathCollection)) + ] + assert len(big_artists) > 0 + for big_artist in big_artists: + assert_not_in_reference_cycle(big_artist) + assert len(ax_children) > 0 + for child in ax_children: + # Make sure this doesn't raise because the child is already removed. + try: + child.remove() + except NotImplementedError: + pass # not implemented is expected for some artists + + def test_boxplot_tick_labels(): # Test the renamed `tick_labels` parameter. # Test for deprecation of old name `labels`. @@ -9079,7 +9923,7 @@ def test_latex_pie_percent(fig_test, fig_ref): ax1.pie(data, autopct=r"%1.0f\%%", textprops={'usetex': True}) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_violinplot_orientation(fig_test, fig_ref): # Test the `orientation : {'vertical', 'horizontal'}` # parameter and deprecation of `vert: bool`. @@ -9106,17 +9950,17 @@ def test_violinplot_orientation(fig_test, fig_ref): # Deprecation of `vert: bool` keyword with pytest.warns(mpl.MatplotlibDeprecationWarning, - match='vert: bool was deprecated in Matplotlib 3.10'): + match='vert: bool was deprecated in Matplotlib 3.11'): # Compare images between a figure that # uses vert and one that uses orientation. ax_ref = fig_ref.subplots() ax_ref.violinplot(all_data, vert=False) - ax_test = fig_test.subplots() - ax_test.violinplot(all_data, orientation='horizontal') + ax_test = fig_test.subplots() + ax_test.violinplot(all_data, orientation='horizontal') -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_boxplot_orientation(fig_test, fig_ref): # Test the `orientation : {'vertical', 'horizontal'}` # parameter and deprecation of `vert: bool`. @@ -9153,3 +9997,218 @@ def test_boxplot_orientation(fig_test, fig_ref): ax_test = fig_test.subplots() ax_test.boxplot(all_data, orientation='horizontal') + + +@image_comparison(["use_colorizer_keyword.png"], + tol=0 if platform.machine() == 'x86_64' else 0.05) +def test_use_colorizer_keyword(): + # test using the colorizer keyword + np.random.seed(0) + rand_x = np.random.random(100) + rand_y = np.random.random(100) + c = np.arange(25, dtype='float32').reshape((5, 5)) + + fig, axes = plt.subplots(3, 4) + norm = mpl.colors.Normalize(4, 20) + cl = mpl.colorizer.Colorizer(norm=norm, cmap='RdBu') + + axes[0, 0].scatter(c, c, c=c, colorizer=cl) + axes[0, 1].hexbin(rand_x, rand_y, colorizer=cl, gridsize=(2, 2)) + axes[0, 2].imshow(c, colorizer=cl) + axes[0, 3].pcolor(c, colorizer=cl) + axes[1, 0].pcolormesh(c, colorizer=cl) + axes[1, 1].pcolorfast(c, colorizer=cl) # style = image + axes[1, 2].pcolorfast((0, 1, 2, 3, 4, 5), (0, 1, 2, 3, 5, 6), c, + colorizer=cl) # style = pcolorimage + axes[1, 3].pcolorfast(c.T, c, c[:4, :4], colorizer=cl) # style = quadmesh + axes[2, 0].contour(c, colorizer=cl) + axes[2, 1].contourf(c, colorizer=cl) + axes[2, 2].tricontour(c.T.ravel(), c.ravel(), c.ravel(), colorizer=cl) + axes[2, 3].tricontourf(c.T.ravel(), c.ravel(), c.ravel(), colorizer=cl) + + fig.figimage(np.repeat(np.repeat(c, 15, axis=0), 15, axis=1), colorizer=cl) + remove_ticks_and_titles(fig) + + +def test_wrong_use_colorizer(): + # test using the colorizer keyword and norm or cmap + np.random.seed(0) + rand_x = np.random.random(100) + rand_y = np.random.random(100) + c = np.arange(25, dtype='float32').reshape((5, 5)) + + fig, axes = plt.subplots(3, 4) + norm = mpl.colors.Normalize(4, 20) + cl = mpl.colorizer.Colorizer(norm=norm, cmap='RdBu') + + match_str = "The `colorizer` keyword cannot be used simultaneously" + kwrds = [{'vmin': 0}, {'vmax': 0}, {'norm': 'log'}, {'cmap': 'viridis'}] + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[0, 0].scatter(c, c, c=c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[0, 0].scatter(c, c, c=c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[0, 1].hexbin(rand_x, rand_y, colorizer=cl, gridsize=(2, 2), **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[0, 2].imshow(c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[0, 3].pcolor(c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[1, 0].pcolormesh(c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[1, 1].pcolorfast(c, colorizer=cl, **kwrd) # style = image + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[1, 2].pcolorfast((0, 1, 2, 3, 4, 5), (0, 1, 2, 3, 5, 6), c, + colorizer=cl, **kwrd) # style = pcolorimage + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[1, 3].pcolorfast(c.T, c, c[:4, :4], colorizer=cl, **kwrd) # quadmesh + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[2, 0].contour(c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[2, 1].contourf(c, colorizer=cl, **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[2, 2].tricontour(c.T.ravel(), c.ravel(), c.ravel(), colorizer=cl, + **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + axes[2, 3].tricontourf(c.T.ravel(), c.ravel(), c.ravel(), colorizer=cl, + **kwrd) + for kwrd in kwrds: + with pytest.raises(ValueError, match=match_str): + fig.figimage(c, colorizer=cl, **kwrd) + + +def test_bar_color_precedence(): + # Test the precedence of 'color' and 'facecolor' in bar plots + fig, ax = plt.subplots() + + # case 1: no color specified + bars = ax.bar([1, 2, 3], [4, 5, 6]) + for bar in bars: + assert mcolors.same_color(bar.get_facecolor(), 'blue') + + # case 2: Only 'color' + bars = ax.bar([11, 12, 13], [4, 5, 6], color='red') + for bar in bars: + assert mcolors.same_color(bar.get_facecolor(), 'red') + + # case 3: Only 'facecolor' + bars = ax.bar([21, 22, 23], [4, 5, 6], facecolor='yellow') + for bar in bars: + assert mcolors.same_color(bar.get_facecolor(), 'yellow') + + # case 4: 'facecolor' and 'color' + bars = ax.bar([31, 32, 33], [4, 5, 6], color='red', facecolor='green') + for bar in bars: + assert mcolors.same_color(bar.get_facecolor(), 'green') + + +@check_figures_equal() +def test_axes_set_position_external_bbox_unchanged(fig_test, fig_ref): + # From #29410: Modifying Axes' position also alters the original Bbox + # object used for initialization + bbox = mtransforms.Bbox([[0.0, 0.0], [1.0, 1.0]]) + ax_test = fig_test.add_axes(bbox) + ax_test.set_position([0.25, 0.25, 0.5, 0.5]) + assert (bbox.x0, bbox.y0, bbox.width, bbox.height) == (0.0, 0.0, 1.0, 1.0) + ax_ref = fig_ref.add_axes((0.25, 0.25, 0.5, 0.5)) + + +def test_bar_shape_mismatch(): + x = ["foo", "bar"] + height = [1, 2, 3] + error_message = ( + r"Mismatch is between 'x' with shape \(2,\) and 'height' with shape \(3,\)" + ) + with pytest.raises(ValueError, match=error_message): + plt.bar(x, height) + + +def test_caps_color(): + + # Creates a simple plot with error bars and a specified ecolor + x = np.linspace(0, 10, 10) + mpl.rcParams['lines.markeredgecolor'] = 'green' + ecolor = 'red' + + fig, ax = plt.subplots() + errorbars = ax.errorbar(x, np.sin(x), yerr=0.1, ecolor=ecolor) + + # Tests if the caps have the specified color + for cap in errorbars[2]: + assert mcolors.same_color(cap.get_edgecolor(), ecolor) + + +def test_caps_no_ecolor(): + + # Creates a simple plot with error bars without specifying ecolor + x = np.linspace(0, 10, 10) + mpl.rcParams['lines.markeredgecolor'] = 'green' + fig, ax = plt.subplots() + errorbars = ax.errorbar(x, np.sin(x), yerr=0.1) + + # Tests if the caps have the default color (blue) + for cap in errorbars[2]: + assert mcolors.same_color(cap.get_edgecolor(), "blue") + + +def test_pie_non_finite_values(): + fig, ax = plt.subplots() + df = [5, float('nan'), float('inf')] + + with pytest.raises(ValueError, match='Wedge sizes must be finite numbers'): + ax.pie(df, labels=['A', 'B', 'C']) + + +def test_pie_all_zeros(): + fig, ax = plt.subplots() + with pytest.raises(ValueError, match="All wedge sizes are zero"): + ax.pie([0, 0], labels=["A", "B"]) + + +def test_animated_artists_not_drawn_by_default(): + fig, (ax1, ax2) = plt.subplots(ncols=2) + + imdata = np.random.random((20, 20)) + lndata = imdata[0] + + im = ax1.imshow(imdata, animated=True) + (ln,) = ax2.plot(lndata, animated=True) + + with (unittest.mock.patch.object(im, "draw", name="im.draw") as mocked_im_draw, + unittest.mock.patch.object(ln, "draw", name="ln.draw") as mocked_ln_draw): + fig.draw_without_rendering() + + mocked_im_draw.assert_not_called() + mocked_ln_draw.assert_not_called() + + +def test_errorbar_uses_rcparams(): + with mpl.rc_context({ + "errorbar.capsize": 5.0, + "errorbar.capthick": 2.5, + "errorbar.elinewidth": 1.75, + }): + fig, ax = plt.subplots() + eb = ax.errorbar([0, 1, 2], [1, 2, 3], yerr=[0.1, 0.2, 0.3], fmt="none") + + data_line, caplines, barlinecols = eb.lines + assert data_line is None + assert caplines + + assert_allclose([cap.get_markersize() for cap in caplines], 10.0) + assert_allclose([cap.get_markeredgewidth() for cap in caplines], 2.5) + for barcol in barlinecols: + assert_allclose(barcol.get_linewidths(), 1.75) diff --git a/lib/matplotlib/tests/test_axis.py b/lib/matplotlib/tests/test_axis.py index 97b5f88dede1..3776b6f054b9 100644 --- a/lib/matplotlib/tests/test_axis.py +++ b/lib/matplotlib/tests/test_axis.py @@ -2,9 +2,129 @@ import matplotlib.pyplot as plt from matplotlib.axis import XTick +from matplotlib.testing.decorators import check_figures_equal def test_tick_labelcolor_array(): # Smoke test that we can instantiate a Tick with labelcolor as array. ax = plt.axes() XTick(ax, 0, labelcolor=np.array([1, 0, 0, 1])) + + +def test_axis_not_in_layout(): + fig1, (ax1_left, ax1_right) = plt.subplots(ncols=2, layout='constrained') + fig2, (ax2_left, ax2_right) = plt.subplots(ncols=2, layout='constrained') + + # 100 label overlapping the end of the axis + ax1_left.set_xlim(0, 100) + # 100 label not overlapping the end of the axis + ax2_left.set_xlim(0, 120) + + for ax in ax1_left, ax2_left: + ax.set_xticks([0, 100]) + ax.xaxis.set_in_layout(False) + + for fig in fig1, fig2: + fig.draw_without_rendering() + + # Positions should not be affected by overlapping 100 label + assert ax1_left.get_position().bounds == ax2_left.get_position().bounds + assert ax1_right.get_position().bounds == ax2_right.get_position().bounds + + +@check_figures_equal() +def test_tick_not_in_layout(fig_test, fig_ref): + # Check that the "very long" ticklabel is ignored from layouting after + # set_in_layout(False); i.e. the layout is as if the ticklabel was empty. + # Ticklabels are set to white so that the actual string doesn't matter. + fig_test.set_layout_engine("constrained") + ax = fig_test.add_subplot(xticks=[0, 1], xticklabels=["short", "very long"]) + ax.tick_params(labelcolor="w") + fig_test.draw_without_rendering() # Ensure ticks are correct. + ax.xaxis.majorTicks[-1].label1.set_in_layout(False) + fig_ref.set_layout_engine("constrained") + ax = fig_ref.add_subplot(xticks=[0, 1], xticklabels=["short", ""]) + ax.tick_params(labelcolor="w") + + +def test_translate_tick_params_reverse(): + fig, ax = plt.subplots() + kw = {'label1On': 'a', 'label2On': 'b', 'tick1On': 'c', 'tick2On': 'd'} + assert (ax.xaxis._translate_tick_params(kw, reverse=True) == + {'labelbottom': 'a', 'labeltop': 'b', 'bottom': 'c', 'top': 'd'}) + assert (ax.yaxis._translate_tick_params(kw, reverse=True) == + {'labelleft': 'a', 'labelright': 'b', 'left': 'c', 'right': 'd'}) + + +def test_get_tick_position_rcParams(): + """Test that get_tick_position() correctly picks up rcParams tick positions.""" + plt.rcParams.update({ + "xtick.top": 1, "xtick.labeltop": 1, "xtick.bottom": 0, "xtick.labelbottom": 0, + "ytick.right": 1, "ytick.labelright": 1, "ytick.left": 0, "ytick.labelleft": 0, + }) + ax = plt.figure().add_subplot() + assert ax.xaxis.get_ticks_position() == "top" + assert ax.yaxis.get_ticks_position() == "right" + + +def test_get_tick_position_tick_top_tick_right(): + """Test that get_tick_position() correctly picks up tick_top() / tick_right().""" + ax = plt.figure().add_subplot() + ax.xaxis.tick_top() + ax.yaxis.tick_right() + assert ax.xaxis.get_ticks_position() == "top" + assert ax.yaxis.get_ticks_position() == "right" + + +def test_get_tick_position_tick_params(): + """Test that get_tick_position() correctly picks up tick_params().""" + ax = plt.figure().add_subplot() + ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False, + right=True, labelright=True, left=False, labelleft=False) + assert ax.xaxis.get_ticks_position() == "top" + assert ax.yaxis.get_ticks_position() == "right" + + +def test_grid_rcparams(): + """Tests that `grid.major/minor.*` overwrites `grid.*` in rcParams.""" + plt.rcParams.update({ + "axes.grid": True, "axes.grid.which": "both", + "ytick.minor.visible": True, "xtick.minor.visible": True, + }) + def_linewidth = plt.rcParams["grid.linewidth"] + def_linestyle = plt.rcParams["grid.linestyle"] + def_alpha = plt.rcParams["grid.alpha"] + + plt.rcParams.update({ + "grid.color": "gray", "grid.minor.color": "red", + "grid.major.linestyle": ":", "grid.major.linewidth": 2, + "grid.minor.alpha": 0.6, + }) + _, ax = plt.subplots() + ax.plot([0, 1]) + + assert ax.xaxis.get_major_ticks()[0].gridline.get_color() == "gray" + assert ax.xaxis.get_minor_ticks()[0].gridline.get_color() == "red" + assert ax.xaxis.get_major_ticks()[0].gridline.get_linewidth() == 2 + assert ax.xaxis.get_minor_ticks()[0].gridline.get_linewidth() == def_linewidth + assert ax.xaxis.get_major_ticks()[0].gridline.get_linestyle() == ":" + assert ax.xaxis.get_minor_ticks()[0].gridline.get_linestyle() == def_linestyle + assert ax.xaxis.get_major_ticks()[0].gridline.get_alpha() == def_alpha + assert ax.xaxis.get_minor_ticks()[0].gridline.get_alpha() == 0.6 + + +def test_set_ticks_emits_lim_changed(): + fig, ax1 = plt.subplots() + ax1.set_xlim(0.5, 1) + called_cartesian = [] + ax1.callbacks.connect("xlim_changed", called_cartesian.append) + ax1.set_xticks([0, 100]) + assert called_cartesian + + fig = plt.figure() + ax2 = fig.add_subplot(projection="polar") + ax2.set_ylim(0.5, 1) + called_polar = [] + ax2.callbacks.connect("ylim_changed", called_polar.append) + ax2.set_rticks([1, 2, 3]) + assert called_polar diff --git a/lib/matplotlib/tests/test_backend_bases.py b/lib/matplotlib/tests/test_backend_bases.py index 3a49f0ec08ec..0205eac42fb3 100644 --- a/lib/matplotlib/tests/test_backend_bases.py +++ b/lib/matplotlib/tests/test_backend_bases.py @@ -38,7 +38,7 @@ def check(master_transform, paths, all_transforms, gc, range(len(raw_paths)), offsets, transforms.AffineDeltaTransform(master_transform), facecolors, edgecolors, [], [], [False], - [], 'screen')] + [], 'screen', hatchcolors=[])] uses = rb._iter_collection_uses_per_path( paths, all_transforms, offsets, facecolors, edgecolors) if raw_paths: @@ -64,7 +64,10 @@ def test_canvas_ctor(): def test_get_default_filename(): - assert plt.figure().canvas.get_default_filename() == 'image.png' + fig = plt.figure() + assert fig.canvas.get_default_filename() == "Figure_1.png" + fig.canvas.manager.set_window_title("0:1/2<3") + assert fig.canvas.get_default_filename() == "0_1_2_3.png" def test_canvas_change(): @@ -260,6 +263,8 @@ def test_interactive_colorbar(plot_func, orientation, tool, button, expected): # Set up the mouse movements start_event = MouseEvent( "button_press_event", fig.canvas, *s0, button) + drag_event = MouseEvent( + "motion_notify_event", fig.canvas, *s1, button, buttons={button}) stop_event = MouseEvent( "button_release_event", fig.canvas, *s1, button) @@ -267,12 +272,12 @@ def test_interactive_colorbar(plot_func, orientation, tool, button, expected): if tool == "zoom": tb.zoom() tb.press_zoom(start_event) - tb.drag_zoom(stop_event) + tb.drag_zoom(drag_event) tb.release_zoom(stop_event) else: tb.pan() tb.press_pan(start_event) - tb.drag_pan(stop_event) + tb.drag_pan(drag_event) tb.release_pan(stop_event) # Should be close, but won't be exact due to screen integer resolution @@ -283,10 +288,11 @@ def test_toolbar_zoompan(): with pytest.warns(UserWarning, match=_EXPECTED_WARNING_TOOLMANAGER): plt.rcParams['toolbar'] = 'toolmanager' ax = plt.gca() + fig = ax.get_figure() assert ax.get_navigate_mode() is None - ax.figure.canvas.manager.toolmanager.trigger_tool('zoom') + fig.canvas.manager.toolmanager.trigger_tool('zoom') assert ax.get_navigate_mode() == "ZOOM" - ax.figure.canvas.manager.toolmanager.trigger_tool('pan') + fig.canvas.manager.toolmanager.trigger_tool('pan') assert ax.get_navigate_mode() == "PAN" @@ -394,6 +400,9 @@ def test_interactive_pan(key, mouseend, expectedxlim, expectedylim): start_event = MouseEvent( "button_press_event", fig.canvas, *sstart, button=MouseButton.LEFT, key=key) + drag_event = MouseEvent( + "motion_notify_event", fig.canvas, *send, button=MouseButton.LEFT, + buttons={MouseButton.LEFT}, key=key) stop_event = MouseEvent( "button_release_event", fig.canvas, *send, button=MouseButton.LEFT, key=key) @@ -401,7 +410,7 @@ def test_interactive_pan(key, mouseend, expectedxlim, expectedylim): tb = NavigationToolbar2(fig.canvas) tb.pan() tb.press_pan(start_event) - tb.drag_pan(stop_event) + tb.drag_pan(drag_event) tb.release_pan(stop_event) # Should be close, but won't be exact due to screen integer resolution assert tuple(ax.get_xlim()) == pytest.approx(expectedxlim, abs=0.02) @@ -509,6 +518,8 @@ def test_interactive_pan_zoom_events(tool, button, patch_vis, forward_nav, t_s): # Set up the mouse movements start_event = MouseEvent("button_press_event", fig.canvas, *s0, button) + drag_event = MouseEvent( + "motion_notify_event", fig.canvas, *s1, button, buttons={button}) stop_event = MouseEvent("button_release_event", fig.canvas, *s1, button) tb = NavigationToolbar2(fig.canvas) @@ -532,18 +543,7 @@ def test_interactive_pan_zoom_events(tool, button, patch_vis, forward_nav, t_s): ylim_b = init_ylim tb.zoom() - tb.press_zoom(start_event) - tb.drag_zoom(stop_event) - tb.release_zoom(stop_event) - - assert ax_t.get_xlim() == pytest.approx(xlim_t, abs=0.15) - assert ax_t.get_ylim() == pytest.approx(ylim_t, abs=0.15) - assert ax_b.get_xlim() == pytest.approx(xlim_b, abs=0.15) - assert ax_b.get_ylim() == pytest.approx(ylim_b, abs=0.15) - # Check if twin-axes are properly triggered - assert ax_t.get_xlim() == pytest.approx(ax_t_twin.get_xlim(), abs=0.15) - assert ax_b.get_xlim() == pytest.approx(ax_b_twin.get_xlim(), abs=0.15) else: # Evaluate expected limits # (call start_pan to make sure ax._pan_start is set) @@ -568,15 +568,16 @@ def test_interactive_pan_zoom_events(tool, button, patch_vis, forward_nav, t_s): ylim_b = init_ylim tb.pan() - tb.press_pan(start_event) - tb.drag_pan(stop_event) - tb.release_pan(stop_event) - assert ax_t.get_xlim() == pytest.approx(xlim_t, abs=0.15) - assert ax_t.get_ylim() == pytest.approx(ylim_t, abs=0.15) - assert ax_b.get_xlim() == pytest.approx(xlim_b, abs=0.15) - assert ax_b.get_ylim() == pytest.approx(ylim_b, abs=0.15) + start_event._process() + drag_event._process() + stop_event._process() + + assert ax_t.get_xlim() == pytest.approx(xlim_t, abs=0.15) + assert ax_t.get_ylim() == pytest.approx(ylim_t, abs=0.15) + assert ax_b.get_xlim() == pytest.approx(xlim_b, abs=0.15) + assert ax_b.get_ylim() == pytest.approx(ylim_b, abs=0.15) - # Check if twin-axes are properly triggered - assert ax_t.get_xlim() == pytest.approx(ax_t_twin.get_xlim(), abs=0.15) - assert ax_b.get_xlim() == pytest.approx(ax_b_twin.get_xlim(), abs=0.15) + # Check if twin-axes are properly triggered + assert ax_t.get_xlim() == pytest.approx(ax_t_twin.get_xlim(), abs=0.15) + assert ax_b.get_xlim() == pytest.approx(ax_b_twin.get_xlim(), abs=0.15) diff --git a/lib/matplotlib/tests/test_backend_cairo.py b/lib/matplotlib/tests/test_backend_cairo.py index 8cc0b319b770..4eaa8fc1ca3c 100644 --- a/lib/matplotlib/tests/test_backend_cairo.py +++ b/lib/matplotlib/tests/test_backend_cairo.py @@ -2,13 +2,14 @@ import pytest +import matplotlib.pyplot as plt from matplotlib.testing.decorators import check_figures_equal from matplotlib import ( collections as mcollections, patches as mpatches, path as mpath) @pytest.mark.backend('cairo') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_patch_alpha_coloring(fig_test, fig_ref): """ Test checks that the patch and collection are rendered with the specified @@ -46,3 +47,7 @@ def test_patch_alpha_coloring(fig_test, fig_ref): facecolor=(1, 0, 0, 0.5), edgecolor=(0, 0, 1, 0.75)) ax.add_collection(col) + + # Have pyplot manage the figures to ensure the cairo backend is used + plt.figure(fig_ref) + plt.figure(fig_test) diff --git a/lib/matplotlib/tests/test_backend_gtk3.py b/lib/matplotlib/tests/test_backend_gtk3.py index d7fa4329cfc8..a299d21a4b7b 100644 --- a/lib/matplotlib/tests/test_backend_gtk3.py +++ b/lib/matplotlib/tests/test_backend_gtk3.py @@ -1,48 +1,29 @@ +import os from matplotlib import pyplot as plt import pytest +from unittest import mock @pytest.mark.backend("gtk3agg", skip_on_importerror=True) -def test_correct_key(): - pytest.xfail("test_widget_send_event is not triggering key_press_event") +def test_save_figure_return(): + from gi.repository import Gtk + fig, ax = plt.subplots() + ax.imshow([[1]]) + with mock.patch("gi.repository.Gtk.FileFilter") as fileFilter: + filt = fileFilter.return_value + filt.get_name.return_value = "Portable Network Graphics" + with mock.patch("gi.repository.Gtk.FileChooserDialog") as dialogChooser: + dialog = dialogChooser.return_value + dialog.get_filter.return_value = filt + dialog.get_filename.return_value = "foobar.png" + dialog.run.return_value = Gtk.ResponseType.OK + fname = fig.canvas.manager.toolbar.save_figure() + os.remove("foobar.png") + assert fname == "foobar.png" - from gi.repository import Gdk, Gtk # type: ignore - fig = plt.figure() - buf = [] - - def send(event): - for key, mod in [ - (Gdk.KEY_a, Gdk.ModifierType.SHIFT_MASK), - (Gdk.KEY_a, 0), - (Gdk.KEY_a, Gdk.ModifierType.CONTROL_MASK), - (Gdk.KEY_agrave, 0), - (Gdk.KEY_Control_L, Gdk.ModifierType.MOD1_MASK), - (Gdk.KEY_Alt_L, Gdk.ModifierType.CONTROL_MASK), - (Gdk.KEY_agrave, - Gdk.ModifierType.CONTROL_MASK - | Gdk.ModifierType.MOD1_MASK - | Gdk.ModifierType.MOD4_MASK), - (0xfd16, 0), # KEY_3270_Play. - (Gdk.KEY_BackSpace, 0), - (Gdk.KEY_BackSpace, Gdk.ModifierType.CONTROL_MASK), - ]: - # This is not actually really the right API: it depends on the - # actual keymap (e.g. on Azerty, shift+agrave -> 0). - Gtk.test_widget_send_key(fig.canvas, key, mod) - - def receive(event): - buf.append(event.key) - if buf == [ - "A", "a", "ctrl+a", - "\N{LATIN SMALL LETTER A WITH GRAVE}", - "alt+control", "ctrl+alt", - "ctrl+alt+super+\N{LATIN SMALL LETTER A WITH GRAVE}", - # (No entry for KEY_3270_Play.) - "backspace", "ctrl+backspace", - ]: - plt.close(fig) - - fig.canvas.mpl_connect("draw_event", send) - fig.canvas.mpl_connect("key_press_event", receive) - plt.show() + with mock.patch("gi.repository.Gtk.MessageDialog"): + dialog.get_filename.return_value = None + dialog.run.return_value = Gtk.ResponseType.OK + fname = fig.canvas.manager.toolbar.save_figure() + assert fname is None diff --git a/lib/matplotlib/tests/test_backend_inline.py b/lib/matplotlib/tests/test_backend_inline.py index 4112eb213e2c..997e1e7186b1 100644 --- a/lib/matplotlib/tests/test_backend_inline.py +++ b/lib/matplotlib/tests/test_backend_inline.py @@ -1,7 +1,6 @@ import os from pathlib import Path from tempfile import TemporaryDirectory -import sys import pytest @@ -13,9 +12,8 @@ pytest.importorskip('matplotlib_inline') -@pytest.mark.skipif(sys.version_info[:2] <= (3, 9), reason="Requires Python 3.10+") def test_ipynb(): - nb_path = Path(__file__).parent / 'test_inline_01.ipynb' + nb_path = Path(__file__).parent / 'data/test_inline_01.ipynb' with TemporaryDirectory() as tmpdir: out_path = Path(tmpdir, "out.ipynb") diff --git a/lib/matplotlib/tests/test_backend_macosx.py b/lib/matplotlib/tests/test_backend_macosx.py index 7431481de8ae..0648e43cde94 100644 --- a/lib/matplotlib/tests/test_backend_macosx.py +++ b/lib/matplotlib/tests/test_backend_macosx.py @@ -1,17 +1,19 @@ import os +import threading +from pathlib import Path import pytest +from unittest import mock import matplotlib as mpl import matplotlib.pyplot as plt -try: - from matplotlib.backends import _macosx -except ImportError: - pytest.skip("These are mac only tests", allow_module_level=True) +from matplotlib.testing import subprocess_run_helper -@pytest.mark.backend('macosx') -def test_cached_renderer(): +_test_timeout = 60 + + +def _test_cached_renderer(): # Make sure that figures have an associated renderer after # a fig.canvas.draw() call fig = plt.figure(1) @@ -23,8 +25,14 @@ def test_cached_renderer(): assert fig.canvas.get_renderer()._renderer is not None -@pytest.mark.backend('macosx') -def test_savefig_rcparam(monkeypatch, tmp_path): +@pytest.mark.backend('macosx', skip_on_importerror=True) +def test_cached_renderer(): + subprocess_run_helper(_test_cached_renderer, timeout=_test_timeout, + extra_env={"MPLBACKEND": "macosx"}) + + +def _test_savefig_rcparam(): + tmp_path = Path(os.environ["TEST_SAVEFIG_PATH"]) def new_choose_save_file(title, directory, filename): # Replacement function instead of opening a GUI window @@ -33,9 +41,10 @@ def new_choose_save_file(title, directory, filename): os.makedirs(f"{directory}/test") return f"{directory}/test/{filename}" - monkeypatch.setattr(_macosx, "choose_save_file", new_choose_save_file) fig = plt.figure() - with mpl.rc_context({"savefig.directory": tmp_path}): + with (mock.patch("matplotlib.backends._macosx.choose_save_file", + new_choose_save_file), + mpl.rc_context({"savefig.directory": tmp_path})): fig.canvas.toolbar.save_figure() # Check the saved location got created save_file = f"{tmp_path}/test/{fig.canvas.get_default_filename()}" @@ -46,7 +55,55 @@ def new_choose_save_file(title, directory, filename): assert mpl.rcParams["savefig.directory"] == f"{tmp_path}/test" -@pytest.mark.backend('macosx') +@pytest.mark.backend('macosx', skip_on_importerror=True) +def test_savefig_rcparam(tmp_path): + subprocess_run_helper( + _test_savefig_rcparam, timeout=_test_timeout, + extra_env={"MPLBACKEND": "macosx", "TEST_SAVEFIG_PATH": tmp_path}) + + +@pytest.mark.backend('macosx', skip_on_importerror=True) def test_ipython(): from matplotlib.testing import ipython_in_subprocess ipython_in_subprocess("osx", {(8, 24): "macosx", (7, 0): "MacOSX"}) + + +def _test_save_figure_return(): + fig, ax = plt.subplots() + ax.imshow([[1]]) + prop = "matplotlib.backends._macosx.choose_save_file" + with mock.patch(prop, return_value="foobar.png"): + fname = fig.canvas.manager.toolbar.save_figure() + os.remove("foobar.png") + assert fname == "foobar.png" + with mock.patch(prop, return_value=None): + fname = fig.canvas.manager.toolbar.save_figure() + assert fname is None + + +@pytest.mark.backend('macosx', skip_on_importerror=True) +def test_save_figure_return(): + subprocess_run_helper(_test_save_figure_return, timeout=_test_timeout, + extra_env={"MPLBACKEND": "macosx"}) + + +def _test_create_figure_on_worker_thread_fails(): + def create_figure(): + warn_msg = "Matplotlib GUI outside of the main thread will likely fail." + err_msg = "Cannot create a GUI FigureManager outside the main thread" + with pytest.warns(UserWarning, match=warn_msg): + with pytest.raises(RuntimeError, match=err_msg): + plt.gcf() + + worker = threading.Thread(target=create_figure) + worker.start() + worker.join() + + +@pytest.mark.backend('macosx', skip_on_importerror=True) +def test_create_figure_on_worker_thread_fails(): + subprocess_run_helper( + _test_create_figure_on_worker_thread_fails, + timeout=_test_timeout, + extra_env={"MPLBACKEND": "macosx"} + ) diff --git a/lib/matplotlib/tests/test_backend_nbagg.py b/lib/matplotlib/tests/test_backend_nbagg.py index 23af88d95086..ccf74df20aab 100644 --- a/lib/matplotlib/tests/test_backend_nbagg.py +++ b/lib/matplotlib/tests/test_backend_nbagg.py @@ -14,7 +14,7 @@ def test_ipynb(): - nb_path = Path(__file__).parent / 'test_nbagg_01.ipynb' + nb_path = Path(__file__).parent / 'data/test_nbagg_01.ipynb' with TemporaryDirectory() as tmpdir: out_path = Path(tmpdir, "out.ipynb") diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index ad565ea9e81b..20776af13307 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -1,8 +1,8 @@ import datetime import decimal import io -import os from pathlib import Path +import string import numpy as np import pytest @@ -13,10 +13,10 @@ ) from matplotlib.cbook import _get_data_path from matplotlib.ft2font import FT2Font -from matplotlib.font_manager import findfont, FontProperties -from matplotlib.backends._backend_pdf_ps import get_glyphs_subset +from matplotlib.backends._backend_pdf_ps import get_glyphs_subset, font_as_file from matplotlib.backends.backend_pdf import PdfPages from matplotlib.patches import Rectangle +from matplotlib.testing import _gen_multi_font_text, _has_tex_package from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.testing._markers import needs_usetex @@ -41,22 +41,6 @@ def test_use14corefonts(): ax.axhline(0.5, linewidth=0.5) -@pytest.mark.parametrize('fontname, fontfile', [ - ('DejaVu Sans', 'DejaVuSans.ttf'), - ('WenQuanYi Zen Hei', 'wqy-zenhei.ttc'), -]) -@pytest.mark.parametrize('fonttype', [3, 42]) -def test_embed_fonts(fontname, fontfile, fonttype): - if Path(findfont(FontProperties(family=[fontname]))).name != fontfile: - pytest.skip(f'Font {fontname!r} may be missing') - - rcParams['pdf.fonttype'] = fonttype - fig, ax = plt.subplots() - ax.plot([1, 2, 3]) - ax.set_title('Axes Title', font=fontname) - fig.savefig(io.BytesIO(), format='pdf') - - def test_multipage_pagecount(): with PdfPages(io.BytesIO()) as pdf: assert pdf.get_pagecount() == 0 @@ -81,48 +65,18 @@ def test_multipage_properfinalize(): def test_multipage_keep_empty(tmp_path): - # test empty pdf files - - # an empty pdf is left behind with keep_empty unset + # An empty pdf deletes itself afterwards. fn = tmp_path / "a.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), PdfPages(fn) as pdf: - pass - assert fn.exists() - - # an empty pdf is left behind with keep_empty=True - fn = tmp_path / "b.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), \ - PdfPages(fn, keep_empty=True) as pdf: - pass - assert fn.exists() - - # an empty pdf deletes itself afterwards with keep_empty=False - fn = tmp_path / "c.pdf" - with PdfPages(fn, keep_empty=False) as pdf: + with PdfPages(fn) as pdf: pass assert not fn.exists() - # test pdf files with content, they should never be deleted - - # a non-empty pdf is left behind with keep_empty unset - fn = tmp_path / "d.pdf" + # Test pdf files with content, they should never be deleted. + fn = tmp_path / "b.pdf" with PdfPages(fn) as pdf: pdf.savefig(plt.figure()) assert fn.exists() - # a non-empty pdf is left behind with keep_empty=True - fn = tmp_path / "e.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), \ - PdfPages(fn, keep_empty=True) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - - # a non-empty pdf is left behind with keep_empty=False - fn = tmp_path / "f.pdf" - with PdfPages(fn, keep_empty=False) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - def test_composite_image(): # Test that figures can be saved with and without combining multiple images @@ -337,19 +291,21 @@ def test_text_urls_tex(): assert annot.Rect[1] == decimal.Decimal('0.7') * 72 -def test_pdfpages_fspath(): - with PdfPages(Path(os.devnull)) as pdf: +def test_pdfpages_fspath(tmp_path): + with PdfPages(tmp_path / 'unused.pdf') as pdf: pdf.savefig(plt.figure()) -@image_comparison(['hatching_legend.pdf']) -def test_hatching_legend(): +@image_comparison(['hatching_legend.pdf'], style='mpl20') +def test_hatching_legend(text_placeholders): """Test for correct hatching on patches in legend""" fig = plt.figure(figsize=(1, 2)) a = Rectangle([0, 0], 0, 0, facecolor="green", hatch="XXXX") b = Rectangle([0, 0], 0, 0, facecolor="blue", hatch="XXXX") + # Verify that hatches in PDFs work after empty labels. See + # https://github.com/matplotlib/matplotlib/issues/4469 fig.legend([a, b, a, b], ["", "", "", ""]) @@ -390,7 +346,7 @@ def test_empty_rasterized(): fig.savefig(io.BytesIO(), format="pdf") -@image_comparison(['kerning.pdf']) +@image_comparison(['kerning.pdf'], style='mpl20') def test_kerning(): fig = plt.figure() s = "AVAVAVAVAVAVAVAV€AAVV" @@ -405,12 +361,13 @@ def test_glyphs_subset(): # non-subsetted FT2Font nosubfont = FT2Font(fpath) nosubfont.set_text(chars) + nosubcmap = nosubfont.get_charmap() # subsetted FT2Font - subfont = FT2Font(get_glyphs_subset(fpath, chars)) + glyph_indices = {nosubcmap[ord(c)] for c in chars} + with get_glyphs_subset(fm.FontPath(fpath, 0), glyph_indices) as subset: + subfont = FT2Font(font_as_file(subset)) subfont.set_text(chars) - - nosubcmap = nosubfont.get_charmap() subcmap = subfont.get_charmap() # all unique chars must be available in subsetted font @@ -423,30 +380,58 @@ def test_glyphs_subset(): assert subfont.get_num_glyphs() == nosubfont.get_num_glyphs() -@image_comparison(["multi_font_type3.pdf"], tol=4.6) +@image_comparison(["multi_font_type3.pdf"], style='mpl20') def test_multi_font_type3(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") - - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) plt.rc('pdf', fonttype=3) - fig = plt.figure() - fig.text(0.15, 0.475, "There are 几个汉字 in between!") + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') -@image_comparison(["multi_font_type42.pdf"], tol=2.2) +@image_comparison(["multi_font_type42.pdf"], style='mpl20') def test_multi_font_type42(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) + plt.rc('pdf', fonttype=42) + + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') + + +@image_comparison(['ttc_type3.pdf'], style='mpl20') +def test_ttc_type3(): + fp = fm.FontProperties(family=['WenQuanYi Zen Hei']) + if Path(fm.findfont(fp)).name != 'wqy-zenhei.ttc': + pytest.skip('Font wqy-zenhei.ttc may be missing') - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + fonts = ['WenQuanYi Zen Hei', 'WenQuanYi Zen Hei Mono'] + plt.rc('font', size=16) + plt.rc('pdf', fonttype=3) + + figs = plt.figure(figsize=(7, len(fonts) / 2)).subfigures(len(fonts)) + for font, fig in zip(fonts, figs): + fig.text(0.5, 0.5, f'{font}: {string.ascii_uppercase}', font=font, + horizontalalignment='center', verticalalignment='center') + + +@image_comparison(['ttc_type42.pdf'], style='mpl20') +def test_ttc_type42(): + fp = fm.FontProperties(family=['WenQuanYi Zen Hei']) + if Path(fm.findfont(fp)).name != 'wqy-zenhei.ttc': + pytest.skip('Font wqy-zenhei.ttc may be missing') + + fonts = ['WenQuanYi Zen Hei', 'WenQuanYi Zen Hei Mono'] + plt.rc('font', size=16) plt.rc('pdf', fonttype=42) - fig = plt.figure() - fig.text(0.15, 0.475, "There are 几个汉字 in between!") + figs = plt.figure(figsize=(7, len(fonts) / 2)).subfigures(len(fonts)) + for font, fig in zip(fonts, figs): + fig.text(0.5, 0.5, f'{font}: {string.ascii_uppercase}', font=font, + horizontalalignment='center', verticalalignment='center') @pytest.mark.parametrize('family_name, file_name', @@ -463,3 +448,283 @@ def test_otf_font_smoke(family_name, file_name): fig = plt.figure() fig.text(0.15, 0.475, "Привет мир!") fig.savefig(io.BytesIO(), format="pdf") + + +@image_comparison(["truetype-conversion.pdf"], style='mpl20') +# mpltest.ttf does not have "l"/"p" glyphs so we get a warning when trying to +# get the font extents. +def test_truetype_conversion(recwarn): + mpl.rcParams['pdf.fonttype'] = 3 + fig, ax = plt.subplots() + ax.text(0, 0, "ABCDE", + font=Path(__file__).parent / "data/mpltest.ttf", fontsize=72) + ax.set_xticks([]) + ax.set_yticks([]) + + +@pytest.mark.skipif(not _has_tex_package("heuristica"), + reason="LaTeX lacks heuristica package") +@image_comparison(["font-heuristica.pdf"]) +def test_font_heuristica(): + # Heuristica uses the callothersubr operator for some glyphs + mpl.rcParams['text.latex.preamble'] = '\n'.join(( + r'\usepackage{heuristica}', + r'\usepackage[T1]{fontenc}', + r'\usepackage[utf8]{inputenc}' + )) + fig, ax = plt.subplots() + ax.text(0.1, 0.1, r"BHTem fi ffl 1234", usetex=True, fontsize=50) + ax.set_xticks([]) + ax.set_yticks([]) + + +@pytest.mark.skipif(not _has_tex_package("DejaVuSans"), + reason="LaTeX lacks DejaVuSans package") +@image_comparison(["font-dejavusans.pdf"]) +def test_font_dejavusans(): + # DejaVuSans uses the seac operator to compose characters with diacritics + mpl.rcParams['text.latex.preamble'] = '\n'.join(( + r'\usepackage{DejaVuSans}', + r'\usepackage[T1]{fontenc}', + r'\usepackage[utf8]{inputenc}' + )) + + fig, ax = plt.subplots() + ax.text(0.1, 0.1, r"\textsf{ñäö ABCDabcd}", usetex=True, fontsize=50) + ax.text(0.1, 0.3, r"\textsf{fi ffl 1234}", usetex=True, fontsize=50) + ax.set_xticks([]) + ax.set_yticks([]) + + +@pytest.mark.skipif(not _has_tex_package("charter"), + reason="LaTeX lacks charter package") +@image_comparison(["font-bitstream-charter.pdf"]) +def test_font_bitstream_charter(): + mpl.rcParams['text.latex.preamble'] = '\n'.join(( + r'\usepackage{charter}', + r'\usepackage[T1]{fontenc}', + r'\usepackage[utf8]{inputenc}' + )) + fig, ax = plt.subplots() + ax.text(0.1, 0.1, r"åüš ABCDabcd", usetex=True, fontsize=50) + ax.text(0.1, 0.3, r"fi ffl 1234", usetex=True, fontsize=50) + ax.set_xticks([]) + ax.set_yticks([]) + + +def test_scatter_offaxis_colored_pdf_size(): + """ + Test that off-axis scatter plots with per-point colors don't bloat PDFs. + + Regression test for issue #2488. When scatter points with per-point colors + are completely outside the visible axes, the PDF backend should skip + writing those markers to significantly reduce file size. + """ + # Use John Hunter's birthday as random seed for reproducibility + rng = np.random.default_rng(19680801) + + n_points = 1000 + x = rng.random(n_points) * 10 + y = rng.random(n_points) * 10 + c = rng.random(n_points) + + # Test 1: Scatter with per-point colors, all points OFF-AXIS + fig1, ax1 = plt.subplots() + ax1.scatter(x, y, c=c) + ax1.set_xlim(20, 30) # Move view completely away from data (x is 0-10) + ax1.set_ylim(20, 30) # Move view completely away from data (y is 0-10) + + buf1 = io.BytesIO() + fig1.savefig(buf1, format='pdf') + size_offaxis_colored = buf1.tell() + plt.close(fig1) + + # Test 2: Empty scatter (baseline - accounts for scatter call overhead) + fig2, ax2 = plt.subplots() + ax2.scatter([], []) # Empty scatter to match the axes structure + ax2.set_xlim(20, 30) + ax2.set_ylim(20, 30) + + buf2 = io.BytesIO() + fig2.savefig(buf2, format='pdf') + size_empty = buf2.tell() + plt.close(fig2) + + # Test 3: Scatter with visible markers (should be much larger) + fig3, ax3 = plt.subplots() + ax3.scatter(x + 20, y + 20, c=c) # Shift points to be visible + ax3.set_xlim(20, 30) + ax3.set_ylim(20, 30) + + buf3 = io.BytesIO() + fig3.savefig(buf3, format='pdf') + size_visible = buf3.tell() + plt.close(fig3) + + # The off-axis colored scatter should be close to empty size. + # Since the axes are identical, the difference should be minimal + # (just the scatter collection setup, no actual marker data). + # Use a tight tolerance since axes output is identical. + assert size_offaxis_colored < size_empty + 5_000, ( + f"Off-axis colored scatter PDF ({size_offaxis_colored} bytes) is too large. " + f"Expected close to empty scatter size ({size_empty} bytes). " + f"Markers may not be properly skipped." + ) + + # The visible scatter should be significantly larger than both empty and + # off-axis, demonstrating the optimization is working. + assert size_visible > size_empty + 15_000, ( + f"Visible scatter PDF ({size_visible} bytes) should be much larger " + f"than empty ({size_empty} bytes) to validate the test." + ) + assert size_visible > size_offaxis_colored + 15_000, ( + f"Visible scatter PDF ({size_visible} bytes) should be much larger " + f"than off-axis ({size_offaxis_colored} bytes) to validate optimization." + ) + + +@check_figures_equal(extensions=["pdf"]) +def test_scatter_offaxis_colored_visual(fig_test, fig_ref): + """ + Test that on-axis scatter with per-point colors still renders correctly. + + Ensures the optimization for off-axis markers doesn't break normal + scatter rendering. + """ + rng = np.random.default_rng(19680801) + + n_points = 100 + x = rng.random(n_points) * 5 + y = rng.random(n_points) * 5 + c = rng.random(n_points) + + # Test figure: scatter with clipping optimization + ax_test = fig_test.subplots() + ax_test.scatter(x, y, c=c, s=50) + ax_test.set_xlim(0, 10) + ax_test.set_ylim(0, 10) + + # Reference figure: should look identical + ax_ref = fig_ref.subplots() + ax_ref.scatter(x, y, c=c, s=50) + ax_ref.set_xlim(0, 10) + ax_ref.set_ylim(0, 10) + + +@check_figures_equal(extensions=["pdf"]) +def test_scatter_mixed_onoff_axis(fig_test, fig_ref): + """ + Test scatter with some points on-axis and some off-axis. + + Ensures the optimization correctly handles the common case where only + some markers are outside the visible area. + """ + rng = np.random.default_rng(19680801) + + # Create points: half on-axis (0-5), half off-axis (15-20) + n_points = 50 + x_on = rng.random(n_points) * 5 + y_on = rng.random(n_points) * 5 + x_off = rng.random(n_points) * 5 + 15 + y_off = rng.random(n_points) * 5 + 15 + + x = np.concatenate([x_on, x_off]) + y = np.concatenate([y_on, y_off]) + c = rng.random(2 * n_points) + + # Test figure: scatter with mixed points + ax_test = fig_test.subplots() + ax_test.scatter(x, y, c=c, s=50) + ax_test.set_xlim(0, 10) + ax_test.set_ylim(0, 10) + + # Reference figure: only the on-axis points should be visible + ax_ref = fig_ref.subplots() + ax_ref.scatter(x_on, y_on, c=c[:n_points], s=50) + ax_ref.set_xlim(0, 10) + ax_ref.set_ylim(0, 10) + + +@check_figures_equal(extensions=["pdf"]) +def test_scatter_large_markers_partial_clip(fig_test, fig_ref): + """ + Test that large markers are rendered when partially visible. + + Addresses reviewer concern: markers with centers outside the canvas but + with edges extending into the visible area should still be rendered. + """ + # Create markers just outside the visible area + # Canvas is 0-10, markers at x=-0.5 and x=10.5 + x = np.array([-0.5, 10.5, 5]) # left edge, right edge, center + y = np.array([5, 5, -0.5]) # center, center, bottom edge + c = np.array([0.2, 0.5, 0.8]) + + # Test figure: large markers (s=500 ≈ 11 points radius) + # Centers are outside, but marker edges extend into visible area + ax_test = fig_test.subplots() + ax_test.scatter(x, y, c=c, s=500) + ax_test.set_xlim(0, 10) + ax_test.set_ylim(0, 10) + + # Reference figure: same plot (should render identically) + ax_ref = fig_ref.subplots() + ax_ref.scatter(x, y, c=c, s=500) + ax_ref.set_xlim(0, 10) + ax_ref.set_ylim(0, 10) + + +@check_figures_equal(extensions=["pdf"]) +def test_scatter_logscale(fig_test, fig_ref): + """ + Test scatter optimization with logarithmic scales. + + Ensures bounds checking works correctly in log-transformed coordinates. + """ + rng = np.random.default_rng(19680801) + + # Create points across several orders of magnitude + n_points = 50 + x = 10 ** (rng.random(n_points) * 4) # 1 to 10000 + y = 10 ** (rng.random(n_points) * 4) + c = rng.random(n_points) + + # Test figure: log scale with points mostly outside view + ax_test = fig_test.subplots() + ax_test.scatter(x, y, c=c, s=50) + ax_test.set_xscale('log') + ax_test.set_yscale('log') + ax_test.set_xlim(100, 1000) # Only show middle range + ax_test.set_ylim(100, 1000) + + # Reference figure: should render identically + ax_ref = fig_ref.subplots() + ax_ref.scatter(x, y, c=c, s=50) + ax_ref.set_xscale('log') + ax_ref.set_yscale('log') + ax_ref.set_xlim(100, 1000) + ax_ref.set_ylim(100, 1000) + + +@check_figures_equal(extensions=["pdf"]) +def test_scatter_polar(fig_test, fig_ref): + """ + Test scatter optimization with polar coordinates. + + Ensures bounds checking works correctly in polar projections. + """ + rng = np.random.default_rng(19680801) + + n_points = 50 + theta = rng.random(n_points) * 2 * np.pi + r = rng.random(n_points) * 3 + c = rng.random(n_points) + + # Test figure: polar projection + ax_test = fig_test.subplots(subplot_kw={'projection': 'polar'}) + ax_test.scatter(theta, r, c=c, s=50) + ax_test.set_ylim(0, 2) # Limit radial range + + # Reference figure: should render identically + ax_ref = fig_ref.subplots(subplot_kw={'projection': 'polar'}) + ax_ref.scatter(theta, r, c=c, s=50) + ax_ref.set_ylim(0, 2) diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index 04b51f4d3781..e5b73c9450f3 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -1,7 +1,9 @@ import datetime from io import BytesIO import os +from pathlib import Path import shutil +import string import numpy as np from packaging.version import parse as parse_version @@ -9,6 +11,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt +from matplotlib.font_manager import FontProperties, findfont from matplotlib.testing import _has_tex_package, _check_for_pgf from matplotlib.testing.exceptions import ImageComparisonFailure from matplotlib.testing.compare import compare_images @@ -288,48 +291,18 @@ def test_pdf_pages_metadata_check(monkeypatch, system): @needs_pgf_xelatex def test_multipage_keep_empty(tmp_path): - # test empty pdf files - - # an empty pdf is left behind with keep_empty unset + # An empty pdf deletes itself afterwards. fn = tmp_path / "a.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), PdfPages(fn) as pdf: - pass - assert fn.exists() - - # an empty pdf is left behind with keep_empty=True - fn = tmp_path / "b.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), \ - PdfPages(fn, keep_empty=True) as pdf: - pass - assert fn.exists() - - # an empty pdf deletes itself afterwards with keep_empty=False - fn = tmp_path / "c.pdf" - with PdfPages(fn, keep_empty=False) as pdf: + with PdfPages(fn) as pdf: pass assert not fn.exists() - # test pdf files with content, they should never be deleted - - # a non-empty pdf is left behind with keep_empty unset - fn = tmp_path / "d.pdf" + # Test pdf files with content, they should never be deleted. + fn = tmp_path / "b.pdf" with PdfPages(fn) as pdf: pdf.savefig(plt.figure()) assert fn.exists() - # a non-empty pdf is left behind with keep_empty=True - fn = tmp_path / "e.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), \ - PdfPages(fn, keep_empty=True) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - - # a non-empty pdf is left behind with keep_empty=False - fn = tmp_path / "f.pdf" - with PdfPages(fn, keep_empty=False) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - @needs_pgf_xelatex def test_tex_restart_after_error(): @@ -360,6 +333,23 @@ def test_png_transparency(): # Actually, also just testing that png works. assert (t[..., 3] == 0).all() # fully transparent. +@needs_pgf_xelatex +@pytest.mark.backend('pgf') +@image_comparison(['ttc_pgf.pdf'], style='mpl20') +def test_ttc_output(): + fp = FontProperties(family=['WenQuanYi Zen Hei']) + if Path(findfont(fp)).name != 'wqy-zenhei.ttc': + pytest.skip('Font wqy-zenhei.ttc may be missing') + + fonts = {'sans-serif': 'WenQuanYi Zen Hei', 'monospace': 'WenQuanYi Zen Hei Mono'} + plt.rc('font', size=16, **fonts) + + figs = plt.figure(figsize=(7, len(fonts) / 2)).subfigures(len(fonts)) + for font, fig in zip(fonts.values(), figs): + fig.text(0.5, 0.5, f'{font}: {string.ascii_uppercase}', font=font, + horizontalalignment='center', verticalalignment='center') + + @needs_pgf_xelatex def test_unknown_font(caplog): with caplog.at_level("WARNING"): diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index c587a00c0af9..6eac82678362 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -1,15 +1,17 @@ from collections import Counter -from pathlib import Path import io +from pathlib import Path import re +import string import tempfile import numpy as np import pytest -from matplotlib import cbook, path, patheffects, font_manager as fm +from matplotlib import cbook, font_manager, path, patheffects from matplotlib.figure import Figure from matplotlib.patches import Ellipse +from matplotlib.testing import _gen_multi_font_text from matplotlib.testing._markers import needs_ghostscript, needs_usetex from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib as mpl @@ -217,11 +219,6 @@ def test_useafm(): ax.text(.5, .5, "qk") -@image_comparison(["type3.eps"]) -def test_type3_font(): - plt.figtext(.5, .5, "I/J") - - @image_comparison(["coloredhatcheszerolw.eps"]) def test_colored_hatch_zero_linewidth(): ax = plt.gca() @@ -318,30 +315,58 @@ def test_no_duplicate_definition(): assert max(Counter(wds).values()) == 1 -@image_comparison(["multi_font_type3.eps"], tol=0.51) +@image_comparison(["multi_font_type3.eps"], style='mpl20') def test_multi_font_type3(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") - - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) plt.rc('ps', fonttype=3) - fig = plt.figure() - fig.text(0.15, 0.475, "There are 几个汉字 in between!") + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') -@image_comparison(["multi_font_type42.eps"], tol=1.6) +@image_comparison(["multi_font_type42.eps"], style='mpl20') def test_multi_font_type42(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") - - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) plt.rc('ps', fonttype=42) - fig = plt.figure() - fig.text(0.15, 0.475, "There are 几个汉字 in between!") + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') + + +@image_comparison(['ttc_type3.eps'], style='mpl20') +def test_ttc_type3(): + fp = font_manager.FontProperties(family=['WenQuanYi Zen Hei']) + if Path(font_manager.findfont(fp)).name != 'wqy-zenhei.ttc': + pytest.skip('Font wqy-zenhei.ttc may be missing') + + fonts = ['WenQuanYi Zen Hei', 'WenQuanYi Zen Hei Mono'] + plt.rc('font', size=16) + plt.rc('pdf', fonttype=3) + + figs = plt.figure(figsize=(7, len(fonts) / 2)).subfigures(len(fonts)) + for font, fig in zip(fonts, figs): + fig.text(0.5, 0.5, f'{font}: {string.ascii_uppercase}', font=font, + horizontalalignment='center', verticalalignment='center') + + +@image_comparison(['ttc_type42.eps'], style='mpl20') +def test_ttc_type42(): + fp = font_manager.FontProperties(family=['WenQuanYi Zen Hei']) + if Path(font_manager.findfont(fp)).name != 'wqy-zenhei.ttc': + pytest.skip('Font wqy-zenhei.ttc may be missing') + + fonts = ['WenQuanYi Zen Hei', 'WenQuanYi Zen Hei Mono'] + plt.rc('font', size=16) + plt.rc('pdf', fonttype=42) + + figs = plt.figure(figsize=(7, len(fonts) / 2)).subfigures(len(fonts)) + for font, fig in zip(fonts, figs): + fig.text(0.5, 0.5, f'{font}: {string.ascii_uppercase}', font=font, + horizontalalignment='center', verticalalignment='center') @image_comparison(["scatter.eps"]) @@ -358,7 +383,11 @@ def test_path_collection(): sizes = [0.02, 0.04] pc = mcollections.PathCollection(paths, sizes, zorder=-1, facecolors='yellow', offsets=offsets) - ax.add_collection(pc) + # Note: autolim=False is used to keep the view limits as is for now, + # given the updated behavior of autolim=True to also update the view + # limits. It may be reasonable to test the limits handling in the future + # as well. This will require regenerating the reference image. + ax.add_collection(pc, autolim=False) ax.set_xlim(0, 1) @@ -371,10 +400,10 @@ def test_colorbar_shift(tmp_path): plt.colorbar() -def test_auto_papersize_deprecation(): +def test_auto_papersize_removal(): fig = plt.figure() - with pytest.warns(mpl.MatplotlibDeprecationWarning): + with pytest.raises(ValueError, match="'auto' is not a valid value"): fig.savefig(io.BytesIO(), format='eps', papertype='auto') - with pytest.warns(mpl.MatplotlibDeprecationWarning): + with pytest.raises(ValueError, match="'auto' is not a valid value"): mpl.rcParams['ps.papersize'] = 'auto' diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index 5eb1ea77554d..fda0f978ea02 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -15,7 +15,9 @@ from matplotlib import _c_internal_utils try: - from matplotlib.backends.qt_compat import QtGui, QtWidgets # type: ignore # noqa + from matplotlib.backends.qt_compat import QtCore # type: ignore[attr-defined] + from matplotlib.backends.qt_compat import QtGui # type: ignore[attr-defined] # noqa: E501, F401 + from matplotlib.backends.qt_compat import QtWidgets # type: ignore[attr-defined] from matplotlib.backends.qt_editor import _formlayout except ImportError: pytestmark = pytest.mark.skip('No usable Qt bindings') @@ -24,12 +26,6 @@ _test_timeout = 60 # A reasonably safe value for slower architectures. -@pytest.fixture -def qt_core(request): - from matplotlib.backends.qt_compat import QtCore - return QtCore - - @pytest.mark.backend('QtAgg', skip_on_importerror=True) def test_fig_close(): @@ -100,7 +96,7 @@ def test_fig_close(): 'QtAgg', marks=pytest.mark.backend('QtAgg', skip_on_importerror=True)), ]) -def test_correct_key(backend, qt_core, qt_key, qt_mods, answer, monkeypatch): +def test_correct_key(backend, qt_key, qt_mods, answer, monkeypatch): """ Make a figure. Send a key_press_event event (using non-public, qtX backend specific api). @@ -153,11 +149,18 @@ def test_device_pixel_ratio_change(): def set_device_pixel_ratio(ratio): p.return_value = ratio - # The value here doesn't matter, as we can't mock the C++ QScreen - # object, but can override the functional wrapper around it. - # Emitting this event is simply to trigger the DPI change handler - # in Matplotlib in the same manner that it would occur normally. - screen.logicalDotsPerInchChanged.emit(96) + window = qt_canvas.window().windowHandle() + current_version = tuple(int(x) for x in QtCore.qVersion().split('.', 2)[:2]) + if current_version >= (6, 6): + QtCore.QCoreApplication.sendEvent( + window, + QtCore.QEvent(QtCore.QEvent.Type.DevicePixelRatioChange)) + else: + # The value here doesn't matter, as we can't mock the C++ QScreen + # object, but can override the functional wrapper around it. + # Emitting this event is simply to trigger the DPI change handler + # in Matplotlib in the same manner that it would occur normally. + window.screen().logicalDotsPerInchChanged.emit(96) qt_canvas.draw() qt_canvas.flush_events() @@ -166,53 +169,43 @@ def set_device_pixel_ratio(ratio): assert qt_canvas.device_pixel_ratio == ratio qt_canvas.manager.show() + qt_canvas.draw() + qt_canvas.flush_events() size = qt_canvas.size() - screen = qt_canvas.window().windowHandle().screen() - set_device_pixel_ratio(3) - - # The DPI and the renderer width/height change - assert fig.dpi == 360 - assert qt_canvas.renderer.width == 1800 - assert qt_canvas.renderer.height == 720 - - # The actual widget size and figure logical size don't change. - assert size.width() == 600 - assert size.height() == 240 - assert qt_canvas.get_width_height() == (600, 240) - assert (fig.get_size_inches() == (5, 2)).all() - - set_device_pixel_ratio(2) - - # The DPI and the renderer width/height change - assert fig.dpi == 240 - assert qt_canvas.renderer.width == 1200 - assert qt_canvas.renderer.height == 480 - # The actual widget size and figure logical size don't change. - assert size.width() == 600 - assert size.height() == 240 - assert qt_canvas.get_width_height() == (600, 240) - assert (fig.get_size_inches() == (5, 2)).all() + options = [ + (None, 360, 1800, 720), # Use ratio at startup time. + (3, 360, 1800, 720), # Change to same ratio. + (2, 240, 1200, 480), # Change to different ratio. + (1.5, 180, 900, 360), # Fractional ratio. + ] + for ratio, dpi, width, height in options: + if ratio is not None: + set_device_pixel_ratio(ratio) - set_device_pixel_ratio(1.5) + # The DPI and the renderer width/height change + assert fig.dpi == dpi + assert qt_canvas.renderer.width == width + assert qt_canvas.renderer.height == height - # The DPI and the renderer width/height change - assert fig.dpi == 180 - assert qt_canvas.renderer.width == 900 - assert qt_canvas.renderer.height == 360 + # The actual widget size and figure logical size don't change. + assert size.width() == 600 + assert size.height() == 240 + assert qt_canvas.get_width_height() == (600, 240) + assert (fig.get_size_inches() == (5, 2)).all() - # The actual widget size and figure logical size don't change. - assert size.width() == 600 - assert size.height() == 240 - assert qt_canvas.get_width_height() == (600, 240) - assert (fig.get_size_inches() == (5, 2)).all() + # check that closing the figure restores the original dpi + plt.close(fig) + assert fig.dpi == 120 @pytest.mark.backend('QtAgg', skip_on_importerror=True) def test_subplottool(): fig, ax = plt.subplots() with mock.patch("matplotlib.backends.qt_compat._exec", lambda obj: None): - fig.canvas.manager.toolbar.configure_subplots() + tool = fig.canvas.manager.toolbar.configure_subplots() + assert tool is not None + assert tool == fig.canvas.manager.toolbar.configure_subplots() @pytest.mark.backend('QtAgg', skip_on_importerror=True) @@ -225,6 +218,21 @@ def test_figureoptions(): fig.canvas.manager.toolbar.edit_parameters() +@pytest.mark.backend('QtAgg', skip_on_importerror=True) +def test_save_figure_return(tmp_path): + fig, ax = plt.subplots() + ax.imshow([[1]]) + expected = tmp_path / "foobar.png" + prop = "matplotlib.backends.qt_compat.QtWidgets.QFileDialog.getSaveFileName" + with mock.patch(prop, return_value=(str(expected), None)): + fname = fig.canvas.manager.toolbar.save_figure() + assert fname == str(expected) + assert expected.exists() + with mock.patch(prop, return_value=(None, None)): + fname = fig.canvas.manager.toolbar.save_figure() + assert fname is None + + @pytest.mark.backend('QtAgg', skip_on_importerror=True) def test_figureoptions_with_datetime_axes(): fig, ax = plt.subplots() @@ -318,7 +326,7 @@ def _get_testable_qt_backends(): @pytest.mark.backend('QtAgg', skip_on_importerror=True) -def test_fig_sigint_override(qt_core): +def test_fig_sigint_override(): from matplotlib.backends.backend_qt5 import _BackendQT5 # Create a figure plt.figure() @@ -333,10 +341,10 @@ def fire_signal_and_quit(): event_loop_handler = signal.getsignal(signal.SIGINT) # Request event loop exit - qt_core.QCoreApplication.exit() + QtCore.QCoreApplication.exit() # Timer to exit event loop - qt_core.QTimer.singleShot(0, fire_signal_and_quit) + QtCore.QTimer.singleShot(0, fire_signal_and_quit) # Save original SIGINT handler original_handler = signal.getsignal(signal.SIGINT) @@ -361,7 +369,7 @@ def custom_handler(signum, frame): # Repeat again to test that SIG_DFL and SIG_IGN will not be overridden for custom_handler in (signal.SIG_DFL, signal.SIG_IGN): - qt_core.QTimer.singleShot(0, fire_signal_and_quit) + QtCore.QTimer.singleShot(0, fire_signal_and_quit) signal.signal(signal.SIGINT, custom_handler) _BackendQT5.mainloop() diff --git a/lib/matplotlib/tests/test_backend_registry.py b/lib/matplotlib/tests/test_backend_registry.py index eaf8417e7a5f..2bd8e161bd6b 100644 --- a/lib/matplotlib/tests/test_backend_registry.py +++ b/lib/matplotlib/tests/test_backend_registry.py @@ -3,7 +3,6 @@ import pytest -import matplotlib as mpl from matplotlib.backends import BackendFilter, backend_registry @@ -86,14 +85,13 @@ def test_is_valid_backend(backend, is_valid): assert backend_registry.is_valid_backend(backend) == is_valid -def test_deprecated_rcsetup_attributes(): - match = "was deprecated in Matplotlib 3.9" - with pytest.warns(mpl.MatplotlibDeprecationWarning, match=match): - mpl.rcsetup.interactive_bk - with pytest.warns(mpl.MatplotlibDeprecationWarning, match=match): - mpl.rcsetup.non_interactive_bk - with pytest.warns(mpl.MatplotlibDeprecationWarning, match=match): - mpl.rcsetup.all_backends +@pytest.mark.parametrize("backend, normalized", [ + ("agg", "matplotlib.backends.backend_agg"), + ("QtAgg", "matplotlib.backends.backend_qtagg"), + ("module://Anything", "Anything"), +]) +def test_backend_normalization(backend, normalized): + assert backend_registry._backend_module_name(backend) == normalized def test_entry_points_inline(): @@ -121,6 +119,17 @@ def test_entry_point_name_duplicate(clear_backend_registry): [('some_name', 'module1'), ('some_name', 'module2')]) +def test_entry_point_identical(clear_backend_registry): + # Issue https://github.com/matplotlib/matplotlib/issues/28367 + # Multiple entry points with the same name and value (value is the module) + # are acceptable. + n = len(backend_registry._name_to_module) + backend_registry._validate_and_store_entry_points( + [('some_name', 'some.module'), ('some_name', 'some.module')]) + assert len(backend_registry._name_to_module) == n+1 + assert backend_registry._name_to_module['some_name'] == 'module://some.module' + + def test_entry_point_name_is_module(clear_backend_registry): with pytest.raises(RuntimeError): backend_registry._validate_and_store_entry_points( diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py index 01edbf870fb4..6c540ccebd76 100644 --- a/lib/matplotlib/tests/test_backend_svg.py +++ b/lib/matplotlib/tests/test_backend_svg.py @@ -10,8 +10,10 @@ import matplotlib as mpl from matplotlib.figure import Figure +from matplotlib.patches import Circle from matplotlib.text import Text import matplotlib.pyplot as plt +from matplotlib.testing import _gen_multi_font_text from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.testing._markers import needs_usetex from matplotlib import font_manager as fm @@ -66,13 +68,14 @@ def test_text_urls(): assert expected in buf -@image_comparison(['bold_font_output.svg']) +@image_comparison(['bold_font_output.svg'], style='mpl20') def test_bold_font_output(): fig, ax = plt.subplots() ax.plot(np.arange(10), np.arange(10)) ax.set_xlabel('nonbold-xlabel') ax.set_ylabel('bold-ylabel', fontweight='bold') - ax.set_title('bold-title', fontweight='bold') + # set weight as integer to assert it's handled properly + ax.set_title('bold-title', fontweight=600) @image_comparison(['bold_font_output_with_none_fonttype.svg']) @@ -82,10 +85,11 @@ def test_bold_font_output_with_none_fonttype(): ax.plot(np.arange(10), np.arange(10)) ax.set_xlabel('nonbold-xlabel') ax.set_ylabel('bold-ylabel', fontweight='bold') - ax.set_title('bold-title', fontweight='bold') + # set weight as integer to assert it's handled properly + ax.set_title('bold-title', fontweight=600) -@check_figures_equal(tol=20) +@check_figures_equal(extensions=['svg'], tol=20) def test_rasterized(fig_test, fig_ref): t = np.arange(0, 100) * (2.3) x = np.cos(t) @@ -100,7 +104,7 @@ def test_rasterized(fig_test, fig_ref): ax_test.plot(x+1, y, "-", c="b", lw=10, rasterized=True) -@check_figures_equal() +@check_figures_equal(extensions=['svg']) def test_rasterized_ordering(fig_test, fig_ref): t = np.arange(0, 100) * (2.3) x = np.cos(t) @@ -214,7 +218,7 @@ def test_unicode_won(): tree = xml.etree.ElementTree.fromstring(buf) ns = 'http://www.w3.org/2000/svg' - won_id = 'SFSS3583-8e' + won_id = 'SFSS1728-232' assert len(tree.findall(f'.//{{{ns}}}path[@d][@id="{won_id}"]')) == 1 assert f'#{won_id}' in tree.find(f'.//{{{ns}}}use').attrib.values() @@ -299,6 +303,33 @@ def include(gid, obj): assert gid in buf +def test_clip_path_ids_reuse(): + fig, circle = Figure(), Circle((0, 0), radius=10) + for i in range(5): + ax = fig.add_subplot() + aimg = ax.imshow([[i]]) + aimg.set_clip_path(circle) + + inner_circle = Circle((0, 0), radius=1) + ax = fig.add_subplot() + aimg = ax.imshow([[0]]) + aimg.set_clip_path(inner_circle) + + with BytesIO() as fd: + fig.savefig(fd, format='svg') + buf = fd.getvalue() + + tree = xml.etree.ElementTree.fromstring(buf) + ns = 'http://www.w3.org/2000/svg' + + clip_path_ids = set() + for node in tree.findall(f'.//{{{ns}}}clipPath[@id]'): + node_id = node.attrib['id'] + assert node_id not in clip_path_ids # assert ID uniqueness + clip_path_ids.add(node_id) + assert len(clip_path_ids) == 2 # only two clipPaths despite reuse in multiple axes + + def test_savefig_tight(): # Check that the draw-disabled renderer correctly disables open/close_group # as well. @@ -315,13 +346,17 @@ def test_url(): s.set_urls(['https://example.com/foo', 'https://example.com/bar', None]) # Line2D - p, = plt.plot([1, 3], [6, 5]) + p, = plt.plot([2, 3, 4], [4, 5, 6]) p.set_url('https://example.com/baz') + # Line2D markers-only + p, = plt.plot([3, 4, 5], [4, 5, 6], linestyle='none', marker='x') + p.set_url('https://example.com/quux') + b = BytesIO() fig.savefig(b, format='svg') b = b.getvalue() - for v in [b'foo', b'bar', b'baz']: + for v in [b'foo', b'bar', b'baz', b'quux']: assert b'https://example.com/' + v in b @@ -492,30 +527,26 @@ def test_svg_metadata(): assert values == metadata['Keywords'] -@image_comparison(["multi_font_aspath.svg"], tol=1.8) -def test_multi_font_type3(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") - - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) +@image_comparison(["multi_font_aspath.svg"], style='mpl20') +def test_multi_font_aspath(): + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) plt.rc('svg', fonttype='path') - fig = plt.figure() - fig.text(0.15, 0.475, "There are 几个汉字 in between!") - + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') -@image_comparison(["multi_font_astext.svg"]) -def test_multi_font_type42(): - fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": - pytest.skip("Font may be missing") - fig = plt.figure() +@image_comparison(["multi_font_astext.svg"], style='mpl20') +def test_multi_font_astext(): + fonts, test_str = _gen_multi_font_text() + plt.rc('font', family=fonts, size=16) plt.rc('svg', fonttype='none') - plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) - fig.text(0.15, 0.475, "There are 几个汉字 in between!") + fig = plt.figure(figsize=(8, 6)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') @pytest.mark.parametrize('metadata,error,message', [ @@ -603,12 +634,13 @@ def test_svg_font_string(font_str, include_generic): text_count = 0 for text_element in tree.findall(f".//{{{ns}}}text"): text_count += 1 - font_info = dict( + font_style = dict( map(lambda x: x.strip(), _.strip().split(":")) for _ in dict(text_element.items())["style"].split(";") - )["font"] + ) - assert font_info == f"{size}px {font_str}" + assert font_style["font-size"] == f"{size}px" + assert font_style["font-family"] == font_str assert text_count == len(ax.texts) @@ -641,3 +673,34 @@ def test_annotationbbox_gid(): expected = '' assert expected in buf + + +def test_svgid(): + """Test that `svg.id` rcparam appears in output svg if not None.""" + + fig, ax = plt.subplots() + ax.plot([1, 2, 3], [3, 2, 1]) + fig.canvas.draw() + + # Default: svg.id = None + with BytesIO() as fd: + fig.savefig(fd, format='svg') + buf = fd.getvalue().decode() + + tree = xml.etree.ElementTree.fromstring(buf) + + assert plt.rcParams['svg.id'] is None + assert not tree.findall('.[@id]') + + # String: svg.id = str + svg_id = 'a test for issue 28535' + plt.rc('svg', id=svg_id) + + with BytesIO() as fd: + fig.savefig(fd, format='svg') + buf = fd.getvalue().decode() + + tree = xml.etree.ElementTree.fromstring(buf) + + assert plt.rcParams['svg.id'] == svg_id + assert tree.findall(f'.[@id="{svg_id}"]') diff --git a/lib/matplotlib/tests/test_backend_template.py b/lib/matplotlib/tests/test_backend_template.py index d7e2a5cd1266..964d15c1559a 100644 --- a/lib/matplotlib/tests/test_backend_template.py +++ b/lib/matplotlib/tests/test_backend_template.py @@ -49,3 +49,14 @@ def test_show_old_global_api(monkeypatch): mpl.use("module://mpl_test_backend") plt.show() mock_show.assert_called_with() + + +def test_load_case_sensitive(monkeypatch): + mpl_test_backend = SimpleNamespace(**vars(backend_template)) + mock_show = MagicMock() + monkeypatch.setattr( + mpl_test_backend.FigureManagerTemplate, "pyplot_show", mock_show) + monkeypatch.setitem(sys.modules, "mpl_Test_Backend", mpl_test_backend) + mpl.use("module://mpl_Test_Backend") + plt.show() + mock_show.assert_called_with() diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index ee20a94042f7..32212610ffc1 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -4,6 +4,7 @@ import platform import subprocess import sys +from unittest.mock import patch import pytest @@ -35,8 +36,8 @@ def _isolated_tk_test(success_count, func=None): reason="missing tkinter" ) @pytest.mark.skipif( - sys.platform == "linux" and not _c_internal_utils.display_is_valid(), - reason="$DISPLAY and $WAYLAND_DISPLAY are unset" + sys.platform == "linux" and not _c_internal_utils.xdisplay_is_valid(), + reason="$DISPLAY is unset" ) @pytest.mark.xfail( # https://github.com/actions/setup-python/issues/649 ('TF_BUILD' in os.environ or 'GITHUB_ACTION' in os.environ) and @@ -168,7 +169,9 @@ def test_never_update(): plt.show(block=False) plt.draw() # Test FigureCanvasTkAgg. - fig.canvas.toolbar.configure_subplots() # Test NavigationToolbar2Tk. + tool = fig.canvas.toolbar.configure_subplots() # Test NavigationToolbar2Tk. + assert tool is not None + assert tool == fig.canvas.toolbar.configure_subplots() # Tool is reused internally. # Test FigureCanvasTk filter_destroy callback fig.canvas.get_tk_widget().after(100, plt.close, fig) @@ -196,6 +199,23 @@ class Toolbar(NavigationToolbar2Tk): print("success") +@_isolated_tk_test(success_count=2) +def test_save_figure_return(): + import matplotlib.pyplot as plt + from unittest import mock + fig = plt.figure() + prop = "tkinter.filedialog.asksaveasfilename" + with mock.patch(prop, return_value="foobar.png"): + fname = fig.canvas.manager.toolbar.save_figure() + os.remove("foobar.png") + assert fname == "foobar.png" + print("success") + with mock.patch(prop, return_value=""): + fname = fig.canvas.manager.toolbar.save_figure() + assert fname is None + print("success") + + @_isolated_tk_test(success_count=1) def test_canvas_focus(): import tkinter as tk @@ -261,3 +281,52 @@ def test_figure(master): foreground="white") test_figure(root) print("success") + + +@_isolated_tk_test(success_count=1) +def test_dpi_change_triggers_resize(): + """ + Test that _update_device_pixel_ratio recalculates figure.size_inches + using the actual widget dimensions, so the render size matches the + visible canvas area even when constrained by a layout manager. + See issue #31126. + """ + import tkinter as tk + from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg + from matplotlib.figure import Figure + + root = tk.Tk() + root.geometry("400x300") + root.update_idletasks() + + fig = Figure(dpi=100) + fig.add_subplot(111) + + canvas = FigureCanvasTkAgg(fig, master=root) + canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + canvas.draw() + root.update_idletasks() + + actual_w = canvas.get_tk_widget().winfo_width() + actual_h = canvas.get_tk_widget().winfo_height() + assert actual_w > 0 and actual_h > 0 + + # Simulate a 2x DPI change through _update_device_pixel_ratio. + # Mock the platform-specific DPI query to return ratio=2.0. + with patch.object(sys, 'platform', 'linux'), \ + patch.object(canvas._tkcanvas, 'winfo_fpixels', + return_value=192.0): + canvas._update_device_pixel_ratio() + + # Verify the render size matches the actual widget size, NOT the + # inflated physical size from get_width_height(physical=True). + size = fig.get_size_inches() + render_w = round(size[0] * fig.dpi) + render_h = round(size[1] * fig.dpi) + assert abs(render_w - actual_w) <= 2, \ + f"render width {render_w} != actual width {actual_w}" + assert abs(render_h - actual_h) <= 2, \ + f"render height {render_h} != actual height {actual_h}" + + print("success") + root.destroy() diff --git a/lib/matplotlib/tests/test_backend_webagg.py b/lib/matplotlib/tests/test_backend_webagg.py index 1d6769494ef9..c63534ad20e3 100644 --- a/lib/matplotlib/tests/test_backend_webagg.py +++ b/lib/matplotlib/tests/test_backend_webagg.py @@ -1,8 +1,13 @@ import os import sys +from unittest.mock import MagicMock + import pytest import matplotlib.backends.backend_webagg_core +from matplotlib.backends.backend_webagg_core import ( + FigureCanvasWebAggCore, NavigationToolbar2WebAgg, +) from matplotlib.testing import subprocess_run_for_testing @@ -30,3 +35,46 @@ def test_webagg_fallback(backend): def test_webagg_core_no_toolbar(): fm = matplotlib.backends.backend_webagg_core.FigureManagerWebAgg assert fm._toolbar2_class is None + + +def test_toolbar_button_dispatch_allowlist(): + """Only declared toolbar items should be dispatched.""" + fig = MagicMock() + canvas = FigureCanvasWebAggCore(fig) + canvas.toolbar = MagicMock(spec=NavigationToolbar2WebAgg) + canvas.toolbar.toolitems = NavigationToolbar2WebAgg.toolitems + + # Valid toolbar action should be dispatched. + canvas.handle_toolbar_button({'name': 'home'}) + canvas.toolbar.home.assert_called_once() + + # Invalid names should be silently ignored. + canvas.toolbar.reset_mock() + canvas.handle_toolbar_button({'name': '__init__'}) + canvas.handle_toolbar_button({'name': 'not_a_real_button'}) + # No methods should have been called. + assert canvas.toolbar.method_calls == [] + + +@pytest.mark.parametrize("host, origin, allowed", [ + ("localhost:8988", "http://localhost:8988", True), + ("localhost:8988", "http://evil.com", False), + ("localhost:8988", "http://127.0.0.1:8988", False), + ("localhost:8988", "http://[::1]:8988", False), + ("127.0.0.1:8988", "http://127.0.0.1:8988", True), + ("127.0.0.1:8988", "http://localhost:8988", False), + ("127.0.0.1:8988", "http://[::1]:8988", False), + ("[::1]:8988", "http://[::1]:8988", True), + ("[::1]:8988", "http://[::2]:8988", False), + ("[::1]:8988", "http://localhost:8988", False), + ("[::1]:8988", "http://evil.com", False), +]) +def test_websocket_rejects_cross_origin(host, origin, allowed): + """Verify Tornado's default check_origin rejects cross-origin requests.""" + pytest.importorskip("tornado") + from matplotlib.backends.backend_webagg import WebAggApplication + + ws = WebAggApplication.WebSocket.__new__(WebAggApplication.WebSocket) + ws.request = MagicMock() + ws.request.headers = {"Host": host} + assert ws.check_origin(origin) is allowed diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index 6830e7d5c845..5287b511ce7b 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -19,7 +19,7 @@ import matplotlib as mpl from matplotlib import _c_internal_utils from matplotlib.backend_tools import ToolToggleBase -from matplotlib.testing import subprocess_run_helper as _run_helper +from matplotlib.testing import subprocess_run_helper as _run_helper, is_ci_environment class _WaitForStringPopen(subprocess.Popen): @@ -57,6 +57,8 @@ def wait_for(self, terminator): def _get_available_interactive_backends(): _is_linux_and_display_invalid = (sys.platform == "linux" and not _c_internal_utils.display_is_valid()) + _is_linux_and_xdisplay_invalid = (sys.platform == "linux" and + not _c_internal_utils.xdisplay_is_valid()) envs = [] for deps, env in [ *[([qt_api], @@ -74,17 +76,30 @@ def _get_available_interactive_backends(): ]: reason = None missing = [dep for dep in deps if not importlib.util.find_spec(dep)] - if _is_linux_and_display_invalid: - reason = "$DISPLAY and $WAYLAND_DISPLAY are unset" - elif missing: + if missing: reason = "{} cannot be imported".format(", ".join(missing)) + elif _is_linux_and_xdisplay_invalid and ( + env["MPLBACKEND"] == "tkagg" + # Remove when https://github.com/wxWidgets/Phoenix/pull/2638 is out. + or env["MPLBACKEND"].startswith("wx")): + reason = "$DISPLAY is unset" + elif _is_linux_and_display_invalid: + reason = "$DISPLAY and $WAYLAND_DISPLAY are unset" elif env["MPLBACKEND"] == 'macosx' and os.environ.get('TF_BUILD'): reason = "macosx backend fails on Azure" elif env["MPLBACKEND"].startswith('gtk'): - import gi # type: ignore + try: + import gi # type: ignore[import] + except ImportError: + # Though we check that `gi` exists above, it is possible that its + # C-level dependencies are not available, and then it still raises an + # `ImportError`, so guard against that. + available_gtk_versions = [] + else: + gi_repo = gi.Repository.get_default() + available_gtk_versions = gi_repo.enumerate_versions('Gtk') version = env["MPLBACKEND"][3] - repo = gi.Repository.get_default() - if f'{version}.0' not in repo.enumerate_versions('Gtk'): + if f'{version}.0' not in available_gtk_versions: reason = "no usable GTK bindings" marks = [] if reason: @@ -92,13 +107,7 @@ def _get_available_interactive_backends(): elif env["MPLBACKEND"].startswith('wx') and sys.platform == 'darwin': # ignore on macosx because that's currently broken (github #16849) marks.append(pytest.mark.xfail(reason='github #16849')) - elif (env['MPLBACKEND'] == 'tkagg' and - ('TF_BUILD' in os.environ or 'GITHUB_ACTION' in os.environ) and - sys.platform == 'darwin' and - sys.version_info[:2] < (3, 11) - ): - marks.append( # https://github.com/actions/setup-python/issues/649 - pytest.mark.xfail(reason='Tk version mismatch on Azure macOS CI')) + envs.append(({**env, 'BACKEND_DEPS': ','.join(deps)}, marks)) return envs @@ -110,29 +119,9 @@ def _get_testable_interactive_backends(): for env, marks in _get_available_interactive_backends()] -def is_ci_environment(): - # Common CI variables - ci_environment_variables = [ - 'CI', # Generic CI environment variable - 'CONTINUOUS_INTEGRATION', # Generic CI environment variable - 'TRAVIS', # Travis CI - 'CIRCLECI', # CircleCI - 'JENKINS', # Jenkins - 'GITLAB_CI', # GitLab CI - 'GITHUB_ACTIONS', # GitHub Actions - 'TEAMCITY_VERSION' # TeamCity - # Add other CI environment variables as needed - ] - - for env_var in ci_environment_variables: - if os.getenv(env_var): - return True - - return False - - # Reasonable safe values for slower CI/Remote and local architectures. _test_timeout = 120 if is_ci_environment() else 20 +_retry_count = 3 if is_ci_environment() else 0 def _test_toolbar_button_la_mode_icon(fig): @@ -168,7 +157,7 @@ def _test_interactive_impl(): import matplotlib as mpl from matplotlib import pyplot as plt - from matplotlib.backend_bases import KeyEvent + from matplotlib.backend_bases import KeyEvent, FigureCanvasBase mpl.rcParams.update({ "webagg.open_in_browser": False, "webagg.port_retries": 1, @@ -179,7 +168,8 @@ def _test_interactive_impl(): if backend.endswith("agg") and not backend.startswith(("gtk", "web")): # Force interactive framework setup. - plt.figure() + fig = plt.figure() + plt.close(fig) # Check that we cannot switch to a backend using another interactive # framework, but can switch to a backend using cairo instead of agg, @@ -218,6 +208,10 @@ def check_alt_backend(alt_backend): if fig.canvas.toolbar: # i.e toolbar2. fig.canvas.toolbar.draw_rubberband(None, 1., 1, 2., 2) + if backend == 'webagg' and sys.version_info >= (3, 14): + import asyncio + asyncio.set_event_loop(asyncio.new_event_loop()) + timer = fig.canvas.new_timer(1.) # Test that floats are cast to int. timer.add_callback(KeyEvent("key_press_event", fig.canvas, "q")._process) # Trigger quitting upon draw. @@ -225,33 +219,37 @@ def check_alt_backend(alt_backend): fig.canvas.mpl_connect("close_event", print) result = io.BytesIO() - fig.savefig(result, format='png') + fig.savefig(result, format='png', dpi=100) plt.show() # Ensure that the window is really closed. plt.pause(0.5) - # Test that saving works after interactive window is closed, but the figure - # is not deleted. + # When the figure is closed, its manager is removed and the canvas is reset to + # FigureCanvasBase. Saving should still be possible. + assert type(fig.canvas) == FigureCanvasBase, str(fig.canvas) result_after = io.BytesIO() - fig.savefig(result_after, format='png') + fig.savefig(result_after, format='png', dpi=100) - if not backend.startswith('qt5') and sys.platform == 'darwin': - # FIXME: This should be enabled everywhere once Qt5 is fixed on macOS - # to not resize incorrectly. + if backend.endswith("agg"): + # agg-based interactive backends should save the same image as a non-interactive + # figure assert result.getvalue() == result_after.getvalue() @pytest.mark.parametrize("env", _get_testable_interactive_backends()) @pytest.mark.parametrize("toolbar", ["toolbar2", "toolmanager"]) -@pytest.mark.flaky(reruns=3) +@pytest.mark.flaky(reruns=_retry_count) def test_interactive_backend(env, toolbar): if env["MPLBACKEND"] == "macosx": if toolbar == "toolmanager": pytest.skip("toolmanager is not implemented for macosx.") if env["MPLBACKEND"] == "wx": pytest.skip("wx backend is deprecated; tests failed on appveyor") + if env["MPLBACKEND"] == "wxagg" and toolbar == "toolmanager": + pytest.skip("Temporarily deactivated: show() changes figure height " + "and thus fails the test") try: proc = _run_helper( _test_interactive_impl, @@ -290,10 +288,13 @@ def _test_thread_impl(): future = ThreadPoolExecutor().submit(fig.canvas.draw) plt.pause(0.5) # flush_events fails here on at least Tkagg (bpo-41176) future.result() # Joins the thread; rethrows any exception. + # stash the current canvas as closing the figure will reset the canvas on + # the figure + canvas = fig.canvas plt.close() # backend is responsible for flushing any events here if plt.rcParams["backend"].lower().startswith("wx"): # TODO: debug why WX needs this only on py >= 3.8 - fig.canvas.flush_events() + canvas.flush_events() _thread_safe_backends = _get_testable_interactive_backends() @@ -334,7 +335,7 @@ def _test_thread_impl(): @pytest.mark.parametrize("env", _thread_safe_backends) -@pytest.mark.flaky(reruns=3) +@pytest.mark.flaky(reruns=_retry_count) def test_interactive_thread_safety(env): proc = _run_helper(_test_thread_impl, timeout=_test_timeout, extra_env=env) assert proc.stdout.count("CloseEvent") == 1 @@ -457,10 +458,12 @@ def qt5_and_qt6_pairs(): for qt5 in qt5_bindings: for qt6 in qt6_bindings: - for pair in ([qt5, qt6], [qt6, qt5]): - yield pair + yield from ([qt5, qt6], [qt6, qt5]) +@pytest.mark.skipif( + sys.platform == "linux" and not _c_internal_utils.display_is_valid(), + reason="$DISPLAY and $WAYLAND_DISPLAY are unset") @pytest.mark.parametrize('host, mpl', [*qt5_and_qt6_pairs()]) def test_cross_Qt_imports(host, mpl): try: @@ -620,7 +623,7 @@ def _test_number_of_draws_script(): @pytest.mark.parametrize("env", _blit_backends) # subprocesses can struggle to get the display, so rerun a few times -@pytest.mark.flaky(reruns=4) +@pytest.mark.flaky(reruns=_retry_count) def test_blitting_events(env): proc = _run_helper( _test_number_of_draws_script, timeout=_test_timeout, extra_env=env) @@ -632,17 +635,29 @@ def test_blitting_events(env): assert 0 < ndraws < 5 +def _fallback_check(): + import IPython.core.interactiveshell as ipsh + import matplotlib.pyplot + ipsh.InteractiveShell.instance() + matplotlib.pyplot.figure() + + +def test_fallback_to_different_backend(): + pytest.importorskip("IPython") + # Runs the process that caused the GH issue 23770 + # making sure that this doesn't crash + # since we're supposed to be switching to a different backend instead. + response = _run_helper(_fallback_check, timeout=_test_timeout) + + def _impl_test_interactive_timers(): # A timer with <1 millisecond gets converted to int and therefore 0 # milliseconds, which the mac framework interprets as singleshot. # We only want singleshot if we specify that ourselves, otherwise we want # a repeating timer - import os from unittest.mock import Mock import matplotlib.pyplot as plt - # increase pause duration on CI to let things spin up - # particularly relevant for gtk3cairo - pause_time = 2 if os.getenv("CI") else 0.5 + pause_time = 0.5 fig = plt.figure() plt.pause(pause_time) timer = fig.canvas.new_timer(0.1) @@ -654,7 +669,7 @@ def _impl_test_interactive_timers(): assert mock.call_count > 1 # Now turn it into a single shot timer and verify only one gets triggered - mock.call_count = 0 + mock.reset_mock() timer.single_shot = True timer.start() plt.pause(pause_time) @@ -687,8 +702,11 @@ def _test_sigint_impl(backend, target_name, kwargs): def interrupter(): if sys.platform == 'win32': - import win32api - win32api.GenerateConsoleCtrlEvent(0, 0) + from ctypes import windll, wintypes + GenerateConsoleCtrlEvent = windll.kernel32.GenerateConsoleCtrlEvent + GenerateConsoleCtrlEvent.argtypes = [wintypes.DWORD, wintypes.DWORD] + GenerateConsoleCtrlEvent.restype = wintypes.BOOL + GenerateConsoleCtrlEvent(0, 0) else: import signal os.kill(os.getpid(), signal.SIGINT) diff --git a/lib/matplotlib/tests/test_basic.py b/lib/matplotlib/tests/test_basic.py index 6bd417876857..f6aa1e458555 100644 --- a/lib/matplotlib/tests/test_basic.py +++ b/lib/matplotlib/tests/test_basic.py @@ -11,7 +11,7 @@ def test_simple(): def test_override_builtins(): - import pylab # type: ignore + import pylab # type: ignore[import] ok_to_override = { '__name__', '__doc__', diff --git a/lib/matplotlib/tests/test_bbox_tight.py b/lib/matplotlib/tests/test_bbox_tight.py index 5f2b397b650d..f6d910a7f208 100644 --- a/lib/matplotlib/tests/test_bbox_tight.py +++ b/lib/matplotlib/tests/test_bbox_tight.py @@ -8,11 +8,12 @@ import matplotlib.path as mpath import matplotlib.patches as mpatches from matplotlib.ticker import FuncFormatter +from mpl_toolkits.axes_grid1.inset_locator import inset_axes -@image_comparison(['bbox_inches_tight'], remove_text=True, +@image_comparison(['bbox_inches_tight'], remove_text=True, style='mpl20', savefig_kwarg={'bbox_inches': 'tight'}) -def test_bbox_inches_tight(): +def test_bbox_inches_tight(text_placeholders): #: Test that a figure saved using bbox_inches='tight' is clipped correctly data = [[66386, 174296, 75131, 577908, 32015], [58230, 381139, 78045, 99308, 160454], @@ -20,7 +21,8 @@ def test_bbox_inches_tight(): [78415, 81858, 150656, 193263, 69638], [139361, 331509, 343164, 781380, 52269]] - col_labels = row_labels = [''] * 5 + col_labels = ('Freeze', 'Wind', 'Flood', 'Quake', 'Hail') + row_labels = [f'{x} year' for x in (100, 50, 20, 10, 5)] rows = len(data) ind = np.arange(len(col_labels)) + 0.3 # the x locations for the groups @@ -30,13 +32,13 @@ def test_bbox_inches_tight(): # the bottom values for stacked bar chart fig, ax = plt.subplots(1, 1) for row in range(rows): - ax.bar(ind, data[row], width, bottom=yoff, align='edge', color='b') + ax.bar(ind, data[row], width, bottom=yoff, align='edge') yoff = yoff + data[row] - cell_text.append(['']) + cell_text.append([f'{x / 1000:1.1f}' for x in yoff]) plt.xticks([]) plt.xlim(0, 5) - plt.legend([''] * 5, loc=(1.2, 0.2)) - fig.legend([''] * 5, bbox_to_anchor=(0, 0.2), loc='lower left') + plt.legend(['1', '2', '3', '4', '5'], loc=(1.2, 0.2)) + fig.legend(['a', 'b', 'c', 'd', 'e'], bbox_to_anchor=(0, 0.2), loc='lower left') # Add a table at the bottom of the axes cell_text.reverse() plt.table(cellText=cell_text, rowLabels=row_labels, colLabels=col_labels, @@ -44,8 +46,8 @@ def test_bbox_inches_tight(): @image_comparison(['bbox_inches_tight_suptile_legend'], - savefig_kwarg={'bbox_inches': 'tight'}, - tol=0.02 if platform.machine() == 'arm64' else 0) + savefig_kwarg={'bbox_inches': 'tight'}, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.024) def test_bbox_inches_tight_suptile_legend(): plt.plot(np.arange(10), label='a straight line') plt.legend(bbox_to_anchor=(0.9, 1), loc='upper left') @@ -64,7 +66,7 @@ def y_formatter(y, pos): @image_comparison(['bbox_inches_tight_suptile_non_default.png'], - savefig_kwarg={'bbox_inches': 'tight'}, + savefig_kwarg={'bbox_inches': 'tight'}, style='mpl20', tol=0.1) # large tolerance because only testing clipping. def test_bbox_inches_tight_suptitle_non_default(): fig, ax = plt.subplots() @@ -108,8 +110,9 @@ def test_bbox_inches_tight_clipping(): plt.gcf().artists.append(patch) -@image_comparison(['bbox_inches_tight_raster'], - remove_text=True, savefig_kwarg={'bbox_inches': 'tight'}) +@image_comparison(['bbox_inches_tight_raster'], tol=0.15, # For Ghostscript 10.06+. + remove_text=True, savefig_kwarg={'bbox_inches': 'tight'}, + style='mpl20') def test_bbox_inches_tight_raster(): """Test rasterization with tight_layout""" fig, ax = plt.subplots() @@ -165,11 +168,25 @@ def test_noop_tight_bbox(): assert im.shape == (7, 10, 4) -@image_comparison(['bbox_inches_fixed_aspect'], extensions=['png'], - remove_text=True, savefig_kwarg={'bbox_inches': 'tight'}) +@image_comparison(['bbox_inches_fixed_aspect.png'], remove_text=True, + savefig_kwarg={'bbox_inches': 'tight'}, style='mpl20') def test_bbox_inches_fixed_aspect(): with plt.rc_context({'figure.constrained_layout.use': True}): fig, ax = plt.subplots() ax.plot([0, 1]) ax.set_xlim(0, 1) ax.set_aspect('equal') + + +@image_comparison(['bbox_inches_inset_rasterized.pdf'], remove_text=True, + savefig_kwarg={'bbox_inches': 'tight'}, style='mpl20') +def test_bbox_inches_inset_rasterized(): + fig, ax = plt.subplots() + + arr = np.arange(100).reshape(10, 10) + im = ax.imshow(arr) + inset = inset_axes( + ax, width='10%', height='30%', loc='upper left', + bbox_to_anchor=(0.045, 0., 1, 1), bbox_transform=ax.transAxes) + + fig.colorbar(im, cax=inset, orientation='horizontal') diff --git a/lib/matplotlib/tests/test_bezier.py b/lib/matplotlib/tests/test_bezier.py index 65e2c616e738..ad5e5acfe49e 100644 --- a/lib/matplotlib/tests/test_bezier.py +++ b/lib/matplotlib/tests/test_bezier.py @@ -2,7 +2,38 @@ Tests specific to the bezier module. """ -from matplotlib.bezier import inside_circle, split_bezier_intersecting_with_closedpath +import pytest +import numpy as np +from numpy.testing import assert_allclose + +from matplotlib.bezier import ( + _real_roots_in_01, inside_circle, split_bezier_intersecting_with_closedpath +) + + +@pytest.mark.parametrize("roots, expected_in_01", [ + ([0.5], [0.5]), + ([0.25, 0.75], [0.25, 0.75]), + ([0.2, 0.5, 0.8], [0.2, 0.5, 0.8]), + ([0.1, 0.2, 0.3, 0.4], [0.1, 0.2, 0.3, 0.4]), + ([0.0, 0.5], [0.0, 0.5]), + ([0.5, 1.0], [0.5, 1.0]), + ([2.0], []), # outside [0, 1] + ([0.5, 2.0], [0.5]), # one in, one out + ([-1j, 1j], []), # complex roots + ([0.5, -1j, 1j], [0.5]), # mix of real and complex + ([0.3, 0.3], [0.3, 0.3]), # repeated root +]) +def test_real_roots_in_01(roots, expected_in_01): + roots = np.array(roots) + coeffs = np.poly(roots)[::-1] # np.poly gives descending, we need ascending + result = _real_roots_in_01(coeffs.real) + assert_allclose(result, expected_in_01, atol=1e-10) + + +@pytest.mark.parametrize("coeffs", [[5], [0, 0, 0]]) +def test_real_roots_in_01_no_roots(coeffs): + assert len(_real_roots_in_01(coeffs)) == 0 def test_split_bezier_with_large_values(): diff --git a/lib/matplotlib/tests/test_category.py b/lib/matplotlib/tests/test_category.py index fd4aec88b574..7917bb17ad59 100644 --- a/lib/matplotlib/tests/test_category.py +++ b/lib/matplotlib/tests/test_category.py @@ -246,6 +246,14 @@ def test_update_plot(self, plotter): axis_test(ax.xaxis, ['a', 'b', 'd', 'c']) axis_test(ax.yaxis, ['e', 'g', 'f', 'a', 'b', 'd']) + def test_update_plot_heterogenous_plotter(self): + ax = plt.figure().subplots() + ax.scatter(['a', 'b'], ['e', 'g']) + ax.plot(['a', 'b', 'd'], ['f', 'a', 'b']) + ax.bar(['b', 'c', 'd'], ['g', 'e', 'd']) + axis_test(ax.xaxis, ['a', 'b', 'd', 'c']) + axis_test(ax.yaxis, ['e', 'g', 'f', 'a', 'b', 'd']) + failing_test_cases = [("mixed", ['A', 3.14]), ("number integer", ['1', 1]), ("string integer", ['42', 42]), @@ -273,7 +281,7 @@ def test_mixed_type_update_exception(self, plotter, xdata): @mpl.style.context('default') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_overriding_units_in_plot(fig_test, fig_ref): from datetime import datetime diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 5d46c0a75775..2db0d66ccbb5 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -1,8 +1,9 @@ from __future__ import annotations -import sys import itertools +import pathlib import pickle +import sys from typing import Any from unittest.mock import patch, Mock @@ -14,6 +15,7 @@ assert_array_almost_equal) import pytest +import matplotlib as mpl from matplotlib import _api, cbook import matplotlib.colors as mcolors from matplotlib.cbook import delete_masked_points, strip_math @@ -180,6 +182,15 @@ def test_boxplot_stats_autorange_false(self): assert_array_almost_equal(bstats_true[0]['fliers'], []) +class Hashable: + def dummy(self): pass + + +class Unhashable: + __hash__ = None # type: ignore[assignment] + def dummy(self): pass + + class Test_callback_registry: def setup_method(self): self.signal = 'test' @@ -195,20 +206,20 @@ def disconnect(self, cid): return self.callbacks.disconnect(cid) def count(self): - count1 = len(self.callbacks._func_cid_map.get(self.signal, [])) + count1 = sum(s == self.signal for s, p in self.callbacks._func_cid_map) count2 = len(self.callbacks.callbacks.get(self.signal)) assert count1 == count2 return count1 def is_empty(self): np.testing.break_cycles() - assert self.callbacks._func_cid_map == {} + assert [*self.callbacks._func_cid_map] == [] assert self.callbacks.callbacks == {} assert self.callbacks._pickled_cids == set() def is_not_empty(self): np.testing.break_cycles() - assert self.callbacks._func_cid_map != {} + assert [*self.callbacks._func_cid_map] != [] assert self.callbacks.callbacks != {} def test_cid_restore(self): @@ -219,12 +230,13 @@ def test_cid_restore(self): assert cid == 1 @pytest.mark.parametrize('pickle', [True, False]) - def test_callback_complete(self, pickle): + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_complete(self, pickle, cls): # ensure we start with an empty registry self.is_empty() # create a class for testing - mini_me = Test_callback_registry() + mini_me = cls() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) @@ -235,7 +247,7 @@ def test_callback_complete(self, pickle): cid2 = self.connect(self.signal, mini_me.dummy, pickle) assert cid1 == cid2 self.is_not_empty() - assert len(self.callbacks._func_cid_map) == 1 + assert len([*self.callbacks._func_cid_map]) == 1 assert len(self.callbacks.callbacks) == 1 del mini_me @@ -244,12 +256,13 @@ def test_callback_complete(self, pickle): self.is_empty() @pytest.mark.parametrize('pickle', [True, False]) - def test_callback_disconnect(self, pickle): + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_disconnect(self, pickle, cls): # ensure we start with an empty registry self.is_empty() # create a class for testing - mini_me = Test_callback_registry() + mini_me = cls() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) @@ -262,12 +275,13 @@ def test_callback_disconnect(self, pickle): self.is_empty() @pytest.mark.parametrize('pickle', [True, False]) - def test_callback_wrong_disconnect(self, pickle): + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_wrong_disconnect(self, pickle, cls): # ensure we start with an empty registry self.is_empty() # create a class for testing - mini_me = Test_callback_registry() + mini_me = cls() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) @@ -280,20 +294,142 @@ def test_callback_wrong_disconnect(self, pickle): self.is_not_empty() @pytest.mark.parametrize('pickle', [True, False]) - def test_registration_on_non_empty_registry(self, pickle): + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_disconnect_func(self, pickle, cls): + # ensure we start with an empty registry + self.is_empty() + + # create a class for testing + mini_me = cls() + + # test that we can add a callback + self.connect(self.signal, mini_me.dummy, pickle) + self.is_not_empty() + + # disconnect by function reference + self.callbacks.disconnect(mini_me.dummy, signal=self.signal) + + # check we now have no callbacks registered + self.is_empty() + + @pytest.mark.parametrize('pickle', [True, False]) + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_disconnect_func_wrong(self, pickle, cls): + # ensure we start with an empty registry + self.is_empty() + + # create a class for testing + mini_me = cls() + + # test that we can add a callback + self.connect(self.signal, mini_me.dummy, pickle) + self.is_not_empty() + + # try to disconnect with wrong signal - should do nothing + self.callbacks.disconnect(mini_me.dummy, signal='wrong_signal') + + # check we still have callbacks registered + self.is_not_empty() + + # try to disconnect with wrong function - should do nothing + mini_me2 = cls() + self.callbacks.disconnect(mini_me2.dummy, signal=self.signal) + + # check we still have callbacks registered + self.is_not_empty() + + def test_callback_disconnect_func_redefined(self): + # Test that redefining a function name doesn't affect disconnect. + # When you redefine a function, it creates a new function object, + # so disconnect should not disconnect the original. + self.is_empty() + + def func(): + pass + + self.callbacks.connect(self.signal, func) + self.is_not_empty() + + # Redefine func - this creates a new function object + def func(): + pass + + # Try to disconnect with the redefined function + self.callbacks.disconnect(func, signal=self.signal) + + # Original callback should still be registered + self.is_not_empty() + + @pytest.mark.parametrize('pickle', [True, False]) + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_disconnect_func_all_signals(self, pickle, cls): + # Test disconnecting a callback from all signals at once + self.is_empty() + + mini_me = cls() + + # Connect to multiple signals + self.callbacks.connect('signal1', mini_me.dummy) + self.callbacks.connect('signal2', mini_me.dummy) + assert len(list(self.callbacks._func_cid_map)) == 2 + + # Disconnect from all signals at once (no signal specified) + self.callbacks.disconnect(mini_me.dummy) + + # All callbacks should be removed + self.is_empty() + + def test_disconnect_cid_with_signal_raises(self): + # Passing signal with a cid should raise an error + self.is_empty() + cid = self.callbacks.connect(self.signal, lambda: None) + with pytest.raises(ValueError, match="signal cannot be specified"): + self.callbacks.disconnect(cid, signal=self.signal) + + @pytest.mark.parametrize('pickle', [True, False]) + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_callback_disconnect_func_selective(self, pickle, cls): + # Test selectively disconnecting a callback from one signal + # while keeping it connected to another + self.is_empty() + + mini_me = cls() + + # Connect same function to multiple signals + self.callbacks.connect('signal1', mini_me.dummy) + self.callbacks.connect('signal2', mini_me.dummy) + assert len(list(self.callbacks._func_cid_map)) == 2 + + # Disconnect from only signal1 + self.callbacks.disconnect(mini_me.dummy, signal='signal1') + + # Should still have one callback registered (on signal2) + assert len(list(self.callbacks._func_cid_map)) == 1 + assert 'signal2' in self.callbacks.callbacks + assert 'signal1' not in self.callbacks.callbacks + + # Disconnect from signal2 + self.callbacks.disconnect(mini_me.dummy, signal='signal2') + + # Now all should be removed + self.is_empty() + + @pytest.mark.parametrize('pickle', [True, False]) + @pytest.mark.parametrize('cls', [Hashable, Unhashable]) + def test_registration_on_non_empty_registry(self, pickle, cls): # ensure we start with an empty registry self.is_empty() # setup the registry with a callback - mini_me = Test_callback_registry() + mini_me = cls() self.connect(self.signal, mini_me.dummy, pickle) # Add another callback - mini_me2 = Test_callback_registry() + mini_me2 = cls() self.connect(self.signal, mini_me2.dummy, pickle) # Remove and add the second callback - mini_me2 = Test_callback_registry() + mini_me2 = cls() self.connect(self.signal, mini_me2.dummy, pickle) # We still have 2 references @@ -305,9 +441,6 @@ def test_registration_on_non_empty_registry(self, pickle): mini_me2 = None self.is_empty() - def dummy(self): - pass - def test_pickling(self): assert hasattr(pickle.loads(pickle.dumps(cbook.CallbackRegistry())), "callbacks") @@ -452,31 +585,75 @@ def test_sanitize_sequence(): assert k == cbook.sanitize_sequence(k) -fail_mapping: tuple[tuple[dict, dict], ...] = ( - ({'a': 1, 'b': 2}, {'alias_mapping': {'a': ['b']}}), - ({'a': 1, 'b': 2}, {'alias_mapping': {'a': ['a', 'b']}}), -) +def test_resize_sequence(): + a_list = [1, 2, 3] + arr = np.array([1, 2, 3]) -pass_mapping: tuple[tuple[Any, dict, dict], ...] = ( + # already same length: passthrough + assert cbook._resize_sequence(a_list, 3) is a_list + assert cbook._resize_sequence(arr, 3) is arr + + # shortening + assert cbook._resize_sequence(a_list, 2) == [1, 2] + assert_array_equal(cbook._resize_sequence(arr, 2), [1, 2]) + + # extending + assert cbook._resize_sequence(a_list, 5) == [1, 2, 3, 1, 2] + assert_array_equal(cbook._resize_sequence(arr, 5), [1, 2, 3, 1, 2]) + + +fail_mapping: list[tuple[dict, dict]] = [ + ({"a": 1, "b": 2}, {"a": ["b"]}), + ({"a": 1, "b": 2}, {"a": ["a", "b"]}), +] + +pass_mapping: list[tuple[Any, dict, dict]] = [ (None, {}, {}), - ({'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {}), - ({'b': 2}, {'a': 2}, {'alias_mapping': {'a': ['a', 'b']}}), -) + ({"a": 1, "b": 2}, {"a": 1, "b": 2}, {}), + ({"b": 2}, {"a": 2}, {"a": ["a", "b"]}), +] + +@pytest.mark.parametrize('inp, alias_def', fail_mapping) +def test_normalize_kwargs_fail(inp, alias_def): -@pytest.mark.parametrize('inp, kwargs_to_norm', fail_mapping) -def test_normalize_kwargs_fail(inp, kwargs_to_norm): - with pytest.raises(TypeError), \ - _api.suppress_matplotlib_deprecation_warning(): - cbook.normalize_kwargs(inp, **kwargs_to_norm) + @_api.define_aliases(alias_def) + class Type(mpl.artist.Artist): + def get_a(self): return None + with pytest.raises(TypeError): + cbook.normalize_kwargs(inp, Type) -@pytest.mark.parametrize('inp, expected, kwargs_to_norm', - pass_mapping) -def test_normalize_kwargs_pass(inp, expected, kwargs_to_norm): - with _api.suppress_matplotlib_deprecation_warning(): - # No other warning should be emitted. - assert expected == cbook.normalize_kwargs(inp, **kwargs_to_norm) + +@pytest.mark.parametrize('inp, expected, alias_def', pass_mapping) +def test_normalize_kwargs_pass(inp, expected, alias_def): + + @_api.define_aliases(alias_def) + class Type(mpl.artist.Artist): + def get_a(self): return None + + assert expected == cbook.normalize_kwargs(inp, Type) + old_alias_map = {} + for alias, prop in Type._alias_to_prop.items(): + old_alias_map.setdefault(prop, []).append(alias) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert expected == cbook.normalize_kwargs(inp, old_alias_map) + + +def test_warn_external(recwarn): + _api.warn_external("oops") + assert len(recwarn) == 1 + if sys.version_info[:2] >= (3, 12): + # With Python 3.12, we let Python figure out the stacklevel using the + # `skip_file_prefixes` argument, which cannot exempt tests, so just confirm + # the filename is not in the package. + basedir = pathlib.Path(__file__).parents[2] + assert not recwarn[0].filename.startswith((str(basedir / 'matplotlib'), + str(basedir / 'mpl_toolkits'))) + else: + # On older Python versions, we manually calculated the stacklevel, and had an + # exception for our own tests. + assert recwarn[0].filename == __file__ def test_warn_external_frame_embedded_python(): @@ -579,6 +756,7 @@ class Dummy: g.join(*objs) assert set(list(g)[0]) == set(objs) assert set(g.get_siblings(a)) == set(objs) + assert a not in g.get_siblings(a, include_self=False) for other in objs[1:]: assert g.joined(a, other) @@ -785,12 +963,6 @@ def test_safe_first_element_pandas_series(pd): assert actual == 0 -def test_warn_external(recwarn): - _api.warn_external("oops") - assert len(recwarn) == 1 - assert recwarn[0].filename == __file__ - - def test_array_patch_perimeters(): # This compares the old implementation as a reference for the # vectorized one. @@ -799,8 +971,8 @@ def check(x, rstride, cstride): row_inds = [*range(0, rows-1, rstride), rows-1] col_inds = [*range(0, cols-1, cstride), cols-1] polys = [] - for rs, rs_next in zip(row_inds[:-1], row_inds[1:]): - for cs, cs_next in zip(col_inds[:-1], col_inds[1:]): + for rs, rs_next in itertools.pairwise(row_inds): + for cs, cs_next in itertools.pairwise(col_inds): # +1 ensures we share edges between polygons ps = cbook._array_perimeter(x[rs:rs_next+1, cs:cs_next+1]).T polys.append(ps) @@ -963,6 +1135,7 @@ def __array__(self): torch_tensor = torch.Tensor(data) result = cbook._unpack_to_numpy(torch_tensor) + assert isinstance(result, np.ndarray) # compare results, do not check for identity: the latter would fail # if not mocked, and the implementation does not guarantee it # is the same Python object, just the same values. @@ -991,6 +1164,7 @@ def __array__(self): jax_array = jax.Array(data) result = cbook._unpack_to_numpy(jax_array) + assert isinstance(result, np.ndarray) # compare results, do not check for identity: the latter would fail # if not mocked, and the implementation does not guarantee it # is the same Python object, just the same values. @@ -1020,6 +1194,38 @@ def __array__(self): tf_tensor = tensorflow.Tensor(data) result = cbook._unpack_to_numpy(tf_tensor) + assert isinstance(result, np.ndarray) + # compare results, do not check for identity: the latter would fail + # if not mocked, and the implementation does not guarantee it + # is the same Python object, just the same values. + assert_array_equal(result, data) + + +def test_unpack_to_numpy_from_mlx(): + """ + Test that mlx arrays are converted to NumPy arrays. + + We don't want to create a dependency on mlx in the test suite, so we mock it. + """ + class Array: + def __init__(self, data): + self.data = data + + def __array__(self): + return self.data + + # mlx is something peculiar + # class `array` is in `mlx.core` + mlx_core = ModuleType('mlx.core') + mlx_core.array = Array + + sys.modules['mlx.core'] = mlx_core + + data = np.arange(10) + mlx_array = mlx_core.array(data) + + result = cbook._unpack_to_numpy(mlx_array) + assert isinstance(result, np.ndarray) # compare results, do not check for identity: the latter would fail # if not mocked, and the implementation does not guarantee it # is the same Python object, just the same values. diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 23e951b17a2f..dc397ffde93e 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -17,6 +17,7 @@ import matplotlib.transforms as mtransforms from matplotlib.collections import (Collection, LineCollection, EventCollection, PolyCollection) +from matplotlib.collections import FillBetweenPolyCollection from matplotlib.testing.decorators import check_figures_equal, image_comparison @@ -65,7 +66,7 @@ def generate_EventCollection_plot(): return ax, coll, props -@image_comparison(['EventCollection_plot__default']) +@image_comparison(['EventCollection_plot__default.png'], style='mpl20') def test__EventCollection__get_props(): _, coll, props = generate_EventCollection_plot() # check that the default segments have the correct coordinates @@ -91,7 +92,7 @@ def test__EventCollection__get_props(): np.testing.assert_array_equal(color, props['color']) -@image_comparison(['EventCollection_plot__set_positions']) +@image_comparison(['EventCollection_plot__set_positions.png'], style='mpl20') def test__EventCollection__set_positions(): splt, coll, props = generate_EventCollection_plot() new_positions = np.hstack([props['positions'], props['extra_positions']]) @@ -105,7 +106,7 @@ def test__EventCollection__set_positions(): splt.set_xlim(-1, 90) -@image_comparison(['EventCollection_plot__add_positions']) +@image_comparison(['EventCollection_plot__add_positions.png'], style='mpl20') def test__EventCollection__add_positions(): splt, coll, props = generate_EventCollection_plot() new_positions = np.hstack([props['positions'], @@ -123,7 +124,7 @@ def test__EventCollection__add_positions(): splt.set_xlim(-1, 35) -@image_comparison(['EventCollection_plot__append_positions']) +@image_comparison(['EventCollection_plot__append_positions.png'], style='mpl20') def test__EventCollection__append_positions(): splt, coll, props = generate_EventCollection_plot() new_positions = np.hstack([props['positions'], @@ -139,7 +140,7 @@ def test__EventCollection__append_positions(): splt.set_xlim(-1, 90) -@image_comparison(['EventCollection_plot__extend_positions']) +@image_comparison(['EventCollection_plot__extend_positions.png'], style='mpl20') def test__EventCollection__extend_positions(): splt, coll, props = generate_EventCollection_plot() new_positions = np.hstack([props['positions'], @@ -155,7 +156,7 @@ def test__EventCollection__extend_positions(): splt.set_xlim(-1, 90) -@image_comparison(['EventCollection_plot__switch_orientation']) +@image_comparison(['EventCollection_plot__switch_orientation.png'], style='mpl20') def test__EventCollection__switch_orientation(): splt, coll, props = generate_EventCollection_plot() new_orientation = 'vertical' @@ -172,7 +173,7 @@ def test__EventCollection__switch_orientation(): splt.set_xlim(0, 2) -@image_comparison(['EventCollection_plot__switch_orientation__2x']) +@image_comparison(['EventCollection_plot__switch_orientation__2x.png'], style='mpl20') def test__EventCollection__switch_orientation_2x(): """ Check that calling switch_orientation twice sets the orientation back to @@ -193,7 +194,7 @@ def test__EventCollection__switch_orientation_2x(): splt.set_title('EventCollection: switch_orientation 2x') -@image_comparison(['EventCollection_plot__set_orientation']) +@image_comparison(['EventCollection_plot__set_orientation.png'], style='mpl20') def test__EventCollection__set_orientation(): splt, coll, props = generate_EventCollection_plot() new_orientation = 'vertical' @@ -210,7 +211,7 @@ def test__EventCollection__set_orientation(): splt.set_xlim(0, 2) -@image_comparison(['EventCollection_plot__set_linelength']) +@image_comparison(['EventCollection_plot__set_linelength.png'], style='mpl20') def test__EventCollection__set_linelength(): splt, coll, props = generate_EventCollection_plot() new_linelength = 15 @@ -225,7 +226,7 @@ def test__EventCollection__set_linelength(): splt.set_ylim(-20, 20) -@image_comparison(['EventCollection_plot__set_lineoffset']) +@image_comparison(['EventCollection_plot__set_lineoffset.png'], style='mpl20') def test__EventCollection__set_lineoffset(): splt, coll, props = generate_EventCollection_plot() new_lineoffset = -5. @@ -241,14 +242,15 @@ def test__EventCollection__set_lineoffset(): @image_comparison([ - 'EventCollection_plot__set_linestyle', - 'EventCollection_plot__set_linestyle', - 'EventCollection_plot__set_linewidth', -]) + 'EventCollection_plot__set_linestyle.png', + 'EventCollection_plot__set_linestyle.png', + 'EventCollection_plot__set_linewidth.png', +], style='mpl20') def test__EventCollection__set_prop(): for prop, value, expected in [ - ('linestyle', 'dashed', [(0, (6.0, 6.0))]), - ('linestyle', (0, (6., 6.)), [(0, (6.0, 6.0))]), + ('linestyle', 'dashed', [(0, [7.4, 3.2])]), + # Dashes are scaled by linewidth. + ('linestyle', (0, (3.7, 1.6)), [(0, [7.4, 3.2])]), ('linewidth', 5, 5), ]: splt, coll, _ = generate_EventCollection_plot() @@ -257,7 +259,7 @@ def test__EventCollection__set_prop(): splt.set_title(f'EventCollection: set_{prop}') -@image_comparison(['EventCollection_plot__set_color']) +@image_comparison(['EventCollection_plot__set_color.png'], style='mpl20') def test__EventCollection__set_color(): splt, coll, _ = generate_EventCollection_plot() new_color = np.array([0, 1, 1, 1]) @@ -333,7 +335,7 @@ def test_add_collection(): @mpl.style.context('mpl20') -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_collection_log_datalim(fig_test, fig_ref): # Data limits should respect the minimum x/y when using log scale. x_vals = [4.38462e-6, 5.54929e-6, 7.02332e-6, 8.88889e-6, 1.12500e-5, @@ -390,7 +392,7 @@ def test_barb_limits(): @image_comparison(['EllipseCollection_test_image.png'], remove_text=True, - tol=0.021 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.021) def test_EllipseCollection(): # Test basic functionality fig, ax = plt.subplots() @@ -407,7 +409,6 @@ def test_EllipseCollection(): ww, hh, aa, units='x', offsets=XY, offset_transform=ax.transData, facecolors='none') ax.add_collection(ec) - ax.autoscale_view() def test_EllipseCollection_setter_getter(): @@ -455,7 +456,7 @@ def test_EllipseCollection_setter_getter(): @image_comparison(['polycollection_close.png'], remove_text=True, style='mpl20') def test_polycollection_close(): - from mpl_toolkits.mplot3d import Axes3D # type: ignore + from mpl_toolkits.mplot3d import Axes3D # type: ignore[import] plt.rcParams['axes3d.automargin'] = True vertsQuad = [ @@ -491,6 +492,28 @@ def test_polycollection_close(): ax.set_ylim3d(0, 4) +@check_figures_equal() +def test_scalarmap_change_cmap(fig_test, fig_ref): + # Ensure that changing the colormap of a 3D scatter after draw updates the colors. + + x, y, z = np.array(list(itertools.product( + np.arange(0, 5, 1), + np.arange(0, 5, 1), + np.arange(0, 5, 1) + ))).T + c = x + y + + # test + ax_test = fig_test.add_subplot(111, projection='3d') + sc_test = ax_test.scatter(x, y, z, c=c, s=40, cmap='jet') + fig_test.canvas.draw() + sc_test.set_cmap('viridis') + + # ref + ax_ref = fig_ref.add_subplot(111, projection='3d') + ax_ref.scatter(x, y, z, c=c, s=40, cmap='viridis') + + @image_comparison(['regularpolycollection_rotate.png'], remove_text=True) def test_regularpolycollection_rotate(): xx, yy = np.mgrid[:10, :10] @@ -502,8 +525,7 @@ def test_regularpolycollection_rotate(): col = mcollections.RegularPolyCollection( 4, sizes=(100,), rotation=alpha, offsets=[xy], offset_transform=ax.transData) - ax.add_collection(col, autolim=True) - ax.autoscale_view() + ax.add_collection(col) @image_comparison(['regularpolycollection_scale.png'], remove_text=True) @@ -518,7 +540,7 @@ def get_transform(self): """Return transform scaling circle areas to data space.""" ax = self.axes - pts2pixels = 72.0 / ax.figure.dpi + pts2pixels = 72.0 / ax.get_figure(root=True).dpi scale_x = pts2pixels * ax.bbox.width / ax.viewLim.width scale_y = pts2pixels * ax.bbox.height / ax.viewLim.height @@ -531,7 +553,7 @@ def get_transform(self): circle_areas = [np.pi / 2] squares = SquareCollection( sizes=circle_areas, offsets=xy, offset_transform=ax.transData) - ax.add_collection(squares, autolim=True) + ax.add_collection(squares) ax.axis([-1, 1, -1, 1]) @@ -699,7 +721,7 @@ def test_joinstyle(): assert col.get_joinstyle() == 'miter' -@image_comparison(['cap_and_joinstyle.png']) +@image_comparison(['cap_and_joinstyle.png'], style='mpl20') def test_cap_and_joinstyle_image(): fig, ax = plt.subplots() ax.set_xlim([-0.5, 1.5]) @@ -830,6 +852,35 @@ def test_collection_set_verts_array(): assert np.array_equal(ap._codes, atp._codes) +@check_figures_equal() +@pytest.mark.parametrize("kwargs", [{}, {"step": "pre"}]) +def test_fill_between_poly_collection_set_data(fig_test, fig_ref, kwargs): + t = np.linspace(0, 16) + f1 = np.sin(t) + f2 = f1 + 0.2 + + fig_ref.subplots().fill_between(t, f1, f2, **kwargs) + + coll = fig_test.subplots().fill_between(t, -1, 1.2, **kwargs) + coll.set_data(t, f1, f2) + + +@pytest.mark.parametrize(("t_direction", "f1", "shape", "where", "msg"), [ + ("z", None, None, None, r"t_direction must be 'x' or 'y', got 'z'"), + ("x", None, (-1, 1), None, r"'x' is not 1-dimensional"), + ("x", None, None, [False] * 3, r"where size \(3\) does not match 'x' size \(\d+\)"), + ("y", [1, 2], None, None, r"'y' has size \d+, but 'x1' has an unequal size of \d+"), +]) +def test_fill_between_poly_collection_raise(t_direction, f1, shape, where, msg): + t = np.linspace(0, 16) + f1 = np.sin(t) if f1 is None else np.asarray(f1) + f2 = f1 + 0.2 + if shape: + t = t.reshape(*shape) + with pytest.raises(ValueError, match=msg): + FillBetweenPolyCollection(t_direction, t, f1, f2, where=where) + + def test_collection_set_array(): vals = [*range(10)] @@ -847,17 +898,24 @@ def test_collection_set_array(): def test_blended_collection_autolim(): - a = [1, 2, 4] - height = .2 + f, ax = plt.subplots() - xy_pairs = np.column_stack([np.repeat(a, 2), np.tile([0, height], len(a))]) - line_segs = xy_pairs.reshape([len(a), 2, 2]) + # sample data to give initial data limits + ax.plot([2, 3, 4], [0.4, 0.6, 0.5]) + np.testing.assert_allclose((ax.dataLim.xmin, ax.dataLim.xmax), (2, 4)) + data_ymin, data_ymax = ax.dataLim.ymin, ax.dataLim.ymax - f, ax = plt.subplots() + # LineCollection with vertical lines spanning the Axes vertical, using transAxes + x = [1, 2, 3, 4, 5] + vertical_lines = [np.array([[xi, 0], [xi, 1]]) for xi in x] trans = mtransforms.blended_transform_factory(ax.transData, ax.transAxes) - ax.add_collection(LineCollection(line_segs, transform=trans)) - ax.autoscale_view(scalex=True, scaley=False) - np.testing.assert_allclose(ax.get_xlim(), [1., 4.]) + ax.add_collection(LineCollection(vertical_lines, transform=trans)) + + # check that the x data limits are updated to include the LineCollection + np.testing.assert_allclose((ax.dataLim.xmin, ax.dataLim.xmax), (1, 5)) + # check that the y data limits are not updated (because they are not transData) + np.testing.assert_allclose((ax.dataLim.ymin, ax.dataLim.ymax), + (data_ymin, data_ymax)) def test_singleton_autolim(): @@ -965,11 +1023,6 @@ def test_polyquadmesh_masked_vertices_array(): # Poly version should have the same facecolors as the end of the quadmesh assert_array_equal(quadmesh_fc, polymesh.get_facecolor()) - # Setting array with 1D compressed values is deprecated - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="Setting a PolyQuadMesh"): - polymesh.set_array(np.ones(5)) - # We should also be able to call set_array with a new mask and get # updated polys # Remove mask, should add all polys back @@ -1262,8 +1315,7 @@ def test_set_offset_units(): np.testing.assert_allclose(off0, sc.get_offsets()) -@image_comparison(baseline_images=["test_check_masked_offsets"], - extensions=["png"], remove_text=True, style="mpl20") +@image_comparison(["test_check_masked_offsets.png"], remove_text=True, style="mpl20") def test_check_masked_offsets(): # Check if masked data is respected by scatter # Ref: Issue #24545 @@ -1281,7 +1333,7 @@ def test_check_masked_offsets(): ax.scatter(unmasked_x, masked_y) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_masked_set_offsets(fig_ref, fig_test): x = np.ma.array([1, 2, 3, 4, 5], mask=[0, 0, 1, 1, 0]) y = np.arange(1, 6) @@ -1315,8 +1367,7 @@ def test_check_offsets_dtype(): @pytest.mark.parametrize('gapcolor', ['orange', ['r', 'k']]) -@check_figures_equal(extensions=['png']) -@mpl.rc_context({'lines.linewidth': 20}) +@check_figures_equal() def test_striped_lines(fig_test, fig_ref, gapcolor): ax_test = fig_test.add_subplot(111) ax_ref = fig_ref.add_subplot(111) @@ -1328,11 +1379,162 @@ def test_striped_lines(fig_test, fig_ref, gapcolor): x = range(1, 6) linestyles = [':', '-', '--'] - ax_test.vlines(x, 0, 1, linestyle=linestyles, gapcolor=gapcolor, alpha=0.5) + ax_test.vlines(x, 0, 1, linewidth=20, linestyle=linestyles, gapcolor=gapcolor, + alpha=0.5) if isinstance(gapcolor, str): gapcolor = [gapcolor] for x, gcol, ls in zip(x, itertools.cycle(gapcolor), itertools.cycle(linestyles)): - ax_ref.axvline(x, 0, 1, linestyle=ls, gapcolor=gcol, alpha=0.5) + ax_ref.axvline(x, 0, 1, linewidth=20, linestyle=ls, gapcolor=gcol, alpha=0.5) + + +@check_figures_equal(extensions=['png', 'pdf', 'svg', 'eps']) +def test_hatch_linewidth(fig_test, fig_ref): + ax_test = fig_test.add_subplot() + ax_ref = fig_ref.add_subplot() + + lw = 2.0 + + polygons = [ + [(0.1, 0.1), (0.1, 0.4), (0.4, 0.4), (0.4, 0.1)], + [(0.6, 0.6), (0.6, 0.9), (0.9, 0.9), (0.9, 0.6)], + ] + ref = PolyCollection(polygons, hatch="x") + ref.set_hatch_linewidth(lw) + + with mpl.rc_context({"hatch.linewidth": lw}): + test = PolyCollection(polygons, hatch="x") + + ax_ref.add_collection(ref) + ax_test.add_collection(test) + + assert test.get_hatch_linewidth() == ref.get_hatch_linewidth() == lw + + +def test_collection_hatchcolor_inherit_logic(): + from matplotlib.collections import PathCollection + path = mpath.Path.unit_rectangle() + + edgecolors = ['purple', 'red', 'green', 'yellow'] + hatchcolors = ['orange', 'cyan', 'blue', 'magenta'] + with mpl.rc_context({'hatch.color': 'edge'}): + # edgecolor and hatchcolor is set + col = PathCollection([path], hatch='//', + edgecolor=edgecolors, hatchcolor=hatchcolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + + # explicitly setting edgecolor and then hatchcolor + col = PathCollection([path], hatch='//') + col.set_edgecolor(edgecolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(edgecolors)) + col.set_hatchcolor(hatchcolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + + # explicitly setting hatchcolor and then edgecolor + col = PathCollection([path], hatch='//') + col.set_hatchcolor(hatchcolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + col.set_edgecolor(edgecolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + + +def test_collection_hatchcolor_fallback_logic(): + from matplotlib.collections import PathCollection + path = mpath.Path.unit_rectangle() + + edgecolors = ['purple', 'red', 'green', 'yellow'] + hatchcolors = ['orange', 'cyan', 'blue', 'magenta'] + + # hatchcolor parameter should take precedence over rcParam + # When edgecolor is not set + with mpl.rc_context({'hatch.color': 'green'}): + col = PathCollection([path], hatch='//', hatchcolor=hatchcolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + # When edgecolor is set + with mpl.rc_context({'hatch.color': 'green'}): + col = PathCollection([path], hatch='//', + edgecolor=edgecolors, hatchcolor=hatchcolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(hatchcolors)) + + # hatchcolor should not be overridden by edgecolor when + # hatchcolor parameter is not passed and hatch.color rcParam is set to a color + with mpl.rc_context({'hatch.color': 'green'}): + col = PathCollection([path], hatch='//') + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array('green')) + col.set_edgecolor(edgecolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array('green')) + + # hatchcolor should match edgecolor when + # hatchcolor parameter is not passed and hatch.color rcParam is set to 'edge' + with mpl.rc_context({'hatch.color': 'edge'}): + col = PathCollection([path], hatch='//', edgecolor=edgecolors) + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(edgecolors)) + # hatchcolor parameter is set to 'edge' + col = PathCollection([path], hatch='//', edgecolor=edgecolors, hatchcolor='edge') + assert_array_equal(col.get_hatchcolor(), mpl.colors.to_rgba_array(edgecolors)) + + # default hatchcolor should be used when hatchcolor parameter is not passed and + # hatch.color rcParam is set to 'edge' and edgecolor is not set + col = PathCollection([path], hatch='//') + assert_array_equal(col.get_hatchcolor(), + mpl.colors.to_rgba_array(mpl.rcParams['patch.edgecolor'])) + + +@pytest.mark.parametrize('backend', ['agg', 'pdf', 'svg', 'ps']) +def test_draw_path_collection_no_hatchcolor(backend): + from matplotlib.collections import PathCollection + path = mpath.Path.unit_rectangle() + + plt.switch_backend(backend) + fig, ax = plt.subplots() + renderer = fig._get_renderer() + + col = PathCollection([path], hatch='//') + ax.add_collection(col) + + gc = renderer.new_gc() + transform = mtransforms.IdentityTransform() + paths = col.get_paths() + transforms = col.get_transforms() + offsets = col.get_offsets() + offset_trf = col.get_offset_transform() + facecolors = col.get_facecolor() + edgecolors = col.get_edgecolor() + linewidths = col.get_linewidth() + linestyles = col.get_linestyle() + antialiaseds = col.get_antialiased() + urls = col.get_urls() + offset_position = "screen" + + renderer.draw_path_collection( + gc, transform, paths, transforms, offsets, offset_trf, + facecolors, edgecolors, linewidths, linestyles, + antialiaseds, urls, offset_position + ) + + +def test_third_party_backend_hatchcolors_arg_fallback(monkeypatch): + fig, ax = plt.subplots() + canvas = fig.canvas + renderer = canvas.get_renderer() + + # monkeypatch the `draw_path_collection` method to simulate a third-party backend + # that does not support the `hatchcolors` argument. + def mock_draw_path_collection(self, gc, master_transform, paths, all_transforms, + offsets, offset_trans, facecolors, edgecolors, + linewidths, linestyles, antialiaseds, urls, + offset_position): + pass + + monkeypatch.setattr(renderer, 'draw_path_collection', mock_draw_path_collection) + + # Create a PathCollection with hatch colors + from matplotlib.collections import PathCollection + path = mpath.Path.unit_rectangle() + coll = PathCollection([path], hatch='//', hatchcolor='red') + + ax.add_collection(coll) + + plt.draw() diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 35911afc7952..e3053ca0b231 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -153,17 +153,13 @@ def test_colorbar_extension_inverted_axis(orientation, extend, expected): @pytest.mark.parametrize('use_gridspec', [True, False]) -@image_comparison(['cbar_with_orientation', - 'cbar_locationing', - 'double_cbar', - 'cbar_sharing', +@image_comparison(['cbar_with_orientation.png', + 'cbar_locationing.png', + 'double_cbar.png', + 'cbar_sharing.png', ], - extensions=['png'], remove_text=True, - savefig_kwarg={'dpi': 40}) + remove_text=True, savefig_kwarg={'dpi': 40}, style='mpl20') def test_colorbar_positioning(use_gridspec): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - data = np.arange(1200).reshape(30, 40) levels = [0, 200, 400, 600, 800, 1000, 1200] @@ -317,7 +313,7 @@ def test_remove_from_figure_cl(): def test_colorbarbase(): # smoke test from #3805 ax = plt.gca() - Colorbar(ax, cmap=plt.cm.bone) + Colorbar(ax, cmap=plt.colormaps["bone"]) def test_parentless_mappable(): @@ -332,11 +328,11 @@ def test_colorbar_closed_patch(): plt.rcParams['pcolormesh.snap'] = False fig = plt.figure(figsize=(8, 6)) - ax1 = fig.add_axes([0.05, 0.85, 0.9, 0.1]) - ax2 = fig.add_axes([0.1, 0.65, 0.75, 0.1]) - ax3 = fig.add_axes([0.05, 0.45, 0.9, 0.1]) - ax4 = fig.add_axes([0.05, 0.25, 0.9, 0.1]) - ax5 = fig.add_axes([0.05, 0.05, 0.9, 0.1]) + ax1 = fig.add_axes((0.05, 0.85, 0.9, 0.1)) + ax2 = fig.add_axes((0.1, 0.65, 0.75, 0.1)) + ax3 = fig.add_axes((0.05, 0.45, 0.9, 0.1)) + ax4 = fig.add_axes((0.05, 0.25, 0.9, 0.1)) + ax5 = fig.add_axes((0.05, 0.05, 0.9, 0.1)) cmap = mpl.colormaps["RdBu"].resampled(5) @@ -491,12 +487,13 @@ def test_colorbar_autotickslog(): pcm = ax[1].pcolormesh(X, Y, 10**Z, norm=LogNorm()) cbar2 = fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical', shrink=0.4) + + fig.draw_without_rendering() # note only -12 to +12 are visible - np.testing.assert_almost_equal(cbar.ax.yaxis.get_ticklocs(), - 10**np.arange(-16., 16.2, 4.)) - # note only -24 to +24 are visible - np.testing.assert_almost_equal(cbar2.ax.yaxis.get_ticklocs(), - 10**np.arange(-24., 25., 12.)) + np.testing.assert_equal(np.log10(cbar.ax.yaxis.get_ticklocs()), + [-18, -12, -6, 0, +6, +12, +18]) + np.testing.assert_equal(np.log10(cbar2.ax.yaxis.get_ticklocs()), + [-36, -12, 12, +36]) def test_colorbar_get_ticks(): @@ -597,7 +594,7 @@ def test_colorbar_renorm(): norm = LogNorm(z.min(), z.max()) im.set_norm(norm) np.testing.assert_allclose(cbar.ax.yaxis.get_majorticklocs(), - np.logspace(-10, 7, 18)) + np.logspace(-9, 6, 16)) # note that set_norm removes the FixedLocator... assert np.isclose(cbar.vmin, z.min()) cbar.set_ticks([1, 2, 3]) @@ -630,7 +627,9 @@ def test_colorbar_format(fmt): assert cbar.ax.yaxis.get_ticklabels()[4].get_text() == '2.00e+02' # but if we change the norm: - im.set_norm(LogNorm(vmin=0.1, vmax=10)) + # when we require numpy >= 2, vmin can be 0.1, but at numpy 1.x this is + # sensitive to floating point errors + im.set_norm(LogNorm(vmin=0.09999, vmax=10)) fig.canvas.draw() assert (cbar.ax.yaxis.get_ticklabels()[0].get_text() == '$\\mathdefault{10^{-2}}$') @@ -844,16 +843,16 @@ def test_colorbar_change_lim_scale(): pc = ax[1].pcolormesh(np.arange(100).reshape(10, 10)+1) cb = fig.colorbar(pc, ax=ax[1], extend='both') - cb.ax.set_ylim([20, 90]) + cb.ax.set_ylim(20, 90) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_axes_handles_same_functions(fig_ref, fig_test): # prove that cax and cb.ax are functionally the same for nn, fig in enumerate([fig_ref, fig_test]): ax = fig.add_subplot() pc = ax.pcolormesh(np.ones(300).reshape(10, 30)) - cax = fig.add_axes([0.9, 0.1, 0.03, 0.8]) + cax = fig.add_axes((0.9, 0.1, 0.03, 0.8)) cb = fig.colorbar(pc, cax=cax) if nn == 0: caxx = cax @@ -893,11 +892,12 @@ def test_twoslope_colorbar(): fig.colorbar(pc) -@check_figures_equal(extensions=["png"]) -def test_remove_cb_whose_mappable_has_no_figure(fig_ref, fig_test): - ax = fig_test.add_subplot() - cb = fig_test.colorbar(cm.ScalarMappable(), cax=ax) +def test_remove_cb_whose_mappable_has_no_figure(): + fig, ax = plt.subplots() + assert fig.get_axes() != [] + cb = fig.colorbar(cm.ScalarMappable(), cax=ax) cb.remove() + assert fig.get_axes() == [] def test_aspects(): @@ -945,9 +945,10 @@ def test_proportional_colorbars(): levels = [-1.25, -0.5, -0.125, 0.125, 0.5, 1.25] cmap = mcolors.ListedColormap( - ['0.3', '0.5', 'white', 'lightblue', 'steelblue']) - cmap.set_under('darkred') - cmap.set_over('crimson') + ['0.3', '0.5', 'white', 'lightblue', 'steelblue'], + under='darkred', + over='crimson', + ) norm = mcolors.BoundaryNorm(levels, cmap.N) extends = ['neither', 'both'] @@ -1139,6 +1140,7 @@ def test_colorbar_set_formatter_locator(): fmt = LogFormatter() cb.minorformatter = fmt assert cb.ax.yaxis.get_minor_formatter() is fmt + assert cb.long_axis is cb.ax.yaxis @image_comparison(['colorbar_extend_alpha.png'], remove_text=True, @@ -1176,16 +1178,16 @@ def test_title_text_loc(): cb.ax.spines['outline'].get_window_extent().ymax) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_passing_location(fig_ref, fig_test): ax_ref = fig_ref.add_subplot() im = ax_ref.imshow([[0, 1], [2, 3]]) - ax_ref.figure.colorbar(im, cax=ax_ref.inset_axes([0, 1.05, 1, 0.05]), - orientation="horizontal", ticklocation="top") + ax_ref.get_figure().colorbar(im, cax=ax_ref.inset_axes([0, 1.05, 1, 0.05]), + orientation="horizontal", ticklocation="top") ax_test = fig_test.add_subplot() im = ax_test.imshow([[0, 1], [2, 3]]) - ax_test.figure.colorbar(im, cax=ax_test.inset_axes([0, 1.05, 1, 0.05]), - location="top") + ax_test.get_figure().colorbar(im, cax=ax_test.inset_axes([0, 1.05, 1, 0.05]), + location="top") @pytest.mark.parametrize("kwargs,error,message", [ @@ -1207,7 +1209,7 @@ def test_colorbar_errors(kwargs, error, message): fig.colorbar(im, **kwargs) -def test_colorbar_axes_parmeters(): +def test_colorbar_axes_parameters(): fig, ax = plt.subplots(2) im = ax[0].imshow([[0, 1], [2, 3]]) # colorbar should accept any form of axes sequence: diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index c8b44b2dea14..808770e1d52c 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -7,7 +7,9 @@ from PIL import Image import pytest import base64 +import platform +from numpy.lib import recfunctions as rfn from numpy.testing import assert_array_equal, assert_array_almost_equal from matplotlib import cbook, cm @@ -15,11 +17,12 @@ import matplotlib as mpl import matplotlib.colors as mcolors import matplotlib.colorbar as mcolorbar +import matplotlib.colorizer as mcolorizer import matplotlib.pyplot as plt import matplotlib.scale as mscale from matplotlib.rcsetup import cycler from matplotlib.testing.decorators import image_comparison, check_figures_equal -from matplotlib.colors import to_rgba_array +from matplotlib.colors import is_color_like, to_rgba_array, ListedColormap @pytest.mark.parametrize('N, result', [ @@ -50,13 +53,9 @@ def test_resampled(): colorlist[:, 1] = 0.2 colorlist[:, 2] = np.linspace(1, 0, n) colorlist[:, 3] = 0.7 - lsc = mcolors.LinearSegmentedColormap.from_list('lsc', colorlist) - lc = mcolors.ListedColormap(colorlist) - # Set some bad values for testing too - for cmap in [lsc, lc]: - cmap.set_under('r') - cmap.set_over('g') - cmap.set_bad('b') + lsc = mcolors.LinearSegmentedColormap.from_list( + 'lsc', colorlist, under='red', over='green', bad='blue') + lc = mcolors.ListedColormap(colorlist, under='red', over='green', bad='blue') lsc3 = lsc.resampled(3) lc3 = lc.resampled(3) expected = np.array([[0.0, 0.2, 1.0, 0.7], @@ -73,6 +72,12 @@ def test_resampled(): assert_array_almost_equal(lc(np.nan), lc3(np.nan)) +def test_monochrome(): + assert mcolors.ListedColormap(["red"]).monochrome + assert mcolors.ListedColormap(["red"] * 5).monochrome + assert not mcolors.ListedColormap(["red", "green"]).monochrome + + def test_colormaps_get_cmap(): cr = mpl.colormaps @@ -101,22 +106,24 @@ def test_double_register_builtin_cmap(): def test_colormap_copy(): - cmap = plt.cm.Reds + cmap = plt.colormaps["Reds"] copied_cmap = copy.copy(cmap) with np.errstate(invalid='ignore'): ret1 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf]) cmap2 = copy.copy(copied_cmap) - cmap2.set_bad('g') + with pytest.warns(PendingDeprecationWarning): + cmap2.set_bad('g') with np.errstate(invalid='ignore'): ret2 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf]) assert_array_equal(ret1, ret2) # again with the .copy method: - cmap = plt.cm.Reds + cmap = plt.colormaps["Reds"] copied_cmap = cmap.copy() with np.errstate(invalid='ignore'): ret1 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf]) cmap2 = copy.copy(copied_cmap) - cmap2.set_bad('g') + with pytest.warns(PendingDeprecationWarning): + cmap2.set_bad('g') with np.errstate(invalid='ignore'): ret2 = copied_cmap([-1, 0, .5, 1, np.nan, np.inf]) assert_array_equal(ret1, ret2) @@ -130,7 +137,8 @@ def test_colormap_equals(): # But the same data should be equal assert cm_copy == cmap # Change the copy - cm_copy.set_bad('y') + with pytest.warns(PendingDeprecationWarning): + cm_copy.set_bad('y') assert cm_copy != cmap # Make sure we can compare different sizes without failure cm_copy._lut = cm_copy._lut[:10, :] @@ -214,6 +222,44 @@ def test_colormap_return_types(): assert cmap(x2d).shape == x2d.shape + (4,) +def test_ListedColormap_bad_under_over(): + cmap = mcolors.ListedColormap(["r", "g", "b"], bad="c", under="m", over="y") + assert mcolors.same_color(cmap.get_bad(), "c") + assert mcolors.same_color(cmap.get_under(), "m") + assert mcolors.same_color(cmap.get_over(), "y") + + +def test_LinearSegmentedColormap_bad_under_over(): + cdict = { + 'red': [(0., 0., 0.), (0.5, 1., 1.), (1., 1., 1.)], + 'green': [(0., 0., 0.), (0.25, 0., 0.), (0.75, 1., 1.), (1., 1., 1.)], + 'blue': [(0., 0., 0.), (0.5, 0., 0.), (1., 1., 1.)], + } + cmap = mcolors.LinearSegmentedColormap("lsc", cdict, bad="c", under="m", over="y") + assert mcolors.same_color(cmap.get_bad(), "c") + assert mcolors.same_color(cmap.get_under(), "m") + assert mcolors.same_color(cmap.get_over(), "y") + + +def test_LinearSegmentedColormap_from_list_bad_under_over(): + cmap = mcolors.LinearSegmentedColormap.from_list( + "lsc", ["r", "g", "b"], bad="c", under="m", over="y") + assert mcolors.same_color(cmap.get_bad(), "c") + assert mcolors.same_color(cmap.get_under(), "m") + assert mcolors.same_color(cmap.get_over(), "y") + + +def test_colormap_with_alpha(): + cmap = ListedColormap(["red", "green", ("blue", 0.8)]) + cmap2 = cmap.with_alpha(0.5) + # color is the same: + vals = [0, 0.5, 1] # numeric positions that map to the listed colors + assert_array_equal(cmap(vals)[:, :3], cmap2(vals)[:, :3]) + # alpha of cmap2 is changed: + assert_array_equal(cmap(vals)[:, 3], [1, 1, 0.8]) + assert_array_equal(cmap2(vals)[:, 3], [0.5, 0.5, 0.5]) + + def test_BoundaryNorm(): """ GitHub issue #1258: interpolation was failing with numpy @@ -324,9 +370,7 @@ def test_BoundaryNorm(): assert_array_equal(mynorm(x), ref) # Without interpolation - cmref = mcolors.ListedColormap(['blue', 'red']) - cmref.set_over('black') - cmref.set_under('white') + cmref = mcolors.ListedColormap(['blue', 'red'], under='white', over='black') cmshould = mcolors.ListedColormap(['white', 'blue', 'red', 'black']) assert mcolors.same_color(cmref.get_over(), 'black') @@ -348,8 +392,7 @@ def test_BoundaryNorm(): assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x))) # Just min - cmref = mcolors.ListedColormap(['blue', 'red']) - cmref.set_under('white') + cmref = mcolors.ListedColormap(['blue', 'red'], under='white') cmshould = mcolors.ListedColormap(['white', 'blue', 'red']) assert mcolors.same_color(cmref.get_under(), 'white') @@ -366,8 +409,7 @@ def test_BoundaryNorm(): assert_array_equal(cmshould(mynorm(x)), cmref(refnorm(x))) # Just max - cmref = mcolors.ListedColormap(['blue', 'red']) - cmref.set_over('black') + cmref = mcolors.ListedColormap(['blue', 'red'], over='black') cmshould = mcolors.ListedColormap(['blue', 'red', 'black']) assert mcolors.same_color(cmref.get_over(), 'black') @@ -590,8 +632,9 @@ def inverse(x): norm = mcolors.FuncNorm((forward, inverse), vmin=0.1, vmax=10) lognorm = mcolors.LogNorm(vmin=0.1, vmax=10) assert_array_almost_equal(norm([0.2, 5, 10]), lognorm([0.2, 5, 10])) - assert_array_almost_equal(norm.inverse([0.2, 5, 10]), - lognorm.inverse([0.2, 5, 10])) + # use assert_allclose here for rtol on large numbers + np.testing.assert_allclose(norm.inverse([0.2, 5, 10]), + lognorm.inverse([0.2, 5, 10])) def test_TwoSlopeNorm_autoscale(): @@ -785,11 +828,8 @@ def _mask_tester(norm_instance, vals): assert_array_equal(masked_array.mask, norm_instance(masked_array).mask) -@image_comparison(['levels_and_colors.png']) +@image_comparison(['levels_and_colors.png'], style='mpl20') def test_cmap_and_norm_from_levels_and_colors(): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - data = np.linspace(-2, 4, 49).reshape(7, 7) levels = [-1, 2, 2.5, 3] colors = ['red', 'green', 'blue', 'yellow', 'black'] @@ -804,17 +844,13 @@ def test_cmap_and_norm_from_levels_and_colors(): ax.tick_params(labelleft=False, labelbottom=False) -@image_comparison(baseline_images=['boundarynorm_and_colorbar'], - extensions=['png'], tol=1.0) +@image_comparison(['boundarynorm_and_colorbar.png']) def test_boundarynorm_and_colorbarbase(): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - # Make a figure and axes with dimensions as desired. fig = plt.figure() - ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15]) - ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15]) - ax3 = fig.add_axes([0.05, 0.15, 0.9, 0.15]) + ax1 = fig.add_axes((0.05, 0.80, 0.9, 0.15)) + ax2 = fig.add_axes((0.05, 0.475, 0.9, 0.15)) + ax3 = fig.add_axes((0.05, 0.15, 0.9, 0.15)) # Set the colormap and bounds bounds = [-1, 2, 5, 7, 12, 15] @@ -881,7 +917,7 @@ def test_cmap_and_norm_from_levels_and_colors2(): for extend, i1, cases in tests: cmap, norm = mcolors.from_levels_and_colors(levels, colors[0:i1], extend=extend) - cmap.set_bad(bad) + cmap = cmap.with_extremes(bad=bad) for d_val, expected_color in cases.items(): if d_val == masked_value: d_val = np.ma.array([1], mask=True) @@ -905,6 +941,11 @@ def test_rgb_hsv_round_trip(): tt, mcolors.rgb_to_hsv(mcolors.hsv_to_rgb(tt))) +def test_rgb_to_hsv_int(): + # Test that int rgb values (still range 0-1) are processed correctly. + assert_array_equal(mcolors.rgb_to_hsv((0, 1, 0)), (1/3, 1, 1)) # green + + def test_autoscale_masked(): # Test for #2336. Previously fully masked data would trigger a ValueError. data = np.ma.masked_all((12, 20)) @@ -943,7 +984,7 @@ def test_light_source_shading_default(): y, x = np.mgrid[-1.2:1.2:8j, -1.2:1.2:8j] z = 10 * np.cos(x**2 + y**2) - cmap = plt.cm.copper + cmap = plt.colormaps["copper"] ls = mcolors.LightSource(315, 45) rgb = ls.shade(z, cmap) @@ -994,7 +1035,7 @@ def test_light_source_shading_empty_mask(): z0 = 10 * np.cos(x**2 + y**2) z1 = np.ma.array(z0) - cmap = plt.cm.copper + cmap = plt.colormaps["copper"] ls = mcolors.LightSource(315, 45) rgb0 = ls.shade(z0, cmap) rgb1 = ls.shade(z1, cmap) @@ -1015,7 +1056,7 @@ def test_light_source_masked_shading(): z = np.ma.masked_greater(z, 9.9) - cmap = plt.cm.copper + cmap = plt.colormaps["copper"] ls = mcolors.LightSource(315, 45) rgb = ls.shade(z, cmap) @@ -1158,8 +1199,8 @@ def test_pandas_iterable(pd): # a single color lst = ['red', 'blue', 'green'] s = pd.Series(lst) - cm1 = mcolors.ListedColormap(lst, N=5) - cm2 = mcolors.ListedColormap(s, N=5) + cm1 = mcolors.ListedColormap(lst) + cm2 = mcolors.ListedColormap(s) assert_array_equal(cm1.colors, cm2.colors) @@ -1184,10 +1225,18 @@ def test_colormap_reversing(name): def test_has_alpha_channel(): assert mcolors._has_alpha_channel((0, 0, 0, 0)) assert mcolors._has_alpha_channel([1, 1, 1, 1]) + assert mcolors._has_alpha_channel('#fff8') + assert mcolors._has_alpha_channel('#0f0f0f80') + assert mcolors._has_alpha_channel(('r', 0.5)) + assert mcolors._has_alpha_channel(([1, 1, 1, 1], None)) assert not mcolors._has_alpha_channel('blue') # 4-char string! assert not mcolors._has_alpha_channel('0.25') assert not mcolors._has_alpha_channel('r') assert not mcolors._has_alpha_channel((1, 0, 0)) + assert not mcolors._has_alpha_channel('#fff') + assert not mcolors._has_alpha_channel('#0f0f0f') + assert not mcolors._has_alpha_channel(('r', None)) + assert not mcolors._has_alpha_channel(([1, 1, 1], None)) def test_cn(): @@ -1375,7 +1424,7 @@ def test_scalarmappable_nan_to_rgba(bytes): # Out-of-range fail x[1, 0, 0] = 42 - with pytest.raises(ValueError, match='0..1 range'): + with pytest.raises(ValueError, match=r'\[0,1\] range'): sm.to_rgba(x[..., :3], bytes=bytes) @@ -1415,7 +1464,7 @@ def test_ndarray_subclass_norm(): # which objects when adding or subtracting with other # arrays. See #6622 and #8696 class MyArray(np.ndarray): - def __isub__(self, other): # type: ignore + def __isub__(self, other): # type: ignore[misc] raise RuntimeError def __add__(self, other): @@ -1488,7 +1537,8 @@ def test_get_under_over_bad(): def test_non_mutable_get_values(kind): cmap = copy.copy(mpl.colormaps['viridis']) init_value = getattr(cmap, f'get_{kind}')() - getattr(cmap, f'set_{kind}')('k') + with pytest.warns(PendingDeprecationWarning): + getattr(cmap, f'set_{kind}')('k') black_value = getattr(cmap, f'get_{kind}')() assert np.all(black_value == [0, 0, 0, 1]) assert not np.all(init_value == black_value) @@ -1552,6 +1602,23 @@ def test_norm_deepcopy(): assert norm2.vmin == norm.vmin +def test_set_clim_emits_single_callback(): + data = np.array([[1, 2], [3, 4]]) + fig, ax = plt.subplots() + image = ax.imshow(data, cmap='viridis') + + callback = unittest.mock.Mock() + image.norm.callbacks.connect('changed', callback) + + callback.assert_not_called() + + # Call set_clim() to update the limits + image.set_clim(1, 5) + + # Assert that only one "changed" callback is sent after calling set_clim() + callback.assert_called_once() + + def test_norm_callback(): increment = unittest.mock.Mock(return_value=None) @@ -1572,7 +1639,7 @@ def test_norm_callback(): assert increment.call_count == 2 # We only want autoscale() calls to send out one update signal - increment.call_count = 0 + increment.reset_mock() norm.autoscale([0, 1, 2]) assert increment.call_count == 1 @@ -1634,7 +1701,8 @@ def test_color_sequences(): assert plt.color_sequences is matplotlib.color_sequences # same registry assert list(plt.color_sequences) == [ 'tab10', 'tab20', 'tab20b', 'tab20c', 'Pastel1', 'Pastel2', 'Paired', - 'Accent', 'Dark2', 'Set1', 'Set2', 'Set3'] + 'Accent', 'okabe_ito', 'Dark2', 'Set1', 'Set2', 'Set3', 'petroff6', + 'petroff8', 'petroff10'] assert len(plt.color_sequences['tab10']) == 10 assert len(plt.color_sequences['tab20']) == 20 @@ -1702,3 +1770,475 @@ def test_to_rgba_array_none_color_with_alpha_param(): assert_array_equal( to_rgba_array(c, alpha), [[0., 0., 1., 1.], [0., 0., 0., 0.]] ) + + +@pytest.mark.parametrize('input, expected', + [('red', True), + (('red', 0.5), True), + (('red', 2), False), + (['red', 0.5], False), + (('red', 'blue'), False), + (['red', 'blue'], False), + ('C3', True), + (('C3', 0.5), True)]) +def test_is_color_like(input, expected): + assert is_color_like(input) is expected + + +def test_colorizer_vmin_vmax(): + ca = mcolorizer.Colorizer() + assert ca.vmin is None + assert ca.vmax is None + ca.vmin = 1 + ca.vmax = 3 + assert ca.vmin == 1.0 + assert ca.vmax == 3.0 + assert ca.norm.vmin == 1.0 + assert ca.norm.vmax == 3.0 + + +def test_LinearSegmentedColormap_from_list_color_alpha_tuple(): + """ + GitHub issue #29042: A bug in 'from_list' causes an error + when passing a tuple (str, float) where the string is a + color name or grayscale value and float is an alpha value. + """ + colors = [("red", 0.3), ("0.42", 0.1), "green"] + cmap = mcolors.LinearSegmentedColormap.from_list("lsc", colors, N=3) + assert_array_almost_equal(cmap([.0, 0.5, 1.]), to_rgba_array(colors)) + + +@pytest.mark.parametrize("colors", + [[(0.42, "blue"), (.1, .1, .1, .1)], + ["blue", (0.42, "red")], + ["blue", (.1, .1, .1, .1), ("red", 2)], + [(0, "red"), (1.1, "blue")], + [(0.52, "red"), (0.42, "blue")]]) +def test_LinearSegmentedColormap_from_list_invalid_inputs(colors): + with pytest.raises(ValueError): + mcolors.LinearSegmentedColormap.from_list("lsc", colors) + + +def test_LinearSegmentedColormap_from_list_value_color_tuple(): + value_color_tuples = [(0, "red"), (0.6, "blue"), (1, "green")] + cmap = mcolors.LinearSegmentedColormap.from_list("lsc", value_color_tuples, N=11) + assert_array_almost_equal( + cmap([value for value, _ in value_color_tuples]), + to_rgba_array([color for _, color in value_color_tuples]), + ) + + +@image_comparison(['test_norm_abc.png'], remove_text=True, + tol=0 if platform.machine() == 'x86_64' else 0.05) +def test_norm_abc(): + + class CustomHalfNorm(mcolors.Norm): + def __init__(self): + super().__init__() + + @property + def vmin(self): + return 0 + + @property + def vmax(self): + return 1 + + @property + def clip(self): + return False + + def __call__(self, value, clip=None): + return value / 2 + + def inverse(self, value): + return 2 * value + + def autoscale(self, A): + pass + + def autoscale_None(self, A): + pass + + def scaled(self): + return True + + @property + def n_components(self): + return 1 + + fig, axes = plt.subplots(2,2) + + r = np.linspace(-1, 3, 16*16).reshape((16,16)) + norm = CustomHalfNorm() + colorizer = mpl.colorizer.Colorizer(cmap='viridis', norm=norm) + c = axes[0,0].imshow(r, colorizer=colorizer) + axes[0,1].pcolor(r, colorizer=colorizer) + axes[1,0].contour(r, colorizer=colorizer) + axes[1,1].contourf(r, colorizer=colorizer) + + +def test_close_error_name(): + with pytest.raises( + KeyError, + match=( + "'grays' is not a valid value for colormap. " + "Did you mean one of ['gray', 'Grays', 'gray_r']?" + )): + matplotlib.colormaps["grays"] + + +def test_multi_norm_creation(): + # tests for mcolors.MultiNorm + + # test wrong input + with pytest.raises(ValueError, + match="MultiNorm must be assigned an iterable"): + mcolors.MultiNorm("linear") + with pytest.raises(ValueError, + match="MultiNorm must be assigned at least one"): + mcolors.MultiNorm([]) + with pytest.raises(ValueError, + match="MultiNorm must be assigned an iterable"): + mcolors.MultiNorm(None) + with pytest.raises(ValueError, + match="not a valid"): + mcolors.MultiNorm(["linear", "bad_norm_name"]) + with pytest.raises(ValueError, + match="Each norm assigned to MultiNorm"): + mcolors.MultiNorm(["linear", object()]) + + norm = mpl.colors.MultiNorm(['linear', 'linear']) + + +def test_multi_norm_call_vmin_vmax(): + # test get vmin, vmax + norm = mpl.colors.MultiNorm(['linear', 'log']) + norm.vmin = (1, 1) + norm.vmax = (2, 2) + assert norm.vmin == (1, 1) + assert norm.vmax == (2, 2) + + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.vmin = 1 + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.vmax = 1 + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.vmin = (1, 2, 3) + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.vmax = (1, 2, 3) + + +def test_multi_norm_call_clip_inverse(): + # test get vmin, vmax + norm = mpl.colors.MultiNorm(['linear', 'log']) + norm.vmin = (1, 1) + norm.vmax = (2, 2) + + # test call with clip + assert_array_equal(norm([3, 3], clip=[False, False]), [2.0, 1.584962500721156]) + assert_array_equal(norm([3, 3], clip=[True, True]), [1.0, 1.0]) + assert_array_equal(norm([3, 3], clip=[True, False]), [1.0, 1.584962500721156]) + norm.clip = [False, False] + assert_array_equal(norm([3, 3]), [2.0, 1.584962500721156]) + norm.clip = [True, True] + assert_array_equal(norm([3, 3]), [1.0, 1.0]) + norm.clip = [True, False] + assert_array_equal(norm([3, 3]), [1.0, 1.584962500721156]) + norm.clip = [True, True] + + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.clip = True + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm.clip = [True, False, True] + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm([3, 3], clip=True) + with pytest.raises(ValueError, match="Expected an iterable of length 2"): + norm([3, 3], clip=[True, True, True]) + + # test inverse + assert_array_almost_equal(norm.inverse([0.5, 0.5849625007211562]), [1.5, 1.5]) + + +def test_multi_norm_autoscale(): + norm = mpl.colors.MultiNorm(['linear', 'log']) + # test autoscale + norm.autoscale([[0, 1, 2, 3], [0.1, 1, 2, 3]]) + assert_array_equal(norm.vmin, [0, 0.1]) + assert_array_equal(norm.vmax, [3, 3]) + + # test autoscale_none + norm0 = mcolors.TwoSlopeNorm(2, vmin=0, vmax=None) + norm = mcolors.MultiNorm([norm0, 'linear'], vmax=[None, 50]) + norm.autoscale_None([[1, 2, 3, 4, 5], [-50, 1, 0, 1, 500]]) + assert_array_equal(norm([5, 0]), [1, 0.5]) + assert_array_equal(norm.vmin, (0, -50)) + assert_array_equal(norm.vmax, (5, 50)) + + +def test_mult_norm_call_types(): + mn = mpl.colors.MultiNorm(['linear', 'linear']) + mn.vmin = (-2, -2) + mn.vmax = (2, 2) + + vals = np.arange(6).reshape((3,2)) + target = np.ma.array([(0.5, 0.75), + (1., 1.25), + (1.5, 1.75)]) + + # test structured array as input + from_mn = mn(rfn.unstructured_to_structured(vals)) + assert_array_almost_equal(from_mn, + target.T) + + # test list of arrays as input + assert_array_almost_equal(mn(list(vals.T)), + list(target.T)) + # test list of floats as input + assert_array_almost_equal(mn(list(vals[0])), + list(target[0])) + # test tuple of arrays as input + assert_array_almost_equal(mn(tuple(vals.T)), + list(target.T)) + + # np.arrays of shapes that are compatible + assert_array_almost_equal(mn(np.zeros(2)), + 0.5*np.ones(2)) + assert_array_almost_equal(mn(np.zeros((2, 3))), + 0.5*np.ones((2, 3))) + assert_array_almost_equal(mn(np.zeros((2, 3, 4))), + 0.5*np.ones((2, 3, 4))) + + # test with NoNorm, list as input + mn_no_norm = mpl.colors.MultiNorm(['linear', mcolors.NoNorm()]) + no_norm_out = mn_no_norm(list(vals.T)) + assert_array_almost_equal(no_norm_out, + [[0., 0.5, 1.], + [1, 3, 5]]) + assert no_norm_out[0].dtype == np.dtype('float64') + assert no_norm_out[1].dtype == vals.dtype + + # test with NoNorm, structured array as input + mn_no_norm = mpl.colors.MultiNorm(['linear', mcolors.NoNorm()]) + no_norm_out = mn_no_norm(rfn.unstructured_to_structured(vals)) + assert_array_almost_equal(no_norm_out, + [[0., 0.5, 1.], + [1, 3, 5]]) + + # test single int as input + with pytest.raises(ValueError, + match="component as input, but got 1 instead"): + mn(1) + + # test list of incompatible size + with pytest.raises(ValueError, + match="but got a sequence with 3 elements"): + mn([3, 2, 1]) + + # last axis matches, len(data.shape) > 2 + with pytest.raises(ValueError, + match=(r"`data_as_list = \[data\[..., i\] for i in " + r"range\(data.shape\[-1\]\)\]`")): + mn(np.zeros((3, 3, 2))) + + # last axis matches, len(data.shape) == 2 + with pytest.raises(ValueError, + match=r"You can use `data_transposed = data.T` to convert"): + mn(np.zeros((3, 2))) + + # incompatible arrays where no relevant axis matches + for data in [np.zeros(3), np.zeros((3, 2, 3))]: + with pytest.raises(ValueError, + match=r"but got a sequence with 3 elements"): + mn(data) + + # test incompatible class + with pytest.raises(ValueError, + match="but got 5, data) + mdata = mcolorizer._ensure_multivariate_data(data, 2) + assert np.all(mdata["f0"].mask[:2] == 0) + assert np.all(mdata["f0"].mask[2:] == 1) + assert np.all(mdata["f1"].mask[:2] == 0) + assert np.all(mdata["f1"].mask[2:] == 1) + + # test tuple of data + data = [0, 1] + mdata = mcolorizer._ensure_multivariate_data(data, 2) + assert mdata.shape == () + + # test wrong input size + data = [[0, 1]] + with pytest.raises(ValueError, match="must contain complex numbers"): + mcolorizer._ensure_multivariate_data(data, 2) + data = [[0, 1]] + with pytest.raises(ValueError, match="have a first dimension 3"): + mcolorizer._ensure_multivariate_data(data, 3) + + # test input of ints as list of lists + data = [[0, 0, 0], [1, 1, 1]] + mdata = mcolorizer._ensure_multivariate_data(data, 2) + assert mdata.shape == (3,) + assert mdata.dtype.fields['f0'][0] == np.int_ + assert mdata.dtype.fields['f1'][0] == np.int_ + + # test input of floats, ints as tuple of lists + data = ([0.0, 0.0], [1, 1]) + mdata = mcolorizer._ensure_multivariate_data(data, 2) + assert mdata.shape == (2,) + assert mdata.dtype.fields['f0'][0] == np.float64 + assert mdata.dtype.fields['f1'][0] == np.int_ + + # test input of array of floats + data = np.array([[0.0, 0, 0], [1, 1, 1]]) + mdata = mcolorizer._ensure_multivariate_data(data, 2) + assert mdata.shape == (3,) + assert mdata.dtype.fields['f0'][0] == np.float64 + assert mdata.dtype.fields['f1'][0] == np.float64 + + # test more input dims + data = np.zeros((3, 4, 5, 6)) + mdata = mcolorizer._ensure_multivariate_data(data, 3) + assert mdata.shape == (4, 5, 6) + + +def test_colorizer_multinorm_implicit(): + ca = mcolorizer.Colorizer('BiOrangeBlue') + ca.vmin = (0, 0) + ca.vmax = (1, 1) + + # test call with two single values + data = [0.1, 0.2] + res = (0.098039, 0.149020, 0.2, 1.0) + assert_array_almost_equal(ca.to_rgba(data), res) + + # test call with two 1d arrays + data = [[0.1, 0.2], [0.3, 0.4]] + res = [[0.09803922, 0.19803922, 0.29803922, 1.], + [0.2, 0.3, 0.4, 1.]] + assert_array_almost_equal(ca.to_rgba(data), res) + + # test call with two 2d arrays + data = [np.linspace(0, 1, 12).reshape(3, 4), + np.linspace(1, 0, 12).reshape(3, 4)] + res = np.array([[[0., 0.5, 1., 1.], + [0.09019608, 0.5, 0.90980392, 1.], + [0.18039216, 0.5, 0.81960784, 1.], + [0.27058824, 0.5, 0.72941176, 1.]], + [[0.36470588, 0.5, 0.63529412, 1.], + [0.45490196, 0.5, 0.54509804, 1.], + [0.54509804, 0.5, 0.45490196, 1.], + [0.63529412, 0.5, 0.36470588, 1.]], + [[0.72941176, 0.5, 0.27058824, 1.], + [0.81960784, 0.5, 0.18039216, 1.], + [0.90980392, 0.5, 0.09019608, 1.], + [1., 0.5, 0., 1.]]]) + assert_array_almost_equal(ca.to_rgba(data), res) + + with pytest.raises(ValueError, match=("This MultiNorm has 2 components, " + "but got a sequence with 3 elements")): + ca.to_rgba([0.1, 0.2, 0.3]) + with pytest.raises(ValueError, match=("This MultiNorm has 2 components, " + "but got a sequence with 1 elements")): + ca.to_rgba([[0.1]]) + + # test multivariate + ca = mcolorizer.Colorizer('3VarAddA') + ca.vmin = (-0.1, -0.2, -0.3) + ca.vmax = (0.1, 0.2, 0.3) + + data = [0.1, 0.1, 0.1] + res = (0.712612, 0.896847, 0.954494, 1.0) + assert_array_almost_equal(ca.to_rgba(data), res) + + +def test_colorizer_multinorm_explicit(): + + with pytest.raises(ValueError, match="MultiNorm must be assigned"): + ca = mcolorizer.Colorizer('BiOrangeBlue', 'linear') + + with pytest.raises(TypeError, + match=("'norm' must be an instance of matplotlib.colors.Norm" + ", str or None, not a list")): + ca = mcolorizer.Colorizer('viridis', ['linear', 'linear']) + + with pytest.raises(ValueError, + match=("Invalid norm for multivariate colormap with 2 inputs")): + ca = mcolorizer.Colorizer('BiOrangeBlue', ['linear', 'linear', 'log']) + + # valid explicit construction + ca = mcolorizer.Colorizer('BiOrangeBlue', [mcolors.Normalize(), 'log']) + ca.vmin = (0, 0.01) + ca.vmax = (1, 1) + + # test call with two single values + data = [0.1, 0.2] + res = (0.098039, 0.374510, 0.65098, 1.) + assert_array_almost_equal(ca.to_rgba(data), res) + + +def test_invalid_cmap_n_components_zero(): + class CustomColormap(mcolors.Colormap): + def __init__(self): + super().__init__("custom") + self.n_variates = 0 + + with pytest.raises(ValueError, match='`n_variates` >= 1'): + ca = mcolorizer.Colorizer(CustomColormap()) + + +def test_colorizer_bivar_cmap(): + ca = mcolorizer.Colorizer('BiOrangeBlue', [mcolors.Normalize(), 'log']) + + with pytest.raises(ValueError, match='The colormap viridis'): + ca.cmap = 'viridis' + + cartist = mcolorizer.ColorizingArtist(ca) + cartist.set_array(np.zeros((2, 4, 4))) + + with pytest.raises(ValueError, match='Invalid data entry for multivariate'): + cartist.set_array(np.zeros((3, 4, 4))) + + dt = np.dtype([('x', 'f4'), ('', 'object')]) + with pytest.raises(TypeError, match='converted to a sequence of floats'): + cartist.set_array(np.zeros((2, 4, 4), dtype=dt)) + + with pytest.raises(ValueError, match='all variates must have same shape'): + cartist.set_array((np.zeros(3), np.zeros(4))) + + # ensure masked value is propagated from input + a = np.arange(3) + cartist.set_array((a, np.ma.masked_where(a > 1, a))) + assert np.all(cartist.get_array()['f0'].mask == np.array([0, 0, 0], dtype=bool)) + assert np.all(cartist.get_array()['f1'].mask == np.array([0, 0, 1], dtype=bool)) + + # test clearing data + cartist.set_array(None) + cartist.get_array() is None + + +def test_colorizer_multivar_cmap(): + ca = mcolorizer.Colorizer('3VarAddA', [mcolors.Normalize(), + mcolors.Normalize(), + 'log']) + cartist = mcolorizer.ColorizingArtist(ca) + cartist.set_array(np.zeros((3, 5, 5))) + with pytest.raises(ValueError, match='Complex numbers are incompatible with'): + cartist.set_array(np.zeros((5, 5), dtype='complex128')) diff --git a/lib/matplotlib/tests/test_compare_images.py b/lib/matplotlib/tests/test_compare_images.py index 6023f3d05468..96b76f790ccd 100644 --- a/lib/matplotlib/tests/test_compare_images.py +++ b/lib/matplotlib/tests/test_compare_images.py @@ -1,11 +1,14 @@ from pathlib import Path import shutil +import numpy as np import pytest from pytest import approx +from matplotlib import _image from matplotlib.testing.compare import compare_images from matplotlib.testing.decorators import _image_directories +from matplotlib.testing.exceptions import ImageComparisonFailure # Tests of the image comparison algorithm. @@ -71,3 +74,27 @@ def test_image_comparison_expect_rms(im1, im2, tol, expect_rms, tmp_path, else: assert results is not None assert results['rms'] == approx(expect_rms, abs=1e-4) + + +def test_invalid_input(): + img = np.zeros((16, 16, 4), dtype=np.uint8) + + with pytest.raises(ImageComparisonFailure, + match='must be 3-dimensional, but is 2-dimensional'): + _image.calculate_rms_and_diff(img[:, :, 0], img) + with pytest.raises(ImageComparisonFailure, + match='must be 3-dimensional, but is 5-dimensional'): + _image.calculate_rms_and_diff(img, img[:, :, :, np.newaxis, np.newaxis]) + with pytest.raises(ImageComparisonFailure, + match='must be RGB or RGBA but has depth 2'): + _image.calculate_rms_and_diff(img[:, :, :2], img) + + with pytest.raises(ImageComparisonFailure, + match=r'expected size: \(16, 16, 4\) actual size \(8, 16, 4\)'): + _image.calculate_rms_and_diff(img, img[:8, :, :]) + with pytest.raises(ImageComparisonFailure, + match=r'expected size: \(16, 16, 4\) actual size \(16, 6, 4\)'): + _image.calculate_rms_and_diff(img, img[:, :6, :]) + with pytest.raises(ImageComparisonFailure, + match=r'expected size: \(16, 16, 4\) actual size \(16, 16, 3\)'): + _image.calculate_rms_and_diff(img, img[:, :, :3]) diff --git a/lib/matplotlib/tests/test_constrainedlayout.py b/lib/matplotlib/tests/test_constrainedlayout.py index 4dc4d9501ec1..ff757c1ce9fc 100644 --- a/lib/matplotlib/tests/test_constrainedlayout.py +++ b/lib/matplotlib/tests/test_constrainedlayout.py @@ -11,6 +11,11 @@ from matplotlib import gridspec, ticker +pytestmark = [ + pytest.mark.usefixtures('text_placeholders') +] + + def example_plot(ax, fontsize=12, nodec=False): ax.plot([1, 2]) ax.locator_params(nbins=3) @@ -36,7 +41,7 @@ def example_pcolor(ax, fontsize=12): return pcm -@image_comparison(['constrained_layout1.png']) +@image_comparison(['constrained_layout1.png'], style='mpl20') def test_constrained_layout1(): """Test constrained_layout for a single subplot""" fig = plt.figure(layout="constrained") @@ -44,7 +49,7 @@ def test_constrained_layout1(): example_plot(ax, fontsize=24) -@image_comparison(['constrained_layout2.png']) +@image_comparison(['constrained_layout2.png'], style='mpl20') def test_constrained_layout2(): """Test constrained_layout for 2x2 subplots""" fig, axs = plt.subplots(2, 2, layout="constrained") @@ -52,7 +57,7 @@ def test_constrained_layout2(): example_plot(ax, fontsize=24) -@image_comparison(['constrained_layout3.png']) +@image_comparison(['constrained_layout3.png'], style='mpl20') def test_constrained_layout3(): """Test constrained_layout for colorbars with subplots""" @@ -66,7 +71,7 @@ def test_constrained_layout3(): fig.colorbar(pcm, ax=ax, pad=pad) -@image_comparison(['constrained_layout4.png']) +@image_comparison(['constrained_layout4.png'], style='mpl20') def test_constrained_layout4(): """Test constrained_layout for a single colorbar with subplots""" @@ -76,7 +81,7 @@ def test_constrained_layout4(): fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6) -@image_comparison(['constrained_layout5.png'], tol=0.002) +@image_comparison(['constrained_layout5.png'], style='mpl20') def test_constrained_layout5(): """ Test constrained_layout for a single colorbar with subplots, @@ -91,12 +96,9 @@ def test_constrained_layout5(): location='bottom') -@image_comparison(['constrained_layout6.png'], tol=0.002) +@image_comparison(['constrained_layout6.png'], style='mpl20') def test_constrained_layout6(): """Test constrained_layout for nested gridspecs""" - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - fig = plt.figure(layout="constrained") gs = fig.add_gridspec(1, 2, figure=fig) gsl = gs[0].subgridspec(2, 2) @@ -154,7 +156,7 @@ def test_constrained_layout7(): fig.draw_without_rendering() -@image_comparison(['constrained_layout8.png']) +@image_comparison(['constrained_layout8.png'], style='mpl20') def test_constrained_layout8(): """Test for gridspecs that are not completely full""" @@ -182,7 +184,7 @@ def test_constrained_layout8(): fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6) -@image_comparison(['constrained_layout9.png']) +@image_comparison(['constrained_layout9.png'], style='mpl20') def test_constrained_layout9(): """Test for handling suptitle and for sharex and sharey""" @@ -197,8 +199,8 @@ def test_constrained_layout9(): fig.suptitle('Test Suptitle', fontsize=28) -@image_comparison(['constrained_layout10.png'], - tol=0.032 if platform.machine() == 'arm64' else 0) +@image_comparison(['constrained_layout10.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.032) def test_constrained_layout10(): """Test for handling legend outside axis""" fig, axs = plt.subplots(2, 2, layout="constrained") @@ -207,7 +209,7 @@ def test_constrained_layout10(): ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5)) -@image_comparison(['constrained_layout11.png']) +@image_comparison(['constrained_layout11.png'], style='mpl20') def test_constrained_layout11(): """Test for multiple nested gridspecs""" @@ -227,7 +229,7 @@ def test_constrained_layout11(): example_plot(ax, fontsize=9) -@image_comparison(['constrained_layout11rat.png']) +@image_comparison(['constrained_layout11rat.png'], style='mpl20') def test_constrained_layout11rat(): """Test for multiple nested gridspecs with width_ratios""" @@ -247,7 +249,7 @@ def test_constrained_layout11rat(): example_plot(ax, fontsize=9) -@image_comparison(['constrained_layout12.png']) +@image_comparison(['constrained_layout12.png'], style='mpl20') def test_constrained_layout12(): """Test that very unbalanced labeling still works.""" fig = plt.figure(layout="constrained", figsize=(6, 8)) @@ -269,7 +271,7 @@ def test_constrained_layout12(): ax.set_xlabel('x-label') -@image_comparison(['constrained_layout13.png'], tol=2.e-2) +@image_comparison(['constrained_layout13.png'], style='mpl20') def test_constrained_layout13(): """Test that padding works.""" fig, axs = plt.subplots(2, 2, layout="constrained") @@ -281,7 +283,7 @@ def test_constrained_layout13(): fig.get_layout_engine().set(w_pad=24./72., h_pad=24./72.) -@image_comparison(['constrained_layout14.png']) +@image_comparison(['constrained_layout14.png'], style='mpl20') def test_constrained_layout14(): """Test that padding works.""" fig, axs = plt.subplots(2, 2, layout="constrained") @@ -293,7 +295,7 @@ def test_constrained_layout14(): hspace=0.2, wspace=0.2) -@image_comparison(['constrained_layout15.png']) +@image_comparison(['constrained_layout15.png'], style='mpl20') def test_constrained_layout15(): """Test that rcparams work.""" mpl.rcParams['figure.constrained_layout.use'] = True @@ -302,15 +304,15 @@ def test_constrained_layout15(): example_plot(ax, fontsize=12) -@image_comparison(['constrained_layout16.png']) +@image_comparison(['constrained_layout16.png'], style='mpl20') def test_constrained_layout16(): """Test ax.set_position.""" fig, ax = plt.subplots(layout="constrained") example_plot(ax, fontsize=12) - ax2 = fig.add_axes([0.2, 0.2, 0.4, 0.4]) + ax2 = fig.add_axes((0.2, 0.2, 0.4, 0.4)) -@image_comparison(['constrained_layout17.png']) +@image_comparison(['constrained_layout17.png'], style='mpl20') def test_constrained_layout17(): """Test uneven gridspecs""" fig = plt.figure(layout="constrained") @@ -355,7 +357,7 @@ def test_constrained_layout20(): img = np.hypot(gx, gx[:, None]) fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1]) + ax = fig.add_axes((0, 0, 1, 1)) mesh = ax.pcolormesh(gx, gx, img[:-1, :-1]) fig.colorbar(mesh) @@ -409,9 +411,6 @@ def test_colorbar_location(): Test that colorbar handling is as expected for various complicated cases... """ - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False - fig, axs = plt.subplots(4, 5, layout="constrained") for ax in axs.flat: pcm = example_pcolor(ax) @@ -435,7 +434,7 @@ def test_hidden_axes(): extents1 = np.copy(axs[0, 0].get_position().extents) np.testing.assert_allclose( - extents1, [0.045552, 0.543288, 0.47819, 0.982638], rtol=1e-5) + extents1, [0.046918, 0.541204, 0.477409, 0.980555], rtol=1e-5) def test_colorbar_align(): @@ -641,7 +640,7 @@ def test_compressed1(): fig.draw_without_rendering() pos = axs[0, 0].get_position() - np.testing.assert_allclose(pos.x0, 0.2344, atol=1e-3) + np.testing.assert_allclose(pos.x0, 0.2381, atol=1e-2) pos = axs[0, 1].get_position() np.testing.assert_allclose(pos.x1, 0.7024, atol=1e-3) @@ -655,11 +654,106 @@ def test_compressed1(): fig.draw_without_rendering() pos = axs[0, 0].get_position() - np.testing.assert_allclose(pos.x0, 0.06195, atol=1e-3) - np.testing.assert_allclose(pos.y1, 0.8537, atol=1e-3) + np.testing.assert_allclose(pos.x0, 0.05653, atol=1e-3) + np.testing.assert_allclose(pos.y1, 0.8603, atol=1e-2) pos = axs[1, 2].get_position() - np.testing.assert_allclose(pos.x1, 0.8618, atol=1e-3) - np.testing.assert_allclose(pos.y0, 0.1934, atol=1e-3) + np.testing.assert_allclose(pos.x1, 0.8728, atol=1e-3) + np.testing.assert_allclose(pos.y0, 0.1808, atol=1e-2) + + +def test_compressed_suptitle(): + fig, (ax0, ax1) = plt.subplots( + nrows=2, figsize=(4, 10), layout="compressed", + gridspec_kw={"height_ratios": (1 / 4, 3 / 4), "hspace": 0}) + + ax0.axis("equal") + ax0.set_box_aspect(1/3) + + ax1.axis("equal") + ax1.set_box_aspect(1) + + title = fig.suptitle("Title") + fig.draw_without_rendering() + assert title.get_position()[1] == pytest.approx(0.7491, abs=1e-3) + + title = fig.suptitle("Title", y=0.98) + fig.draw_without_rendering() + assert title.get_position()[1] == 0.98 + + title = fig.suptitle("Title", in_layout=False) + fig.draw_without_rendering() + assert title.get_position()[1] == 0.98 + + +@image_comparison(['test_compressed_suptitle_colorbar.png'], style='mpl20') +def test_compressed_suptitle_colorbar(): + """Test that colorbars align with axes in compressed layout with suptitle.""" + arr = np.arange(100).reshape((10, 10)) + fig, axs = plt.subplots(ncols=2, figsize=(4, 2), layout='compressed') + + im0 = axs[0].imshow(arr) + im1 = axs[1].imshow(arr) + + cb0 = plt.colorbar(im0, ax=axs[0]) + cb1 = plt.colorbar(im1, ax=axs[1]) + + fig.suptitle('Title') + + # Verify colorbar heights match axes heights + # After layout, colorbar should have same height as parent axes + fig.canvas.draw() + + for ax, cb in zip(axs, [cb0, cb1]): + ax_pos = ax.get_position() + cb_pos = cb.ax.get_position() + + # Check that colorbar height matches axes height (within tolerance) + # Note: We check the actual rendered positions, not the bbox + assert abs(cb_pos.height - ax_pos.height) < 0.01, \ + f"Colorbar height {cb_pos.height} doesn't match axes height {ax_pos.height}" + + # Also verify vertical alignment (y0 and y1 should match) + assert abs(cb_pos.y0 - ax_pos.y0) < 0.01, \ + f"Colorbar y0 {cb_pos.y0} doesn't match axes y0 {ax_pos.y0}" + assert abs(cb_pos.y1 - ax_pos.y1) < 0.01, \ + f"Colorbar y1 {cb_pos.y1} doesn't match axes y1 {ax_pos.y1}" + + +@image_comparison(['test_compressed_supylabel_colorbar.png'], style='mpl20') +def test_compressed_supylabel_colorbar(): + """ + Test that horizontal colorbars align with axes + in compressed layout with supylabel. + """ + arr = np.arange(100).reshape((10, 10)) + fig, axs = plt.subplots(nrows=2, figsize=(3, 4), layout='compressed') + + im0 = axs[0].imshow(arr) + im1 = axs[1].imshow(arr) + + cb0 = plt.colorbar(im0, ax=axs[0], orientation='horizontal') + cb1 = plt.colorbar(im1, ax=axs[1], orientation='horizontal') + + fig.supylabel('Title') + + # Verify colorbar widths match axes widths + # After layout, colorbar should have same width as parent axes + fig.canvas.draw() + + for ax, cb in zip(axs, [cb0, cb1]): + ax_pos = ax.get_position() + cb_pos = cb.ax.get_position() + + # Check that colorbar width matches axes width (within tolerance) + # Note: We check the actual rendered positions, not the bbox + assert abs(cb_pos.width - ax_pos.width) < 0.01, \ + f"Colorbar width {cb_pos.width} doesn't match axes width {ax_pos.width}" + + # Also verify horizontal alignment (x0 and x1 should match) + assert abs(cb_pos.x0 - ax_pos.x0) < 0.01, \ + f"Colorbar x0 {cb_pos.x0} doesn't match axes x0 {ax_pos.x0}" + assert abs(cb_pos.x1 - ax_pos.x1) < 0.01, \ + f"Colorbar x1 {cb_pos.x1} doesn't match axes x1 {ax_pos.x1}" @pytest.mark.parametrize('arg, state', [ @@ -695,3 +789,23 @@ def test_layout_leak(): gc.collect() assert not any(isinstance(obj, mpl._layoutgrid.LayoutGrid) for obj in gc.get_objects()) + + +def test_submerged_subfig(): + """ + Test that the submerged margin logic does not get called multiple times + on same axes if it is already in a subfigure + """ + fig = plt.figure(figsize=(4, 5), layout='constrained') + figures = fig.subfigures(3, 1) + axs = [] + for f in figures.flatten(): + gs = f.add_gridspec(2, 2) + for i in range(2): + axs += [f.add_subplot(gs[i, 0])] + axs[-1].plot() + f.add_subplot(gs[:, 1]).plot() + fig.draw_without_rendering() + for ax in axs[1:]: + assert np.allclose(ax.get_position().bounds[-1], + axs[0].get_position().bounds[-1], atol=1e-6) diff --git a/lib/matplotlib/tests/test_container.py b/lib/matplotlib/tests/test_container.py index 1e4577c518ae..b7dfe1196685 100644 --- a/lib/matplotlib/tests/test_container.py +++ b/lib/matplotlib/tests/test_container.py @@ -1,4 +1,5 @@ import numpy as np +from numpy.testing import assert_array_equal import matplotlib.pyplot as plt @@ -35,3 +36,43 @@ def test_nonstring_label(): # Test for #26824 plt.bar(np.arange(10), np.random.rand(10), label=1) plt.legend() + + +def test_barcontainer_position_centers__bottoms__tops(): + fig, ax = plt.subplots() + pos = [1, 2, 4] + bottoms = np.array([1, 5, 3]) + heights = np.array([2, 3, 4]) + + container = ax.bar(pos, heights, bottom=bottoms) + assert_array_equal(container.position_centers, pos) + assert_array_equal(container.bottoms, bottoms) + assert_array_equal(container.tops, bottoms + heights) + + container = ax.barh(pos, heights, left=bottoms) + assert_array_equal(container.position_centers, pos) + assert_array_equal(container.bottoms, bottoms) + assert_array_equal(container.tops, bottoms + heights) + + +def test_piecontainer_remove(): + fig, ax = plt.subplots() + pie = ax.pie([2, 3], labels=['foo', 'bar'], autopct="%1.0f%%") + ax.pie_label(pie, ['baz', 'qux']) + assert len(ax.patches) == 2 + assert len(ax.texts) == 6 + + pie.remove() + assert not ax.patches + assert not ax.texts + + +def test_piecontainer_unpack_backcompat(): + fig, ax = plt.subplots() + wedges, texts, autotexts = ax.pie( + [2, 3], labels=['foo', 'bar'], autopct="%1.0f%%", labeldistance=None) + + assert len(wedges) == 2 + assert isinstance(texts, list) + assert not texts + assert len(autotexts) == 2 diff --git a/lib/matplotlib/tests/test_contour.py b/lib/matplotlib/tests/test_contour.py index d4600a14fe1c..98f6de210eab 100644 --- a/lib/matplotlib/tests/test_contour.py +++ b/lib/matplotlib/tests/test_contour.py @@ -5,8 +5,7 @@ import contourpy import numpy as np -from numpy.testing import ( - assert_array_almost_equal, assert_array_almost_equal_nulp, assert_array_equal) +from numpy.testing import assert_array_almost_equal, assert_array_almost_equal_nulp import matplotlib as mpl from matplotlib import pyplot as plt, rc_context, ticker from matplotlib.colors import LogNorm, same_color @@ -15,19 +14,6 @@ import pytest -# Helper to test the transition from ContourSets holding multiple Collections to being a -# single Collection; remove once the deprecated old layout expires. -def _maybe_split_collections(do_split): - if not do_split: - return - for fig in map(plt.figure, plt.get_fignums()): - for ax in fig.axes: - for coll in ax.collections: - if isinstance(coll, mpl.contour.ContourSet): - with pytest.warns(mpl._api.MatplotlibDeprecationWarning): - coll.collections - - def test_contour_shape_1d_valid(): x = np.arange(10) @@ -100,7 +86,7 @@ def test_contour_Nlevels(): assert (cs1.levels == cs2.levels).all() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_contour_set_paths(fig_test, fig_ref): cs_test = fig_test.subplots().contour([[0, 1], [1, 2]]) cs_ref = fig_ref.subplots().contour([[1, 0], [2, 1]]) @@ -108,17 +94,14 @@ def test_contour_set_paths(fig_test, fig_ref): cs_test.set_paths(cs_ref.get_paths()) -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(['contour_manual_labels'], remove_text=True, style='mpl20', tol=0.26) -def test_contour_manual_labels(split_collections): +@image_comparison(['contour_manual_labels'], remove_text=True, style='mpl20') +def test_contour_manual_labels(): x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10)) z = np.max(np.dstack([abs(x), abs(y)]), 2) plt.figure(figsize=(6, 2), dpi=200) cs = plt.contour(x, y, z) - _maybe_split_collections(split_collections) - pts = np.array([(1.0, 3.0), (1.0, 4.4), (1.0, 6.0)]) plt.clabel(cs, manual=pts) pts = np.array([(2.0, 3.0), (2.0, 4.4), (2.0, 6.0)]) @@ -144,29 +127,21 @@ def test_contour_manual_moveto(): assert clabels[0].get_text() == "0" -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(['contour_disconnected_segments'], - remove_text=True, style='mpl20', extensions=['png']) -def test_contour_label_with_disconnected_segments(split_collections): +@image_comparison(['contour_disconnected_segments.png'], + remove_text=True, style='mpl20') +def test_contour_label_with_disconnected_segments(): x, y = np.mgrid[-1:1:21j, -1:1:21j] z = 1 / np.sqrt(0.01 + (x + 0.3) ** 2 + y ** 2) z += 1 / np.sqrt(0.01 + (x - 0.3) ** 2 + y ** 2) plt.figure() cs = plt.contour(x, y, z, levels=[7]) - - # Adding labels should invalidate the old style - _maybe_split_collections(split_collections) - cs.clabel(manual=[(0.2, 0.1)]) - _maybe_split_collections(split_collections) - -@pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_manual_colors_and_levels.png'], remove_text=True, - tol=0.018 if platform.machine() == 'arm64' else 0) -def test_given_colors_levels_and_extends(split_collections): + tol=0 if platform.machine() == 'x86_64' else 0.018) +def test_given_colors_levels_and_extends(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -195,12 +170,30 @@ def test_given_colors_levels_and_extends(split_collections): plt.colorbar(c, ax=ax) - _maybe_split_collections(split_collections) + +@image_comparison(['contourf_hatch_colors.png'], remove_text=True, style='mpl20') +def test_hatch_colors(): + fig, ax = plt.subplots() + cf = ax.contourf([[0, 1], [1, 2]], hatches=['-', '/', '\\', '//'], cmap='gray') + cf.set_edgecolors(["blue", "grey", "yellow", "red"]) + + +@pytest.mark.parametrize('color, extend', [('darkred', 'neither'), + ('darkred', 'both'), + (('r', 0.5), 'neither'), + ((0.1, 0.2, 0.5, 0.3), 'neither')]) +def test_single_color_and_extend(color, extend): + z = [[0, 1], [1, 2]] + + _, ax = plt.subplots() + levels = [0.5, 0.75, 1, 1.25, 1.5] + cs = ax.contour(z, levels=levels, colors=color, extend=extend) + for c in cs.get_edgecolors(): + assert same_color(c, color) -@pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_log_locator.svg'], style='mpl20', remove_text=False) -def test_log_locator_levels(split_collections): +def test_log_locator_levels(): fig, ax = plt.subplots() @@ -219,12 +212,24 @@ def test_log_locator_levels(split_collections): cb = fig.colorbar(c, ax=ax) assert_array_almost_equal(cb.ax.get_yticks(), c.levels) - _maybe_split_collections(split_collections) + +@pytest.mark.parametrize("n_levels", [2, 3, 4, 5, 6]) +def test_lognorm_levels(n_levels): + x, y = np.mgrid[1:10:0.1, 1:10:0.1] + data = np.abs(np.sin(x)*np.exp(y)) + + fig, ax = plt.subplots() + im = ax.contour(x, y, data, norm=LogNorm(), levels=n_levels) + fig.colorbar(im, ax=ax) + + levels = im.levels + visible_levels = levels[(levels <= data.max()) & (levels >= data.min())] + # levels parameter promises "no more than n+1 "nice" contour levels " + assert len(visible_levels) <= n_levels + 1 -@pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_datetime_axis.png'], style='mpl20') -def test_contour_datetime_axis(split_collections): +def test_contour_datetime_axis(): fig = plt.figure() fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15) base = datetime.datetime(2013, 1, 1) @@ -247,13 +252,11 @@ def test_contour_datetime_axis(split_collections): label.set_ha('right') label.set_rotation(30) - _maybe_split_collections(split_collections) - -@pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_test_label_transforms.png'], - remove_text=True, style='mpl20', tol=1.1) -def test_labels(split_collections): + remove_text=True, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.005) +def test_labels(): # Adapted from pylab_examples example code: contour_demo.py # see issues #2475, #2843, and #2818 for explanation delta = 0.025 @@ -272,9 +275,6 @@ def test_labels(split_collections): disp_units = [(216, 177), (359, 290), (521, 406)] data_units = [(-2, .5), (0, -1.5), (2.8, 1)] - # Adding labels should invalidate the old style - _maybe_split_collections(split_collections) - CS.clabel() for x, y in data_units: @@ -283,8 +283,6 @@ def test_labels(split_collections): for x, y in disp_units: CS.add_label_near(x, y, inline=True, transform=False) - _maybe_split_collections(split_collections) - def test_label_contour_start(): # Set up data and figure/axes that result in automatic labelling adding the @@ -311,10 +309,18 @@ def test_label_contour_start(): assert 0 in idxs -@pytest.mark.parametrize("split_collections", [False, True]) +def test_clabel_raises_on_filled_contours(): + X, Y = np.meshgrid(np.arange(10), np.arange(10)) + _, ax = plt.subplots() + cs = ax.contourf(X, Y, X + Y) + # will be an exception once the deprecation expires + with pytest.warns(mpl.MatplotlibDeprecationWarning): + ax.clabel(cs) + + @image_comparison(['contour_corner_mask_False.png', 'contour_corner_mask_True.png'], remove_text=True, tol=1.88) -def test_corner_mask(split_collections): +def test_corner_mask(): n = 60 mask_level = 0.95 noise_amp = 1.0 @@ -328,8 +334,6 @@ def test_corner_mask(split_collections): plt.figure() plt.contourf(z, corner_mask=corner_mask) - _maybe_split_collections(split_collections) - def test_contourf_decreasing_levels(): # github issue 5477. @@ -363,21 +367,16 @@ def test_clabel_zorder(use_clabeltext, contour_zorder, clabel_zorder): x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10)) z = np.max(np.dstack([abs(x), abs(y)]), 2) - fig, (ax1, ax2) = plt.subplots(ncols=2) - cs = ax1.contour(x, y, z, zorder=contour_zorder) - cs_filled = ax2.contourf(x, y, z, zorder=contour_zorder) - clabels1 = cs.clabel(zorder=clabel_zorder, use_clabeltext=use_clabeltext) - clabels2 = cs_filled.clabel(zorder=clabel_zorder, - use_clabeltext=use_clabeltext) + fig, ax = plt.subplots() + cs = ax.contour(x, y, z, zorder=contour_zorder) + clabels = cs.clabel(zorder=clabel_zorder, use_clabeltext=use_clabeltext) if clabel_zorder is None: expected_clabel_zorder = 2+contour_zorder else: expected_clabel_zorder = clabel_zorder - for clabel in clabels1: - assert clabel.get_zorder() == expected_clabel_zorder - for clabel in clabels2: + for clabel in clabels: assert clabel.get_zorder() == expected_clabel_zorder @@ -400,11 +399,10 @@ def test_clabel_with_large_spacing(): # tol because ticks happen to fall on pixel boundaries so small # floating point changes in tick location flip which pixel gets # the tick. -@pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_log_extension.png'], remove_text=True, style='mpl20', tol=1.444) -def test_contourf_log_extension(split_collections): +def test_contourf_log_extension(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -420,8 +418,11 @@ def test_contourf_log_extension(split_collections): levels = np.power(10., levels_exp) # original data + # FIXME: Force tick locations for now for backcompat with old test + # (log-colorbar extension is not really optimal anyways). c1 = ax1.contourf(data, - norm=LogNorm(vmin=data.min(), vmax=data.max())) + norm=LogNorm(vmin=data.min(), vmax=data.max()), + locator=mpl.ticker.FixedLocator(10.**np.arange(-8, 12, 2))) # just show data in levels c2 = ax2.contourf(data, levels=levels, norm=LogNorm(vmin=levels.min(), vmax=levels.max()), @@ -436,17 +437,12 @@ def test_contourf_log_extension(split_collections): assert_array_almost_equal_nulp(cb.ax.get_ylim(), np.array((1e-4, 1e6))) cb = plt.colorbar(c3, ax=ax3) - _maybe_split_collections(split_collections) - -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison( - ['contour_addlines.png'], remove_text=True, style='mpl20', - tol=0.15 if platform.machine() in ('aarch64', 'ppc64le', 's390x') - else 0.03) +@image_comparison(['contour_addlines.png'], remove_text=True, style='mpl20', + tol=0.03 if platform.machine() == 'x86_64' else 0.15) # tolerance is because image changed minutely when tick finding on # colorbars was cleaned up... -def test_contour_addlines(split_collections): +def test_contour_addlines(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -460,13 +456,9 @@ def test_contour_addlines(split_collections): cb.add_lines(cont) assert_array_almost_equal(cb.ax.get_ylim(), [114.3091, 9972.30735], 3) - _maybe_split_collections(split_collections) - -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(baseline_images=['contour_uneven'], - extensions=['png'], remove_text=True, style='mpl20') -def test_contour_uneven(split_collections): +@image_comparison(['contour_uneven.png'], remove_text=True, style='mpl20') +def test_contour_uneven(): # Remove this line when this test image is regenerated. plt.rcParams['pcolormesh.snap'] = False @@ -479,8 +471,6 @@ def test_contour_uneven(split_collections): cs = ax.contourf(z, levels=[2, 4, 6, 10, 20]) fig.colorbar(cs, ax=ax, spacing='uniform') - _maybe_split_collections(split_collections) - @pytest.mark.parametrize( "rc_lines_linewidth, rc_contour_linewidth, call_linewidths, expected", [ @@ -497,8 +487,6 @@ def test_contour_linewidth( X = np.arange(4*3).reshape(4, 3) cs = ax.contour(X, linewidths=call_linewidths) assert cs.get_linewidths()[0] == expected - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="tlinewidths"): - assert cs.tlinewidths[0][0] == expected @pytest.mark.backend("pdf") @@ -507,10 +495,8 @@ def test_label_nonagg(): plt.clabel(plt.contour([[1, 2], [3, 4]])) -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(baseline_images=['contour_closed_line_loop'], - extensions=['png'], remove_text=True) -def test_contour_closed_line_loop(split_collections): +@image_comparison(['contour_closed_line_loop.png'], remove_text=True) +def test_contour_closed_line_loop(): # github issue 19568. z = [[0, 0, 0], [0, 2, 0], [0, 0, 0], [2, 1, 2]] @@ -519,8 +505,6 @@ def test_contour_closed_line_loop(split_collections): ax.set_xlim(-0.1, 2.1) ax.set_ylim(-0.1, 3.1) - _maybe_split_collections(split_collections) - def test_quadcontourset_reuse(): # If QuadContourSet returned from one contour(f) call is passed as first @@ -535,10 +519,8 @@ def test_quadcontourset_reuse(): assert qcs3._contour_generator == qcs1._contour_generator -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(baseline_images=['contour_manual'], - extensions=['png'], remove_text=True, tol=0.89) -def test_contour_manual(split_collections): +@image_comparison(['contour_manual.png'], remove_text=True, tol=0.89) +def test_contour_manual(): # Manually specifying contour lines/polygons to plot. from matplotlib.contour import ContourSet @@ -561,13 +543,9 @@ def test_contour_manual(split_collections): ContourSet(ax, [2, 3], [segs], [kinds], filled=True, cmap=cmap) ContourSet(ax, [2], [segs], [kinds], colors='k', linewidths=3) - _maybe_split_collections(split_collections) - -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(baseline_images=['contour_line_start_on_corner_edge'], - extensions=['png'], remove_text=True) -def test_contour_line_start_on_corner_edge(split_collections): +@image_comparison(['contour_line_start_on_corner_edge.png'], remove_text=True) +def test_contour_line_start_on_corner_edge(): fig, ax = plt.subplots(figsize=(6, 5)) x, y = np.meshgrid([0, 1, 2, 3, 4], [0, 1, 2]) @@ -581,8 +559,6 @@ def test_contour_line_start_on_corner_edge(split_collections): lines = ax.contour(x, y, z, corner_mask=True, colors='k') cbar.add_lines(lines) - _maybe_split_collections(split_collections) - def test_find_nearest_contour(): xy = np.indices((15, 15)) @@ -640,8 +616,7 @@ def test_contourf_legend_elements(): cs = plt.contourf(h, levels=[10, 30, 50], colors=['#FFFF00', '#FF00FF', '#00FFFF'], extend='both') - cs.cmap.set_over('red') - cs.cmap.set_under('blue') + cs.cmap = cs.cmap.with_extremes(over='red', under='blue') cs.changed() artists, labels = cs.legend_elements() assert labels == ['$x \\leq -1e+250s$', @@ -703,10 +678,8 @@ def test_algorithm_supports_corner_mask(algorithm): plt.contourf(z, algorithm=algorithm, corner_mask=True) -@pytest.mark.parametrize("split_collections", [False, True]) -@image_comparison(baseline_images=['contour_all_algorithms'], - extensions=['png'], remove_text=True, tol=0.06) -def test_all_algorithms(split_collections): +@image_comparison(['contour_all_algorithms.png'], remove_text=True, tol=0.06) +def test_all_algorithms(): algorithms = ['mpl2005', 'mpl2014', 'serial', 'threaded'] rng = np.random.default_rng(2981) @@ -722,8 +695,6 @@ def test_all_algorithms(split_collections): ax.contour(x, y, z, algorithm=algorithm, colors='k') ax.set_title(algorithm) - _maybe_split_collections(split_collections) - def test_subfigure_clabel(): # Smoke test for gh#23173 @@ -832,6 +803,15 @@ def test_contour_remove(): assert ax.get_children() == orig_children +def test_contour_remove_with_labels(): + ax = plt.figure().add_subplot() + cs = ax.contour(np.arange(16).reshape((4, 4))) + labels = cs.clabel() + labels[0].remove() + with pytest.warns(UserWarning, match="Some labels were manually removed"): + cs.remove() + + def test_contour_no_args(): fig, ax = plt.subplots() data = [[0, 1], [1, 0]] @@ -884,17 +864,23 @@ def test_allsegs_allkinds(): assert len(result[1]) == 4 -def test_deprecated_apis(): - cs = plt.contour(np.arange(16).reshape((4, 4))) - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="collections"): - colls = cs.collections - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="tcolors"): - assert_array_equal(cs.tcolors, [c.get_edgecolor() for c in colls]) - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="tlinewidths"): - assert cs.tlinewidths == [c.get_linewidth() for c in colls] - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="antialiased"): - assert cs.antialiased - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="antialiased"): - cs.antialiased = False - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="antialiased"): - assert not cs.antialiased +@image_comparison(['contour_rasterization.pdf'], savefig_kwarg={'dpi': 25}, + style='mpl20') +def test_contourf_rasterize(): + fig, ax = plt.subplots() + data = [[0, 1], [1, 0]] + circle = mpatches.Circle([0.5, 0.5], 0.5, transform=ax.transAxes) + cs = ax.contourf(data, clip_path=circle, rasterized=True) + assert cs._rasterized + + +@check_figures_equal() +def test_contour_aliases(fig_test, fig_ref): + data = np.arange(100).reshape((10, 10)) ** 2 + fig_test.add_subplot().contour(data, linestyle=":") + fig_ref.add_subplot().contour(data, linestyles="dotted") + + +def test_contour_singular_color(): + with pytest.raises(TypeError): + plt.figure().add_subplot().contour([[0, 1], [2, 3]], color="r") diff --git a/lib/matplotlib/tests/test_dates.py b/lib/matplotlib/tests/test_dates.py index 4133524e0e1a..45948ea1e7f0 100644 --- a/lib/matplotlib/tests/test_dates.py +++ b/lib/matplotlib/tests/test_dates.py @@ -140,7 +140,7 @@ def test_axhline(): mdates._reset_epoch_test_example() -@image_comparison(['date_axhspan.png']) +@image_comparison(['date_axhspan.png'], style='mpl20') def test_date_axhspan(): # test axhspan with date inputs t0 = datetime.datetime(2009, 1, 20) @@ -152,7 +152,7 @@ def test_date_axhspan(): fig.subplots_adjust(left=0.25) -@image_comparison(['date_axvspan.png']) +@image_comparison(['date_axvspan.png'], style='mpl20') def test_date_axvspan(): # test axvspan with date inputs t0 = datetime.datetime(2000, 1, 20) @@ -164,7 +164,7 @@ def test_date_axvspan(): fig.autofmt_xdate() -@image_comparison(['date_axhline.png']) +@image_comparison(['date_axhline.png'], style='mpl20') def test_date_axhline(): # test axhline with date inputs t0 = datetime.datetime(2009, 1, 20) @@ -176,7 +176,7 @@ def test_date_axhline(): fig.subplots_adjust(left=0.25) -@image_comparison(['date_axvline.png']) +@image_comparison(['date_axvline.png'], style='mpl20') def test_date_axvline(): # test axvline with date inputs t0 = datetime.datetime(2000, 1, 20) @@ -199,7 +199,7 @@ def test_too_many_date_ticks(caplog): tf = datetime.datetime(2000, 1, 20) fig, ax = plt.subplots() with pytest.warns(UserWarning) as rec: - ax.set_xlim((t0, tf), auto=True) + ax.set_xlim(t0, tf, auto=True) assert len(rec) == 1 assert ('Attempting to set identical low and high xlims' in str(rec[0].message)) @@ -226,7 +226,7 @@ def wrapper(): return wrapper -@image_comparison(['RRuleLocator_bounds.png']) +@image_comparison(['RRuleLocator_bounds.png'], style='mpl20') def test_RRuleLocator(): import matplotlib.testing.jpl_units as units units.register() @@ -270,12 +270,12 @@ def test_RRuleLocator_close_minmax(): assert list(map(str, mdates.num2date(loc.tick_values(d1, d2)))) == expected -@image_comparison(['DateFormatter_fractionalSeconds.png']) +@image_comparison(['DateFormatter_fractionalSeconds.png'], style='mpl20') def test_DateFormatter(): import matplotlib.testing.jpl_units as units units.register() - # Lets make sure that DateFormatter will allow us to have tick marks + # Let's make sure that DateFormatter will allow us to have tick marks # at intervals of fractional seconds. t0 = datetime.datetime(2001, 1, 1, 0, 0, 0) @@ -373,7 +373,7 @@ def test_drange(): end = datetime.datetime(2011, 1, 2, tzinfo=mdates.UTC) delta = datetime.timedelta(hours=1) # We expect 24 values in drange(start, end, delta), because drange returns - # dates from an half open interval [start, end) + # dates from a half open interval [start, end) assert len(mdates.drange(start, end, delta)) == 24 # Same if interval ends slightly earlier @@ -636,6 +636,23 @@ def test_concise_formatter_show_offset(t_delta, expected): assert formatter.get_offset() == expected +def test_concise_formatter_show_offset_inverted(): + # Test for github issue #28481 + d1 = datetime.datetime(1997, 1, 1) + d2 = d1 + datetime.timedelta(days=60) + + fig, ax = plt.subplots() + locator = mdates.AutoDateLocator() + formatter = mdates.ConciseDateFormatter(locator) + ax.xaxis.set_major_locator(locator) + ax.xaxis.set_major_formatter(formatter) + ax.invert_xaxis() + + ax.plot([d1, d2], [0, 0]) + fig.canvas.draw() + assert formatter.get_offset() == '1997-Jan' + + def test_concise_converter_stays(): # This test demonstrates problems introduced by gh-23417 (reverted in gh-25278) # In particular, downstream libraries like Pandas had their designated converters @@ -651,10 +668,12 @@ def test_concise_converter_stays(): fig, ax = plt.subplots() ax.plot(x, y) # Bypass Switchable date converter - ax.xaxis.converter = conv = mdates.ConciseDateConverter() + conv = mdates.ConciseDateConverter() + with pytest.warns(UserWarning, match="already has a converter"): + ax.xaxis.set_converter(conv) assert ax.xaxis.units is None ax.set_xlim(*x) - assert ax.xaxis.converter == conv + assert ax.xaxis.get_converter() == conv def test_offset_changes(): @@ -934,7 +953,7 @@ def _create_auto_date_locator(date1, date2, tz): assert st == expected -@image_comparison(['date_inverted_limit.png']) +@image_comparison(['date_inverted_limit.png'], style='mpl20') def test_date_inverted_limit(): # test ax hline with date inputs t0 = datetime.datetime(2009, 1, 20) diff --git a/lib/matplotlib/tests/test_datetime.py b/lib/matplotlib/tests/test_datetime.py index 276056d044ae..c246fb23f011 100644 --- a/lib/matplotlib/tests/test_datetime.py +++ b/lib/matplotlib/tests/test_datetime.py @@ -259,11 +259,22 @@ def test_bxp(self): ax.xaxis.set_major_formatter(mpl.dates.DateFormatter("%Y-%m-%d")) ax.set_title('Box plot with datetime data') - @pytest.mark.xfail(reason="Test for clabel not written yet") @mpl.style.context("default") def test_clabel(self): + dates = [datetime.datetime(2023, 10, 1) + datetime.timedelta(days=i) + for i in range(10)] + x = np.arange(-10.0, 5.0, 0.5) + X, Y = np.meshgrid(x, dates) + Z = np.arange(X.size).reshape(X.shape) + fig, ax = plt.subplots() - ax.clabel(...) + CS = ax.contour(X, Y, Z) + labels = ax.clabel(CS, manual=[(x[0], dates[0])]) + assert len(labels) == 1 + assert labels[0].get_text() == '0' + x_pos, y_pos = labels[0].get_position() + assert x_pos == pytest.approx(-10.0, abs=1e-3) + assert y_pos == pytest.approx(mpl.dates.date2num(dates[0]), abs=1e-3) @mpl.style.context("default") def test_contour(self): @@ -642,26 +653,6 @@ def test_plot(self): ax2.plot(range(1, N), x) ax3.plot(x, x) - @mpl.style.context("default") - def test_plot_date(self): - mpl.rcParams["date.converter"] = "concise" - range_threshold = 10 - fig, (ax1, ax2, ax3) = plt.subplots(3, 1, layout="constrained") - - x_dates = np.array( - [datetime.datetime(2023, 10, delta) for delta in range(1, range_threshold)] - ) - y_dates = np.array( - [datetime.datetime(2023, 10, delta) for delta in range(1, range_threshold)] - ) - x_ranges = np.array(range(1, range_threshold)) - y_ranges = np.array(range(1, range_threshold)) - - with pytest.warns(mpl.MatplotlibDeprecationWarning): - ax1.plot_date(x_dates, y_dates) - ax2.plot_date(x_dates, y_ranges) - ax3.plot_date(x_ranges, y_dates) - @pytest.mark.xfail(reason="Test for quiver not written yet") @mpl.style.context("default") def test_quiver(self): @@ -830,11 +821,32 @@ def test_triplot(self): fig, ax = plt.subplots() ax.triplot(...) - @pytest.mark.xfail(reason="Test for violin not written yet") + @pytest.mark.parametrize("orientation", ["vertical", "horizontal"]) @mpl.style.context("default") - def test_violin(self): + def test_violin(self, orientation): fig, ax = plt.subplots() - ax.violin(...) + datetimes = [ + datetime.datetime(2023, 2, 10), + datetime.datetime(2023, 5, 18), + datetime.datetime(2023, 6, 6) + ] + ax.violin( + [ + { + 'coords': datetimes, + 'vals': [0.1, 0.5, 0.2], + 'mean': datetimes[1], + 'median': datetimes[1], + 'min': datetimes[0], + 'max': datetimes[-1], + 'quantiles': datetimes + } + ], + orientation=orientation, + # TODO: It should be possible for positions to be datetimes too + # https://github.com/matplotlib/matplotlib/issues/30417 + # positions=[datetime.datetime(2020, 1, 1)] + ) @pytest.mark.xfail(reason="Test for violinplot not written yet") @mpl.style.context("default") diff --git a/lib/matplotlib/tests/test_determinism.py b/lib/matplotlib/tests/test_determinism.py index 3865dbc7fa43..c0e4adbef40b 100644 --- a/lib/matplotlib/tests/test_determinism.py +++ b/lib/matplotlib/tests/test_determinism.py @@ -8,31 +8,37 @@ import pytest import matplotlib as mpl -import matplotlib.testing.compare from matplotlib import pyplot as plt -from matplotlib.testing._markers import needs_ghostscript, needs_usetex +from matplotlib.cbook import get_sample_data +from matplotlib.collections import PathCollection +from matplotlib.image import BboxImage +from matplotlib.offsetbox import AnchoredOffsetbox, AuxTransformBox +from matplotlib.patches import Circle, PathPatch +from matplotlib.path import Path from matplotlib.testing import subprocess_run_for_testing +from matplotlib.testing._markers import needs_ghostscript, needs_usetex +import matplotlib.testing.compare +from matplotlib.text import TextPath +from matplotlib.transforms import IdentityTransform -def _save_figure(objects='mhi', fmt="pdf", usetex=False): +def _save_figure(objects='mhip', fmt="pdf", usetex=False): mpl.use(fmt) mpl.rcParams.update({'svg.hashsalt': 'asdf', 'text.usetex': usetex}) - fig = plt.figure() - - if 'm' in objects: + def plot_markers(fig): # use different markers... - ax1 = fig.add_subplot(1, 6, 1) + ax = fig.add_subplot() x = range(10) - ax1.plot(x, [1] * 10, marker='D') - ax1.plot(x, [2] * 10, marker='x') - ax1.plot(x, [3] * 10, marker='^') - ax1.plot(x, [4] * 10, marker='H') - ax1.plot(x, [5] * 10, marker='v') + ax.plot(x, [1] * 10, marker='D') + ax.plot(x, [2] * 10, marker='x') + ax.plot(x, [3] * 10, marker='^') + ax.plot(x, [4] * 10, marker='H') + ax.plot(x, [5] * 10, marker='v') - if 'h' in objects: + def plot_hatch(fig): # also use different hatch patterns - ax2 = fig.add_subplot(1, 6, 2) + ax2 = fig.add_subplot() bars = (ax2.bar(range(1, 5), range(1, 5)) + ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5))) ax2.set_xticks([1.5, 2.5, 3.5, 4.5]) @@ -41,17 +47,104 @@ def _save_figure(objects='mhi', fmt="pdf", usetex=False): for bar, pattern in zip(bars, patterns): bar.set_hatch(pattern) - if 'i' in objects: + def plot_image(fig): + axs = fig.subplots(1, 3, sharex=True, sharey=True) # also use different images A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]] - fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest') + axs[0].imshow(A, interpolation='nearest') A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]] - fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear') + axs[1].imshow(A, interpolation='bilinear') A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]] - fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic') + axs[2].imshow(A, interpolation='bicubic') + + def plot_paths(fig): + # clipping support class, copied from demo_text_path.py gallery example + class PathClippedImagePatch(PathPatch): + """ + The given image is used to draw the face of the patch. Internally, + it uses BboxImage whose clippath set to the path of the patch. + + FIXME : The result is currently dpi dependent. + """ + + def __init__(self, path, bbox_image, **kwargs): + super().__init__(path, **kwargs) + self.bbox_image = BboxImage( + self.get_window_extent, norm=None, origin=None) + self.bbox_image.set_data(bbox_image) + + def set_facecolor(self, color): + """Simply ignore facecolor.""" + super().set_facecolor("none") + + def draw(self, renderer=None): + # the clip path must be updated every draw. any solution? -JJ + self.bbox_image.set_clip_path(self._path, self.get_transform()) + self.bbox_image.draw(renderer) + super().draw(renderer) + + subfigs = fig.subfigures(1, 3) + + # add a polar projection + px = subfigs[0].add_subplot(projection="polar") + pimg = px.imshow([[2]]) + pimg.set_clip_path(Circle((0, 1), radius=0.3333)) + + # add a text-based clipping path (origin: demo_text_path.py) + ax = subfigs[1].add_subplot() + arr = plt.imread(get_sample_data("grace_hopper.jpg")) + text_path = TextPath((0, 0), "!?", size=150) + p = PathClippedImagePatch(text_path, arr, ec="k") + offsetbox = AuxTransformBox(IdentityTransform()) + offsetbox.add_artist(p) + ao = AnchoredOffsetbox(loc='upper left', child=offsetbox, frameon=True, + borderpad=0.2) + ax.add_artist(ao) + + # add a 2x2 grid of path-clipped axes (origin: test_artist.py) + exterior = Path.unit_rectangle().deepcopy() + exterior.vertices *= 4 + exterior.vertices -= 2 + interior = Path.unit_circle().deepcopy() + interior.vertices = interior.vertices[::-1] + clip_path = Path.make_compound_path(exterior, interior) + + star = Path.unit_regular_star(6).deepcopy() + star.vertices *= 2.6 + + (row1, row2) = subfigs[2].subplots(2, 2, sharex=True, sharey=True, + gridspec_kw=dict(hspace=0, wspace=0)) + for row in (row1, row2): + ax1, ax2 = row + collection = PathCollection([star], lw=5, edgecolor='blue', + facecolor='red', alpha=0.7, hatch='*') + collection.set_clip_path(clip_path, ax1.transData) + ax1.add_collection(collection) + + patch = PathPatch(star, lw=5, edgecolor='blue', facecolor='red', + alpha=0.7, hatch='*') + patch.set_clip_path(clip_path, ax2.transData) + ax2.add_patch(patch) + + ax1.set_xlim([-3, 3]) + ax1.set_ylim([-3, 3]) + + nfigs = len(objects) + 1 + fig = plt.figure(figsize=(7, 3 * nfigs)) + subfigs = iter(fig.subfigures(nfigs, squeeze=False).flat) + fig.subplots_adjust(bottom=0.15) + + if 'm' in objects: + plot_markers(next(subfigs)) + if 'h' in objects: + plot_hatch(next(subfigs)) + if 'i' in objects: + plot_image(next(subfigs)) + if 'p' in objects: + plot_paths(next(subfigs)) x = range(5) - ax = fig.add_subplot(1, 6, 6) + ax = next(subfigs).add_subplot() ax.plot(x, x) ax.set_title('A string $1+2+\\sigma$') ax.set_xlabel('A string $1+2+\\sigma$') @@ -67,24 +160,23 @@ def _save_figure(objects='mhi', fmt="pdf", usetex=False): ("m", "pdf", False), ("h", "pdf", False), ("i", "pdf", False), - ("mhi", "pdf", False), - ("mhi", "ps", False), - pytest.param( - "mhi", "ps", True, marks=[needs_usetex, needs_ghostscript]), - ("mhi", "svg", False), - pytest.param("mhi", "svg", True, marks=needs_usetex), + ("mhip", "pdf", False), + ("mhip", "ps", False), + pytest.param("mhip", "ps", True, marks=[needs_usetex, needs_ghostscript]), + ("p", "svg", False), + ("mhip", "svg", False), + pytest.param("mhip", "svg", True, marks=needs_usetex), ] ) def test_determinism_check(objects, fmt, usetex): """ - Output three times the same graphs and checks that the outputs are exactly - the same. + Output the same graph three times and check that the outputs are exactly the same. Parameters ---------- objects : str Objects to be included in the test document: 'm' for markers, 'h' for - hatch patterns, 'i' for images. + hatch patterns, 'i' for images, and 'p' for paths. fmt : {"pdf", "ps", "svg"} Output format. """ @@ -118,10 +210,11 @@ def test_determinism_check(objects, fmt, usetex): ) def test_determinism_source_date_epoch(fmt, string): """ - Test SOURCE_DATE_EPOCH support. Output a document with the environment - variable SOURCE_DATE_EPOCH set to 2000-01-01 00:00 UTC and check that the - document contains the timestamp that corresponds to this date (given as an - argument). + Test SOURCE_DATE_EPOCH support. + + Output a document with the environment variable SOURCE_DATE_EPOCH set to + 2000-01-01 00:00 UTC and check that the document contains the timestamp that + corresponds to this date (given as an argument). Parameters ---------- diff --git a/lib/matplotlib/tests/test_doc.py b/lib/matplotlib/tests/test_doc.py index 3e28fd1b8eb7..f3d6d6e3fd5d 100644 --- a/lib/matplotlib/tests/test_doc.py +++ b/lib/matplotlib/tests/test_doc.py @@ -7,9 +7,9 @@ def test_sphinx_gallery_example_header(): This test monitors that the version we have copied is still the same as the EXAMPLE_HEADER in sphinx-gallery. If sphinx-gallery changes its EXAMPLE_HEADER, this test will start to fail. In that case, please update - the monkey-patching of EXAMPLE_HEADER in conf.py. + the monkey-patching of EXAMPLE_HEADER in sphinxext/util.py. """ - pytest.importorskip('sphinx_gallery', minversion='0.16.0') + pytest.importorskip('sphinx_gallery', minversion='0.20.0') from sphinx_gallery import gen_rst EXAMPLE_HEADER = """ @@ -25,7 +25,7 @@ def test_sphinx_gallery_example_header(): :class: sphx-glr-download-link-note :ref:`Go to the end ` - to download the full example code.{2} + to download the full example code{2} .. rst-class:: sphx-glr-example-title diff --git a/lib/matplotlib/tests/test_dviread.py b/lib/matplotlib/tests/test_dviread.py index 7b7ff151be18..3581a97c1390 100644 --- a/lib/matplotlib/tests/test_dviread.py +++ b/lib/matplotlib/tests/test_dviread.py @@ -1,8 +1,10 @@ import json from pathlib import Path import shutil +import sys -import matplotlib.dviread as dr +from matplotlib import cbook, dviread as dr +from matplotlib.testing import subprocess_run_for_testing, _has_tex_package import pytest @@ -60,18 +62,88 @@ def test_PsfontsMap(monkeypatch): fontmap[b'%'] -@pytest.mark.skipif(shutil.which("kpsewhich") is None, +@pytest.mark.skipif(sys.platform == "emscripten" or shutil.which("kpsewhich") is None, reason="kpsewhich is not available") -def test_dviread(): - dirpath = Path(__file__).parent / 'baseline_images/dviread' - with (dirpath / 'test.json').open() as f: - correct = json.load(f) - with dr.Dvi(str(dirpath / 'test.dvi'), None) as dvi: - data = [{'text': [[t.x, t.y, - chr(t.glyph), - t.font.texname.decode('ascii'), - round(t.font.size, 2)] - for t in page.text], - 'boxes': [[b.x, b.y, b.height, b.width] for b in page.boxes]} - for page in dvi] +@pytest.mark.parametrize("engine", ["pdflatex", "xelatex", "lualatex"]) +def test_dviread(tmp_path, engine, monkeypatch): + dirpath = Path(__file__).parent / "baseline_images/dviread" + shutil.copy(dirpath / "test.tex", tmp_path) + shutil.copy(cbook._get_data_path("fonts/ttf/DejaVuSans.ttf"), tmp_path) + cmd, fmt = { + "pdflatex": (["latex", "-no-shell-escape"], "dvi"), + "xelatex": (["xelatex", "-no-pdf", "-no-shell-escape"], "xdv"), + "lualatex": (["lualatex", "-output-format=dvi", "-no-shell-escape"], "dvi"), + }[engine] + if shutil.which(cmd[0]) is None: + pytest.skip(f"{cmd[0]} is not available") + subprocess_run_for_testing( + [*cmd, "test.tex"], cwd=tmp_path, check=True, capture_output=True) + # dviread must be run from the tmppath directory because {xe,lua}tex output + # records the path to DejaVuSans.ttf as it is written in the tex source, + # i.e. as a relative path. + monkeypatch.chdir(tmp_path) + with dr.Dvi(tmp_path / f"test.{fmt}", None) as dvi: + try: + pages = [*dvi] + except FileNotFoundError as exc: + for note in getattr(exc, "__notes__", []): + if "too-old version of luaotfload" in note: + pytest.skip(note) + raise + data = [ + { + "text": [ + [ + t.x, t.y, + t._as_unicode_or_name(), + t.font.resolve_path().name, + round(t.font.size, 2), + t.font.effects, + ] for t in page.text + ], + "boxes": [[b.x, b.y, b.height, b.width] for b in page.boxes] + } for page in pages + ] + correct = json.loads((dirpath / f"{engine}.json").read_text()) + assert data == correct + + +@pytest.mark.skipif(shutil.which("latex") is None, reason="latex is not available") +@pytest.mark.skipif(not _has_tex_package("concmath"), reason="needs concmath.sty") +def test_dviread_pk(tmp_path): + (tmp_path / "test.tex").write_text(r""" + \documentclass{article} + \usepackage{concmath} + \pagestyle{empty} + \begin{document} + Hi! + \end{document} + """) + subprocess_run_for_testing( + ["latex", "-no-shell-escape", "test.tex"], + cwd=tmp_path, check=True, capture_output=True) + with dr.Dvi(tmp_path / "test.dvi", None) as dvi: + pages = [*dvi] + data = [ + { + "text": [ + [ + t.x, t.y, + t._as_unicode_or_name(), + t.font.resolve_path().name, + round(t.font.size, 2), + t.font.effects, + ] for t in page.text + ], + "boxes": [[b.x, b.y, b.height, b.width] for b in page.boxes] + } for page in pages + ] + correct = [{ + 'boxes': [], + 'text': [ + [5046272, 4128768, 'H?', 'ccr10.600pk', 9.96, {}], + [5530510, 4128768, 'i?', 'ccr10.600pk', 9.96, {}], + [5716195, 4128768, '!?', 'ccr10.600pk', 9.96, {}], + ], + }] assert data == correct diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 58aecd3dea8b..0e318cab0d4f 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -3,6 +3,7 @@ import io import pickle import platform +import sys from threading import Timer from types import SimpleNamespace import warnings @@ -25,7 +26,7 @@ import matplotlib.dates as mdates -@image_comparison(['figure_align_labels'], extensions=['png', 'svg'], +@image_comparison(['figure_align_labels'], extensions=['png', 'svg'], style='mpl20', tol=0 if platform.machine() == 'x86_64' else 0.01) def test_align_labels(): fig = plt.figure(layout='tight') @@ -68,8 +69,7 @@ def test_align_labels(): @image_comparison(['figure_align_titles_tight.png', 'figure_align_titles_constrained.png'], - tol=0 if platform.machine() == 'x86_64' else 0.022, - style='mpl20') + style='mpl20', tol=0 if platform.machine() == 'x86_64' else 0.021) def test_align_titles(): for layout in ['tight', 'constrained']: fig, axs = plt.subplots(1, 2, layout=layout, width_ratios=[2, 1]) @@ -147,8 +147,29 @@ def test_figure_label(): assert plt.get_figlabels() == ['', 'today'] plt.figure(fig_today) assert plt.gcf() == fig_today - with pytest.raises(ValueError): - plt.figure(Figure()) + + +def test_figure_label_replaced(): + plt.close('all') + fig = plt.figure(1) + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match="Changing 'Figure.number' is deprecated"): + fig.number = 2 + assert fig.number == 2 + + +def test_figure_no_label(): + # standalone figures do not have a figure attribute + fig = Figure() + with pytest.raises(AttributeError): + fig.number + # but one can set one + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match="Changing 'Figure.number' is deprecated"): + fig.number = 5 + assert fig.number == 5 + # even though it's not known by pyplot + assert not plt.fignum_exists(fig.number) def test_fignum_exists(): @@ -186,8 +207,8 @@ def test_clf_keyword(): assert [t.get_text() for t in fig2.texts] == [] -@image_comparison(['figure_today'], - tol=0.015 if platform.machine() == 'arm64' else 0) +@image_comparison(['figure_today.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.022) def test_figure(): # named figure support fig = plt.figure('today') @@ -202,7 +223,7 @@ def test_figure(): plt.close('tomorrow') -@image_comparison(['figure_legend']) +@image_comparison(['figure_legend.png'], style='mpl20') def test_figure_legend(): fig, axs = plt.subplots(2) axs[0].plot([0, 1], [1, 0], label='x', color='g') @@ -218,7 +239,7 @@ def test_gca(): fig = plt.figure() # test that gca() picks up Axes created via add_axes() - ax0 = fig.add_axes([0, 0, 1, 1]) + ax0 = fig.add_axes((0, 0, 1, 1)) assert fig.gca() is ax0 # test that gca() picks up Axes created via add_subplot() @@ -299,7 +320,7 @@ def test_add_subplot_invalid(): fig.add_subplot(ax) -@image_comparison(['figure_suptitle']) +@image_comparison(['figure_suptitle.png'], style='mpl20') def test_suptitle(): fig, _ = plt.subplots() fig.suptitle('hello', color='r') @@ -341,6 +362,28 @@ def test_get_suptitle_supxlabel_supylabel(): assert fig.get_supylabel() == 'supylabel' +def test_remove_suptitle_supxlabel_supylabel(): + fig = plt.figure() + + title = fig.suptitle('suptitle') + xlabel = fig.supxlabel('supxlabel') + ylabel = fig.supylabel('supylabel') + + assert len(fig.texts) == 3 + assert fig._suptitle is not None + assert fig._supxlabel is not None + assert fig._supylabel is not None + + title.remove() + assert fig._suptitle is None + xlabel.remove() + assert fig._supxlabel is None + ylabel.remove() + assert fig._supylabel is None + + assert not fig.texts + + @image_comparison(['alpha_background'], # only test png and svg. The PDF output appears correct, # but Ghostscript does not preserve the background color. @@ -473,6 +516,20 @@ def test_autofmt_xdate(which): assert int(label.get_rotation()) == angle +def test_autofmt_xdate_colorbar_constrained(): + # check works with a colorbar. + # with constrained layout, colorbars do not have a gridspec, + # but autofmt_xdate checks if all axes have a gridspec before being + # applied. + fig, ax = plt.subplots(layout="constrained") + im = ax.imshow([[1, 4, 6], [2, 3, 5]]) + plt.colorbar(im) + fig.autofmt_xdate() + fig.draw_without_rendering() + label = ax.get_xticklabels(which='major')[1] + assert label.get_rotation() == 30.0 + + @mpl.style.context('default') def test_change_dpi(): fig = plt.figure(figsize=(4, 4)) @@ -509,7 +566,7 @@ def test_invalid_figure_add_axes(): fig.add_axes((.1, .1, .5, np.nan)) with pytest.raises(TypeError, match="multiple values for argument 'rect'"): - fig.add_axes([0, 0, 1, 1], rect=[0, 0, 1, 1]) + fig.add_axes((0, 0, 1, 1), rect=[0, 0, 1, 1]) fig2, ax = plt.subplots() with pytest.raises(ValueError, @@ -518,13 +575,11 @@ def test_invalid_figure_add_axes(): fig.add_axes(ax) fig2.delaxes(ax) - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="Passing more than one positional argument"): + with pytest.raises(TypeError, match=r"add_axes\(\) takes 1 positional arguments"): fig2.add_axes(ax, "extra positional argument") - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="Passing more than one positional argument"): - fig.add_axes([0, 0, 1, 1], "extra positional argument") + with pytest.raises(TypeError, match=r"add_axes\(\) takes 1 positional arguments"): + fig.add_axes((0, 0, 1, 1), "extra positional argument") def test_subplots_shareax_loglabels(): @@ -614,7 +669,7 @@ def test_savefig_locate_colorbar(): @mpl.rc_context({"savefig.transparent": True}) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_savefig_transparent(fig_test, fig_ref): # create two transparent subfigures with corresponding transparent inset # axes. the entire background of the image should be transparent. @@ -707,7 +762,7 @@ def test_invalid_layouts(): fig.set_layout_engine("constrained") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_tightlayout_autolayout_deconflict(fig_test, fig_ref): for fig, autolayout in zip([fig_ref, fig_test], [False, True]): with mpl.rc_context({'figure.autolayout': autolayout}): @@ -779,7 +834,7 @@ def test_tightbbox(): ax.set_xlim(0, 1) t = ax.text(1., 0.5, 'This dangles over end') renderer = fig.canvas.get_renderer() - x1Nom0 = 9.035 # inches + x1Nom0 = 8.9875 # inches assert abs(t.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(fig.get_tightbbox(renderer).x1 - x1Nom0) < 0.05 @@ -967,7 +1022,7 @@ def test_animated_with_canvas_change(fig_test, fig_ref): class TestSubplotMosaic: - @check_figures_equal(extensions=["png"]) + @check_figures_equal() @pytest.mark.parametrize( "x", [ [["A", "A", "B"], ["C", "D", "B"]], @@ -999,7 +1054,7 @@ def test_basic(self, fig_test, fig_ref, x): axD = fig_ref.add_subplot(gs[1, 1]) axD.set_title(labels[3]) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_all_nested(self, fig_test, fig_ref): x = [["A", "B"], ["C", "D"]] y = [["E", "F"], ["G", "H"]] @@ -1022,7 +1077,7 @@ def test_all_nested(self, fig_test, fig_ref): for k, label in enumerate(r): fig_ref.add_subplot(gs_right[j, k]).set_title(label) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_nested(self, fig_test, fig_ref): fig_ref.set_layout_engine("constrained") @@ -1056,7 +1111,7 @@ def test_nested(self, fig_test, fig_ref): axF = fig_ref.add_subplot(gs[0, 0]) axF.set_title("F") - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_nested_tuple(self, fig_test, fig_ref): x = [["A", "B", "B"], ["C", "C", "D"]] xt = (("A", "B", "B"), ("C", "C", "D")) @@ -1084,7 +1139,7 @@ def test_nested_height_ratios(self): assert axd["D"].get_gridspec().get_height_ratios() == height_ratios assert axd["B"].get_gridspec().get_height_ratios() != height_ratios - @check_figures_equal(extensions=["png"]) + @check_figures_equal() @pytest.mark.parametrize( "x, empty_sentinel", [ @@ -1129,7 +1184,7 @@ def test_fail_list_of_str(self): with pytest.raises(ValueError, match='must be 2D'): plt.subplot_mosaic([['a', 'b'], [('a', 'b'), 'c']]) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() @pytest.mark.parametrize("subplot_kw", [{}, {"projection": "polar"}, None]) def test_subplot_kw(self, fig_test, fig_ref, subplot_kw): x = [[1, 2]] @@ -1141,7 +1196,7 @@ def test_subplot_kw(self, fig_test, fig_ref, subplot_kw): axB = fig_ref.add_subplot(gs[0, 1], **subplot_kw) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() @pytest.mark.parametrize("multi_value", ['BC', tuple('BC')]) def test_per_subplot_kw(self, fig_test, fig_ref, multi_value): x = 'AB;CD' @@ -1196,7 +1251,7 @@ def test_extra_per_subplot_kw(self): ): Figure().subplot_mosaic("A", per_subplot_kw={"B": {}}) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() @pytest.mark.parametrize("str_pattern", ["AAA\nBBB", "\nAAA\nBBB\n", "ABC\nDEF"] ) @@ -1233,7 +1288,7 @@ def test_fail(self, x, match): with pytest.raises(ValueError, match=match): fig.subplot_mosaic(x) - @check_figures_equal(extensions=["png"]) + @check_figures_equal() def test_hashable_keys(self, fig_test, fig_ref): fig_test.subplot_mosaic([[object(), object()]]) fig_ref.subplot_mosaic([["A", "B"]]) @@ -1341,7 +1396,8 @@ def test_subfigure_dpi(): @image_comparison(['test_subfigure_ss.png'], style='mpl20', - savefig_kwarg={'facecolor': 'teal'}, tol=0.02) + savefig_kwarg={'facecolor': 'teal'}, + tol=0.022 if sys.platform == 'darwin' else 0) def test_subfigure_ss(): # test assigning the subfigure via subplotspec np.random.seed(19680801) @@ -1516,6 +1572,7 @@ def test_subfigures_wspace_hspace(): def test_subfigure_remove(): fig = plt.figure() sfs = fig.subfigures(2, 2) + sfs[1, 1].subplots() sfs[1, 1].remove() assert len(fig.subfigs) == 3 @@ -1548,28 +1605,30 @@ def test_add_subplot_kwargs(): def test_add_axes_kwargs(): # fig.add_axes() always creates new axes, even if axes kwargs differ. fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1]) - ax1 = fig.add_axes([0, 0, 1, 1]) + ax = fig.add_axes((0, 0, 1, 1)) + ax1 = fig.add_axes((0, 0, 1, 1)) assert ax is not None assert ax1 is not ax plt.close() fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1], projection='polar') - ax1 = fig.add_axes([0, 0, 1, 1], projection='polar') + ax = fig.add_axes((0, 0, 1, 1), projection='polar') + ax1 = fig.add_axes((0, 0, 1, 1), projection='polar') assert ax is not None assert ax1 is not ax plt.close() fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1], projection='polar') - ax1 = fig.add_axes([0, 0, 1, 1]) + ax = fig.add_axes((0, 0, 1, 1), projection='polar') + ax1 = fig.add_axes((0, 0, 1, 1)) assert ax is not None assert ax1.name == 'rectilinear' assert ax1 is not ax plt.close() +@pytest.mark.skipif(sys.platform == 'emscripten', + reason='emscripten does not support threads') def test_ginput(recwarn): # recwarn undoes warn filters at exit. warnings.filterwarnings("ignore", "cannot show the figure") fig, ax = plt.subplots() @@ -1592,6 +1651,8 @@ def multi_presses(): np.testing.assert_allclose(fig.ginput(3), [(.3, .4), (.5, .6)]) +@pytest.mark.skipif(sys.platform == 'emscripten', + reason='emscripten does not support threads') def test_waitforbuttonpress(recwarn): # recwarn undoes warn filters at exit. warnings.filterwarnings("ignore", "cannot show the figure") fig = plt.figure() @@ -1610,7 +1671,7 @@ def test_kwargs_pass(): assert sub_fig.get_label() == 'sub figure' -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_rcparams(fig_test, fig_ref): fig_ref.supxlabel("xlabel", weight='bold', size=15) fig_ref.supylabel("ylabel", weight='bold', size=15) @@ -1655,6 +1716,9 @@ def test_unpickle_with_device_pixel_ratio(): assert fig.dpi == 42*7 fig2 = pickle.loads(pickle.dumps(fig)) assert fig2.dpi == 42 + assert all( + [orig / 7 == restore for orig, restore in zip(fig.bbox.max, fig2.bbox.max)] + ) def test_gridspec_no_mutate_input(): @@ -1733,3 +1797,106 @@ def test_warn_colorbar_mismatch(): subfig3_1.colorbar(im3_2) # should not warn with pytest.warns(UserWarning, match="different Figure"): subfig3_1.colorbar(im4_1) + + +def test_clf_subplotpars(): + keys = ('left', 'right', 'bottom', 'top', 'wspace', 'hspace') + rc_params = {key: plt.rcParams['figure.subplot.' + key] for key in keys} + + fig = plt.figure(1) + fig.subplots_adjust(**{k: v+0.01 for k, v in rc_params.items()}) + fig.clf() + assert fig.subplotpars.to_dict() == rc_params + + +def test_suplots_adjust_incremental(): + fig = plt.figure() + fig.subplots_adjust(left=0) + fig.subplots_adjust(right=1) + assert fig.subplotpars.left == 0 + assert fig.subplotpars.right == 1 + + +def test_set_figure(): + fig = plt.figure() + sfig1 = fig.subfigures() + sfig2 = sfig1.subfigures() + + for f in fig, sfig1, sfig2: + with pytest.warns(mpl.MatplotlibDeprecationWarning): + f.set_figure(fig) + + with pytest.raises(ValueError, match="cannot be changed"): + sfig2.set_figure(sfig1) + + with pytest.raises(ValueError, match="cannot be changed"): + sfig1.set_figure(plt.figure()) + + +def test_subfigure_row_order(): + # Test that subfigures are drawn in row-major order. + fig = plt.figure() + sf_arr = fig.subfigures(4, 3) + for a, b in zip(sf_arr.ravel(), fig.subfigs): + assert a is b + + +def test_subfigure_stale_propagation(): + fig = plt.figure() + + fig.draw_without_rendering() + assert not fig.stale + + sfig1 = fig.subfigures() + assert fig.stale + + fig.draw_without_rendering() + assert not fig.stale + assert not sfig1.stale + + sfig2 = sfig1.subfigures() + assert fig.stale + assert sfig1.stale + + fig.draw_without_rendering() + assert not fig.stale + assert not sfig1.stale + assert not sfig2.stale + + sfig2.stale = True + assert sfig1.stale + assert fig.stale + + +@pytest.mark.parametrize("figsize, figsize_inches", [ + ((6, 4), (6, 4)), + ((6, 4, "in"), (6, 4)), + ((5.08, 2.54, "cm"), (2, 1)), + ((600, 400, "px"), (6, 4)), +]) +def test_figsize(figsize, figsize_inches): + fig = plt.figure(figsize=figsize, dpi=100) + assert tuple(fig.get_size_inches()) == figsize_inches + + +def test_figsize_partial_none(): + default_w, default_h = mpl.rcParams["figure.figsize"] + + fig = plt.figure(figsize=(None, 4)) + w, h = fig.get_size_inches() + assert (w, h) == (default_w, 4) + + fig = plt.figure(figsize=(6, None)) + w, h = fig.get_size_inches() + assert (w, h) == (6, default_h) + + +def test_figsize_both_none(): + with pytest.raises(ValueError, + match=r"figsize=\(None, None\) is invalid"): + plt.figure(figsize=(None, None)) + + +def test_figsize_invalid_unit(): + with pytest.raises(ValueError, match="Invalid unit 'um'"): + plt.figure(figsize=(6, 4, "um")) diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 2dc530bf984b..ab6c1d91b106 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -1,4 +1,4 @@ -from io import BytesIO, StringIO +from io import BytesIO import gc import multiprocessing import os @@ -11,15 +11,52 @@ import numpy as np import pytest +from unittest.mock import MagicMock, patch + +import matplotlib as mpl +import matplotlib.font_manager as fm_mod from matplotlib.font_manager import ( - findfont, findSystemFonts, FontEntry, FontProperties, fontManager, + findfont, findSystemFonts, FontEntry, FontPath, FontProperties, fontManager, json_dump, json_load, get_font, is_opentype_cff_font, - MSUserFontDirectories, _get_fontconfig_fonts, ttfFontProperty) + MSUserFontDirectories, ttfFontProperty, _get_font_alt_names, + _get_fontconfig_fonts, _normalize_weight) from matplotlib import cbook, ft2font, pyplot as plt, rc_context, figure as mfigure -from matplotlib.testing import subprocess_run_helper - - -has_fclist = shutil.which('fc-list') is not None +from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing + + +has_fclist = sys.platform != 'emscripten' and shutil.which('fc-list') is not None + + +def test_font_path(): + fp = FontPath('foo', 123) + fp2 = FontPath('foo', 321) + assert str(fp) == 'foo' + assert repr(fp) == "FontPath('foo', 123)" + assert fp.path == 'foo' + assert fp.face_index == 123 + # Should be immutable. + with pytest.raises(AttributeError, match='has no setter'): + fp.path = 'bar' + with pytest.raises(AttributeError, match='has no setter'): + fp.face_index = 321 + # Should be comparable with str and itself. + assert fp == 'foo' + assert fp == FontPath('foo', 123) + assert fp <= fp + assert fp >= fp + assert fp != fp2 + assert fp < fp2 + assert fp <= fp2 + assert fp2 > fp + assert fp2 >= fp + # Should be hashable, but not the same as str. + d = {fp: 1, 'bar': 2} + assert fp in d + assert d[fp] == 1 + assert d[FontPath('foo', 123)] == 1 + assert fp2 not in d + assert 'foo' not in d + assert FontPath('bar', 0) not in d def test_font_priority(): @@ -65,12 +102,14 @@ def test_json_serialization(tmp_path): def test_otf(): fname = '/usr/share/fonts/opentype/freefont/FreeMono.otf' if Path(fname).exists(): - assert is_opentype_cff_font(fname) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert is_opentype_cff_font(fname) for f in fontManager.ttflist: if 'otf' in f.fname: with open(f.fname, 'rb') as fd: res = fd.read(4) == b'OTTO' - assert res == is_opentype_cff_font(f.fname) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert res == is_opentype_cff_font(f.fname) @pytest.mark.skipif(sys.platform == "win32" or not has_fclist, @@ -113,8 +152,17 @@ def test_utf16m_sfnt(): def test_find_ttc(): fp = FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(findfont(fp)).name != "wqy-zenhei.ttc": + fontpath = findfont(fp) + if Path(fontpath).name != "wqy-zenhei.ttc": pytest.skip("Font wqy-zenhei.ttc may be missing") + # All fonts from this collection should have loaded as well. + for name in ["WenQuanYi Zen Hei Mono", "WenQuanYi Zen Hei Sharp"]: + subfontpath = findfont(FontProperties(family=[name]), fallback_to_default=False) + assert subfontpath.path == fontpath.path + assert subfontpath.face_index != fontpath.face_index + subfont = get_font(subfontpath) + assert subfont.fname == subfontpath.path + assert subfont.face_index == subfontpath.face_index fig, ax = plt.subplots() ax.text(.5, .5, "\N{KANGXI RADICAL DRAGON}", fontproperties=fp) for fmt in ["raw", "svg", "pdf", "ps"]: @@ -133,6 +181,34 @@ def test_find_noto(): fig.savefig(BytesIO(), format=fmt) +def test_find_valid(): + class PathLikeClass: + def __init__(self, filename): + self.filename = filename + + def __fspath__(self): + return self.filename + + file_str = findfont('DejaVu Sans') + file_bytes = os.fsencode(file_str) + + font = get_font(file_str) + assert font.fname == file_str + font = get_font(file_bytes) + assert font.fname == file_bytes + font = get_font(PathLikeClass(file_str)) + assert font.fname == file_str + font = get_font(PathLikeClass(file_bytes)) + assert font.fname == file_bytes + font = get_font(FontPath(file_str, 0)) + assert font.fname == file_str + + # Note, fallbacks are not currently accessible. + font = get_font([file_str, file_bytes, + PathLikeClass(file_str), PathLikeClass(file_bytes)]) + assert font.fname == file_str + + def test_find_invalid(tmp_path): with pytest.raises(FileNotFoundError): @@ -144,11 +220,6 @@ def test_find_invalid(tmp_path): with pytest.raises(FileNotFoundError): get_font(bytes(tmp_path / 'non-existent-font-name.ttf')) - # Not really public, but get_font doesn't expose non-filename constructor. - from matplotlib.ft2font import FT2Font - with pytest.raises(TypeError, match='font file or a binary-mode file'): - FT2Font(StringIO()) # type: ignore[arg-type] - @pytest.mark.skipif(sys.platform != 'linux' or not has_fclist, reason='only Linux with fontconfig installed') @@ -163,7 +234,7 @@ def test_user_fonts_linux(tmpdir, monkeypatch): # Prepare a temporary user font directory user_fonts_dir = tmpdir.join('fonts') user_fonts_dir.ensure(dir=True) - shutil.copyfile(Path(__file__).parent / font_test_file, + shutil.copyfile(Path(__file__).parent / 'data' / font_test_file, user_fonts_dir.join(font_test_file)) with monkeypatch.context() as m: @@ -180,11 +251,11 @@ def test_user_fonts_linux(tmpdir, monkeypatch): def test_addfont_as_path(): """Smoke test that addfont() accepts pathlib.Path.""" font_test_file = 'mpltest.ttf' - path = Path(__file__).parent / font_test_file + path = Path(__file__).parent / 'data' / font_test_file try: fontManager.addfont(path) - added, = [font for font in fontManager.ttflist - if font.fname.endswith(font_test_file)] + added, = (font for font in fontManager.ttflist + if font.fname.endswith(font_test_file)) fontManager.ttflist.remove(added) finally: to_remove = [font for font in fontManager.ttflist @@ -214,7 +285,7 @@ def test_user_fonts_win32(): os.makedirs(user_fonts_dir) # Copy the test font to the user font directory - shutil.copy(Path(__file__).parent / font_test_file, user_fonts_dir) + shutil.copy(Path(__file__).parent / 'data' / font_test_file, user_fonts_dir) # Now, the font should be available fonts = findSystemFonts() @@ -227,6 +298,8 @@ def _model_handler(_): plt.close() +@pytest.mark.skipif(sys.platform == 'emscripten', + reason='emscripten does not support subprocesses') @pytest.mark.skipif(not hasattr(os, "register_at_fork"), reason="Cannot register at_fork handlers") def test_fork(): @@ -250,7 +323,7 @@ def test_missing_family(caplog): def _test_threading(): import threading - from matplotlib.ft2font import LOAD_NO_HINTING + from matplotlib.ft2font import LoadFlags import matplotlib.font_manager as fm def loud_excepthook(args): @@ -265,7 +338,7 @@ def bad_idea(n): b.wait(timeout=5) for j in range(100): font = fm.get_font(fm.findfont("DejaVu Sans")) - font.set_text(str(n), 0.0, flags=LOAD_NO_HINTING) + font.set_text(str(n), 0.0, flags=LoadFlags.NO_HINTING) threads = [ threading.Thread(target=bad_idea, name=f"bad_thread_{j}", args=(j,)) @@ -287,6 +360,28 @@ def test_fontcache_thread_safe(): subprocess_run_helper(_test_threading, timeout=10) +def test_lockfilefailure(tmp_path): + # The logic here: + # 1. get a temp directory from pytest + # 2. import matplotlib which makes sure it exists + # 3. get the cache dir (where we check it is writable) + # 4. make it not writable + # 5. try to write into it via font manager + proc = subprocess_run_for_testing( + [ + sys.executable, + "-c", + "import matplotlib;" + "import os;" + "p = matplotlib.get_cachedir();" + "os.chmod(p, 0o555);" + "import matplotlib.font_manager;" + ], + env={**os.environ, 'MPLCONFIGDIR': str(tmp_path)}, + check=True + ) + + def test_fontentry_dataclass(): fontent = FontEntry(name='font-name') @@ -310,19 +405,145 @@ def test_get_font_names(): paths_mpl = [cbook._get_data_path('fonts', subdir) for subdir in ['ttf']] fonts_mpl = findSystemFonts(paths_mpl, fontext='ttf') fonts_system = findSystemFonts(fontext='ttf') - ttf_fonts = [] + ttf_fonts = set() for path in fonts_mpl + fonts_system: try: font = ft2font.FT2Font(path) prop = ttfFontProperty(font) - ttf_fonts.append(prop.name) + ttf_fonts.add(prop.name) + for face_index in range(1, font.num_faces): + font = ft2font.FT2Font(path, face_index=face_index) + prop = ttfFontProperty(font) + ttf_fonts.add(prop.name) except Exception: pass - available_fonts = sorted(list(set(ttf_fonts))) - mpl_font_names = sorted(fontManager.get_font_names()) - assert set(available_fonts) == set(mpl_font_names) - assert len(available_fonts) == len(mpl_font_names) - assert available_fonts == mpl_font_names + # fontManager may contain additional entries for alternative family names + # (e.g. typographic family, platform-specific Name ID 1) registered by + # addfont(), so primary names must be a subset of the manager's names. + assert ttf_fonts <= set(fontManager.get_font_names()) + + +def test_addfont_alternative_names(tmp_path): + """ + Fonts that advertise different family names across platforms or name IDs + should be registered under all of those names so users can address the font + by any of them. + + Two real-world patterns are covered: + + - **MS platform ID 1 differs from Mac platform ID 1** (e.g. Ubuntu Light): + FreeType returns the Mac ID 1 value as ``family_name``; the MS ID 1 + value ("Ubuntu Light") is an equally valid name that users expect to work. + - **Name ID 16 (Typographic Family) differs from ID 1** (older fonts): + some fonts store a broader family name in ID 16. + """ + mac_key = (1, 0, 0) + ms_key = (3, 1, 0x0409) + + # Case 1: MS ID1 differs from Mac ID1 (Ubuntu Light pattern) + # Mac ID1="Test Family" → FreeType family_name (primary) + # MS ID1="Test Family Light" → alternate name users expect to work + ubuntu_style_sfnt = { + (*mac_key, 1): "Test Family".encode("latin-1"), + (*ms_key, 1): "Test Family Light".encode("utf-16-be"), + (*mac_key, 2): "Light".encode("latin-1"), + (*ms_key, 2): "Regular".encode("utf-16-be"), + } + fake_font = MagicMock() + fake_font.get_sfnt.return_value = ubuntu_style_sfnt + + assert _get_font_alt_names(fake_font, "Test Family") == [("Test Family Light", 400)] + assert _get_font_alt_names(fake_font, "Test Family Light") == [ + ("Test Family", 300)] + + # Case 2: ID 16 differs from ID 1 (older typographic-family pattern) + # ID 17 (typographic subfamily) is absent → defaults to weight 400 + id16_sfnt = { + (*mac_key, 1): "Test Family".encode("latin-1"), + (*ms_key, 1): "Test Family".encode("utf-16-be"), + (*ms_key, 16): "Test Family Light".encode("utf-16-be"), + } + fake_font_id16 = MagicMock() + fake_font_id16.get_sfnt.return_value = id16_sfnt + + assert _get_font_alt_names( + fake_font_id16, "Test Family" + ) == [("Test Family Light", 400)] + + # Case 3: all entries agree → no alternates + same_sfnt = { + (*mac_key, 1): "Test Family".encode("latin-1"), + (*ms_key, 1): "Test Family".encode("utf-16-be"), + } + fake_font_same = MagicMock() + fake_font_same.get_sfnt.return_value = same_sfnt + assert _get_font_alt_names(fake_font_same, "Test Family") == [] + + # Case 4: get_sfnt() raises ValueError (e.g. non-SFNT font) → empty list + fake_font_no_sfnt = MagicMock() + fake_font_no_sfnt.get_sfnt.side_effect = ValueError + assert _get_font_alt_names(fake_font_no_sfnt, "Test Family") == [] + + fake_path = str(tmp_path / "fake.ttf") + primary_entry = FontEntry(fname=fake_path, name="Test Family", + style="normal", variant="normal", + weight=300, stretch="normal", size="scalable") + + with patch("matplotlib.font_manager.ft2font.FT2Font", + return_value=fake_font), \ + patch("matplotlib.font_manager.ttfFontProperty", + return_value=primary_entry): + fm_instance = fm_mod.FontManager.__new__(fm_mod.FontManager) + fm_instance.ttflist = [] + fm_instance.afmlist = [] + fm_instance._findfont_cached = MagicMock() + fm_instance._findfont_cached.cache_clear = MagicMock() + fm_instance.addfont(fake_path) + + names = [e.name for e in fm_instance.ttflist] + assert names == ["Test Family", "Test Family Light"] + alt_entry = fm_instance.ttflist[1] + assert alt_entry.weight == 400 + assert alt_entry.style == primary_entry.style + assert alt_entry.fname == primary_entry.fname + + +@pytest.mark.parametrize("subfam,expected", [ + ("Thin", 100), + ("ExtraLight", 200), + ("UltraLight", 200), + ("DemiLight", 350), + ("SemiLight", 350), + ("Light", 300), + ("Book", 380), + ("Regular", 400), + ("Normal", 400), + ("Medium", 500), + ("DemiBold", 600), + ("Demi", 600), + ("SemiBold", 600), + ("ExtraBold", 800), + ("SuperBold", 800), + ("UltraBold", 800), + ("Bold", 700), + ("UltraBlack", 1000), + ("SuperBlack", 1000), + ("ExtraBlack", 1000), + ("Ultra", 1000), + ("Black", 900), + ("Heavy", 900), + ("", 400), # fallback: unrecognised → regular +]) +def test_alt_name_weight_from_subfamily(subfam, expected): + """_get_font_alt_names derives weight from the paired subfamily string.""" + ms_key = (3, 1, 0x0409) + fake_font = MagicMock() + fake_font.get_sfnt.return_value = { + (*ms_key, 1): "Family Alt".encode("utf-16-be"), + (*ms_key, 2): subfam.encode("utf-16-be"), + } + result = _get_font_alt_names(fake_font, "Family") + assert result == [("Family Alt", expected)] def test_donot_cache_tracebacks(): @@ -345,3 +566,82 @@ def inner(): for obj in gc.get_objects(): if isinstance(obj, SomeObject): pytest.fail("object from inner stack still alive") + + +def test_fontproperties_init_deprecation(): + """ + Test the deprecated API of FontProperties.__init__. + + The deprecation does not change behavior, it only adds a deprecation warning + via a decorator. Therefore, the purpose of this test is limited to check + which calls do and do not issue deprecation warnings. Behavior is still + tested via the existing regular tests. + """ + with pytest.warns(mpl.MatplotlibDeprecationWarning): + # multiple positional arguments + FontProperties("Times", "italic") + + with pytest.warns(mpl.MatplotlibDeprecationWarning): + # Mixed positional and keyword arguments + FontProperties("Times", size=10) + + with pytest.warns(mpl.MatplotlibDeprecationWarning): + # passing a family list positionally + FontProperties(["Times"]) + + # still accepted: + FontProperties(family="Times", style="italic") + FontProperties(family="Times") + FontProperties("Times") # works as pattern and family + FontProperties("serif-24:style=oblique:weight=bold") # pattern + + # also still accepted: + # passing as pattern via family kwarg was not covered by the docs but + # historically worked. This is left unchanged for now. + # AFAICT, we cannot detect this: We can determine whether a string + # works as pattern, but that doesn't help, because there are strings + # that are both pattern and family. We would need to identify, whether + # a string is *not* a valid family. + # Since this case is not covered by docs, I've refrained from jumping + # extra hoops to detect this possible API misuse. + FontProperties(family="serif-24:style=oblique:weight=bold") + + +def test_normalize_weights(): + assert _normalize_weight(300) == 300 # passthrough + assert _normalize_weight('ultralight') == 100 + assert _normalize_weight('light') == 200 + assert _normalize_weight('normal') == 400 + assert _normalize_weight('regular') == 400 + assert _normalize_weight('book') == 400 + assert _normalize_weight('medium') == 500 + assert _normalize_weight('roman') == 500 + assert _normalize_weight('semibold') == 600 + assert _normalize_weight('demibold') == 600 + assert _normalize_weight('demi') == 600 + assert _normalize_weight('bold') == 700 + assert _normalize_weight('heavy') == 800 + assert _normalize_weight('extra bold') == 800 + assert _normalize_weight('black') == 900 + with pytest.raises(KeyError): + _normalize_weight('invalid') + + +def test_font_match_warning(caplog): + findfont(FontProperties(family=["DejaVu Sans"], weight=750)) + logs = [rec.message for rec in caplog.records] + assert 'findfont: Failed to find font weight 750, now using 700.' in logs + + +def test_mutable_fontproperty_cache_invalidation(): + fp = FontProperties() + assert findfont(fp).endswith("DejaVuSans.ttf") + fp.set_weight("bold") + assert findfont(fp).endswith("DejaVuSans-Bold.ttf") + + +def test_fontproperty_default_cache_invalidation(): + mpl.rcParams["font.weight"] = "normal" + assert findfont("DejaVu Sans").endswith("DejaVuSans.ttf") + mpl.rcParams["font.weight"] = "bold" + assert findfont("DejaVu Sans").endswith("DejaVuSans-Bold.ttf") diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index 2e2ce673f4b8..8b44792a0c2d 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -1,78 +1,981 @@ -from pathlib import Path +import itertools import io +import os +from pathlib import Path +from typing import cast +import numpy as np import pytest +import matplotlib as mpl from matplotlib import ft2font -from matplotlib.testing.decorators import check_figures_equal +from matplotlib.testing import _gen_multi_font_text +from matplotlib.testing.decorators import image_comparison import matplotlib.font_manager as fm +import matplotlib.path as mpath import matplotlib.pyplot as plt -def test_fallback_errors(): - file_name = fm.findfont('DejaVu Sans') +def test_ft2image_draw_rect_filled(): + width = 23 + height = 42 + for x0, y0, x1, y1 in itertools.product([1, 100], [2, 200], [4, 400], [8, 800]): + with pytest.warns(mpl.MatplotlibDeprecationWarning): + im = ft2font.FT2Image(width, height) + im.draw_rect_filled(x0, y0, x1, y1) + a = np.asarray(im) + assert a.dtype == np.uint8 + assert a.shape == (height, width) + if x0 == 100 or y0 == 200: + # All the out-of-bounds starts should get automatically clipped. + assert np.sum(a) == 0 + else: + # Otherwise, ends are clipped to the dimension, but are also _inclusive_. + filled = (min(x1 + 1, width) - x0) * (min(y1 + 1, height) - y0) + assert np.sum(a) == 255 * filled + + +def test_ft2font_dejavu_attrs(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file) + assert font.fname == file + # Names extracted from FontForge: Font Information → PS Names tab. + assert font.postscript_name == 'DejaVuSans' + assert font.family_name == 'DejaVu Sans' + assert font.style_name == 'Book' + assert font.num_faces == 1 # Single TTF. + assert font.num_named_instances == 0 # Not a variable font. + assert font.num_glyphs == 6241 # From compact encoding view in FontForge. + assert font.num_fixed_sizes == 0 # All glyphs are scalable. + assert font.num_charmaps == 5 + # Other internal flags are set, so only check the ones we're allowed to test. + expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT | + ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.KERNING | + ft2font.FaceFlags.GLYPH_NAMES) + assert expected_flags in font.face_flags + assert font.style_flags == ft2font.StyleFlags.NORMAL + assert font.scalable + # From FontForge: Font Information → General tab → entry name below. + assert font.units_per_EM == 2048 # Em Size. + assert font.underline_position == -175 # Underline position. + assert font.underline_thickness == 90 # Underline height. + # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below. + assert font.ascender == 1901 # HHead Ascent. + assert font.descender == -483 # HHead Descent. + # Unconfirmed values. + assert font.height == 2384 + assert font.max_advance_width == 3838 + assert font.max_advance_height == 2384 + assert font.bbox == (-2090, -948, 3673, 2524) + + +def test_ft2font_cm_attrs(): + file = fm.findfont('cmtt10') + font = ft2font.FT2Font(file) + assert font.fname == file + # Names extracted from FontForge: Font Information → PS Names tab. + assert font.postscript_name == 'Cmtt10' + assert font.family_name == 'cmtt10' + assert font.style_name == 'Regular' + assert font.num_faces == 1 # Single TTF. + assert font.num_named_instances == 0 # Not a variable font. + assert font.num_glyphs == 133 # From compact encoding view in FontForge. + assert font.num_fixed_sizes == 0 # All glyphs are scalable. + assert font.num_charmaps == 2 + # Other internal flags are set, so only check the ones we're allowed to test. + expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT | + ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.GLYPH_NAMES) + assert expected_flags in font.face_flags + assert font.style_flags == ft2font.StyleFlags.NORMAL + assert font.scalable + # From FontForge: Font Information → General tab → entry name below. + assert font.units_per_EM == 2048 # Em Size. + assert font.underline_position == -143 # Underline position. + assert font.underline_thickness == 20 # Underline height. + # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below. + assert font.ascender == 1276 # HHead Ascent. + assert font.descender == -489 # HHead Descent. + # Unconfirmed values. + assert font.height == 1765 + assert font.max_advance_width == 1536 + assert font.max_advance_height == 1765 + assert font.bbox == (-12, -477, 1280, 1430) + + +def test_ft2font_stix_bold_attrs(): + file = fm.findfont('STIXSizeTwoSym:bold') + font = ft2font.FT2Font(file) + assert font.fname == file + # Names extracted from FontForge: Font Information → PS Names tab. + assert font.postscript_name == 'STIXSizeTwoSym-Bold' + assert font.family_name == 'STIXSizeTwoSym' + assert font.style_name == 'Bold' + assert font.num_faces == 1 # Single TTF. + assert font.num_named_instances == 0 # Not a variable font. + assert font.num_glyphs == 20 # From compact encoding view in FontForge. + assert font.num_fixed_sizes == 0 # All glyphs are scalable. + assert font.num_charmaps == 3 + # Other internal flags are set, so only check the ones we're allowed to test. + expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT | + ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.GLYPH_NAMES) + assert expected_flags in font.face_flags + assert font.style_flags == ft2font.StyleFlags.BOLD + assert font.scalable + # From FontForge: Font Information → General tab → entry name below. + assert font.units_per_EM == 1000 # Em Size. + assert font.underline_position == -133 # Underline position. + assert font.underline_thickness == 20 # Underline height. + # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below. + assert font.ascender == 2095 # HHead Ascent. + assert font.descender == -404 # HHead Descent. + # Unconfirmed values. + assert font.height == 2499 + assert font.max_advance_width == 1130 + assert font.max_advance_height == 2499 + assert font.bbox == (4, -355, 1185, 2095) + + +def test_ft2font_valid_args(): + class PathLikeClass: + def __init__(self, filename): + self.filename = filename + + def __fspath__(self): + return self.filename - with pytest.raises(TypeError, match="Fallback list must be a list"): + file_str = fm.findfont('DejaVu Sans') + file_bytes = os.fsencode(file_str) + + font = ft2font.FT2Font(file_str) + assert font.fname == file_str + font = ft2font.FT2Font(file_bytes) + assert font.fname == file_bytes + font = ft2font.FT2Font(PathLikeClass(file_str)) + assert font.fname == file_str + font = ft2font.FT2Font(PathLikeClass(file_bytes)) + assert font.fname == file_bytes + + +def test_ft2font_invalid_args(tmp_path): + # filename argument. + with pytest.raises(TypeError, match='to a font file or a binary-mode file object'): + ft2font.FT2Font(None) + with pytest.raises(TypeError, match='to a font file or a binary-mode file object'): + ft2font.FT2Font(object()) # Not bytes or string, and has no read() method. + file = tmp_path / 'invalid-font.ttf' + file.write_text('This is not a valid font file.') + with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'), + file.open('rt') as fd): + ft2font.FT2Font(fd) + with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'), + file.open('wt') as fd): + ft2font.FT2Font(fd) + with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'), + file.open('wb') as fd): + ft2font.FT2Font(fd) + + file = fm.findfont('DejaVu Sans') + + # hinting_factor argument. + with pytest.raises(TypeError, match='incompatible constructor arguments'): + ft2font.FT2Font(file, 1.3) + with pytest.raises(ValueError, match='hinting_factor must be greater than 0'): + ft2font.FT2Font(file, 0) + + with pytest.raises(TypeError, match='incompatible constructor arguments'): # failing to be a list will fail before the 0 - ft2font.FT2Font(file_name, _fallback_list=(0,)) # type: ignore[arg-type] - - with pytest.raises( - TypeError, match="Fallback fonts must be FT2Font objects." - ): - ft2font.FT2Font(file_name, _fallback_list=[0]) # type: ignore[list-item] - - -def test_ft2font_positive_hinting_factor(): - file_name = fm.findfont('DejaVu Sans') - with pytest.raises( - ValueError, match="hinting_factor must be greater than 0" - ): - ft2font.FT2Font(file_name, 0) - - -@pytest.mark.parametrize('family_name, file_name', - [("WenQuanYi Zen Hei", "wqy-zenhei.ttc"), - ("Noto Sans CJK JP", "NotoSansCJK.ttc"), - ("Noto Sans TC", "NotoSansTC-Regular.otf")] - ) -def test_fallback_smoke(family_name, file_name): - fp = fm.FontProperties(family=[family_name]) - if Path(fm.findfont(fp)).name != file_name: - pytest.skip(f"Font {family_name} ({file_name}) is missing") - plt.rcParams['font.size'] = 20 - fig = plt.figure(figsize=(4.75, 1.85)) - fig.text(0.05, 0.45, "There are 几个汉字 in between!", - family=['DejaVu Sans', family_name]) - fig.text(0.05, 0.85, "There are 几个汉字 in between!", - family=[family_name]) + ft2font.FT2Font(file, _fallback_list=(0,)) # type: ignore[arg-type] + with pytest.raises(TypeError, match='incompatible constructor arguments'): + ft2font.FT2Font(file, _fallback_list=[0]) # type: ignore[list-item] + + # kerning_factor argument. + with pytest.raises(TypeError, match='incompatible constructor arguments'): + ft2font.FT2Font(file, _kerning_factor=1.3) + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='text.kerning_factor rcParam was deprecated .+ 3.11'): + mpl.rcParams['text.kerning_factor'] = 0 + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='_kerning_factor parameter was deprecated .+ 3.11'): + ft2font.FT2Font(file, _kerning_factor=123) + + +@pytest.mark.parametrize('name, size, skippable', + [('DejaVu Sans', 1, False), ('WenQuanYi Zen Hei', 3, True)]) +def test_ft2font_face_index(name, size, skippable): + try: + file = fm.findfont(name, fallback_to_default=False) + except ValueError: + if skippable: + pytest.skip(r'Font {name} may be missing') + raise + for index in range(size): + font = ft2font.FT2Font(file, face_index=index) + assert font.num_faces >= size + assert font.face_index == index + with pytest.raises(ValueError, match='must be between'): # out of bounds for spec + ft2font.FT2Font(file, face_index=0x1ffff) + with pytest.raises(RuntimeError, match='invalid argument'): # invalid for this font + ft2font.FT2Font(file, face_index=0xff) + + +def test_ft2font_clear(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file) + assert font.get_num_glyphs() == 0 + assert font.get_width_height() == (0, 0) + assert font.get_bitmap_offset() == (0, 0) + font.set_text('ABabCDcd') + assert font.get_num_glyphs() == 8 + assert font.get_width_height() != (0, 0) + assert font.get_bitmap_offset() != (0, 0) + font.clear() + assert font.get_num_glyphs() == 0 + assert font.get_width_height() == (0, 0) + assert font.get_bitmap_offset() == (0, 0) + + +def test_ft2font_set_size(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + font.set_text('ABabCDcd') + orig = font.get_width_height() + font.set_size(24, 72) + font.set_text('ABabCDcd') + assert font.get_width_height() == tuple(pytest.approx(2 * x, 1e-1) for x in orig) + font.set_size(12, 144) + font.set_text('ABabCDcd') + assert font.get_width_height() == tuple(pytest.approx(2 * x, 1e-1) for x in orig) + + +def test_ft2font_features(): + # Smoke test that these are accepted as intended. + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file) + font.set_text('foo', features=None) # unset + font.set_text('foo', features=['calt', 'dlig']) # list + font.set_text('foo', features=('calt', 'dlig')) # tuple + with pytest.raises(TypeError): + font.set_text('foo', features=123) + with pytest.raises(TypeError): + font.set_text('foo', features=[123, 456]) + + +def test_ft2font_charmaps(): + def enc(name): + # We don't expose the encoding enum from FreeType, but can generate it here. + # For DejaVu, there are 5 charmaps, but only 2 have enum entries in FreeType. + e = 0 + for x in name: + e <<= 8 + e += ord(x) + return e + + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file) + assert font.num_charmaps == 5 + + # Unicode. + font.select_charmap(enc('unic')) + unic = font.get_charmap() + font.set_charmap(0) # Unicode platform, Unicode BMP only. + after = font.get_charmap() + assert len(after) <= len(unic) + for chr, glyph in after.items(): + assert unic[chr] == glyph == font.get_char_index(chr) + font.set_charmap(1) # Unicode platform, modern subtable. + after = font.get_charmap() + assert unic == after + font.set_charmap(3) # Windows platform, Unicode BMP only. + after = font.get_charmap() + assert len(after) <= len(unic) + for chr, glyph in after.items(): + assert unic[chr] == glyph == font.get_char_index(chr) + font.set_charmap(4) # Windows platform, Unicode full repertoire, modern subtable. + after = font.get_charmap() + assert unic == after + + # This is just a random sample from FontForge. + glyph_names = cast(dict[str, ft2font.GlyphIndexType], { + 'non-existent-glyph-name': 0, + 'plusminus': 115, + 'Racute': 278, + 'perthousand': 2834, + 'seveneighths': 3057, + 'triagup': 3721, + 'uni01D3': 405, + 'uni0417': 939, + 'uni2A02': 4464, + 'u1D305': 5410, + 'u1F0A1': 5784, + }) + for name, index in glyph_names.items(): + assert font.get_name_index(name) == index + if name == 'non-existent-glyph-name': + name = '.notdef' + # This doesn't always apply, but it does for DejaVu Sans. + assert font.get_glyph_name(index) == name + + # Apple Roman. + font.select_charmap(enc('armn')) + armn = font.get_charmap() + font.set_charmap(2) # Macintosh platform, Roman. + after = font.get_charmap() + assert armn == after + assert len(armn) <= 256 # 8-bit encoding. + # The first 128 characters of Apple Roman match ASCII, which also matches Unicode. + for o in range(1, 128): + if o not in armn or o not in unic: + continue + assert unic[o] == armn[o] + # Check a couple things outside the ASCII set that are different in each charset. + examples = [ + # (Unicode, Macintosh) + (0x2020, 0xA0), # Dagger. + (0x00B0, 0xA1), # Degree symbol. + (0x00A3, 0xA3), # Pound sign. + (0x00A7, 0xA4), # Section sign. + (0x00B6, 0xA6), # Pilcrow. + (0x221E, 0xB0), # Infinity symbol. + ] + for u, m in examples: + # Though the encoding is different, the glyph should be the same. + assert unic[u] == armn[m] + + +_expected_sfnt_names = { + 'DejaVu Sans': { + 0: 'Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.\n' + 'Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.\n' + 'DejaVu changes are in public domain\n', + 1: 'DejaVu Sans', + 2: 'Book', + 3: 'DejaVu Sans', + 4: 'DejaVu Sans', + 5: 'Version 2.35', + 6: 'DejaVuSans', + 8: 'DejaVu fonts team', + 11: 'http://dejavu.sourceforge.net', + 13: 'Fonts are (c) Bitstream (see below). ' + 'DejaVu changes are in public domain. ' + '''Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below) + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. ''' ''' - # TODO enable fallback for other backends! - for fmt in ['png', 'raw']: # ["svg", "pdf", "ps"]: - fig.savefig(io.BytesIO(), format=fmt) +Arev Fonts Copyright +------------------------------ +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. -@pytest.mark.parametrize('family_name, file_name', - [("WenQuanYi Zen Hei", "wqy-zenhei"), - ("Noto Sans CJK JP", "NotoSansCJK"), - ("Noto Sans TC", "NotoSansTC-Regular.otf")] - ) -@check_figures_equal(extensions=["png", "pdf", "eps", "svg"]) -def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name): - fp = fm.FontProperties(family=[family_name]) - if file_name not in Path(fm.findfont(fp)).name: - pytest.skip(f"Font {family_name} ({file_name}) is missing") +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: - text = ["There are", "几个汉字", "in between!"] +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. - plt.rcParams["font.size"] = 20 - test_fonts = [["DejaVu Sans", family_name]] * 3 - ref_fonts = [["DejaVu Sans"], [family_name], ["DejaVu Sans"]] +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". - for j, (txt, test_font, ref_font) in enumerate( - zip(text, test_fonts, ref_fonts) - ): - fig_ref.text(0.05, .85 - 0.15*j, txt, family=ref_font) - fig_test.text(0.05, .85 - 0.15*j, txt, family=test_font) +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the ''' ''' +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr.''', + 14: 'http://dejavu.sourceforge.net/wiki/index.php/License', + 16: 'DejaVu Sans', + 17: 'Book', + }, + 'cmtt10': { + 0: 'Copyright (C) 1994, Basil K. Malyshev. All Rights Reserved.' + '012BaKoMa Fonts Collection, Level-B.', + 1: 'cmtt10', + 2: 'Regular', + 3: 'FontMonger:cmtt10', + 4: 'cmtt10', + 5: '1.1/12-Nov-94', + 6: 'Cmtt10', + }, + 'STIXSizeTwoSym:bold': { + 0: 'Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the ' + 'American Chemical Society, the American Institute of Physics, the American ' + 'Mathematical Society, the American Physical Society, Elsevier, Inc., and ' + 'The Institute of Electrical and Electronic Engineers, Inc. Portions ' + 'copyright (c) 1998-2003 by MicroPress, Inc. Portions copyright (c) 1990 by ' + 'Elsevier, Inc. All rights reserved.', + 1: 'STIXSizeTwoSym', + 2: 'Bold', + 3: 'FontMaster:STIXSizeTwoSym-Bold:1.0.0', + 4: 'STIXSizeTwoSym-Bold', + 5: 'Version 1.0.0', + 6: 'STIXSizeTwoSym-Bold', + 7: 'STIX Fonts(TM) is a trademark of The Institute of Electrical and ' + 'Electronics Engineers, Inc.', + 9: 'MicroPress Inc., with final additions and corrections provided by Coen ' + 'Hoffman, Elsevier (retired)', + 10: 'Arie de Ruiter, who in 1995 was Head of Information Technology ' + 'Development at Elsevier Science, made a proposal to the STI Pub group, an ' + 'informal group of publishers consisting of representatives from the ' + 'American Chemical Society (ACS), American Institute of Physics (AIP), ' + 'American Mathematical Society (AMS), American Physical Society (APS), ' + 'Elsevier, and Institute of Electrical and Electronics Engineers (IEEE). ' + 'De Ruiter encouraged the members to consider development of a series of ' + 'Web fonts, which he proposed should be called the Scientific and ' + 'Technical Information eXchange, or STIX, Fonts. All STI Pub member ' + 'organizations enthusiastically endorsed this proposal, and the STI Pub ' + 'group agreed to embark on what has become a twelve-year project. The goal ' + 'of the project was to identify all alphabetic, symbolic, and other ' + 'special characters used in any facet of scientific publishing and to ' + 'create a set of Unicode-based fonts that would be distributed free to ' + 'every scientist, student, and other interested party worldwide. The fonts ' + 'would be consistent with the emerging Unicode standard, and would permit ' + 'universal representation of every character. With the release of the STIX ' + "fonts, de Ruiter's vision has been realized.", + 11: 'http://www.stixfonts.org', + 12: 'http://www.micropress-inc.com', + 13: 'As a condition for receiving these fonts at no charge, each person ' + 'downloading the fonts must agree to some simple license terms. The ' + 'license is based on the SIL Open Font License ' + '. The ' + 'SIL License is a free and open source license specifically designed for ' + 'fonts and related software. The basic terms are that the recipient will ' + 'not remove the copyright and trademark statements from the fonts and ' + 'that, if the person decides to create a derivative work based on the STIX ' + 'Fonts but incorporating some changes or enhancements, the derivative work ' + '("Modified Version") will carry a different name. The copyright and ' + 'trademark restrictions are part of the agreement between the STI Pub ' + 'companies and the typeface designer. The "renaming" restriction results ' + 'from the desire of the STI Pub companies to assure that the STIX Fonts ' + 'will continue to function in a predictable fashion for all that use them. ' + 'No copy of one or more of the individual Font typefaces that form the ' + 'STIX Fonts(TM) set may be sold by itself, but other than this one ' + 'restriction, licensees are free to sell the fonts either separately or as ' + 'part of a package that combines other software or fonts with this font ' + 'set.', + 14: 'http://www.stixfonts.org/user_license.html', + }, +} + + +@pytest.mark.parametrize('font_name, expected', _expected_sfnt_names.items(), + ids=_expected_sfnt_names.keys()) +def test_ft2font_get_sfnt(font_name, expected): + file = fm.findfont(font_name) + font = ft2font.FT2Font(file) + sfnt = font.get_sfnt() + for name, value in expected.items(): + # Macintosh, Unicode 1.0, English, name. + assert sfnt.pop((1, 0, 0, name)) == value.encode('ascii') + # Microsoft, Unicode, English United States, name. + assert sfnt.pop((3, 1, 1033, name)) == value.encode('utf-16be') + assert sfnt == {} + + +_expected_sfnt_tables = { + 'DejaVu Sans': { + 'invalid': None, + 'head': { + 'version': (1, 0), + 'fontRevision': (2, 22937), + 'checkSumAdjustment': -175678572, + 'magicNumber': 0x5F0F3CF5, + 'flags': 31, + 'unitsPerEm': 2048, + 'created': (0, 3514699492), 'modified': (0, 3514699492), + 'xMin': -2090, 'yMin': -948, 'xMax': 3673, 'yMax': 2524, + 'macStyle': 0, + 'lowestRecPPEM': 8, + 'fontDirectionHint': 0, + 'indexToLocFormat': 1, + 'glyphDataFormat': 0, + }, + 'maxp': { + 'version': (1, 0), + 'numGlyphs': 6241, + 'maxPoints': 852, 'maxComponentPoints': 104, 'maxTwilightPoints': 16, + 'maxContours': 43, 'maxComponentContours': 12, + 'maxZones': 2, + 'maxStorage': 153, + 'maxFunctionDefs': 64, + 'maxInstructionDefs': 0, + 'maxStackElements': 1045, + 'maxSizeOfInstructions': 534, + 'maxComponentElements': 8, + 'maxComponentDepth': 4, + }, + 'OS/2': { + 'version': 1, + 'xAvgCharWidth': 1038, + 'usWeightClass': 400, 'usWidthClass': 5, + 'fsType': 0, + 'ySubscriptXSize': 1331, 'ySubscriptYSize': 1433, + 'ySubscriptXOffset': 0, 'ySubscriptYOffset': 286, + 'ySuperscriptXSize': 1331, 'ySuperscriptYSize': 1433, + 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 983, + 'yStrikeoutSize': 102, 'yStrikeoutPosition': 530, + 'sFamilyClass': 0, + 'panose': b'\x02\x0b\x06\x03\x03\x08\x04\x02\x02\x04', + 'ulUnicodeRange': (3875565311, 3523280383, 170156073, 67117068), + 'achVendID': b'PfEd', + 'fsSelection': 64, 'usFirstCharIndex': 32, 'usLastCharIndex': 65535, + 'sTypoAscender': 1556, 'sTypoDescender': -492, 'sTypoLineGap': 410, + 'usWinAscent': 1901, 'usWinDescent': 483, + 'ulCodePageRange': (1610613247, 3758030848), + }, + 'hhea': { + 'version': (1, 0), + 'ascent': 1901, 'descent': -483, 'lineGap': 0, + 'advanceWidthMax': 3838, + 'minLeftBearing': -2090, 'minRightBearing': -1455, + 'xMaxExtent': 3673, + 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0, + 'metricDataFormat': 0, 'numOfLongHorMetrics': 6226, + }, + 'vhea': None, + 'post': { + 'format': (2, 0), + 'isFixedPitch': 0, 'italicAngle': (0, 0), + 'underlinePosition': -130, 'underlineThickness': 90, + 'minMemType42': 0, 'maxMemType42': 0, + 'minMemType1': 0, 'maxMemType1': 0, + }, + 'pclt': None, + }, + 'cmtt10': { + 'invalid': None, + 'head': { + 'version': (1, 0), + 'fontRevision': (1, 0), + 'checkSumAdjustment': 555110277, + 'magicNumber': 0x5F0F3CF5, + 'flags': 3, + 'unitsPerEm': 2048, + 'created': (0, 0), 'modified': (0, 0), + 'xMin': -12, 'yMin': -477, 'xMax': 1280, 'yMax': 1430, + 'macStyle': 0, + 'lowestRecPPEM': 6, + 'fontDirectionHint': 2, + 'indexToLocFormat': 1, + 'glyphDataFormat': 0, + }, + 'maxp': { + 'version': (1, 0), + 'numGlyphs': 133, + 'maxPoints': 94, 'maxComponentPoints': 0, 'maxTwilightPoints': 12, + 'maxContours': 5, 'maxComponentContours': 0, + 'maxZones': 2, + 'maxStorage': 6, + 'maxFunctionDefs': 64, + 'maxInstructionDefs': 0, + 'maxStackElements': 200, + 'maxSizeOfInstructions': 100, + 'maxComponentElements': 4, + 'maxComponentDepth': 1, + }, + 'OS/2': { + 'version': 0, + 'xAvgCharWidth': 1075, + 'usWeightClass': 400, 'usWidthClass': 5, + 'fsType': 0, + 'ySubscriptXSize': 410, 'ySubscriptYSize': 369, + 'ySubscriptXOffset': 0, 'ySubscriptYOffset': -469, + 'ySuperscriptXSize': 410, 'ySuperscriptYSize': 369, + 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 1090, + 'yStrikeoutSize': 102, 'yStrikeoutPosition': 530, + 'sFamilyClass': 0, + 'panose': b'\x02\x0b\x05\x00\x00\x00\x00\x00\x00\x00', + 'ulUnicodeRange': (0, 0, 0, 0), + 'achVendID': b'\x00\x00\x00\x00', + 'fsSelection': 64, 'usFirstCharIndex': 32, 'usLastCharIndex': 9835, + 'sTypoAscender': 1276, 'sTypoDescender': -469, 'sTypoLineGap': 0, + 'usWinAscent': 1430, 'usWinDescent': 477, + }, + 'hhea': { + 'version': (1, 0), + 'ascent': 1276, 'descent': -489, 'lineGap': 0, + 'advanceWidthMax': 1536, + 'minLeftBearing': -12, 'minRightBearing': -29, + 'xMaxExtent': 1280, + 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0, + 'metricDataFormat': 0, 'numOfLongHorMetrics': 133, + }, + 'vhea': None, + 'post': { + 'format': (2, 0), + 'isFixedPitch': 0, 'italicAngle': (0, 0), + 'underlinePosition': -133, 'underlineThickness': 20, + 'minMemType42': 0, 'maxMemType42': 0, + 'minMemType1': 0, 'maxMemType1': 0, + }, + 'pclt': { + 'version': (1, 0), + 'fontNumber': 2147483648, + 'pitch': 1075, + 'xHeight': 905, + 'style': 0, + 'typeFamily': 0, + 'capHeight': 1276, + 'symbolSet': 0, + 'typeFace': b'cmtt10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 'characterComplement': b'\xff\xff\xff\xff7\xff\xff\xfe', + 'strokeWeight': 0, + 'widthType': -5, + 'serifStyle': 64, + }, + }, + 'STIXSizeTwoSym:bold': { + 'invalid': None, + 'head': { + 'version': (1, 0), + 'fontRevision': (1, 0), + 'checkSumAdjustment': 1803408080, + 'magicNumber': 0x5F0F3CF5, + 'flags': 11, + 'unitsPerEm': 1000, + 'created': (0, 3359035786), 'modified': (0, 3359035786), + 'xMin': 4, 'yMin': -355, 'xMax': 1185, 'yMax': 2095, + 'macStyle': 1, + 'lowestRecPPEM': 8, + 'fontDirectionHint': 2, + 'indexToLocFormat': 0, + 'glyphDataFormat': 0, + }, + 'maxp': { + 'version': (1, 0), + 'numGlyphs': 20, + 'maxPoints': 37, 'maxComponentPoints': 0, 'maxTwilightPoints': 0, + 'maxContours': 1, 'maxComponentContours': 0, + 'maxZones': 2, + 'maxStorage': 1, + 'maxFunctionDefs': 64, + 'maxInstructionDefs': 0, + 'maxStackElements': 64, + 'maxSizeOfInstructions': 0, + 'maxComponentElements': 0, + 'maxComponentDepth': 0, + }, + 'OS/2': { + 'version': 2, + 'xAvgCharWidth': 598, + 'usWeightClass': 700, 'usWidthClass': 5, + 'fsType': 0, + 'ySubscriptXSize': 500, 'ySubscriptYSize': 500, + 'ySubscriptXOffset': 0, 'ySubscriptYOffset': 250, + 'ySuperscriptXSize': 500, 'ySuperscriptYSize': 500, + 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 500, + 'yStrikeoutSize': 20, 'yStrikeoutPosition': 1037, + 'sFamilyClass': 0, + 'panose': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 'ulUnicodeRange': (3, 192, 0, 0), + 'achVendID': b'STIX', + 'fsSelection': 32, 'usFirstCharIndex': 32, 'usLastCharIndex': 10217, + 'sTypoAscender': 750, 'sTypoDescender': -250, 'sTypoLineGap': 1499, + 'usWinAscent': 2095, 'usWinDescent': 404, + 'ulCodePageRange': (2688417793, 2432565248), + 'sxHeight': 0, + 'sCapHeight': 0, + 'usDefaultChar': 0, + 'usBreakChar': 32, + 'usMaxContext': 1, + }, + 'hhea': { + 'version': (1, 0), + 'ascent': 2095, 'descent': -404, 'lineGap': 0, + 'advanceWidthMax': 1130, + 'minLeftBearing': 0, 'minRightBearing': -55, + 'xMaxExtent': 1185, + 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0, + 'metricDataFormat': 0, 'numOfLongHorMetrics': 19, + }, + 'vhea': None, + 'post': { + 'format': (2, 0), + 'isFixedPitch': 0, 'italicAngle': (0, 0), + 'underlinePosition': -123, 'underlineThickness': 20, + 'minMemType42': 0, 'maxMemType42': 0, + 'minMemType1': 0, 'maxMemType1': 0, + }, + 'pclt': None, + }, +} + + +@pytest.mark.parametrize('font_name', _expected_sfnt_tables.keys()) +@pytest.mark.parametrize('header', _expected_sfnt_tables['DejaVu Sans'].keys()) +def test_ft2font_get_sfnt_table(font_name, header): + file = fm.findfont(font_name) + font = ft2font.FT2Font(file) + assert font.get_sfnt_table(header) == _expected_sfnt_tables[font_name][header] + + +@pytest.mark.parametrize('left, right, unscaled, unfitted, default', [ + # These are all the same class. + ('A', 'A', 57, 247, 256), ('A', 'À', 57, 247, 256), ('A', 'Á', 57, 247, 256), + ('A', 'Â', 57, 247, 256), ('A', 'Ã', 57, 247, 256), ('A', 'Ä', 57, 247, 256), + # And a few other random ones. + ('D', 'A', -36, -156, -128), ('T', '.', -243, -1055, -1024), + ('X', 'C', -149, -647, -640), ('-', 'J', 114, 495, 512), +]) +def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): + file = fm.findfont('DejaVu Sans') + # With unscaled, these settings should produce exact values found in FontForge. + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(100, 100) + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + ft2font.Kerning.UNSCALED) == unscaled + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + ft2font.Kerning.UNFITTED) == unfitted + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + ft2font.Kerning.DEFAULT) == default + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning.UNSCALED instead'): + k = ft2font.KERNING_UNSCALED + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning enum values instead'): + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + int(k)) == unscaled + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning.UNFITTED instead'): + k = ft2font.KERNING_UNFITTED + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning enum values instead'): + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + int(k)) == unfitted + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning.DEFAULT instead'): + k = ft2font.KERNING_DEFAULT + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='Use Kerning enum values instead'): + assert font.get_kerning(font.get_char_index(ord(left)), + font.get_char_index(ord(right)), + int(k)) == default + + +def test_ft2font_set_text(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + xys = font.set_text('') + np.testing.assert_array_equal(xys, np.empty((0, 2))) + assert font.get_width_height() == (0, 0) + assert font.get_num_glyphs() == 0 + assert font.get_descent() == 0 + assert font.get_bitmap_offset() == (0, 0) + # This string uses all the kerning pairs defined for test_ft2font_get_kerning. + xys = font.set_text('AADAT.XC-J') + np.testing.assert_array_equal( + xys, + [(0, 0), (533, 0), (1045, 0), (1608, 0), (2060, 0), (2417, 0), (2609, 0), + (3065, 0), (3577, 0), (3940, 0)]) + assert font.get_width_height() == (4196, 768) + assert font.get_num_glyphs() == 10 + assert font.get_descent() == 192 + assert font.get_bitmap_offset() == (6, 0) + + +@pytest.mark.parametrize( + 'input', + [ + [1, 2, 3], + [(1, 2)], + [('en', 'foo', 2)], + [('en', 1, 'foo')], + ], + ids=[ + 'nontuple', + 'wrong length', + 'wrong start type', + 'wrong end type', + ], +) +def test_ft2font_language_invalid(input): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + with pytest.raises(TypeError): + font.set_text('foo', language=input) + + +def test_ft2font_language(): + # This is just a smoke test. + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_text('foo') + font.set_text('foo', language='en') + font.set_text('foo', language=[('en', 1, 2)]) + + +def test_ft2font_loading(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + for glyph in [font.load_char(ord('M')), + font.load_glyph(font.get_char_index(ord('M')))]: + assert glyph is not None + assert glyph.width == 576 + assert glyph.height == 576 + assert glyph.horiBearingX == 0 + assert glyph.horiBearingY == 576 + assert glyph.horiAdvance == 640 + assert glyph.linearHoriAdvance == 678528 + assert glyph.vertBearingX == -384 + assert glyph.vertBearingY == 64 + assert glyph.vertAdvance == 832 + assert glyph.bbox == (54, 0, 574, 576) + assert font.get_num_glyphs() == 2 # Both count as loaded. + # But neither has been placed anywhere. + assert font.get_width_height() == (0, 0) + assert font.get_descent() == 0 + assert font.get_bitmap_offset() == (0, 0) + + +def test_ft2font_drawing(): + expected_str = ( + ' ', + '11 11 ', + '11 11 ', + '1 1 1 1 ', + '1 1 1 1 ', + '1 1 1 1 ', + '1 11 1 ', + '1 11 1 ', + '1 1 ', + '1 1 ', + ' ', + ) + expected = np.array([ + [int(c) for c in line.replace(' ', '0')] for line in expected_str + ]) + expected *= 255 + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + font.set_text('M') + font.draw_glyphs_to_bitmap(antialiased=False) + image = font.get_image() + np.testing.assert_array_equal(image, expected) + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + glyph = font.load_char(ord('M')) + image = np.zeros(expected.shape, np.uint8) + font.draw_glyph_to_bitmap(image, -1, 1, glyph, antialiased=False) + np.testing.assert_array_equal(image, expected) + + +def test_ft2font_get_path(): + file = fm.findfont('DejaVu Sans') + font = ft2font.FT2Font(file, hinting_factor=1) + font.set_size(12, 72) + vertices, codes = font.get_path() + assert vertices.shape == (0, 2) + assert codes.shape == (0, ) + font.load_char(ord('M')) + vertices, codes = font.get_path() + expected_vertices = np.array([ + (0.843750, 9.000000), (2.609375, 9.000000), # Top left. + (4.906250, 2.875000), # Top of midpoint. + (7.218750, 9.000000), (8.968750, 9.000000), # Top right. + (8.968750, 0.000000), (7.843750, 0.000000), # Bottom right. + (7.843750, 7.906250), # Point under top right. + (5.531250, 1.734375), (4.296875, 1.734375), # Bar under midpoint. + (1.984375, 7.906250), # Point under top left. + (1.984375, 0.000000), (0.843750, 0.000000), # Bottom left. + (0.843750, 9.000000), # Back to top left corner. + (0.000000, 0.000000), + ]) + np.testing.assert_array_equal(vertices, expected_vertices) + expected_codes = np.full(expected_vertices.shape[0], mpath.Path.LINETO, + dtype=mpath.Path.code_type) + expected_codes[0] = mpath.Path.MOVETO + expected_codes[-1] = mpath.Path.CLOSEPOLY + np.testing.assert_array_equal(codes, expected_codes) + + +@pytest.mark.parametrize('fmt', ['pdf', 'png', 'ps', 'raw', 'svg']) +def test_fallback_smoke(fmt): + fonts, test_str = _gen_multi_font_text() + plt.rcParams['font.size'] = 16 + fig = plt.figure(figsize=(4.75, 1.85)) + fig.text(0.5, 0.5, test_str, + horizontalalignment='center', verticalalignment='center') + + fig.savefig(io.BytesIO(), format=fmt) @pytest.mark.parametrize("font_list", @@ -90,29 +993,31 @@ def test_fallback_missing(recwarn, font_list): assert all([font in recwarn[0].message.args[0] for font in font_list]) -@pytest.mark.parametrize( - "family_name, file_name", - [ - ("WenQuanYi Zen Hei", "wqy-zenhei"), - ("Noto Sans CJK JP", "NotoSansCJK"), - ("Noto Sans TC", "NotoSansTC-Regular.otf") - ], -) -def test__get_fontmap(family_name, file_name): - fp = fm.FontProperties(family=[family_name]) - found_file_name = Path(fm.findfont(fp)).name - if file_name not in found_file_name: - pytest.skip(f"Font {family_name} ({file_name}) is missing") +@image_comparison(['last_resort'], style='mpl20') +def test_fallback_last_resort(recwarn): + fig = plt.figure(figsize=(3, 0.5)) + fig.text(.5, .5, "Hello 🙃 World!", size=24, + horizontalalignment='center', verticalalignment='center') + fig.canvas.draw() + assert all(isinstance(warn.message, UserWarning) for warn in recwarn) + assert recwarn[0].message.args[0].startswith( + "Glyph 128579 (\\N{UPSIDE-DOWN FACE}) missing from font(s)") + + +def test__layout(): + fonts, test_str = _gen_multi_font_text() + # Add some glyphs that don't exist in either font to check the Last Resort fallback. + missing_glyphs = '\n几个汉字' + test_str += missing_glyphs - text = "There are 几个汉字 in between!" ft = fm.get_font( - fm.fontManager._find_fonts_by_props( - fm.FontProperties(family=["DejaVu Sans", family_name]) - ) + fm.fontManager._find_fonts_by_props(fm.FontProperties(family=fonts)) ) - fontmap = ft._get_fontmap(text) - for char, font in fontmap.items(): - if ord(char) > 127: - assert Path(font.fname).name == found_file_name - else: - assert Path(font.fname).name == "DejaVuSans.ttf" + for substr in test_str.split('\n'): + for item in ft._layout(substr, ft2font.LoadFlags.DEFAULT): + if item.char in missing_glyphs: + assert Path(item.ft_object.fname).name == 'LastResortHE-Regular.ttf' + elif ord(item.char) > 127: + assert Path(item.ft_object.fname).name == 'DejaVuSans.ttf' + else: + assert Path(item.ft_object.fname).name == 'cmr10.ttf' diff --git a/lib/matplotlib/tests/test_getattr.py b/lib/matplotlib/tests/test_getattr.py index f0f5823600ca..fe302220067a 100644 --- a/lib/matplotlib/tests/test_getattr.py +++ b/lib/matplotlib/tests/test_getattr.py @@ -1,25 +1,29 @@ from importlib import import_module from pkgutil import walk_packages +import sys +import warnings -import matplotlib import pytest +import matplotlib +from matplotlib.testing import is_ci_environment, subprocess_run_helper + # Get the names of all matplotlib submodules, # except for the unit tests and private modules. -module_names = [ - m.name - for m in walk_packages( - path=matplotlib.__path__, prefix=f'{matplotlib.__name__}.' - ) - if not m.name.startswith(__package__) - and not any(x.startswith('_') for x in m.name.split('.')) -] +module_names = [] +backend_module_names = [] +for m in walk_packages(path=matplotlib.__path__, prefix=f'{matplotlib.__name__}.'): + if m.name.startswith(__package__): + continue + if any(x.startswith('_') for x in m.name.split('.')): + continue + if 'backends.backend_' in m.name: + backend_module_names.append(m.name) + else: + module_names.append(m.name) -@pytest.mark.parametrize('module_name', module_names) -@pytest.mark.filterwarnings('ignore::DeprecationWarning') -@pytest.mark.filterwarnings('ignore::ImportWarning') -def test_getattr(module_name): +def _test_getattr(module_name, use_pytest=True): """ Test that __getattr__ methods raise AttributeError for unknown keys. See #20822, #20855. @@ -28,8 +32,35 @@ def test_getattr(module_name): module = import_module(module_name) except (ImportError, RuntimeError, OSError) as e: # Skip modules that cannot be imported due to missing dependencies - pytest.skip(f'Cannot import {module_name} due to {e}') + if use_pytest: + pytest.skip(f'Cannot import {module_name} due to {e}') + else: + print(f'SKIP: Cannot import {module_name} due to {e}') + return key = 'THIS_SYMBOL_SHOULD_NOT_EXIST' if hasattr(module, key): delattr(module, key) + + +@pytest.mark.parametrize('module_name', module_names) +@pytest.mark.filterwarnings('ignore::DeprecationWarning') +@pytest.mark.filterwarnings('ignore::ImportWarning') +def test_getattr(module_name): + _test_getattr(module_name) + + +def _test_module_getattr(): + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('ignore', category=ImportWarning) + module_name = sys.argv[1] + _test_getattr(module_name, use_pytest=False) + + +@pytest.mark.parametrize('module_name', backend_module_names) +def test_backend_getattr(module_name): + proc = subprocess_run_helper(_test_module_getattr, module_name, + timeout=120 if is_ci_environment() else 20) + if 'SKIP: ' in proc.stdout: + pytest.skip(proc.stdout.removeprefix('SKIP: ')) + print(proc.stdout) diff --git a/lib/matplotlib/tests/test_gridspec.py b/lib/matplotlib/tests/test_gridspec.py index deda73c3b6ab..625a79816ed3 100644 --- a/lib/matplotlib/tests/test_gridspec.py +++ b/lib/matplotlib/tests/test_gridspec.py @@ -1,3 +1,4 @@ +import matplotlib import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import pytest @@ -9,6 +10,13 @@ def test_equal(): assert gs[:, 0] == gs[:, 0] +def test_update(): + gs = gridspec.GridSpec(2, 1) + + gs.update(left=.1) + assert gs.left == .1 + + def test_width_ratios(): """ Addresses issue #5835. @@ -27,6 +35,23 @@ def test_height_ratios(): gridspec.GridSpec(1, 1, height_ratios=[2, 1, 3]) +def test_SubplotParams(): + s = gridspec.SubplotParams(.1, .1, .9, .9) + assert s.left == 0.1 + + s.reset() + assert s.left == matplotlib.rcParams['figure.subplot.left'] + + with pytest.raises(ValueError, match='left cannot be >= right'): + s.update(left=s.right + .01) + + with pytest.raises(ValueError, match='bottom cannot be >= top'): + s.update(bottom=s.top + .01) + + with pytest.raises(ValueError, match='left cannot be >= right'): + gridspec.SubplotParams(.1, .1, .09, .9) + + def test_repr(): ss = gridspec.GridSpec(3, 3)[2, 1:3] assert repr(ss) == "GridSpec(3, 3)[2:3, 1:3]" diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 599265a2d4d8..78dacfd72907 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -1,5 +1,4 @@ from contextlib import ExitStack -from copy import copy import functools import io import os @@ -9,7 +8,7 @@ import urllib.request import numpy as np -from numpy.testing import assert_array_equal +from numpy.testing import assert_allclose, assert_array_equal from PIL import Image import matplotlib as mpl @@ -17,13 +16,26 @@ colors, image as mimage, patches, pyplot as plt, style, rcParams) from matplotlib.image import (AxesImage, BboxImage, FigureImage, NonUniformImage, PcolorImage) +from matplotlib.patches import Rectangle from matplotlib.testing.decorators import check_figures_equal, image_comparison -from matplotlib.transforms import Bbox, Affine2D, TransformedBbox +from matplotlib.transforms import Bbox, Affine2D, Transform, TransformedBbox import matplotlib.ticker as mticker import pytest +@pytest.fixture +def nonaffine_identity(): + """Non-affine identity transform for compositing with any affine transform""" + class NonAffineIdentityTransform(Transform): + input_dims = 2 + output_dims = 2 + + def inverted(self): + return self + return NonAffineIdentityTransform() + + @image_comparison(['interp_alpha.png'], remove_text=True) def test_alpha_interp(): """Test the interpolation of the alpha channel on RGBA images""" @@ -37,8 +49,8 @@ def test_alpha_interp(): axr.imshow(img, interpolation="bilinear") -@image_comparison(['interp_nearest_vs_none'], - extensions=['pdf', 'svg'], remove_text=True) +@image_comparison(['interp_nearest_vs_none'], tol=3.7, # For Ghostscript 10.06+. + extensions=['pdf', 'svg'], remove_text=True, style='mpl20') def test_interp_nearest_vs_none(): """Test the effect of "nearest" and "none" interpolation""" # Setting dpi to something really small makes the difference very @@ -88,7 +100,7 @@ def test_image_python_io(): (3, 2.9, "hanning"), # <3 upsample. (3, 9.1, "nearest"), # >3 upsample. ]) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_imshow_antialiased(fig_test, fig_ref, img_size, fig_size, interpolation): np.random.seed(19680801) @@ -98,13 +110,13 @@ def test_imshow_antialiased(fig_test, fig_ref, fig.set_size_inches(fig_size, fig_size) ax = fig_test.subplots() ax.set_position([0, 0, 1, 1]) - ax.imshow(A, interpolation='antialiased') + ax.imshow(A, interpolation='auto') ax = fig_ref.subplots() ax.set_position([0, 0, 1, 1]) ax.imshow(A, interpolation=interpolation) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_imshow_zoom(fig_test, fig_ref): # should be less than 3 upsample, so should be nearest... np.random.seed(19680801) @@ -113,13 +125,13 @@ def test_imshow_zoom(fig_test, fig_ref): for fig in [fig_test, fig_ref]: fig.set_size_inches(2.9, 2.9) ax = fig_test.subplots() - ax.imshow(A, interpolation='antialiased') - ax.set_xlim([10, 20]) - ax.set_ylim([10, 20]) + ax.imshow(A, interpolation='auto') + ax.set_xlim(10, 20) + ax.set_ylim(10, 20) ax = fig_ref.subplots() ax.imshow(A, interpolation='nearest') - ax.set_xlim([10, 20]) - ax.set_ylim([10, 20]) + ax.set_xlim(10, 20) + ax.set_ylim(10, 20) @check_figures_equal() @@ -184,6 +196,28 @@ def test_imsave(fmt): assert_array_equal(arr_dpi1, arr_dpi100) +def test_imsave_python_sequences(): + # Tests saving an image with data passed using Python sequence types + # such as lists or tuples. + + # RGB image: 3 rows × 2 columns, with float values in [0.0, 1.0] + img_data = [ + [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], + [(0.0, 0.0, 1.0), (1.0, 1.0, 0.0)], + [(0.0, 1.0, 1.0), (1.0, 0.0, 1.0)], + ] + + buff = io.BytesIO() + plt.imsave(buff, img_data, format="png") + buff.seek(0) + read_img = plt.imread(buff) + + assert_array_equal( + np.array(img_data), + read_img[:, :, :3] # Drop alpha if present + ) + + @pytest.mark.parametrize("origin", ["upper", "lower"]) def test_imsave_rgba_origin(origin): # test that imsave always passes c-contiguous arrays down to pillow @@ -193,8 +227,8 @@ def test_imsave_rgba_origin(origin): @pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"]) -def test_imsave_fspath(fmt): - plt.imsave(Path(os.devnull), np.array([[0, 1]]), format=fmt) +def test_imsave_fspath(fmt, tmp_path): + plt.imsave(tmp_path / f'unused.{fmt}', np.array([[0, 1]]), format=fmt) def test_imsave_color_alpha(): @@ -256,19 +290,19 @@ def test_image_alpha(): @mpl.style.context('mpl20') -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_imshow_alpha(fig_test, fig_ref): np.random.seed(19680801) - rgbf = np.random.rand(6, 6, 3) + rgbf = np.random.rand(6, 6, 3).astype(np.float32) rgbu = np.uint8(rgbf * 255) ((ax0, ax1), (ax2, ax3)) = fig_test.subplots(2, 2) ax0.imshow(rgbf, alpha=0.5) ax1.imshow(rgbf, alpha=0.75) - ax2.imshow(rgbu, alpha=0.5) - ax3.imshow(rgbu, alpha=0.75) + ax2.imshow(rgbu, alpha=127/255) + ax3.imshow(rgbu, alpha=191/255) - rgbaf = np.concatenate((rgbf, np.ones((6, 6, 1))), axis=2) + rgbaf = np.concatenate((rgbf, np.ones((6, 6, 1))), axis=2).astype(np.float32) rgbau = np.concatenate((rgbu, np.full((6, 6, 1), 255, np.uint8)), axis=2) ((ax0, ax1), (ax2, ax3)) = fig_ref.subplots(2, 2) rgbaf[:, :, 3] = 0.5 @@ -281,6 +315,33 @@ def test_imshow_alpha(fig_test, fig_ref): ax3.imshow(rgbau) +@pytest.mark.parametrize('n_channels, is_int, alpha_arr, opaque', + [(3, False, False, False), # RGB float + (4, False, False, False), # RGBA float + (4, False, True, False), # RGBA float with alpha array + (4, False, False, True), # RGBA float with solid color + (4, True, False, False)]) # RGBA unint8 +def test_imshow_multi_draw(n_channels, is_int, alpha_arr, opaque): + if is_int: + array = np.random.randint(0, 256, (2, 2, n_channels)) + else: + array = np.random.random((2, 2, n_channels)) + if opaque: + array[:, :, 3] = 1 + + if alpha_arr: + alpha = np.array([[0.3, 0.5], [1, 0.8]]) + else: + alpha = None + + fig, ax = plt.subplots() + im = ax.imshow(array, alpha=alpha) + fig.draw_without_rendering() + + # Draw should not modify original array + np.testing.assert_array_equal(array, im._A) + + def test_cursor_data(): from matplotlib.backend_bases import MouseEvent @@ -390,7 +451,8 @@ def test_cursor_data_nonuniform(xy, data): ([[.123, .987]], "[0.123]"), ([[np.nan, 1, 2]], "[]"), ([[1, 1+1e-15]], "[1.0000000000000000]"), - ([[-1, -1]], "[-1.0000000000000000]"), + ([[-1, -1]], "[-1.0]"), + ([[0, 0]], "[0.00]"), ]) def test_format_cursor_data(data, text): from matplotlib.backend_bases import MouseEvent @@ -403,6 +465,43 @@ def test_format_cursor_data(data, text): assert im.format_cursor_data(im.get_cursor_data(event)) == text +@pytest.mark.parametrize( + "data, text", [ + ([[[10001, 10000]], [[0, 0]]], "[10001.000, 0.000]"), + ([[[.123, .987]], [[0.1, 0]]], "[0.123, 0.100]"), + ([[[np.nan, 1, 2]], [[0, 0, 0]]], "[]"), + ]) +def test_format_cursor_data_multinorm(data, text): + from matplotlib.backend_bases import MouseEvent + fig, ax = plt.subplots() + cmap_bivar = mpl.bivar_colormaps['BiOrangeBlue'] + cmap_multivar = mpl.multivar_colormaps['2VarAddA'] + + # This is a test for ColorizingArtist._format_cursor_data_override() + # with data with multiple channels. + # It includes a workaround so that we can test this functionality + # before the MultiVar/BiVariate colormaps and MultiNorm are exposed + # via the top-level methods (ax.imshow()) + # i.e. we here set the hidden variables _cmap and _norm + # and use set_array() on the ColorizingArtist rather than the _ImageBase + # but this workaround should be replaced by: + # `ax.imshow(data, cmap=cmap_bivar, vmin=(0,0), vmax=(1,1))` + # once the functionality is available. + # see https://github.com/matplotlib/matplotlib/issues/14168 + im = ax.imshow([[0, 1]]) + im.colorizer._cmap = cmap_bivar + im.colorizer._norm = colors.MultiNorm([im.norm, im.norm]) + mpl.colorizer.ColorizingArtist.set_array(im, data) + + xdisp, ydisp = ax.transData.transform([0, 0]) + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.format_cursor_data(im.get_cursor_data(event)) == text + + im.colorizer._cmap = cmap_multivar + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.format_cursor_data(im.get_cursor_data(event)) == text + + @image_comparison(['image_clip'], style='mpl20') def test_image_clip(): d = [[1, 2], [3, 4]] @@ -425,7 +524,7 @@ def test_image_cliprect(): im.set_clip_path(rect) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_imshow_10_10_1(fig_test, fig_ref): # 10x10x1 should be the same as 10x10 arr = np.arange(100).reshape((10, 10, 1)) @@ -476,7 +575,7 @@ def test_image_shift(): def test_image_edges(): fig = plt.figure(figsize=[1, 1]) - ax = fig.add_axes([0, 0, 1, 1], frameon=False) + ax = fig.add_axes((0, 0, 1, 1), frameon=False) data = np.tile(np.arange(12), 15).reshape(20, 9) @@ -484,8 +583,8 @@ def test_image_edges(): interpolation='none', cmap='gray') x = y = 2 - ax.set_xlim([-x, x]) - ax.set_ylim([-y, y]) + ax.set_xlim(-x, x) + ax.set_ylim(-y, y) ax.set_xticks([]) ax.set_yticks([]) @@ -510,10 +609,10 @@ def test_image_composite_background(): ax.imshow(arr, extent=[0, 2, 15, 0]) ax.imshow(arr, extent=[4, 6, 15, 0]) ax.set_facecolor((1, 0, 0, 0.5)) - ax.set_xlim([0, 12]) + ax.set_xlim(0, 12) -@image_comparison(['image_composite_alpha'], remove_text=True) +@image_comparison(['image_composite_alpha'], remove_text=True, tol=0.07) def test_image_composite_alpha(): """ Tests that the alpha value is recognized and correctly applied in the @@ -536,8 +635,8 @@ def test_image_composite_alpha(): ax.imshow(arr2, extent=[0, 5, 2, 3], alpha=0.6) ax.imshow(arr2, extent=[0, 5, 3, 4], alpha=0.3) ax.set_facecolor((0, 0.5, 0, 1)) - ax.set_xlim([0, 5]) - ax.set_ylim([5, 0]) + ax.set_xlim(0, 5) + ax.set_ylim(5, 0) @check_figures_equal(extensions=["pdf"]) @@ -601,7 +700,7 @@ def test_bbox_image_inverted(): image = np.identity(10) bbox_im = BboxImage(TransformedBbox(Bbox([[0.1, 0.2], [0.3, 0.25]]), - ax.figure.transFigure), + ax.get_figure().transFigure), interpolation='nearest') bbox_im.set_data(image) bbox_im.set_clip_on(False) @@ -696,7 +795,7 @@ def test_jpeg_alpha(): # If this fails, there will be only one color (all black). If this # is working, we should have all 256 shades of grey represented. num_colors = len(image.getcolors(256)) - assert 175 <= num_colors <= 210 + assert 175 <= num_colors <= 230 # The fully transparent part should be red. corner_pixel = image.getpixel((0, 0)) assert corner_pixel == (254, 0, 0) @@ -853,8 +952,6 @@ def test_image_preserve_size2(): @image_comparison(['mask_image_over_under.png'], remove_text=True, tol=1.0) def test_mask_image_over_under(): - # Remove this line when this test image is regenerated. - plt.rcParams['pcolormesh.snap'] = False delta = 0.025 x = y = np.arange(-3.0, 3.0, delta) @@ -864,7 +961,7 @@ def test_mask_image_over_under(): (2 * np.pi * 0.5 * 1.5)) Z = 10*(Z2 - Z1) # difference of Gaussians - palette = plt.cm.gray.with_extremes(over='r', under='g', bad='b') + palette = plt.colormaps["gray"].with_extremes(over='r', under='g', bad='b') Zm = np.ma.masked_where(Z > 1.2, Z) fig, (ax1, ax2) = plt.subplots(1, 2) im = ax1.imshow(Zm, interpolation='bilinear', @@ -953,6 +1050,7 @@ def test_imshow_masked_interpolation(): fig, ax_grid = plt.subplots(3, 6) interps = sorted(mimage._interpd_) + interps.remove('auto') interps.remove('antialiased') for interp, ax in zip(interps, ax_grid.ravel()): @@ -1119,22 +1217,7 @@ def test_respects_bbox(): assert buf_before.getvalue() != buf_after.getvalue() # Not all white. -def test_image_cursor_formatting(): - fig, ax = plt.subplots() - # Create a dummy image to be able to call format_cursor_data - im = ax.imshow(np.zeros((4, 4))) - - data = np.ma.masked_array([0], mask=[True]) - assert im.format_cursor_data(data) == '[]' - - data = np.ma.masked_array([0], mask=[False]) - assert im.format_cursor_data(data) == '[0]' - - data = np.nan - assert im.format_cursor_data(data) == '[nan]' - - -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_image_array_alpha(fig_test, fig_ref): """Per-pixel alpha channel test.""" x = np.linspace(0, 1) @@ -1160,12 +1243,11 @@ def test_image_array_alpha_validation(): @mpl.style.context('mpl20') def test_exact_vmin(): - cmap = copy(mpl.colormaps["autumn_r"]) - cmap.set_under(color="lightgrey") + cmap = mpl.colormaps["autumn_r"].with_extremes(under="lightgrey") # make the image exactly 190 pixels wide fig = plt.figure(figsize=(1.9, 0.1), dpi=100) - ax = fig.add_axes([0, 0, 1, 1]) + ax = fig.add_axes((0, 0, 1, 1)) data = np.array( [[-1, -1, -1, 0, 0, 0, 0, 43, 79, 95, 66, 1, -1, -1, -1, 0, 0, 0, 34]], @@ -1287,7 +1369,7 @@ def test_imshow_quantitynd(): fig.canvas.draw() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_norm_change(fig_test, fig_ref): # LogNorm should not mask anything invalid permanently. data = np.full((5, 5), 1, dtype=np.float64) @@ -1316,7 +1398,7 @@ def test_norm_change(fig_test, fig_ref): @pytest.mark.parametrize('x', [-1, 1]) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_huge_range_log(fig_test, fig_ref, x): # parametrize over bad lognorm -1 values and large range 1 -> 1e20 data = np.full((5, 5), x, dtype=np.float64) @@ -1405,9 +1487,7 @@ def test_nonuniform_logscale(): ax.add_image(im) -@image_comparison( - ['rgba_antialias.png'], style='mpl20', remove_text=True, - tol=0 if platform.machine() == 'x86_64' else 0.007) +@image_comparison(['rgba_antialias.png'], style='mpl20', remove_text=True) def test_rgba_antialias(): fig, axs = plt.subplots(2, 2, figsize=(3.5, 3.5), sharex=False, sharey=False, constrained_layout=True) @@ -1437,30 +1517,67 @@ def test_rgba_antialias(): aa[70:90, 195:215] = 1e6 aa[20:30, 195:215] = -1e6 - cmap = copy(plt.cm.RdBu_r) - cmap.set_over('yellow') - cmap.set_under('cyan') + cmap = plt.colormaps["RdBu_r"].with_extremes(over='yellow', under='cyan') axs = axs.flatten() # zoom in axs[0].imshow(aa, interpolation='nearest', cmap=cmap, vmin=-1.2, vmax=1.2) - axs[0].set_xlim([N/2-25, N/2+25]) - axs[0].set_ylim([N/2+50, N/2-10]) + axs[0].set_xlim(N/2-25, N/2+25) + axs[0].set_ylim(N/2+50, N/2-10) # no anti-alias axs[1].imshow(aa, interpolation='nearest', cmap=cmap, vmin=-1.2, vmax=1.2) # data antialias: Note no purples, and white in circle. Note # that alternating red and blue stripes become white. - axs[2].imshow(aa, interpolation='antialiased', interpolation_stage='data', + axs[2].imshow(aa, interpolation='auto', interpolation_stage='data', cmap=cmap, vmin=-1.2, vmax=1.2) # rgba antialias: Note purples at boundary with circle. Note that # alternating red and blue stripes become purple - axs[3].imshow(aa, interpolation='antialiased', interpolation_stage='rgba', + axs[3].imshow(aa, interpolation='auto', interpolation_stage='rgba', cmap=cmap, vmin=-1.2, vmax=1.2) +@check_figures_equal() +def test_upsample_interpolation_stage(fig_test, fig_ref): + """ + Show that interpolation_stage='auto' gives the same as 'data' + for upsampling. + """ + # Fixing random state for reproducibility. This non-standard seed + # gives red splotches for 'rgba'. + np.random.seed(19680801+9) + + grid = np.random.rand(4, 4) + ax = fig_ref.subplots() + ax.imshow(grid, interpolation='bilinear', cmap='viridis', + interpolation_stage='data') + + ax = fig_test.subplots() + ax.imshow(grid, interpolation='bilinear', cmap='viridis', + interpolation_stage='auto') + + +@check_figures_equal() +def test_downsample_interpolation_stage(fig_test, fig_ref): + """ + Show that interpolation_stage='auto' gives the same as 'rgba' + for downsampling. + """ + # Fixing random state for reproducibility + np.random.seed(19680801) + + grid = np.random.rand(400, 400) + ax = fig_ref.subplots() + ax.imshow(grid, interpolation='auto', cmap='viridis', + interpolation_stage='rgba') + + ax = fig_test.subplots() + ax.imshow(grid, interpolation='auto', cmap='viridis', + interpolation_stage='auto') + + def test_rc_interpolation_stage(): for val in ["data", "rgba"]: with mpl.rc_context({"image.interpolation_stage": val}): @@ -1478,8 +1595,8 @@ def test_rc_interpolation_stage(): @pytest.mark.parametrize( 'dim, size, msg', [['row', 2**23, r'2\*\*23 columns'], ['col', 2**24, r'2\*\*24 rows']]) -@check_figures_equal(extensions=('png', )) -def test_large_image(fig_test, fig_ref, dim, size, msg, origin): +@check_figures_equal() +def test_large_image(fig_test, fig_ref, dim, size, msg, origin, high_memory): # Check that Matplotlib downsamples images that are too big for AGG # See issue #19276. Currently the fix only works for png output but not # pdf or svg output. @@ -1498,10 +1615,12 @@ def test_large_image(fig_test, fig_ref, dim, size, msg, origin): with pytest.warns(UserWarning, match=f'Data with more than {msg} cannot be ' 'accurately displayed.'): - fig_test.canvas.draw() + with io.BytesIO() as buffer: + # Write to a buffer to trigger the warning + fig_test.savefig(buffer) - array = np.zeros((1, 2)) - array[:, 1] = 1 + array = np.zeros((1, size // 2 + 1)) + array[:, array.size // 2:] = 1 if dim == 'col': array = array.T im = ax_ref.imshow(array, vmin=0, vmax=1, aspect='auto', @@ -1510,7 +1629,7 @@ def test_large_image(fig_test, fig_ref, dim, size, msg, origin): origin=origin) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_str_norms(fig_test, fig_ref): t = np.random.rand(10, 10) * .8 + .1 # between 0 and 1 axts = fig_test.subplots(1, 5) @@ -1555,6 +1674,50 @@ def test__resample_valid_output(): resample(np.zeros((9, 9)), out) +@pytest.mark.parametrize("data, interpolation, expected", + [(np.array([[0.1, 0.3, 0.2]]), mimage.NEAREST, + np.array([[0.1, 0.1, 0.1, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2]])), + (np.array([[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]), mimage.NEAREST, + np.array([[0.1, 0.2, 0.2, 0.3, 0.4, 0.4, 0.5, 0.6, 0.6]])), + (np.array([[0.1, 0.3, 0.2]]), mimage.BILINEAR, + np.array([[0.1, 0.1, 0.15, 0.21, 0.27, 0.285, 0.255, 0.225, 0.2, 0.2]])), + (np.array([[0.1, 0.9]]), mimage.BILINEAR, + np.array([[0.1, 0.1, 0.1, 0.1, 0.1, 0.14, 0.22, 0.3, 0.38, 0.46, + 0.54, 0.62, 0.7, 0.78, 0.86, 0.9, 0.9, 0.9, 0.9, 0.9]])), + (np.array([[0.1, 0.1]]), mimage.BILINEAR, np.full((1, 10), 0.1)), + # Test at the subpixel level + (np.array([[0.1, 0.9]]), mimage.NEAREST, + np.concatenate([np.full(512, 0.1), np.full(512, 0.9)]).reshape(1, -1)), + (np.array([[0.1, 0.9]]), mimage.BILINEAR, + np.concatenate([np.full(256, 0.1), + np.linspace(0.5, 256, 512).astype(int) / 256 * 0.8 + 0.1, + np.full(256, 0.9)]).reshape(1, -1)), + ] +) +def test_resample_nonaffine(data, interpolation, expected, nonaffine_identity): + # Test that both affine and nonaffine transforms resample to the correct answer + + # If the array is constant, the tolerance can be tight + # Otherwise, the tolerance is limited by the subpixel approach in the agg backend + atol = 0 if np.all(data == data.ravel()[0]) else 2e-3 + + # Create a simple affine transform for scaling the input array + affine_transform = Affine2D().scale(sx=expected.shape[1] / data.shape[1], sy=1) + + affine_result = np.empty_like(expected) + mimage.resample(data, affine_result, affine_transform, interpolation=interpolation) + assert_allclose(affine_result, expected, atol=atol) + + # Create a nonaffine version of the same transform + # by compositing with a nonaffine identity transform + nonaffine_transform = nonaffine_identity + affine_transform + + nonaffine_result = np.empty_like(expected) + mimage.resample(data, nonaffine_result, nonaffine_transform, + interpolation=interpolation) + assert_allclose(nonaffine_result, expected, atol=atol) + + def test_axesimage_get_shape(): # generate dummy image to test get_shape method ax = plt.gca() @@ -1576,3 +1739,316 @@ def test_non_transdata_image_does_not_touch_aspect(): assert ax.get_aspect() == 1 ax.imshow(im, transform=ax.transAxes, aspect=2) assert ax.get_aspect() == 2 + + +@image_comparison(['downsampling.png'], style='mpl20', remove_text=True, + tol=0 if platform.machine() == 'x86_64' else 0.07) +def test_downsampling(): + N = 450 + x = np.arange(N) / N - 0.5 + y = np.arange(N) / N - 0.5 + aa = np.ones((N, N)) + aa[::2, :] = -1 + + X, Y = np.meshgrid(x, y) + R = np.sqrt(X**2 + Y**2) + f0 = 5 + k = 100 + a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2)) + # make the left hand side of this + a[:int(N / 2), :][R[:int(N / 2), :] < 0.4] = -1 + a[:int(N / 2), :][R[:int(N / 2), :] < 0.3] = 1 + aa[:, int(N / 3):] = a[:, int(N / 3):] + a = aa + + fig, axs = plt.subplots(2, 3, figsize=(7, 6), layout='compressed') + axs[0, 0].imshow(a, interpolation='nearest', interpolation_stage='rgba', + cmap='RdBu_r') + axs[0, 0].set_xlim(125, 175) + axs[0, 0].set_ylim(250, 200) + axs[0, 0].set_title('Zoom') + + for ax, interp, space in zip(axs.flat[1:], ['nearest', 'nearest', 'hanning', + 'hanning', 'auto'], + ['data', 'rgba', 'data', 'rgba', 'auto']): + ax.imshow(a, interpolation=interp, interpolation_stage=space, + cmap='RdBu_r') + ax.set_title(f"interpolation='{interp}'\nspace='{space}'") + + +@image_comparison(['downsampling_speckle.png'], style='mpl20', remove_text=True) +def test_downsampling_speckle(): + fig, axs = plt.subplots(1, 2, figsize=(5, 2.7), sharex=True, sharey=True, + layout="compressed") + axs = axs.flatten() + img = ((np.arange(1024).reshape(-1, 1) * np.ones(720)) // 50).T + + cm = plt.get_cmap("viridis").with_extremes(over="m") + norm = colors.LogNorm(vmin=3, vmax=11) + + # old default cannot be tested because it creates over/under speckles + # in the following that are machine dependent. + + axs[0].set_title("interpolation='auto', stage='rgba'") + axs[0].imshow(np.triu(img), cmap=cm, norm=norm, interpolation_stage='rgba') + + # Should be same as previous + axs[1].set_title("interpolation='auto', stage='auto'") + axs[1].imshow(np.triu(img), cmap=cm, norm=norm) + + +@image_comparison( + ['upsampling.png'], style='mpl20', remove_text=True) +def test_upsampling(): + + np.random.seed(19680801+9) # need this seed to get yellow next to blue + a = np.random.rand(4, 4) + + fig, axs = plt.subplots(1, 3, figsize=(6.5, 3), layout='compressed') + im = axs[0].imshow(a, cmap='viridis') + axs[0].set_title( + "interpolation='auto'\nstage='antialaised'\n(default for upsampling)") + + # probably what people want: + axs[1].imshow(a, cmap='viridis', interpolation='sinc') + axs[1].set_title( + "interpolation='sinc'\nstage='auto'\n(default for upsampling)") + + # probably not what people want: + axs[2].imshow(a, cmap='viridis', interpolation='sinc', interpolation_stage='rgba') + axs[2].set_title("interpolation='sinc'\nstage='rgba'") + fig.colorbar(im, ax=axs, shrink=0.7, extend='both') + + +@pytest.mark.parametrize( + 'dtype', + ('float64', 'float32', 'int16', 'uint16', 'int8', 'uint8'), +) +@pytest.mark.parametrize('ndim', (2, 3)) +def test_resample_dtypes(dtype, ndim): + # Issue 28448, incorrect dtype comparisons in C++ image_resample can raise + # ValueError: arrays must be of dtype byte, short, float32 or float64 + rng = np.random.default_rng(4181) + shape = (2, 2) if ndim == 2 else (2, 2, 3) + data = rng.uniform(size=shape).astype(np.dtype(dtype, copy=True)) + fig, ax = plt.subplots() + axes_image = ax.imshow(data) + # Before fix the following raises ValueError for some dtypes. + axes_image.make_image(None)[0] + + +@pytest.mark.parametrize('intp_stage', ('data', 'rgba')) +@check_figures_equal(extensions=['png', 'pdf', 'svg']) +def test_interpolation_stage_rgba_respects_alpha_param(fig_test, fig_ref, intp_stage): + axs_tst = fig_test.subplots(2, 3) + axs_ref = fig_ref.subplots(2, 3) + ny, nx = 3, 3 + scalar_alpha = 0.5 + array_alpha = np.random.rand(ny, nx) + + # When the image does not have an alpha channel, alpha should be specified + # by the user or default to 1.0 + im_rgb = np.random.rand(ny, nx, 3) + im_concat_default_a = np.ones((ny, nx, 1)) # alpha defaults to 1.0 + im_rgba = np.concatenate( # combine rgb channels with array alpha + (im_rgb, array_alpha.reshape((ny, nx, 1))), axis=-1 + ) + axs_tst[0][0].imshow(im_rgb) + axs_ref[0][0].imshow(np.concatenate((im_rgb, im_concat_default_a), axis=-1)) + axs_tst[0][1].imshow(im_rgb, interpolation_stage=intp_stage, alpha=scalar_alpha) + axs_ref[0][1].imshow( + np.concatenate( # combine rgb channels with broadcasted scalar alpha + (im_rgb, scalar_alpha * im_concat_default_a), axis=-1 + ), interpolation_stage=intp_stage + ) + axs_tst[0][2].imshow(im_rgb, interpolation_stage=intp_stage, alpha=array_alpha) + axs_ref[0][2].imshow(im_rgba, interpolation_stage=intp_stage) + + # When the image already has an alpha channel, multiply it by the + # alpha param (both scalar and array alpha multiply the existing alpha) + axs_tst[1][0].imshow(im_rgba) + axs_ref[1][0].imshow(im_rgb, alpha=array_alpha) + axs_tst[1][1].imshow(im_rgba, interpolation_stage=intp_stage, alpha=scalar_alpha) + axs_ref[1][1].imshow( + np.concatenate( # combine rgb channels with scaled array alpha + (im_rgb, scalar_alpha * array_alpha.reshape((ny, nx, 1))), axis=-1 + ), interpolation_stage=intp_stage + ) + new_array_alpha = np.random.rand(ny, nx) + axs_tst[1][2].imshow(im_rgba, interpolation_stage=intp_stage, alpha=new_array_alpha) + axs_ref[1][2].imshow( + np.concatenate( # combine rgb channels with multiplied array alpha + (im_rgb, array_alpha.reshape((ny, nx, 1)) + * new_array_alpha.reshape((ny, nx, 1))), axis=-1 + ), interpolation_stage=intp_stage + ) + + +@image_comparison(['nn_pixel_alignment.png']) +def test_nn_pixel_alignment(nonaffine_identity): + fig, axs = plt.subplots(2, 3) + + for j, N in enumerate([3, 7, 11]): + # In each column, the plots use the same data array + data = np.arange(N**2).reshape((N, N)) % 4 + seps = np.arange(-0.5, N) + + for i in range(2): + if i == 0: + # Top row uses an affine transform + axs[i, j].imshow(data, cmap='Grays', interpolation='nearest') + else: + # Bottom row uses a non-affine transform + axs[i, j].imshow(data, cmap='Grays', interpolation='nearest', + transform=nonaffine_identity + axs[i, j].transData) + + axs[i, j].set_axis_off() + axs[i, j].vlines(seps, -1, N, lw=0.5, color='red', ls='dashed') + axs[i, j].hlines(seps, -1, N, lw=0.5, color='red', ls='dashed') + + +@image_comparison(['alignment_half_display_pixels.png'], style='mpl20') +def test_alignment_half_display_pixels(nonaffine_identity): + # All values in this test are chosen carefully so that many display pixels are + # aligned with an edge or a corner of an input pixel + + # Layout: + # Top row is origin='upper', bottom row is origin='lower' + # Column 1: affine transform, anchored at whole pixel + # Column 2: affine transform, anchored at half pixel + # Column 3: nonaffine transform, anchored at whole pixel + # Column 4: nonaffine transform, anchored at half pixel + # Column 5: affine transform, anchored at half pixel, interpolation='hanning' + + # Each axes patch is magenta, so seeing a magenta line at an edge of the image + # means that the image is not filling the axes + + fig = plt.figure(figsize=(5, 2), dpi=100) + fig.set_facecolor('g') + + corner_x = [0.01, 0.199, 0.41, 0.599, 0.809] + corner_y = [0.05, 0.53] + + axs = [] + for cy in corner_y: + for ix, cx in enumerate(corner_x): + my = cy + 0.0125 if ix in [1, 3, 4] else cy + axs.append(fig.add_axes([cx, my, 0.17, 0.425], xticks=[], yticks=[])) + + # Verify that each axes has been created with the correct width/height and that all + # four corners are on whole pixels (columns 1 and 3) or half pixels (columns 2, 4, + # and 5) + for i, ax in enumerate(axs): + extents = ax.get_window_extent().extents + assert_allclose(extents[2:4] - extents[0:2], 85, rtol=0, atol=1e-13) + assert_allclose(extents % 1, 0.5 if i % 5 in [1, 3, 4] else 0, + rtol=0, atol=1e-13) + + N = 10 + + data = np.arange(N**2).reshape((N, N)) % 9 + seps = np.arange(-0.5, N) + + for i, ax in enumerate(axs): + ax.set_facecolor('m') + + transform = nonaffine_identity + ax.transData if i % 4 >= 2 else ax.transData + ax.imshow(data, cmap='Blues', + interpolation='hanning' if i % 5 == 4 else 'nearest', + origin='upper' if i >= 5 else 'lower', + transform=transform) + + ax.vlines(seps, -0.5, N - 0.5, lw=0.5, color='red', ls=(0, (2, 4))) + ax.hlines(seps, -0.5, N - 0.5, lw=0.5, color='red', ls=(0, (2, 4))) + + for spine in ax.spines: + ax.spines[spine].set_linestyle((0, (5, 10))) + + +@image_comparison(['image_bounds_handling.png'], tol=0.006) +def test_image_bounds_handling(nonaffine_identity): + # TODO: The second and third panels in the bottom row show that the handling of + # image bounds is bugged for non-affine transforms and non-nearest-neighbor + # interpolation. If this bug gets fixed, the baseline image should be updated. + + fig, axs = plt.subplots(2, 3) + + N = 11 + + for j, interpolation in enumerate(['nearest', 'hanning', 'bilinear']): + data = np.arange(N**2).reshape((N, N)) + data = data / N**2 + (data % 4) / 6 + rotation = Affine2D().rotate_around(N/2-0.5, N/2-0.5, 1) + + for i in range(2): + transform = rotation + axs[i, j].transData + if i == 1: + # Bottom row uses a non-affine transform + transform = nonaffine_identity + transform + + axs[i, j].imshow(data, cmap='Grays', interpolation=interpolation, + transform=transform) + + axs[i, j].set_axis_off() + box = Rectangle((-0.5, -0.5), N, N, + edgecolor='red', facecolor='none', lw=0.5, ls='dashed', + transform=rotation + axs[i, j].transData) + axs[i, j].add_artist(box) + + +@image_comparison(['rgba_clean_edges.png'], tol=0.003) +def test_rgba_clean_edges(): + np.random.seed(19680801+9) # same as in test_upsampling() + data = np.random.rand(8, 8) + data = np.stack([data, data]) + data[1, 2:4, 2:4] = np.nan + + rotation = Affine2D().rotate_around(3.5, 3.5, 1) + + fig, axs = plt.subplots(1, 2) + + for i in range(2): + # Add background patches to check the fading to non-white colors + black = Rectangle((3.75, 2), 5, 5, color='black', zorder=0) + gray = Rectangle((0, -2), 3.75, 4, color='gray', zorder=0) + partly_black = Rectangle((3.75, -2), 5, 4, fc='black', ec='none', + alpha=0.5, zorder=0) + axs[i].add_patch(black) + axs[i].add_patch(gray) + axs[i].add_patch(partly_black) + + axs[i].imshow(data[i, ...], + interpolation='bilinear', interpolation_stage='rgba', + transform=rotation + axs[i].transData) + + axs[i].set_axis_off() + axs[i].set_xlim(-2.5, 9.5) + axs[i].set_ylim(-2.5, 9.5) + + +@image_comparison(['affine_fill_to_edges.png']) +def test_affine_fill_to_edges(): + # The two rows show the two settings of origin + # The three columns show the original and the two mirror flips + fig, axs = plt.subplots(2, 3) + + N = 7 + data = np.arange(N**2).reshape((N, N)) % 3 + + transform = [Affine2D(), + Affine2D().translate(0, -N + 1).scale(1, -1), + Affine2D().translate(-N + 1, 0).scale(-1, 1)] + + for j in range(3): + for i in range(2): + origin = 'upper' if i == 0 else 'lower' + + axs[i, j].imshow(data, cmap='Grays', + interpolation='hanning', origin=origin, + transform=transform[j] + axs[i, j].transData) + + axs[i, j].set_axis_off() + axs[i, j].vlines([-0.5, N - 0.5], -1, 2, lw=0.5, color='red') + axs[i, j].vlines([-0.5, N - 0.5], N - 3, N, lw=0.5, color='red') + axs[i, j].hlines([-0.5, N - 0.5], -1, 2, lw=0.5, color='red') + axs[i, j].hlines([-0.5, N - 0.5], N - 3, N, lw=0.5, color='red') diff --git a/lib/matplotlib/tests/test_inset.py b/lib/matplotlib/tests/test_inset.py new file mode 100644 index 000000000000..e368a4af4e1b --- /dev/null +++ b/lib/matplotlib/tests/test_inset.py @@ -0,0 +1,142 @@ +import platform + +import pytest + +import matplotlib.colors as mcolors +import matplotlib.pyplot as plt +import matplotlib.transforms as mtransforms +from matplotlib.testing.decorators import image_comparison, check_figures_equal + + +def test_indicate_inset_no_args(): + fig, ax = plt.subplots() + with pytest.raises(ValueError, match='At least one of bounds or inset_ax'): + ax.indicate_inset() + + +@check_figures_equal() +def test_zoom_inset_update_limits(fig_test, fig_ref): + # Updating the inset axes limits should also update the indicator #19768 + ax_ref = fig_ref.add_subplot() + ax_test = fig_test.add_subplot() + + for ax in ax_ref, ax_test: + ax.set_xlim([0, 5]) + ax.set_ylim([0, 5]) + + inset_ref = ax_ref.inset_axes([0.6, 0.6, 0.3, 0.3]) + inset_test = ax_test.inset_axes([0.6, 0.6, 0.3, 0.3]) + + inset_ref.set_xlim([1, 2]) + inset_ref.set_ylim([3, 4]) + ax_ref.indicate_inset_zoom(inset_ref) + + ax_test.indicate_inset_zoom(inset_test) + inset_test.set_xlim([1, 2]) + inset_test.set_ylim([3, 4]) + + +def test_inset_indicator_update_styles(): + fig, ax = plt.subplots() + inset = ax.inset_axes([0.6, 0.6, 0.3, 0.3]) + inset.set_xlim([0.2, 0.4]) + inset.set_ylim([0.2, 0.4]) + + indicator = ax.indicate_inset_zoom( + inset, edgecolor='red', alpha=0.5, linewidth=2, linestyle='solid') + + # Changing the rectangle styles should not affect the connectors. + indicator.rectangle.set(color='blue', linestyle='dashed', linewidth=42, alpha=0.2) + for conn in indicator.connectors: + assert mcolors.same_color(conn.get_edgecolor()[:3], 'red') + assert conn.get_alpha() == 0.5 + assert conn.get_linestyle() == 'solid' + assert conn.get_linewidth() == 2 + + # Changing the indicator styles should affect both rectangle and connectors. + indicator.set(color='green', linestyle='dotted', linewidth=7, alpha=0.8) + assert mcolors.same_color(indicator.rectangle.get_facecolor()[:3], 'green') + for patch in (*indicator.connectors, indicator.rectangle): + assert mcolors.same_color(patch.get_edgecolor()[:3], 'green') + assert patch.get_alpha() == 0.8 + assert patch.get_linestyle() == 'dotted' + assert patch.get_linewidth() == 7 + + indicator.set_edgecolor('purple') + for patch in (*indicator.connectors, indicator.rectangle): + assert mcolors.same_color(patch.get_edgecolor()[:3], 'purple') + + # This should also be true if connectors weren't created yet. + indicator._connectors = [] + indicator.set(color='burlywood', linestyle='dashdot', linewidth=4, alpha=0.4) + assert mcolors.same_color(indicator.rectangle.get_facecolor()[:3], 'burlywood') + for patch in (*indicator.connectors, indicator.rectangle): + assert mcolors.same_color(patch.get_edgecolor()[:3], 'burlywood') + assert patch.get_alpha() == 0.4 + assert patch.get_linestyle() == 'dashdot' + assert patch.get_linewidth() == 4 + + indicator._connectors = [] + indicator.set_edgecolor('thistle') + for patch in (*indicator.connectors, indicator.rectangle): + assert mcolors.same_color(patch.get_edgecolor()[:3], 'thistle') + + +def test_inset_indicator_zorder(): + fig, ax = plt.subplots() + rect = [0.2, 0.2, 0.3, 0.4] + + inset = ax.indicate_inset(rect) + assert inset.get_zorder() == 4.99 + + inset = ax.indicate_inset(rect, zorder=42) + assert inset.get_zorder() == 42 + + +@image_comparison(['zoom_inset_connector_styles.png'], remove_text=True, style='mpl20', + tol=0.024 if platform.machine() == 'arm64' else 0) +def test_zoom_inset_connector_styles(): + fig, axs = plt.subplots(2) + for ax in axs: + ax.plot([1, 2, 3]) + + axs[1].set_xlim(0.5, 1.5) + indicator = axs[0].indicate_inset_zoom(axs[1], linewidth=5) + # Make one visible connector a different style + indicator.connectors[1].set_linestyle('dashed') + indicator.connectors[1].set_color('blue') + + +@image_comparison(['zoom_inset_transform.png'], remove_text=True, style='mpl20', + tol=0.01) +def test_zoom_inset_transform(): + fig, ax = plt.subplots() + + ax_ins = ax.inset_axes([0.2, 0.2, 0.3, 0.15]) + ax_ins.set_ylim([0.3, 0.6]) + ax_ins.set_xlim([0.5, 0.9]) + + tr = mtransforms.Affine2D().rotate_deg(30) + indicator = ax.indicate_inset_zoom(ax_ins, transform=tr + ax.transData) + for conn in indicator.connectors: + conn.set_visible(True) + + +def test_zoom_inset_external_transform(): + # Smoke test that an external transform that requires an axes (i.e. + # Cartopy) will work. + class FussyDataTr: + def _as_mpl_transform(self, axes=None): + if axes is None: + raise ValueError("I am a fussy transform that requires an axes") + return axes.transData + + fig, ax = plt.subplots() + + ax_ins = ax.inset_axes([0.2, 0.2, 0.3, 0.15]) + ax_ins.set_xlim([0.7, 0.8]) + ax_ins.set_ylim([0.7, 0.8]) + + ax.indicate_inset_zoom(ax_ins, transform=FussyDataTr()) + + fig.draw_without_rendering() diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 0353f1408b73..fe9405bcbdae 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -1,6 +1,8 @@ import collections +import io import itertools import platform +import sys import time from unittest import mock import warnings @@ -19,7 +21,7 @@ import matplotlib.lines as mlines from matplotlib.legend_handler import HandlerTuple import matplotlib.legend as mlegend -from matplotlib import _api, rc_context +from matplotlib import rc_context from matplotlib.font_manager import FontProperties @@ -41,7 +43,19 @@ def test_legend_ordereddict(): loc='center left', bbox_to_anchor=(1, .5)) -@image_comparison(['legend_auto1'], remove_text=True) +def test_legend_generator(): + # smoketest that generator inputs work + fig, ax = plt.subplots() + ax.plot([0, 1]) + ax.plot([0, 2]) + + handles = (line for line in ax.get_lines()) + labels = (label for label in ['spam', 'eggs']) + + ax.legend(handles, labels, loc='upper left') + + +@image_comparison(['legend_auto1.png'], remove_text=True, style='mpl20') def test_legend_auto1(): """Test automatic legend placement""" fig, ax = plt.subplots() @@ -51,7 +65,7 @@ def test_legend_auto1(): ax.legend(loc='best') -@image_comparison(['legend_auto2'], remove_text=True) +@image_comparison(['legend_auto2.png'], remove_text=True, style='mpl20') def test_legend_auto2(): """Test automatic legend placement""" fig, ax = plt.subplots() @@ -61,7 +75,7 @@ def test_legend_auto2(): ax.legend([b1[0], b2[0]], ['up', 'down'], loc='best') -@image_comparison(['legend_auto3']) +@image_comparison(['legend_auto3.png'], style='mpl20') def test_legend_auto3(): """Test automatic legend placement""" fig, ax = plt.subplots() @@ -127,7 +141,7 @@ def test_legend_auto5(): assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds) -@image_comparison(['legend_various_labels'], remove_text=True) +@image_comparison(['legend_various_labels.png'], remove_text=True, style='mpl20') def test_various_labels(): # tests all sorts of label types fig = plt.figure() @@ -138,21 +152,8 @@ def test_various_labels(): ax.legend(numpoints=1, loc='best') -def test_legend_label_with_leading_underscore(): - """ - Test that artists with labels starting with an underscore are not added to - the legend, and that a warning is issued if one tries to add them - explicitly. - """ - fig, ax = plt.subplots() - line, = ax.plot([0, 1], label='_foo') - with pytest.warns(_api.MatplotlibDeprecationWarning, match="with an underscore"): - legend = ax.legend(handles=[line]) - assert len(legend.legend_handles) == 0 - - -@image_comparison(['legend_labels_first.png'], remove_text=True, - tol=0.013 if platform.machine() == 'arm64' else 0) +@image_comparison(['legend_labels_first.png'], remove_text=True, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.015) def test_labels_first(): # test labels to left of markers fig, ax = plt.subplots() @@ -162,8 +163,8 @@ def test_labels_first(): ax.legend(loc='best', markerfirst=False) -@image_comparison(['legend_multiple_keys.png'], remove_text=True, - tol=0.013 if platform.machine() == 'arm64' else 0) +@image_comparison(['legend_multiple_keys.png'], remove_text=True, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.033) def test_multiple_keys(): # test legend entries with multiple keys fig, ax = plt.subplots() @@ -176,16 +177,18 @@ def test_multiple_keys(): (p2, p1): HandlerTuple(ndivide=None, pad=0)}) -@image_comparison(['rgba_alpha.png'], remove_text=True, +@image_comparison(['rgba_alpha.png'], remove_text=True, style='mpl20', tol=0 if platform.machine() == 'x86_64' else 0.03) def test_alpha_rgba(): + # This rcParam would override the explicit setting below, so disable it. + plt.rcParams['legend.framealpha'] = None fig, ax = plt.subplots() ax.plot(range(10), lw=5) leg = plt.legend(['Longlabel that will go away'], loc='center') leg.legendPatch.set_facecolor([1, 0, 0, 0.5]) -@image_comparison(['rcparam_alpha.png'], remove_text=True, +@image_comparison(['rcparam_alpha.png'], remove_text=True, style='mpl20', tol=0 if platform.machine() == 'x86_64' else 0.03) def test_alpha_rcparam(): fig, ax = plt.subplots() @@ -199,10 +202,9 @@ def test_alpha_rcparam(): leg.legendPatch.set_facecolor([1, 0, 0, 0.5]) -@image_comparison(['fancy'], remove_text=True, tol=0.05) +@image_comparison(['fancy.png'], remove_text=True, style='mpl20', + tol=0.01 if sys.platform == 'darwin' else 0) def test_fancy(): - # Tolerance caused by changing default shadow "shade" from 0.3 to 1 - 0.7 = - # 0.30000000000000004 # using subplot triggers some offsetbox functionality untested elsewhere plt.subplot(121) plt.plot([5] * 10, 'o--', label='XX') @@ -213,18 +215,20 @@ def test_fancy(): ncols=2, shadow=True, title="My legend", numpoints=1) -@image_comparison(['framealpha'], remove_text=True, - tol=0 if platform.machine() == 'x86_64' else 0.024) +@image_comparison(['framealpha'], remove_text=True, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.021) def test_framealpha(): x = np.linspace(1, 100, 100) y = x plt.plot(x, y, label='mylabel', lw=10) - plt.legend(framealpha=0.5) + plt.legend(framealpha=0.5, loc='upper right') -@image_comparison(['scatter_rc3', 'scatter_rc1'], remove_text=True) +@image_comparison(['scatter_rc3.png', 'scatter_rc1.png'], remove_text=True, + style='mpl20') def test_rc(): # using subplot triggers some offsetbox functionality untested elsewhere + mpl.rcParams['legend.scatterpoints'] = 3 plt.figure() ax = plt.subplot(121) ax.scatter(np.arange(10), np.arange(10, 0, -1), label='three') @@ -239,7 +243,7 @@ def test_rc(): title="My legend") -@image_comparison(['legend_expand'], remove_text=True) +@image_comparison(['legend_expand.png'], remove_text=True, style='mpl20') def test_legend_expand(): """Test expand mode""" legend_modes = [None, "expand"] @@ -258,9 +262,6 @@ def test_legend_expand(): @image_comparison(['hatching'], remove_text=True, style='default') def test_hatching(): # Remove legend texts when this image is regenerated. - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() # Patches @@ -292,7 +293,7 @@ def test_hatching(): def test_legend_remove(): fig, ax = plt.subplots() lines = ax.plot(range(10)) - leg = fig.legend(lines, "test") + leg = fig.legend(lines, ["test"]) leg.remove() assert fig.legends == [] leg = ax.legend("test") @@ -318,7 +319,7 @@ def test_reverse_legend_handles_and_labels(): assert actual_markers == list(reversed(markers)) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_reverse_legend_display(fig_test, fig_ref): """Check that the rendered legend entries are reversed""" ax = fig_test.subplots() @@ -402,20 +403,17 @@ def test_legend_kwargs_handles_labels(self): ax.legend(labels=('a', 'b'), handles=(lnc, lns)) Legend.assert_called_with(ax, (lnc, lns), ('a', 'b')) - def test_warn_mixed_args_and_kwargs(self): + def test_error_mixed_args_and_kwargs(self): fig, ax = plt.subplots() th = np.linspace(0, 2*np.pi, 1024) lns, = ax.plot(th, np.sin(th), label='sin') lnc, = ax.plot(th, np.cos(th), label='cos') - with pytest.warns(DeprecationWarning) as record: + msg = 'must both be passed positionally or both as keywords' + with pytest.raises(TypeError, match=msg): ax.legend((lnc, lns), labels=('a', 'b')) - assert len(record) == 1 - assert str(record[0].message).startswith( - "You have mixed positional and keyword arguments, some input may " - "be discarded.") def test_parasite(self): - from mpl_toolkits.axes_grid1 import host_subplot # type: ignore + from mpl_toolkits.axes_grid1 import host_subplot # type: ignore[import] host = host_subplot(111) par = host.twinx() @@ -427,6 +425,15 @@ def test_parasite(self): plt.legend() Legend.assert_called_with(host, [p1, p2], ['Density', 'Temperature']) + def test_legend_warns_on_unequal_number_of_handles_and_labels(self): + fig, ax = plt.subplots() + line1, = ax.plot([1, 2]) + line2, = ax.plot([3, 4]) + with pytest.warns(UserWarning, match="Mismatched number of handles and labels"): + ax.legend([line1, line2], ['only_one']) # 2 handles, 1 label + with pytest.warns(UserWarning, match="Mismatched number of handles and labels"): + ax.legend([line1], ['label_a', 'label_b']) # 1 handle, 2 labels + class TestLegendFigureFunction: # Tests the legend function for figure @@ -472,16 +479,13 @@ def test_legend_kw_args(self): fig, (lines, lines2), ('a', 'b'), loc='right', bbox_transform=fig.transFigure) - def test_warn_args_kwargs(self): + def test_error_args_kwargs(self): fig, axs = plt.subplots(1, 2) lines = axs[0].plot(range(10)) lines2 = axs[1].plot(np.arange(10) * 2.) - with pytest.warns(DeprecationWarning) as record: + msg = 'must both be passed positionally or both as keywords' + with pytest.raises(TypeError, match=msg): fig.legend((lines, lines2), labels=('a', 'b')) - assert len(record) == 1 - assert str(record[0].message).startswith( - "You have mixed positional and keyword arguments, some input may " - "be discarded.") def test_figure_legend_outside(): @@ -490,27 +494,27 @@ def test_figure_legend_outside(): todos += ['left ' + pos for pos in ['lower', 'center', 'upper']] todos += ['right ' + pos for pos in ['lower', 'center', 'upper']] - upperext = [20.347556, 27.722556, 790.583, 545.499] - lowerext = [20.347556, 71.056556, 790.583, 588.833] - leftext = [151.681556, 27.722556, 790.583, 588.833] - rightext = [20.347556, 27.722556, 659.249, 588.833] + upperext = [20.722556, 26.389222, 790.333, 545.16762] + lowerext = [20.722556, 70.723222, 790.333, 589.50162] + leftext = [152.056556, 26.389222, 790.333, 589.50162] + rightext = [20.722556, 26.389222, 658.999, 589.50162] axbb = [upperext, upperext, upperext, lowerext, lowerext, lowerext, leftext, leftext, leftext, rightext, rightext, rightext] - legbb = [[10., 555., 133., 590.], # upper left - [338.5, 555., 461.5, 590.], # upper center - [667, 555., 790., 590.], # upper right - [10., 10., 133., 45.], # lower left - [338.5, 10., 461.5, 45.], # lower center - [667., 10., 790., 45.], # lower right - [10., 10., 133., 45.], # left lower - [10., 282.5, 133., 317.5], # left center - [10., 555., 133., 590.], # left upper - [667, 10., 790., 45.], # right lower - [667., 282.5, 790., 317.5], # right center - [667., 555., 790., 590.]] # right upper + legbb = [[10., 554., 133., 590.], # upper left + [338.5, 554., 461.5, 590.], # upper center + [667, 554., 790., 590.], # upper right + [10., 10., 133., 46.], # lower left + [338.5, 10., 461.5, 46.], # lower center + [667., 10., 790., 46.], # lower right + [10., 10., 133., 46.], # left lower + [10., 282., 133., 318.], # left center + [10., 554., 133., 590.], # left upper + [667, 10., 790., 46.], # right lower + [667., 282., 790., 318.], # right center + [667., 554., 790., 590.]] # right upper for nn, todo in enumerate(todos): print(todo) @@ -519,14 +523,13 @@ def test_figure_legend_outside(): leg = fig.legend(loc='outside ' + todo) fig.draw_without_rendering() - assert_allclose(axs.get_window_extent().extents, - axbb[nn]) - assert_allclose(leg.get_window_extent().extents, - legbb[nn]) + assert_allclose(axs.get_window_extent().extents, axbb[nn], + rtol=1e-4) + assert_allclose(leg.get_window_extent().extents, legbb[nn], + rtol=1e-4) -@image_comparison(['legend_stackplot.png'], - tol=0.031 if platform.machine() == 'arm64' else 0) +@image_comparison(['legend_stackplot.png'], style='mpl20') def test_legend_stackplot(): """Test legend for PolyCollection using stackplot.""" # related to #1341, #1943, and PR #3303 @@ -536,8 +539,8 @@ def test_legend_stackplot(): y2 = 2.0 * x + 1 y3 = 3.0 * x + 2 ax.stackplot(x, y1, y2, y3, labels=['y1', 'y2', 'y3']) - ax.set_xlim((0, 10)) - ax.set_ylim((0, 70)) + ax.set_xlim(0, 10) + ax.set_ylim(0, 70) ax.legend(loc='best') @@ -584,7 +587,7 @@ def test_legend_repeatcheckok(): assert len(lab) == 2 -@image_comparison(['not_covering_scatter.png']) +@image_comparison(['not_covering_scatter.png'], style='mpl20') def test_not_covering_scatter(): colors = ['b', 'g', 'r'] @@ -596,7 +599,7 @@ def test_not_covering_scatter(): plt.gca().set_ylim(-0.5, 2.2) -@image_comparison(['not_covering_scatter_transform.png']) +@image_comparison(['not_covering_scatter_transform.png'], style='mpl20') def test_not_covering_scatter_transform(): # Offsets point to top left, the default auto position offset = mtransforms.Affine2D().translate(-20, 20) @@ -662,7 +665,7 @@ def test_empty_bar_chart_with_legend(): @image_comparison(['shadow_argument_types.png'], remove_text=True, style='mpl20', - tol=0.028 if platform.machine() == 'arm64' else 0) + tol=0.028 if sys.platform == 'darwin' else 0) def test_shadow_argument_types(): # Test that different arguments for shadow work as expected fig, ax = plt.subplots() @@ -868,8 +871,8 @@ def test_legend_pathcollection_labelcolor_linecolor_iterable(): # test the labelcolor for labelcolor='linecolor' on PathCollection # with iterable colors fig, ax = plt.subplots() - colors = np.random.default_rng().choice(['r', 'g', 'b'], 10) - ax.scatter(np.arange(10), np.arange(10)*1, label='#1', c=colors) + colors = np.array(['r', 'g', 'b', 'c', 'm'] * 2) + ax.scatter(np.arange(10), np.arange(10), label='#1', c=colors) leg = ax.legend(labelcolor='linecolor') text, = leg.get_texts() @@ -915,8 +918,8 @@ def test_legend_pathcollection_labelcolor_markeredgecolor_iterable(): # test the labelcolor for labelcolor='markeredgecolor' on PathCollection # with iterable colors fig, ax = plt.subplots() - colors = np.random.default_rng().choice(['r', 'g', 'b'], 10) - ax.scatter(np.arange(10), np.arange(10)*1, label='#1', edgecolor=colors) + colors = np.array(['r', 'g', 'b', 'c', 'm'] * 2) + ax.scatter(np.arange(10), np.arange(10), label='#1', edgecolor=colors) leg = ax.legend(labelcolor='markeredgecolor') for text, color in zip(leg.get_texts(), ['k']): @@ -927,7 +930,7 @@ def test_legend_pathcollection_labelcolor_markeredgecolor_cmap(): # test the labelcolor for labelcolor='markeredgecolor' on PathCollection # with a colormap fig, ax = plt.subplots() - edgecolors = mpl.cm.viridis(np.random.rand(10)) + edgecolors = mpl.colormaps["viridis"](np.random.rand(10)) ax.scatter( np.arange(10), np.arange(10), @@ -970,8 +973,8 @@ def test_legend_pathcollection_labelcolor_markerfacecolor_iterable(): # test the labelcolor for labelcolor='markerfacecolor' on PathCollection # with iterable colors fig, ax = plt.subplots() - colors = np.random.default_rng().choice(['r', 'g', 'b'], 10) - ax.scatter(np.arange(10), np.arange(10)*1, label='#1', facecolor=colors) + colors = np.array(['r', 'g', 'b', 'c', 'm'] * 2) + ax.scatter(np.arange(10), np.arange(10), label='#1', facecolor=colors) leg = ax.legend(labelcolor='markerfacecolor') for text, color in zip(leg.get_texts(), ['k']): @@ -982,13 +985,12 @@ def test_legend_pathcollection_labelcolor_markfacecolor_cmap(): # test the labelcolor for labelcolor='markerfacecolor' on PathCollection # with colormaps fig, ax = plt.subplots() - facecolors = mpl.cm.viridis(np.random.rand(10)) + colors = mpl.colormaps["viridis"](np.random.rand(10)) ax.scatter( np.arange(10), np.arange(10), label='#1', - c=np.arange(10), - facecolor=facecolors + c=colors ) leg = ax.legend(labelcolor='markerfacecolor') @@ -1075,6 +1077,201 @@ def test_legend_labelcolor_rcparam_markerfacecolor_short(): assert mpl.colors.same_color(text.get_color(), color) +def assert_last_legend_patch_color(histogram, leg, expected_color, + facecolor=False, edgecolor=False): + """ + Check that histogram color, legend handle color, and legend label color all + match the expected input. Provide facecolor and edgecolor flags to clarify + which feature to match. + """ + label_color = leg.texts[-1].get_color() + patch = leg.get_patches()[-1] + histogram = histogram[-1][0] + assert mpl.colors.same_color(label_color, expected_color) + if facecolor: + assert mpl.colors.same_color(label_color, patch.get_facecolor()) + assert mpl.colors.same_color(label_color, histogram.get_facecolor()) + if edgecolor: + assert mpl.colors.same_color(label_color, patch.get_edgecolor()) + assert mpl.colors.same_color(label_color, histogram.get_edgecolor()) + + +def test_legend_labelcolor_linecolor_histograms(): + x = np.arange(10) + + # testing c kwarg for bar, step, and stepfilled histograms + fig, ax = plt.subplots() + h = ax.hist(x, histtype='bar', color='r', label="red bar hist with a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + h = ax.hist(x, histtype='step', color='g', label="green step hist, green label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'g', edgecolor=True) + + h = ax.hist(x, histtype='stepfilled', color='b', + label="blue stepfilled hist with a blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'b', facecolor=True) + + # testing c, fc, and ec combinations for bar histograms + h = ax.hist(x, histtype='bar', color='r', ec='b', + label="red bar hist with blue edges and a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + h = ax.hist(x, histtype='bar', fc='r', ec='b', + label="red bar hist with blue edges and a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + h = ax.hist(x, histtype='bar', fc='none', ec='b', + label="unfilled blue bar hist with a blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'b', edgecolor=True) + + # testing c, and ec combinations for step histograms + h = ax.hist(x, histtype='step', color='r', ec='b', + label="blue step hist with a blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'b', edgecolor=True) + + h = ax.hist(x, histtype='step', ec='b', + label="blue step hist with a blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'b', edgecolor=True) + + # testing c, fc, and ec combinations for stepfilled histograms + h = ax.hist(x, histtype='stepfilled', color='r', ec='b', + label="red stepfilled hist, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + h = ax.hist(x, histtype='stepfilled', fc='r', ec='b', + label="red stepfilled hist, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + h = ax.hist(x, histtype='stepfilled', fc='none', ec='b', + label="unfilled blue stepfilled hist, blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'b', edgecolor=True) + + h = ax.hist(x, histtype='stepfilled', fc='r', ec='none', + label="edgeless red stepfilled hist with a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_patch_color(h, leg, 'r', facecolor=True) + + +def assert_last_legend_linemarker_color(line_marker, leg, expected_color, color=False, + facecolor=False, edgecolor=False): + """ + Check that line marker color, legend handle color, and legend label color all + match the expected input. Provide color, facecolor and edgecolor flags to clarify + which feature to match. + """ + label_color = leg.texts[-1].get_color() + leg_marker = leg.get_lines()[-1] + assert mpl.colors.same_color(label_color, expected_color) + if color: + assert mpl.colors.same_color(label_color, leg_marker.get_color()) + assert mpl.colors.same_color(label_color, line_marker.get_color()) + if facecolor: + assert mpl.colors.same_color(label_color, leg_marker.get_markerfacecolor()) + assert mpl.colors.same_color(label_color, line_marker.get_markerfacecolor()) + if edgecolor: + assert mpl.colors.same_color(label_color, leg_marker.get_markeredgecolor()) + assert mpl.colors.same_color(label_color, line_marker.get_markeredgecolor()) + + +def test_legend_labelcolor_linecolor_plot(): + x = np.arange(5) + + # testing line plot + fig, ax = plt.subplots() + l, = ax.plot(x, c='r', label="red line with a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'r', color=True) + + # testing c, fc, and ec combinations for maker plots + l, = ax.plot(x, 'o', c='r', label="red circles with a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'r', color=True) + + l, = ax.plot(x, 'o', c='r', mec='b', label="red circles, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'r', color=True) + + l, = ax.plot(x, 'o', mfc='r', mec='b', label="red circles, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'r', facecolor=True) + + # 'none' cases + l, = ax.plot(x, 'o', mfc='none', mec='b', + label="blue unfilled circles, blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'b', edgecolor=True) + + l, = ax.plot(x, 'o', mfc='r', mec='none', label="red edgeless circles, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'r', facecolor=True) + + l, = ax.plot(x, 'o', c='none', mec='none', + label="black label despite invisible circles for dummy entries") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_linemarker_color(l, leg, 'k') + + +def assert_last_legend_scattermarker_color(scatter_marker, leg, expected_color, + facecolor=False, edgecolor=False): + """ + Check that scatter marker color, legend handle color, and legend label color all + match the expected input. Provide facecolor and edgecolor flags to clarify + which feature to match. + """ + label_color = leg.texts[-1].get_color() + leg_handle = leg.legend_handles[-1] + assert mpl.colors.same_color(label_color, expected_color) + if facecolor: + assert mpl.colors.same_color(label_color, leg_handle.get_facecolor()) + assert mpl.colors.same_color(label_color, scatter_marker.get_facecolor()) + if edgecolor: + assert mpl.colors.same_color(label_color, leg_handle.get_edgecolor()) + assert mpl.colors.same_color(label_color, scatter_marker.get_edgecolor()) + + +def test_legend_labelcolor_linecolor_scatter(): + x = np.arange(5) + + # testing c, fc, and ec combinations for scatter plots + fig, ax = plt.subplots() + s = ax.scatter(x, x, c='r', label="red circles with a red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'r', facecolor=True) + + s = ax.scatter(x, x, c='r', ec='b', label="red circles, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'r', facecolor=True) + + s = ax.scatter(x, x, fc='r', ec='b', label="red circles, blue edges, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'r', facecolor=True) + + # 'none' cases + s = ax.scatter(x, x, fc='none', ec='b', label="blue unfilled circles, blue label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'b', edgecolor=True) + + s = ax.scatter(x, x, fc='r', ec='none', label="red edgeless circles, red label") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'r', facecolor=True) + + s = ax.scatter(x, x, c='none', ec='none', + label="black label despite invisible circles for dummy entries") + leg = ax.legend(labelcolor='linecolor') + assert_last_legend_scattermarker_color(s, leg, 'k') + + @pytest.mark.filterwarnings("ignore:No artists with labels found to put in legend") def test_get_set_draggable(): legend = plt.legend() @@ -1191,21 +1388,15 @@ def test_plot_multiple_input_single_label(label): assert legend_texts == [str(label)] * 2 -@pytest.mark.parametrize('label_array', [['low', 'high'], - ('low', 'high'), - np.array(['low', 'high'])]) -def test_plot_single_input_multiple_label(label_array): +def test_plot_single_input_multiple_label(): # test ax.plot() with 1D array like input # and iterable label x = [1, 2, 3] y = [2, 5, 6] fig, ax = plt.subplots() - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match='Passing label as a length 2 sequence'): - ax.plot(x, y, label=label_array) - leg = ax.legend() - assert len(leg.get_texts()) == 1 - assert leg.get_texts()[0].get_text() == str(label_array) + with pytest.raises(ValueError, + match='label must be scalar or have the same length'): + ax.plot(x, y, label=['low', 'high']) def test_plot_single_input_list_label(): @@ -1259,7 +1450,7 @@ def test_subfigure_legend(): ax = subfig.subplots() ax.plot([0, 1], [0, 1], label="line") leg = subfig.legend() - assert leg.figure is subfig + assert leg.get_figure(root=False) is subfig def test_setting_alpha_keeps_polycollection_color(): @@ -1440,6 +1631,21 @@ def test_legend_text(): assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds) +def test_legend_annotate(): + fig, ax = plt.subplots() + + ax.plot([1, 2, 3], label="Line") + ax.annotate("a", xy=(1, 1)) + ax.legend(loc=0) + + with mock.patch.object( + fig, '_get_renderer', wraps=fig._get_renderer) as mocked_get_renderer: + fig.savefig(io.BytesIO()) + + # Finding the legend position should not require _get_renderer to be called + mocked_get_renderer.assert_not_called() + + def test_boxplot_legend_labels(): # Test that legend entries are generated when passing `label`. np.random.seed(19680801) @@ -1470,3 +1676,86 @@ def test_boxplot_legend_labels(): bp4 = axs[3].boxplot(data, label='box A') assert bp4['medians'][0].get_label() == 'box A' assert all(x.get_label().startswith("_") for x in bp4['medians'][1:]) + + +def test_legend_linewidth(): + """Test legend.linewidth parameter and rcParam.""" + fig, ax = plt.subplots() + ax.plot([1, 2, 3], label='data') + + # Test direct parameter + leg = ax.legend(linewidth=2.5) + assert leg.legendPatch.get_linewidth() == 2.5 + + # Test rcParam + with mpl.rc_context({'legend.linewidth': 3.0}): + fig, ax = plt.subplots() + ax.plot([1, 2, 3], label='data') + leg = ax.legend() + assert leg.legendPatch.get_linewidth() == 3.0 + + # Test None default (should inherit from patch.linewidth) + with mpl.rc_context({'legend.linewidth': None, 'patch.linewidth': 1.5}): + fig, ax = plt.subplots() + ax.plot([1, 2, 3], label='data') + leg = ax.legend() + assert leg.legendPatch.get_linewidth() == 1.5 + + # Test that direct parameter overrides rcParam + with mpl.rc_context({'legend.linewidth': 1.0}): + fig, ax = plt.subplots() + ax.plot([1, 2, 3], label='data') + leg = ax.legend(linewidth=4.0) + assert leg.legendPatch.get_linewidth() == 4.0 + + +def test_patchcollection_legend(): + # Test that PatchCollection labels show up in legend and preserve visual + # properties (issue #23998) + fig, ax = plt.subplots() + + pc = mcollections.PatchCollection( + [mpatches.Circle((0, 0), 1), mpatches.Circle((2, 0), 1)], + label="patch collection", + facecolor='red', + edgecolor='blue', + linewidths=3, + linestyle='--', + ) + ax.add_collection(pc) + ax.autoscale_view() + + leg = ax.legend() + + # Check that the legend contains our label + assert len(leg.get_texts()) == 1 + assert leg.get_texts()[0].get_text() == "patch collection" + + # Check that the legend handle exists and has correct visual properties + assert len(leg.legend_handles) == 1 + legend_patch = leg.legend_handles[0] + assert mpl.colors.same_color(legend_patch.get_facecolor(), + pc.get_facecolor()[0]) + assert mpl.colors.same_color(legend_patch.get_edgecolor(), + pc.get_edgecolor()[0]) + assert legend_patch.get_linewidth() == pc.get_linewidths()[0] + assert legend_patch.get_linestyle() == pc.get_linestyles()[0] + + +def test_patchcollection_legend_empty(): + # Test that empty PatchCollection doesn't crash + fig, ax = plt.subplots() + + # Create an empty PatchCollection + pc = mcollections.PatchCollection([], label="empty collection") + ax.add_collection(pc) + + # This should not crash + leg = ax.legend() + + # Check that the label still appears + assert len(leg.get_texts()) == 1 + assert leg.get_texts()[0].get_text() == "empty collection" + + # The legend handle should exist + assert len(leg.legend_handles) == 1 diff --git a/lib/matplotlib/tests/test_lines.py b/lib/matplotlib/tests/test_lines.py index 531237b2ba28..e2a63bbf962b 100644 --- a/lib/matplotlib/tests/test_lines.py +++ b/lib/matplotlib/tests/test_lines.py @@ -4,7 +4,6 @@ import itertools import platform -import timeit from types import SimpleNamespace from cycler import cycler @@ -31,52 +30,6 @@ def test_segment_hits(): assert_array_equal(mlines.segment_hits(cx, cy, x, y, radius), [0]) -# Runtimes on a loaded system are inherently flaky. Not so much that a rerun -# won't help, hopefully. -@pytest.mark.flaky(reruns=3) -def test_invisible_Line_rendering(): - """ - GitHub issue #1256 identified a bug in Line.draw method - - Despite visibility attribute set to False, the draw method was not - returning early enough and some pre-rendering code was executed - though not necessary. - - Consequence was an excessive draw time for invisible Line instances - holding a large number of points (Npts> 10**6) - """ - # Creates big x and y data: - N = 10**7 - x = np.linspace(0, 1, N) - y = np.random.normal(size=N) - - # Create a plot figure: - fig = plt.figure() - ax = plt.subplot() - - # Create a "big" Line instance: - l = mlines.Line2D(x, y) - l.set_visible(False) - # but don't add it to the Axis instance `ax` - - # [here Interactive panning and zooming is pretty responsive] - # Time the canvas drawing: - t_no_line = min(timeit.repeat(fig.canvas.draw, number=1, repeat=3)) - # (gives about 25 ms) - - # Add the big invisible Line: - ax.add_line(l) - - # [Now interactive panning and zooming is very slow] - # Time the canvas drawing: - t_invisible_line = min(timeit.repeat(fig.canvas.draw, number=1, repeat=3)) - # gives about 290 ms for N = 10**7 pts - - slowdown_factor = t_invisible_line / t_no_line - slowdown_threshold = 2 # trying to avoid false positive failures - assert slowdown_factor < slowdown_threshold - - def test_set_line_coll_dash(): fig, ax = plt.subplots() np.random.seed(0) @@ -139,8 +92,15 @@ def test_valid_linestyles(): line.set_linestyle('aardvark') +@mpl.style.context('mpl20') +def test_zero_linewidth_dashed_uses_solid_gc_dashes(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1], ls='--', lw=0) + fig.draw_without_rendering() + + @image_comparison(['drawstyle_variants.png'], remove_text=True, - tol=0.03 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.03) def test_drawstyle_variants(): fig, axs = plt.subplots(6) dss = ["default", "steps-mid", "steps-pre", "steps-post", "steps", None] @@ -153,7 +113,7 @@ def test_drawstyle_variants(): ax.set(xlim=(0, 2), ylim=(0, 2)) -@check_figures_equal(extensions=('png',)) +@check_figures_equal() def test_no_subslice_with_transform(fig_ref, fig_test): ax = fig_ref.add_subplot() x = np.arange(2000) @@ -183,9 +143,8 @@ def test_set_drawstyle(): assert len(line.get_path().vertices) == len(x) -@image_comparison( - ['line_collection_dashes'], remove_text=True, style='mpl20', - tol=0 if platform.machine() == 'x86_64' else 0.65) +@image_comparison(['line_collection_dashes'], remove_text=True, style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.65) def test_set_line_coll_dash_image(): fig, ax = plt.subplots() np.random.seed(0) @@ -220,8 +179,8 @@ def test_marker_fill_styles(): markeredgecolor=color, markeredgewidth=2) - ax.set_ylim([0, 7.5]) - ax.set_xlim([-5, 155]) + ax.set_ylim(0, 7.5) + ax.set_xlim(-5, 155) def test_markerfacecolor_fillstyle(): @@ -260,7 +219,7 @@ def test_step_markers(fig_test, fig_ref): @pytest.mark.parametrize("parent", ["figure", "axes"]) -@check_figures_equal(extensions=('png',)) +@check_figures_equal() def test_markevery(fig_test, fig_ref, parent): np.random.seed(42) x = np.linspace(0, 1, 14) @@ -333,13 +292,13 @@ def test_marker_as_markerstyle(): @image_comparison(['striped_line.png'], remove_text=True, style='mpl20') -def test_striped_lines(): +def test_striped_lines(text_placeholders): rng = np.random.default_rng(19680801) _, ax = plt.subplots() ax.plot(rng.uniform(size=12), color='orange', gapcolor='blue', - linestyle='--', lw=5, label=' ') + linestyle='--', lw=5, label='blue in orange') ax.plot(rng.uniform(size=12), color='red', gapcolor='black', - linestyle=(0, (2, 5, 4, 2)), lw=5, label=' ', alpha=0.5) + linestyle=(0, (2, 5, 4, 2)), lw=5, label='black in red', alpha=0.5) ax.legend(handlelength=5) @@ -386,7 +345,7 @@ def test_input_copy(fig_test, fig_ref): fig_ref.add_subplot().plot([0, 2, 4], [0, 2, 4], ".-", drawstyle="steps") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_markevery_prop_cycle(fig_test, fig_ref): """Test that we can set markevery prop_cycle.""" cases = [None, 8, (30, 8), [16, 24, 30], [0, -1], @@ -417,16 +376,20 @@ def test_axline_setters(): line2 = ax.axline((.1, .1), (.8, .4)) # Testing xy1, xy2 and slope setters. # This should not produce an error. - line1.set_xy1(.2, .3) + line1.set_xy1((.2, .3)) line1.set_slope(2.4) - line2.set_xy1(.3, .2) - line2.set_xy2(.6, .8) + line2.set_xy1((.3, .2)) + line2.set_xy2((.6, .8)) # Testing xy1, xy2 and slope getters. # Should return the modified values. assert line1.get_xy1() == (.2, .3) assert line1.get_slope() == 2.4 assert line2.get_xy1() == (.3, .2) assert line2.get_xy2() == (.6, .8) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + line1.set_xy1(.2, .3) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + line2.set_xy2(.6, .8) # Testing setting xy2 and slope together. # These test should raise a ValueError with pytest.raises(ValueError, @@ -436,3 +399,14 @@ def test_axline_setters(): with pytest.raises(ValueError, match="Cannot set a 'slope' value while 'xy2' is set"): line2.set_slope(3) + + +def test_axline_small_slope(): + """Test that small slopes are not coerced to zero in the transform.""" + line = plt.axline((0, 0), slope=1e-14) + p1 = line.get_transform().transform_point((0, 0)) + p2 = line.get_transform().transform_point((1, 1)) + # y-values must be slightly different + dy = p2[1] - p1[1] + assert dy > 0 + assert dy < 4e-12 diff --git a/lib/matplotlib/tests/test_marker.py b/lib/matplotlib/tests/test_marker.py index 463ff1d05c96..171d06fd3d93 100644 --- a/lib/matplotlib/tests/test_marker.py +++ b/lib/matplotlib/tests/test_marker.py @@ -22,6 +22,7 @@ def test_marker_fillstyle(): r'$\frac{1}{2}$', "$\u266B$", 1, + np.int64(1), markers.TICKLEFT, [[-1, 0], [1, 0]], np.array([[-1, 0], [1, 0]]), @@ -63,7 +64,7 @@ def _recache(self): self._snap_threshold = None -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_poly_marker(fig_test, fig_ref): ax_test = fig_test.add_subplot() ax_ref = fig_ref.add_subplot() @@ -123,7 +124,7 @@ def test_star_marker(): # are corners and get a slight bevel. The reference markers are just singular # lines without corners, so they have no bevel, and we need to add a slight # tolerance. -@check_figures_equal(tol=1.45) +@check_figures_equal(extensions=['png', 'pdf', 'svg'], tol=1.45) def test_asterisk_marker(fig_test, fig_ref, request): ax_test = fig_test.add_subplot() ax_ref = fig_ref.add_subplot() @@ -159,7 +160,7 @@ def draw_ref_marker(y, style, size): # The bullet mathtext marker is not quite a circle, so this is not a perfect match, but # it is close enough to confirm that the text-based marker is centred correctly. But we # still need a small tolerance to work around that difference. -@check_figures_equal(extensions=['png'], tol=1.86) +@check_figures_equal(tol=1.86) def test_text_marker(fig_ref, fig_test): ax_ref = fig_ref.add_subplot() ax_test = fig_test.add_subplot() @@ -168,7 +169,7 @@ def test_text_marker(fig_ref, fig_test): ax_test.plot(0, 0, marker=r'$\bullet$', markersize=100, markeredgewidth=0) -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_marker_clipping(fig_ref, fig_test): # Plotting multiple markers can trigger different optimized paths in # backends, so compare single markers vs multiple to ensure they are @@ -181,9 +182,9 @@ def test_marker_clipping(fig_ref, fig_test): width = 2 * marker_size * ncol height = 2 * marker_size * nrow * 2 fig_ref.set_size_inches((width / fig_ref.dpi, height / fig_ref.dpi)) - ax_ref = fig_ref.add_axes([0, 0, 1, 1]) + ax_ref = fig_ref.add_axes((0, 0, 1, 1)) fig_test.set_size_inches((width / fig_test.dpi, height / fig_ref.dpi)) - ax_test = fig_test.add_axes([0, 0, 1, 1]) + ax_test = fig_test.add_axes((0, 0, 1, 1)) for i, marker in enumerate(markers.MarkerStyle.markers): x = i % ncol diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 6ce327f38341..33fb8918d22a 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -4,9 +4,9 @@ from pathlib import Path import platform import re -import shlex -from xml.etree import ElementTree as ET +import textwrap from typing import Any +from xml.etree import ElementTree as ET import numpy as np from packaging.version import parse as parse_version @@ -17,7 +17,8 @@ import matplotlib as mpl from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib.pyplot as plt -from matplotlib import mathtext, _mathtext +from matplotlib import font_manager as fm, mathtext, _mathtext +from matplotlib.ft2font import LoadFlags pyparsing_version = parse_version(pyparsing.__version__) @@ -77,7 +78,7 @@ r'$x+{y}^{\frac{2}{k+1}}$', r'$\frac{a}{b/2}$', r'${a}_{0}+\frac{1}{{a}_{1}+\frac{1}{{a}_{2}+\frac{1}{{a}_{3}+\frac{1}{{a}_{4}}}}}$', - r'${a}_{0}+\frac{1}{{a}_{1}+\frac{1}{{a}_{2}+\frac{1}{{a}_{3}+\frac{1}{{a}_{4}}}}}$', + r'${a}_{0}+\dfrac{1}{{a}_{1}+\dfrac{1}{{a}_{2}+\dfrac{1}{{a}_{3}+\dfrac{1}{{a}_{4}}}}}$', r'$\binom{n}{k/2}$', r'$\binom{p}{2}{x}^{2}{y}^{p-2}-\frac{1}{1-x}\frac{1}{1-{x}^{2}}$', r'${x}^{2y}$', @@ -124,12 +125,21 @@ r'$,$ $.$ $1{,}234{, }567{ , }890$ and $1,234,567,890$', # github issue 5799 r'$\left(X\right)_{a}^{b}$', # github issue 7615 r'$\dfrac{\$100.00}{y}$', # github issue #1888 - r'$a=-b-c$' # github issue #28180 + r'$a=-b-c$', # github issue #28180 ] # 'svgastext' tests switch svg output to embed text as text (rather than as # paths). svgastext_math_tests = [ r'$-$-', + # Check all AutoHeightChar substitutions. + *[ + r'$\left' + lc + r' M \middle/ ? \middle\backslash ? \right' + rc + ' ' + # Normal size. + r'\left' + lc + r' \frac{M}{B} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # big size. + r'\left' + lc + r' \frac{\frac{M}{I}}{B} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # bigg size. + r'\left' + lc + r' \frac{\frac{M}{I}}{\frac{B}{U}} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # Big size. + r'\left' + lc + r'\frac{\frac{\frac{M}{I}}{N}}{\frac{\frac{B}{U}}{G}} \middle/ ? \middle\backslash ? \right' + rc + '$' # Bigg size. + for lc, rc in ['()', '[]', '<>', (r'\{', r'\}'), (r'\lfloor', r'\rfloor'), (r'\lceil', r'\rceil')] + ], ] # 'lightweight' tests test only a single fontset (dejavusans, which is the # default) and only png outputs, in order to minimize the size of baseline @@ -198,8 +208,8 @@ *('}' for font in fonts), '$', ]) - for set in chars: - font_tests.append(wrapper % set) + for font_set in chars: + font_tests.append(wrapper % font_set) @pytest.fixture @@ -214,12 +224,15 @@ def baseline_images(request, fontset, index, text): @pytest.mark.parametrize( 'fontset', ['cm', 'stix', 'stixsans', 'dejavusans', 'dejavuserif']) @pytest.mark.parametrize('baseline_images', ['mathtext'], indirect=True) -@image_comparison(baseline_images=None, - tol=0.011 if platform.machine() in ('ppc64le', 's390x') else 0) +@image_comparison( + baseline_images=None, style='mpl20', + tol=(0.013 + if platform.machine() in ('ppc64le', 's390x') or platform.system() == 'Windows' + else 0)) def test_mathtext_rendering(baseline_images, fontset, index, text): mpl.rcParams['mathtext.fontset'] = fontset fig = plt.figure(figsize=(5.25, 0.75)) - fig.text(0.5, 0.5, text, + fig.text(0.5, 0.5, text, fontsize=12, horizontalalignment='center', verticalalignment='center') @@ -228,7 +241,7 @@ def test_mathtext_rendering(baseline_images, fontset, index, text): @pytest.mark.parametrize('fontset', ['cm', 'dejavusans']) @pytest.mark.parametrize('baseline_images', ['mathtext0'], indirect=True) @image_comparison( - baseline_images=None, extensions=['svg'], + baseline_images=None, extensions=['svg'], style='mpl20', savefig_kwarg={'metadata': { # Minimize image size. 'Creator': None, 'Date': None, 'Format': None, 'Type': None}}) def test_mathtext_rendering_svgastext(baseline_images, fontset, index, text): @@ -236,7 +249,7 @@ def test_mathtext_rendering_svgastext(baseline_images, fontset, index, text): mpl.rcParams['svg.fonttype'] = 'none' # Minimize image size. fig = plt.figure(figsize=(5.25, 0.75)) fig.patch.set(visible=False) # Minimize image size. - fig.text(0.5, 0.5, text, + fig.text(0.5, 0.5, text, fontsize=16, horizontalalignment='center', verticalalignment='center') @@ -244,10 +257,10 @@ def test_mathtext_rendering_svgastext(baseline_images, fontset, index, text): ids=range(len(lightweight_math_tests))) @pytest.mark.parametrize('fontset', ['dejavusans']) @pytest.mark.parametrize('baseline_images', ['mathtext1'], indirect=True) -@image_comparison(baseline_images=None, extensions=['png']) +@image_comparison(baseline_images=None, extensions=['png'], style='mpl20') def test_mathtext_rendering_lightweight(baseline_images, fontset, index, text): fig = plt.figure(figsize=(5.25, 0.75)) - fig.text(0.5, 0.5, text, math_fontfamily=fontset, + fig.text(0.5, 0.5, text, fontsize=12, math_fontfamily=fontset, horizontalalignment='center', verticalalignment='center') @@ -256,22 +269,22 @@ def test_mathtext_rendering_lightweight(baseline_images, fontset, index, text): @pytest.mark.parametrize( 'fontset', ['cm', 'stix', 'stixsans', 'dejavusans', 'dejavuserif']) @pytest.mark.parametrize('baseline_images', ['mathfont'], indirect=True) -@image_comparison(baseline_images=None, extensions=['png'], +@image_comparison(baseline_images=None, extensions=['png'], style='mpl20', tol=0.011 if platform.machine() in ('ppc64le', 's390x') else 0) def test_mathfont_rendering(baseline_images, fontset, index, text): mpl.rcParams['mathtext.fontset'] = fontset fig = plt.figure(figsize=(5.25, 0.75)) - fig.text(0.5, 0.5, text, + fig.text(0.5, 0.5, text, fontsize=12, horizontalalignment='center', verticalalignment='center') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_short_long_accents(fig_test, fig_ref): acc_map = _mathtext.Parser._accent_map short_accs = [s for s in acc_map if len(s) == 1] corresponding_long_accs = [] for s in short_accs: - l, = [l for l in acc_map if len(l) > 1 and acc_map[l] == acc_map[s]] + l, = (l for l in acc_map if len(l) > 1 and acc_map[l] == acc_map[s]) corresponding_long_accs.append(l) fig_test.text(0, .5, "$" + "".join(rf"\{s}a" for s in short_accs) + "$") fig_ref.text( @@ -374,13 +387,13 @@ def test_single_minus_sign(): assert (t != 0xff).any() # assert that canvas is not all white. -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_spaces(fig_test, fig_ref): fig_test.text(.5, .5, r"$1\,2\>3\ 4$") fig_ref.text(.5, .5, r"$1\/2\:3~4$") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_operator_space(fig_test, fig_ref): fig_test.text(0.1, 0.1, r"$\log 6$") fig_test.text(0.1, 0.2, r"$\log(6)$") @@ -390,7 +403,7 @@ def test_operator_space(fig_test, fig_ref): fig_test.text(0.1, 0.6, r"$\operatorname{op}[6]$") fig_test.text(0.1, 0.7, r"$\cos^2$") fig_test.text(0.1, 0.8, r"$\log_2$") - fig_test.text(0.1, 0.9, r"$\sin^2 \cos$") # GitHub issue #17852 + fig_test.text(0.1, 0.9, r"$\sin^2 \max \cos$") # GitHub issue #17852 fig_ref.text(0.1, 0.1, r"$\mathrm{log\,}6$") fig_ref.text(0.1, 0.2, r"$\mathrm{log}(6)$") @@ -400,16 +413,16 @@ def test_operator_space(fig_test, fig_ref): fig_ref.text(0.1, 0.6, r"$\mathrm{op}[6]$") fig_ref.text(0.1, 0.7, r"$\mathrm{cos}^2$") fig_ref.text(0.1, 0.8, r"$\mathrm{log}_2$") - fig_ref.text(0.1, 0.9, r"$\mathrm{sin}^2 \mathrm{\,cos}$") + fig_ref.text(0.1, 0.9, r"$\mathrm{sin}^2 \mathrm{\,max} \mathrm{\,cos}$") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_inverted_delimiters(fig_test, fig_ref): fig_test.text(.5, .5, r"$\left)\right($", math_fontfamily="dejavusans") fig_ref.text(.5, .5, r"$)($", math_fontfamily="dejavusans") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_genfrac_displaystyle(fig_test, fig_ref): fig_test.text(0.1, 0.1, r"$\dfrac{2x}{3y}$") @@ -433,10 +446,10 @@ def test_mathtext_fallback_invalid(): @pytest.mark.parametrize( "fallback,fontlist", [("cm", ['DejaVu Sans', 'mpltest', 'STIXGeneral', 'cmr10', 'STIXGeneral']), - ("stix", ['DejaVu Sans', 'mpltest', 'STIXGeneral'])]) + ("stix", ['DejaVu Sans', 'mpltest', 'STIXGeneral', 'STIXGeneral', 'STIXGeneral'])]) def test_mathtext_fallback(fallback, fontlist): mpl.font_manager.fontManager.addfont( - str(Path(__file__).resolve().parent / 'mpltest.ttf')) + (Path(__file__).resolve().parent / 'data/mpltest.ttf')) mpl.rcParams["svg.fonttype"] = 'none' mpl.rcParams['mathtext.fontset'] = 'custom' mpl.rcParams['mathtext.rm'] = 'mpltest' @@ -453,10 +466,10 @@ def test_mathtext_fallback(fallback, fontlist): fig.savefig(buff, format="svg") tspans = (ET.fromstring(buff.getvalue()) .findall(".//{http://www.w3.org/2000/svg}tspan[@style]")) - # Getting the last element of the style attrib is a close enough - # approximation for parsing the font property. - char_fonts = [shlex.split(tspan.attrib["style"])[-1] for tspan in tspans] - assert char_fonts == fontlist + char_fonts = [ + re.search(r"font-family: '([\w ]+)'", tspan.attrib["style"]).group(1) + for tspan in tspans] + assert char_fonts == fontlist, f'Expected {fontlist}, got {char_fonts}' mpl.font_manager.fontManager.ttflist.pop() @@ -467,7 +480,7 @@ def test_math_to_image(tmp_path): @image_comparison(baseline_images=['math_fontfamily_image.png'], - savefig_kwarg={'dpi': 40}) + savefig_kwarg={'dpi': 40}, style='mpl20') def test_math_fontfamily(): fig = plt.figure(figsize=(10, 3)) fig.text(0.2, 0.7, r"$This\ text\ should\ have\ one\ font$", @@ -555,7 +568,68 @@ def test_mathtext_operators(): fig.draw_without_rendering() -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_boldsymbol(fig_test, fig_ref): fig_test.text(0.1, 0.2, r"$\boldsymbol{\mathrm{abc0123\alpha}}$") fig_ref.text(0.1, 0.2, r"$\mathrm{abc0123\alpha}$") + + +@check_figures_equal() +def test_mathnormal(fig_test, fig_ref): + # ensure that \mathnormal is parsed and sets digits upright + fig_test.text(0.1, 0.2, r"$\mathnormal{0123456789}$") + fig_ref.text(0.1, 0.2, r"$\mathrm{0123456789}$") + + +# Test vector output because in raster output some minor differences remain, +# likely due to double-striking. +@check_figures_equal(extensions=["pdf"]) +def test_phantoms(fig_test, fig_ref): + fig_test.text(0.5, 0.9, r"$\rlap{rlap}extra$", ha="left") + fig_ref.text(0.5, 0.9, r"$rlap$", ha="left") + fig_ref.text(0.5, 0.9, r"$extra$", ha="left") + + fig_test.text(0.5, 0.8, r"$extra\llap{llap}$", ha="right") + fig_ref.text(0.5, 0.8, r"$llap$", ha="right") + fig_ref.text(0.5, 0.8, r"$extra$", ha="right") + + fig_test.text(0.5, 0.7, r"$\phantom{phantom}$") + + +def test_box_repr(): + s = repr(_mathtext.Parser().parse( + r"$\frac{1}{2}$", + _mathtext.DejaVuSansFonts(fm.FontProperties(), LoadFlags.NO_HINTING), + fontsize=12, dpi=100)) + assert s == textwrap.dedent("""\ + Hlist[ + Hlist[], + Hlist[ + Hlist[ + Hbox, + Vlist[ + HCentered[ + Glue, + Hlist[ + `1`, + k2.36, + ], + Glue, + ], + Vbox, + Hrule, + Vbox, + HCentered[ + Glue, + Hlist[ + `2`, + k2.02, + ], + Glue, + ], + ], + Hbox, + ], + ], + Hlist[], + ]""") diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py index 37b41fafdb78..6dab056f9170 100644 --- a/lib/matplotlib/tests/test_matplotlib.py +++ b/lib/matplotlib/tests/test_matplotlib.py @@ -1,6 +1,7 @@ import os import subprocess import sys +from unittest.mock import patch import pytest @@ -18,9 +19,9 @@ def test_parse_to_version_info(version_str, version_tuple): assert matplotlib._parse_to_version_info(version_str) == version_tuple -@pytest.mark.skipif(sys.platform == "win32", - reason="chmod() doesn't work as is on Windows") -@pytest.mark.skipif(sys.platform != "win32" and os.geteuid() == 0, +@pytest.mark.skipif(sys.platform not in ["linux", "darwin"], + reason="chmod() doesn't work on this platform") +@pytest.mark.skipif(sys.platform in ["linux", "darwin"] and os.geteuid() == 0, reason="chmod() doesn't work as root") def test_tmpconfigdir_warning(tmp_path): """Test that a warning is emitted if a temporary configdir must be used.""" @@ -80,3 +81,62 @@ def test_importable_with__OO(): [sys.executable, "-OO", "-c", program], env={**os.environ, "MPLBACKEND": ""}, check=True ) + + +@patch('matplotlib.subprocess.check_output') +def test_get_executable_info_timeout(mock_check_output): + """ + Test that _get_executable_info raises ExecutableNotFoundError if the + command times out. + """ + + mock_check_output.side_effect = subprocess.TimeoutExpired(cmd=['mock'], timeout=30) + + with pytest.raises(matplotlib.ExecutableNotFoundError, match='Timed out'): + matplotlib._get_executable_info.__wrapped__('inkscape') + + +@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test") +def test_configdir_uses_localappdata_on_windows(tmp_path): + """Test that on Windows, config/cache dir uses LOCALAPPDATA for fresh installs.""" + localappdata = tmp_path / "AppData/Local" + localappdata.mkdir(parents=True) + # Set USERPROFILE to tmp_path so the old location check finds nothing + fake_home = tmp_path / "home" + fake_home.mkdir() + + proc = subprocess_run_for_testing( + [sys.executable, "-c", + "import matplotlib; print(matplotlib.get_configdir())"], + env={**os.environ, "LOCALAPPDATA": str(localappdata), + "USERPROFILE": str(fake_home), "MPLCONFIGDIR": ""}, + capture_output=True, text=True, check=True) + + configdir = proc.stdout.strip() + # On Windows with no existing old config, should use LOCALAPPDATA\matplotlib + assert configdir == str(localappdata / "matplotlib") + + +@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test") +def test_configdir_uses_userprofile_on_windows_if_exists(tmp_path): + """ + Test that on Windows, config/cache dir uses %USERPROFILE% if .matplotlib + exists. + """ + localappdata = tmp_path / "AppData/Local" + localappdata.mkdir(parents=True) + fake_home = tmp_path / "home" + fake_home.mkdir() + old_configdir = fake_home / ".matplotlib" + old_configdir.mkdir() + + proc = subprocess_run_for_testing( + [sys.executable, "-c", + "import matplotlib; print(matplotlib.get_configdir())"], + env={**os.environ, "LOCALAPPDATA": str(localappdata), + "USERPROFILE": str(fake_home), "MPLCONFIGDIR": ""}, + capture_output=True, text=True, check=True) + + configdir = proc.stdout.strip() + # On Windows with existing old config, should continue using it + assert configdir == str(old_configdir) diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index 3b0d2529b5f1..82f877b4cc01 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -1,3 +1,5 @@ +import sys + from numpy.testing import (assert_allclose, assert_almost_equal, assert_array_equal, assert_array_almost_equal_nulp) import numpy as np @@ -429,7 +431,16 @@ def test_spectral_helper_psd(self, mode, case): assert spec.shape[0] == freqs.shape[0] assert spec.shape[1] == getattr(self, f"t_{case}").shape[0] - def test_csd(self): + @pytest.mark.parametrize('bitsize', [ + pytest.param(None, id='default'), + pytest.param(32, + marks=pytest.mark.skipif(sys.maxsize <= 2**32, + reason='System is already 32-bit'), + id='32-bit') + ]) + def test_csd(self, bitsize, monkeypatch): + if bitsize is not None: + monkeypatch.setattr(sys, 'maxsize', 2**bitsize) freqs = self.freqs_density spec, fsp = mlab.csd(x=self.y, y=self.y+1, NFFT=self.NFFT_density, @@ -873,6 +884,8 @@ def test_single_dataset_element(self): with pytest.raises(ValueError): mlab.GaussianKDE([42]) + @pytest.mark.skipif(sys.platform == 'emscripten', + reason="WASM doesn't support floating-point exceptions") def test_silverman_multidim_dataset(self): """Test silverman's for a multi-dimensional array.""" x1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) @@ -886,6 +899,8 @@ def test_silverman_singledim_dataset(self): y_expected = 0.76770389927475502 assert_almost_equal(mygauss.covariance_factor(), y_expected, 7) + @pytest.mark.skipif(sys.platform == 'emscripten', + reason="WASM doesn't support floating-point exceptions") def test_scott_multidim_dataset(self): """Test scott's output for a multi-dimensional array.""" x1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) diff --git a/lib/matplotlib/tests/test_multivariate_colormaps.py b/lib/matplotlib/tests/test_multivariate_colormaps.py new file mode 100644 index 000000000000..592058212a24 --- /dev/null +++ b/lib/matplotlib/tests/test_multivariate_colormaps.py @@ -0,0 +1,593 @@ +import numpy as np +from numpy.testing import assert_array_equal, assert_allclose +import matplotlib.pyplot as plt +from matplotlib.testing.decorators import (image_comparison, + remove_ticks_and_titles) +import matplotlib as mpl +import pytest +from pathlib import Path +from io import BytesIO +from PIL import Image +import base64 + + +@image_comparison(["bivariate_cmap_shapes.png"]) +def test_bivariate_cmap_shapes(): + x_0 = np.repeat(np.linspace(-0.1, 1.1, 10, dtype='float32')[None, :], 10, axis=0) + x_1 = x_0.T + + fig, axes = plt.subplots(1, 4, figsize=(10, 2)) + + # shape = 'square' + cmap = mpl.bivar_colormaps['BiPeak'] + axes[0].imshow(cmap((x_0, x_1)), interpolation='nearest') + + # shape = 'circle' + cmap = mpl.bivar_colormaps['BiCone'] + axes[1].imshow(cmap((x_0, x_1)), interpolation='nearest') + + # shape = 'ignore' + cmap = mpl.bivar_colormaps['BiPeak'] + cmap = cmap.with_extremes(shape='ignore') + axes[2].imshow(cmap((x_0, x_1)), interpolation='nearest') + + # shape = circleignore + cmap = mpl.bivar_colormaps['BiCone'] + cmap = cmap.with_extremes(shape='circleignore') + axes[3].imshow(cmap((x_0, x_1)), interpolation='nearest') + remove_ticks_and_titles(fig) + + +def test_multivar_creation(): + # test creation of a custom multivariate colorbar + blues = mpl.colormaps['Blues'] + cmap = mpl.colors.MultivarColormap((blues, 'Oranges'), 'sRGB_sub') + y, x = np.mgrid[0:3, 0:3]/2 + im = cmap((y, x)) + res = np.array([[[0.96862745, 0.94509804, 0.92156863, 1], + [0.96004614, 0.53504037, 0.23277201, 1], + [0.46666667, 0.1372549, 0.01568627, 1]], + [[0.41708574, 0.64141484, 0.75980008, 1], + [0.40850442, 0.23135717, 0.07100346, 1], + [0, 0, 0, 1]], + [[0.03137255, 0.14901961, 0.34117647, 1], + [0.02279123, 0, 0, 1], + [0, 0, 0, 1]]]) + assert_allclose(im, res, atol=0.01) + + with pytest.raises(ValueError, match="colormaps must be a list of"): + cmap = mpl.colors.MultivarColormap((blues, [blues]), 'sRGB_sub') + with pytest.raises(ValueError, match="A MultivarColormap must"): + cmap = mpl.colors.MultivarColormap('blues', 'sRGB_sub') + with pytest.raises(ValueError, match="A MultivarColormap must"): + cmap = mpl.colors.MultivarColormap((blues), 'sRGB_sub') + + +@image_comparison(["multivar_alpha_mixing.png"]) +def test_multivar_alpha_mixing(): + # test creation of a custom colormap using 'rainbow' + # and a colormap that goes from alpha = 1 to alpha = 0 + rainbow = mpl.colormaps['rainbow'] + alpha = np.zeros((256, 4)) + alpha[:, 3] = np.linspace(1, 0, 256) + alpha_cmap = mpl.colors.LinearSegmentedColormap.from_list('from_list', alpha) + + cmap = mpl.colors.MultivarColormap((rainbow, alpha_cmap), 'sRGB_add') + y, x = np.mgrid[0:10, 0:10]/9 + im = cmap((y, x)) + + fig, ax = plt.subplots() + ax.imshow(im, interpolation='nearest') + remove_ticks_and_titles(fig) + + +def test_multivar_cmap_call(): + cmap = mpl.multivar_colormaps['2VarAddA'] + assert_array_equal(cmap((0.0, 0.0)), (0, 0, 0, 1)) + assert_array_equal(cmap((1.0, 1.0)), (1, 1, 1, 1)) + assert_allclose(cmap((0.0, 0.0), alpha=0.1), (0, 0, 0, 0.1), atol=0.1) + + cmap = mpl.multivar_colormaps['2VarSubA'] + assert_array_equal(cmap((0.0, 0.0)), (1, 1, 1, 1)) + assert_allclose(cmap((1.0, 1.0)), (0, 0, 0, 1), atol=0.1) + + # check outside and bad + cs = cmap([(0., 0., 0., 1.2, np.nan), (0., 1.2, np.nan, 0., 0., )]) + assert_allclose(cs, [[1., 1., 1., 1.], + [0.801, 0.426, 0.119, 1.], + [0., 0., 0., 0.], + [0.199, 0.574, 0.881, 1.], + [0., 0., 0., 0.]]) + + assert_array_equal(cmap((0.0, 0.0), bytes=True), (255, 255, 255, 255)) + + with pytest.raises(ValueError, match="alpha is array-like but its shape"): + cs = cmap([(0, 5, 9), (0, 0, 0)], alpha=(0.5, 0.3)) + + with pytest.raises(ValueError, match="For the selected colormap the data"): + cs = cmap([(0, 5, 9), (0, 0, 0), (0, 0, 0)]) + + with pytest.raises(ValueError, match="clip cannot be false"): + cs = cmap([(0, 5, 9), (0, 0, 0)], bytes=True, clip=False) + # Tests calling a multivariate colormap with integer values + cmap = mpl.multivar_colormaps['2VarSubA'] + + # call only integers + cs = cmap([(0, 50, 100, 0, 0, 300), (0, 0, 0, 50, 100, 300)]) + res = np.array([[1, 1, 1, 1], + [0.85176471, 0.91029412, 0.96023529, 1], + [0.70452941, 0.82764706, 0.93358824, 1], + [0.94358824, 0.88505882, 0.83511765, 1], + [0.89729412, 0.77417647, 0.66823529, 1], + [0, 0, 0, 1]]) + assert_allclose(cs, res, atol=0.01) + + # call only integers, wrong byte order + swapped_dt = np.dtype(int).newbyteorder() + cs = cmap([np.array([0, 50, 100, 0, 0, 300], dtype=swapped_dt), + np.array([0, 0, 0, 50, 100, 300], dtype=swapped_dt)]) + assert_allclose(cs, res, atol=0.01) + + # call mix floats integers + # check calling with bytes = True + cs = cmap([(0, 50, 100, 0, 0, 300), (0, 0, 0, 50, 100, 300)], bytes=True) + res = np.array([[255, 255, 255, 255], + [217, 232, 244, 255], + [179, 211, 238, 255], + [240, 225, 212, 255], + [228, 197, 170, 255], + [0, 0, 0, 255]]) + assert_allclose(cs, res, atol=0.01) + + cs = cmap([(0, 50, 100, 0, 0, 300), (0, 0, 0, 50, 100, 300)], alpha=0.5) + res = np.array([[1, 1, 1, 0.5], + [0.85176471, 0.91029412, 0.96023529, 0.5], + [0.70452941, 0.82764706, 0.93358824, 0.5], + [0.94358824, 0.88505882, 0.83511765, 0.5], + [0.89729412, 0.77417647, 0.66823529, 0.5], + [0, 0, 0, 0.5]]) + assert_allclose(cs, res, atol=0.01) + # call with tuple + assert_allclose(cmap((100, 120), bytes=True, alpha=0.5), + [149, 142, 136, 127], atol=0.01) + + # alpha and bytes + cs = cmap([(0, 5, 9, 0, 0, 10), (0, 0, 0, 5, 11, 12)], bytes=True, alpha=0.5) + res = np.array([[0, 0, 255, 127], + [141, 0, 255, 127], + [255, 0, 255, 127], + [0, 115, 255, 127], + [0, 255, 255, 127], + [255, 255, 255, 127]]) + + # bad alpha shape + with pytest.raises(ValueError, match="alpha is array-like but its shape"): + cs = cmap([(0, 5, 9), (0, 0, 0)], bytes=True, alpha=(0.5, 0.3)) + + cmap = cmap.with_extremes(bad=(1, 1, 1, 1)) + cs = cmap([(0., 1.1, np.nan), (0., 1.2, 1.)]) + res = np.array([[1., 1., 1., 1.], + [0., 0., 0., 1.], + [1., 1., 1., 1.]]) + assert_allclose(cs, res, atol=0.01) + + # call outside with tuple + assert_allclose(cmap((300, 300), bytes=True, alpha=0.5), + [0, 0, 0, 127], atol=0.01) + with pytest.raises(ValueError, + match="For the selected colormap the data must have"): + cs = cmap((0, 5, 9)) + + # test over/under + cmap = mpl.multivar_colormaps['2VarAddA'] + with pytest.raises(ValueError, match='i.e. be of length 2'): + cmap.with_extremes(over=0) + with pytest.raises(ValueError, match='i.e. be of length 2'): + cmap.with_extremes(under=0) + + cmap = cmap.with_extremes(under=[(0, 0, 0, 0)]*2) + assert_allclose((0, 0, 0, 0), cmap((-1., 0)), atol=1e-2) + cmap = cmap.with_extremes(over=[(0, 0, 0, 0)]*2) + assert_allclose((0, 0, 0, 0), cmap((2., 0)), atol=1e-2) + + +def test_multivar_bad_mode(): + cmap = mpl.multivar_colormaps['2VarSubA'] + with pytest.raises(ValueError, match="is not a valid value for"): + cmap = mpl.colors.MultivarColormap(cmap[:], 'bad') + + +def test_multivar_resample(): + cmap = mpl.multivar_colormaps['3VarAddA'] + cmap_resampled = cmap.resampled((None, 10, 3)) + + assert_allclose(cmap_resampled[1](0.25), (0.093, 0.116, 0.059, 1.0)) + assert_allclose(cmap_resampled((0, 0.25, 0)), (0.093, 0.116, 0.059, 1.0)) + assert_allclose(cmap_resampled((1, 0.25, 1)), (0.417271, 0.264624, 0.274976, 1.), + atol=0.01) + + with pytest.raises(ValueError, match="lutshape must be of length"): + cmap = cmap.resampled(4) + + +def test_bivar_cmap_call_tuple(): + cmap = mpl.bivar_colormaps['BiOrangeBlue'] + assert_allclose(cmap((1.0, 1.0)), (1, 1, 1, 1)) + assert_allclose(cmap((0.0, 0.0)), (0, 0, 0, 1)) + assert_allclose(cmap((0.2, 0.8)), (0.2, 0.5, 0.8, 1)) + assert_allclose(cmap((0.0, 0.0), alpha=0.1), (0, 0, 0, 0.1)) + + +def test_bivar_cmap_lut_smooth(): + cmap = mpl.bivar_colormaps['BiOrangeBlue'] + + assert_allclose(cmap.lut[:, 0, 0], np.linspace(0, 1, 256)) + assert_allclose(cmap.lut[:, 255, 0], np.linspace(0, 1, 256)) + assert_allclose(cmap.lut[:, 0, 1], np.linspace(0, 0.5, 256)) + assert_allclose(cmap.lut[:, 153, 1], np.linspace(0.3, 0.8, 256)) + assert_allclose(cmap.lut[:, 255, 1], np.linspace(0.5, 1, 256)) + + assert_allclose(cmap.lut[0, :, 1], np.linspace(0, 0.5, 256)) + assert_allclose(cmap.lut[102, :, 1], np.linspace(0.2, 0.7, 256)) + assert_allclose(cmap.lut[255, :, 1], np.linspace(0.5, 1, 256)) + assert_allclose(cmap.lut[0, :, 2], np.linspace(0, 1, 256)) + assert_allclose(cmap.lut[255, :, 2], np.linspace(0, 1, 256)) + + +def test_bivar_cmap_call(): + """ + Tests calling a bivariate colormap with integer values + """ + im = np.ones((10, 12, 4)) + im[:, :, 0] = np.linspace(0, 1, 10)[:, np.newaxis] + im[:, :, 1] = np.linspace(0, 1, 12)[np.newaxis, :] + cmap = mpl.colors.BivarColormapFromImage(im) + + # call only integers + cs = cmap([(0, 5, 9, 0, 0, 10), (0, 0, 0, 5, 11, 12)]) + res = np.array([[0, 0, 1, 1], + [0.556, 0, 1, 1], + [1, 0, 1, 1], + [0, 0.454, 1, 1], + [0, 1, 1, 1], + [1, 1, 1, 1]]) + assert_allclose(cs, res, atol=0.01) + # call only integers, wrong byte order + swapped_dt = np.dtype(int).newbyteorder() + cs = cmap([np.array([0, 5, 9, 0, 0, 10], dtype=swapped_dt), + np.array([0, 0, 0, 5, 11, 12], dtype=swapped_dt)]) + assert_allclose(cs, res, atol=0.01) + + # call mix floats integers + cmap = cmap.with_extremes(outside=(1, 0, 0, 0)) + cs = cmap([(0.5, 0), (0, 3)]) + res = np.array([[0.555, 0, 1, 1], + [0, 0.2727, 1, 1]]) + assert_allclose(cs, res, atol=0.01) + + # check calling with bytes = True + cs = cmap([(0, 5, 9, 0, 0, 10), (0, 0, 0, 5, 11, 12)], bytes=True) + res = np.array([[0, 0, 255, 255], + [141, 0, 255, 255], + [255, 0, 255, 255], + [0, 115, 255, 255], + [0, 255, 255, 255], + [255, 255, 255, 255]]) + assert_allclose(cs, res, atol=0.01) + + # test alpha + cs = cmap([(0, 5, 9, 0, 0, 10), (0, 0, 0, 5, 11, 12)], alpha=0.5) + res = np.array([[0, 0, 1, 0.5], + [0.556, 0, 1, 0.5], + [1, 0, 1, 0.5], + [0, 0.454, 1, 0.5], + [0, 1, 1, 0.5], + [1, 1, 1, 0.5]]) + assert_allclose(cs, res, atol=0.01) + # call with tuple + assert_allclose(cmap((10, 12), bytes=True, alpha=0.5), + [255, 255, 255, 127], atol=0.01) + + # alpha and bytes + cs = cmap([(0, 5, 9, 0, 0, 10), (0, 0, 0, 5, 11, 12)], bytes=True, alpha=0.5) + res = np.array([[0, 0, 255, 127], + [141, 0, 255, 127], + [255, 0, 255, 127], + [0, 115, 255, 127], + [0, 255, 255, 127], + [255, 255, 255, 127]]) + + # bad alpha shape + with pytest.raises(ValueError, match="alpha is array-like but its shape"): + cs = cmap([(0, 5, 9), (0, 0, 0)], bytes=True, alpha=(0.5, 0.3)) + + # set shape to 'ignore'. + # final point is outside colormap and should then receive + # the 'outside' (in this case [1,0,0,0]) + # also test 'bad' (in this case [1,1,1,0]) + cmap = cmap.with_extremes(outside=(1, 0, 0, 0), bad=(1, 1, 1, 0), shape='ignore') + cs = cmap([(0., 1.1, np.nan), (0., 1.2, 1.)]) + res = np.array([[0, 0, 1, 1], + [1, 0, 0, 0], + [1, 1, 1, 0]]) + assert_allclose(cs, res, atol=0.01) + # call outside with tuple + assert_allclose(cmap((10, 12), bytes=True, alpha=0.5), + [255, 0, 0, 127], atol=0.01) + # with integers + cs = cmap([(0, 10), (0, 12)]) + res = np.array([[0, 0, 1, 1], + [1, 0, 0, 0]]) + assert_allclose(cs, res, atol=0.01) + + with pytest.raises(ValueError, + match="For a `BivarColormap` the data must have"): + cs = cmap((0, 5, 9)) + + cmap = cmap.with_extremes(shape='circle') + with pytest.raises(NotImplementedError, + match="only implemented for use with with floats"): + cs = cmap([(0, 5, 9, 0, 0, 9), (0, 0, 0, 5, 11, 11)]) + + +def test_bivar_cmap_1d_origin(): + """ + Test getting 1D colormaps with different origins + """ + cmap0 = mpl.bivar_colormaps['BiOrangeBlue'] + assert_allclose(cmap0[0].colors[:, 0], np.linspace(0, 1, 256)) + assert_allclose(cmap0[0].colors[:, 1], np.linspace(0, 0.5, 256)) + assert_allclose(cmap0[0].colors[:, 2], 0) + assert_allclose(cmap0[1].colors[:, 0], 0) + assert_allclose(cmap0[1].colors[:, 1], np.linspace(0, 0.5, 256)) + assert_allclose(cmap0[1].colors[:, 2], np.linspace(0, 1, 256)) + + cmap1 = cmap0.with_extremes(origin=(0, 1)) + assert_allclose(cmap1[0].colors[:, 0], np.linspace(0, 1, 256)) + assert_allclose(cmap1[0].colors[:, 1], np.linspace(0.5, 1, 256)) + assert_allclose(cmap1[0].colors[:, 2], 1) + assert_allclose(cmap1[1].colors, cmap0[1].colors) + + cmap2 = cmap0.with_extremes(origin=(0.2, 0.4)) + assert_allclose(cmap2[0].colors[:, 0], np.linspace(0, 1, 256)) + assert_allclose(cmap2[0].colors[:, 1], np.linspace(0.2, 0.7, 256)) + assert_allclose(cmap2[0].colors[:, 2], 0.4) + assert_allclose(cmap2[1].colors[:, 0], 0.2) + assert_allclose(cmap2[1].colors[:, 1], np.linspace(0.1, 0.6, 256)) + assert_allclose(cmap2[1].colors[:, 2], np.linspace(0, 1, 256)) + + with pytest.raises(KeyError, + match="only 0 or 1 are valid keys"): + cs = cmap0[2] + + +def test_bivar_getitem(): + """Test __getitem__ on BivarColormap""" + xA = ([.0, .25, .5, .75, 1., -1, 2], [.5]*7) + xB = ([.5]*7, [.0, .25, .5, .75, 1., -1, 2]) + + cmaps = mpl.bivar_colormaps['BiPeak'] + assert_array_equal(cmaps(xA), cmaps[0](xA[0])) + assert_array_equal(cmaps(xB), cmaps[1](xB[1])) + + cmaps = cmaps.with_extremes(shape='ignore') + assert_array_equal(cmaps(xA), cmaps[0](xA[0])) + assert_array_equal(cmaps(xB), cmaps[1](xB[1])) + + xA = ([.0, .25, .5, .75, 1., -1, 2], [.0]*7) + xB = ([.0]*7, [.0, .25, .5, .75, 1., -1, 2]) + cmaps = mpl.bivar_colormaps['BiOrangeBlue'] + assert_array_equal(cmaps(xA), cmaps[0](xA[0])) + assert_array_equal(cmaps(xB), cmaps[1](xB[1])) + + cmaps = cmaps.with_extremes(shape='ignore') + assert_array_equal(cmaps(xA), cmaps[0](xA[0])) + assert_array_equal(cmaps(xB), cmaps[1](xB[1])) + + +def test_bivar_cmap_bad_shape(): + """ + Tests calling a bivariate colormap with integer values + """ + cmap = mpl.bivar_colormaps['BiCone'] + _ = cmap.lut + with pytest.raises(ValueError, + match="is not a valid value for shape"): + cmap.with_extremes(shape='bad_shape') + + with pytest.raises(ValueError, + match="is not a valid value for shape"): + mpl.colors.BivarColormapFromImage(np.ones((3, 3, 4)), + shape='bad_shape') + + +def test_bivar_cmap_bad_lut(): + """ + Tests calling a bivariate colormap with integer values + """ + with pytest.raises(ValueError, + match="The lut must be an array of shape"): + cmap = mpl.colors.BivarColormapFromImage(np.ones((3, 3, 5))) + + +def test_bivar_cmap_from_image(): + """ + This tests the creation and use of a bivariate colormap + generated from an image + """ + + data_0 = np.arange(6).reshape((2, 3))/5 + data_1 = np.arange(6).reshape((3, 2)).T/5 + + # bivariate colormap from array + cim = np.ones((10, 12, 3)) + cim[:, :, 0] = np.arange(10)[:, np.newaxis]/10 + cim[:, :, 1] = np.arange(12)[np.newaxis, :]/12 + + cmap = mpl.colors.BivarColormapFromImage(cim) + im = cmap((data_0, data_1)) + res = np.array([[[0, 0, 1, 1], + [0.2, 0.33333333, 1, 1], + [0.4, 0.75, 1, 1]], + [[0.6, 0.16666667, 1, 1], + [0.8, 0.58333333, 1, 1], + [0.9, 0.91666667, 1, 1]]]) + assert_allclose(im, res, atol=0.01) + + # input as unit8 + cim = np.ones((10, 12, 3))*255 + cim[:, :, 0] = np.arange(10)[:, np.newaxis]/10*255 + cim[:, :, 1] = np.arange(12)[np.newaxis, :]/12*255 + + cmap = mpl.colors.BivarColormapFromImage(cim.astype(np.uint8)) + im = cmap((data_0, data_1)) + res = np.array([[[0, 0, 1, 1], + [0.2, 0.33333333, 1, 1], + [0.4, 0.75, 1, 1]], + [[0.6, 0.16666667, 1, 1], + [0.8, 0.58333333, 1, 1], + [0.9, 0.91666667, 1, 1]]]) + assert_allclose(im, res, atol=0.01) + + # bivariate colormap from array + png_path = Path(__file__).parent / "baseline_images/pngsuite/basn2c16.png" + cim = Image.open(png_path) + cim = np.asarray(cim.convert('RGBA')) + + cmap = mpl.colors.BivarColormapFromImage(cim) + im = cmap((data_0, data_1), bytes=True) + res = np.array([[[255, 255, 0, 255], + [156, 206, 0, 255], + [49, 156, 49, 255]], + [[206, 99, 0, 255], + [99, 49, 107, 255], + [0, 0, 255, 255]]]) + assert_allclose(im, res, atol=0.01) + + +def test_bivar_resample(): + cmap = mpl.bivar_colormaps['BiOrangeBlue'] + + assert_allclose(cmap.resampled((2, 2))((0.25, 0.25)), (0, 0, 0, 1)) + assert_allclose(cmap.resampled((-2, 2))((0.25, 0.25)), (1., 0.5, 0., 1.)) + assert_allclose(cmap.resampled((2, -2))((0.25, 0.25)), (0., 0.5, 1., 1.)) + assert_allclose(cmap.resampled((-2, -2))((0.25, 0.25)), (1, 1, 1, 1)) + + assert_allclose(cmap((0.8, 0.4)), (0.8, 0.6, 0.4, 1.)) + assert_allclose(cmap.reversed()((1 - 0.8, 1 - 0.4)), (0.8, 0.6, 0.4, 1.)) + + assert_allclose(cmap((0.6, 0.2)), (0.6, 0.4, 0.2, 1.)) + assert_allclose(cmap.transposed()((0.2, 0.6)), (0.6, 0.4, 0.2, 1.)) + + with pytest.raises(ValueError, match="lutshape must be of length"): + cmap = cmap.resampled(4) + + +def test_bivariate_repr_png(): + cmap = mpl.bivar_colormaps['BiCone'] + png = cmap._repr_png_() + assert len(png) > 0 + img = Image.open(BytesIO(png)) + assert img.width > 0 + assert img.height > 0 + assert 'Title' in img.text + assert 'Description' in img.text + assert 'Author' in img.text + assert 'Software' in img.text + + +def test_bivariate_repr_html(): + cmap = mpl.bivar_colormaps['BiCone'] + html = cmap._repr_html_() + assert len(html) > 0 + png = cmap._repr_png_() + assert base64.b64encode(png).decode('ascii') in html + assert cmap.name in html + assert html.startswith('') + + +def test_multivariate_repr_png(): + cmap = mpl.multivar_colormaps['3VarAddA'] + png = cmap._repr_png_() + assert len(png) > 0 + img = Image.open(BytesIO(png)) + assert img.width > 0 + assert img.height > 0 + assert 'Title' in img.text + assert 'Description' in img.text + assert 'Author' in img.text + assert 'Software' in img.text + + +def test_multivariate_repr_html(): + cmap = mpl.multivar_colormaps['3VarAddA'] + html = cmap._repr_html_() + assert len(html) > 0 + for c in cmap: + png = c._repr_png_() + assert base64.b64encode(png).decode('ascii') in html + assert cmap.name in html + assert html.startswith('') + + +def test_bivar_eq(): + """ + Tests equality between multivariate colormaps + """ + cmap_0 = mpl.bivar_colormaps['BiPeak'] + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + assert (cmap_0 == cmap_1) is True + + cmap_1 = mpl.multivar_colormaps['2VarAddA'] + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.bivar_colormaps['BiCone'] + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + cmap_1 = cmap_1.with_extremes(bad='k') + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + cmap_1 = cmap_1.with_extremes(outside='k') + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + cmap_1._init() + cmap_1._lut *= 0.5 + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + cmap_1 = cmap_1.with_extremes(shape='ignore') + assert (cmap_0 == cmap_1) is False + + +def test_multivar_eq(): + """ + Tests equality between multivariate colormaps + """ + cmap_0 = mpl.multivar_colormaps['2VarAddA'] + + cmap_1 = mpl.multivar_colormaps['2VarAddA'] + assert (cmap_0 == cmap_1) is True + + cmap_1 = mpl.bivar_colormaps['BiPeak'] + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.colors.MultivarColormap([cmap_0[0]]*2, + 'sRGB_add') + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.multivar_colormaps['3VarAddA'] + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.multivar_colormaps['2VarAddA'] + cmap_1 = cmap_1.with_extremes(bad='k') + assert (cmap_0 == cmap_1) is False + + cmap_1 = mpl.multivar_colormaps['2VarAddA'] + cmap_1 = mpl.colors.MultivarColormap(cmap_1[:], 'sRGB_sub') + assert (cmap_0 == cmap_1) is False diff --git a/lib/matplotlib/tests/test_offsetbox.py b/lib/matplotlib/tests/test_offsetbox.py index f18fa7c777d1..f126b1cbb466 100644 --- a/lib/matplotlib/tests/test_offsetbox.py +++ b/lib/matplotlib/tests/test_offsetbox.py @@ -48,8 +48,8 @@ def test_offsetbox_clipping(): da.add_artist(bg) da.add_artist(line) ax.add_artist(anchored_box) - ax.set_xlim((0, 1)) - ax.set_ylim((0, 1)) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) def test_offsetbox_clip_children(): @@ -455,8 +455,55 @@ def test_remove_draggable(): def test_draggable_in_subfigure(): fig = plt.figure() # Put annotation at lower left corner to make it easily pickable below. - ann = fig.subfigures().add_axes([0, 0, 1, 1]).annotate("foo", (0, 0)) + ann = fig.subfigures().add_axes((0, 0, 1, 1)).annotate("foo", (0, 0)) ann.draggable(True) fig.canvas.draw() # Texts are non-pickable until the first draw. MouseEvent("button_press_event", fig.canvas, 1, 1)._process() assert ann._draggable.got_artist + # Stop dragging the annotation. + MouseEvent("button_release_event", fig.canvas, 1, 1)._process() + assert not ann._draggable.got_artist + # A scroll event should not initiate a drag. + MouseEvent("scroll_event", fig.canvas, 1, 1)._process() + assert not ann._draggable.got_artist + # An event outside the annotation should not initiate a drag. + bbox = ann.get_window_extent() + MouseEvent("button_press_event", fig.canvas, bbox.x1+2, bbox.y1+2)._process() + assert not ann._draggable.got_artist + + +def test_anchored_offsetbox_tuple_and_float_borderpad(): + """ + Test AnchoredOffsetbox correctly handles both float and tuple for borderpad. + """ + + fig, ax = plt.subplots() + + # Case 1: Establish a baseline with float value + text_float = AnchoredText("float", loc='lower left', borderpad=5) + ax.add_artist(text_float) + + # Case 2: Test that a symmetric tuple gives the exact same result. + text_tuple_equal = AnchoredText("tuple", loc='lower left', borderpad=(5, 5)) + ax.add_artist(text_tuple_equal) + + # Case 3: Test that an asymmetric tuple with different values works as expected. + text_tuple_asym = AnchoredText("tuple_asym", loc='lower left', borderpad=(10, 4)) + ax.add_artist(text_tuple_asym) + + # Draw the canvas to calculate final positions + fig.canvas.draw() + + pos_float = text_float.get_window_extent() + pos_tuple_equal = text_tuple_equal.get_window_extent() + pos_tuple_asym = text_tuple_asym.get_window_extent() + + # Assertion 1: Prove that borderpad=5 is identical to borderpad=(5, 5). + assert pos_tuple_equal.x0 == pos_float.x0 + assert pos_tuple_equal.y0 == pos_float.y0 + + # Assertion 2: Prove that the asymmetric padding moved the box + # further from the origin than the baseline in the x-direction and less far + # in the y-direction. + assert pos_tuple_asym.x0 > pos_float.x0 + assert pos_tuple_asym.y0 < pos_float.y0 diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 3544ce8cb10c..12a12cf3e90d 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -178,7 +178,7 @@ def test_rotate_rect(): assert_almost_equal(rect1.get_verts(), new_verts) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_rotate_rect_draw(fig_test, fig_ref): ax_test = fig_test.add_subplot() ax_ref = fig_ref.add_subplot() @@ -199,7 +199,7 @@ def test_rotate_rect_draw(fig_test, fig_ref): assert rect_test.get_angle() == angle -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_dash_offset_patch_draw(fig_test, fig_ref): ax_test = fig_test.add_subplot() ax_ref = fig_ref.add_subplot() @@ -241,7 +241,7 @@ def test_negative_rect(): assert_array_equal(np.roll(neg_vertices, 2, 0), pos_vertices) -@image_comparison(['clip_to_bbox']) +@image_comparison(['clip_to_bbox.png'], style='mpl20') def test_clip_to_bbox(): fig, ax = plt.subplots() ax.set_xlim([-18, 20]) @@ -395,7 +395,7 @@ def test_patch_linestyle_accents(): fig.canvas.draw() -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_patch_linestyle_none(fig_test, fig_ref): circle = mpath.Path.unit_circle() @@ -438,7 +438,7 @@ def test_wedge_movement(): @image_comparison(['wedge_range'], remove_text=True, - tol=0.009 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.009) def test_wedge_range(): ax = plt.axes() @@ -550,7 +550,7 @@ def test_multi_color_hatch(): ax.add_patch(r) -@image_comparison(['units_rectangle.png']) +@image_comparison(['units_rectangle.png'], style='mpl20') def test_units_rectangle(): import matplotlib.testing.jpl_units as U U.register() @@ -564,7 +564,7 @@ def test_units_rectangle(): @image_comparison(['connection_patch.png'], style='mpl20', remove_text=True, - tol=0.024 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.024) def test_connection_patch(): fig, (ax1, ax2) = plt.subplots(1, 2) @@ -583,7 +583,7 @@ def test_connection_patch(): ax2.add_artist(con) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_connection_patch_fig(fig_test, fig_ref): # Test that connection patch can be added as figure artist, and that figure # pixels count negative values from the top right corner (this API may be @@ -606,6 +606,28 @@ def test_connection_patch_fig(fig_test, fig_ref): fig_ref.add_artist(con) +@check_figures_equal() +def test_connection_patch_pixel_points(fig_test, fig_ref): + xyA_pts = (.3, .2) + xyB_pts = (-30, -20) + + ax1, ax2 = fig_test.subplots(1, 2) + con = mpatches.ConnectionPatch(xyA=xyA_pts, coordsA="axes points", axesA=ax1, + xyB=xyB_pts, coordsB="figure points", + arrowstyle="->", shrinkB=5) + fig_test.add_artist(con) + + plt.rcParams["savefig.dpi"] = plt.rcParams["figure.dpi"] + + ax1, ax2 = fig_ref.subplots(1, 2) + xyA_pix = (xyA_pts[0]*(fig_ref.dpi/72), xyA_pts[1]*(fig_ref.dpi/72)) + xyB_pix = (xyB_pts[0]*(fig_ref.dpi/72), xyB_pts[1]*(fig_ref.dpi/72)) + con = mpatches.ConnectionPatch(xyA=xyA_pix, coordsA="axes pixels", axesA=ax1, + xyB=xyB_pix, coordsB="figure pixels", + arrowstyle="->", shrinkB=5) + fig_ref.add_artist(con) + + def test_datetime_rectangle(): # Check that creating a rectangle with timedeltas doesn't fail from datetime import datetime, timedelta @@ -656,7 +678,7 @@ def test_contains_points(): # Currently fails with pdf/svg, probably because some parts assume a dpi of 72. -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_shadow(fig_test, fig_ref): xy = np.array([.2, .3]) dxy = np.array([.1, .2]) @@ -791,7 +813,7 @@ def test_boxstyle_errors(fmt, match): BoxStyle(fmt) -@image_comparison(baseline_images=['annulus'], extensions=['png']) +@image_comparison(['annulus.png'], style='mpl20') def test_annulus(): fig, ax = plt.subplots() @@ -803,7 +825,7 @@ def test_annulus(): ax.set_aspect('equal') -@image_comparison(baseline_images=['annulus'], extensions=['png']) +@image_comparison(['annulus.png'], style='mpl20') def test_annulus_setters(): fig, ax = plt.subplots() @@ -824,7 +846,7 @@ def test_annulus_setters(): ell.angle = 45 -@image_comparison(baseline_images=['annulus'], extensions=['png']) +@image_comparison(['annulus.png'], style='mpl20') def test_annulus_setters2(): fig, ax = plt.subplots() @@ -885,6 +907,14 @@ def test_default_linestyle(): assert patch.get_linestyle() == 'solid' +@mpl.style.context('mpl20') +def test_patch_zero_linewidth_dashed_uses_solid_gc_dashes(): + fig, ax = plt.subplots() + ax.add_patch(Rectangle( + (0, 0), 1, 1, fill=False, linewidth=0, linestyle='--')) + fig.draw_without_rendering() + + def test_default_capstyle(): patch = Patch() assert patch.get_capstyle() == 'butt' @@ -919,7 +949,9 @@ def test_arc_in_collection(fig_test, fig_ref): arc2 = Arc([.5, .5], .5, 1, theta1=0, theta2=60, angle=20) col = mcollections.PatchCollection(patches=[arc2], facecolors='none', edgecolors='k') - fig_ref.subplots().add_patch(arc1) + ax_ref = fig_ref.subplots() + ax_ref.add_patch(arc1) + ax_ref.autoscale_view() fig_test.subplots().add_collection(col) @@ -960,3 +992,198 @@ def test_arrow_set_data(): ) arrow.set_data(x=.5, dx=3, dy=8, width=1.2) assert np.allclose(expected2, np.round(arrow.get_verts(), 2)) + + +@check_figures_equal(extensions=["png", "pdf", "svg", "eps"]) +def test_set_and_get_hatch_linewidth(fig_test, fig_ref): + ax_test = fig_test.add_subplot() + ax_ref = fig_ref.add_subplot() + + lw = 2.0 + + with plt.rc_context({"hatch.linewidth": lw}): + ax_ref.add_patch(mpatches.Rectangle((0, 0), 1, 1, hatch="x")) + + ax_test.add_patch(mpatches.Rectangle((0, 0), 1, 1, hatch="x")) + ax_test.patches[0].set_hatch_linewidth(lw) + + assert ax_ref.patches[0].get_hatch_linewidth() == lw + assert ax_test.patches[0].get_hatch_linewidth() == lw + + +def test_patch_hatchcolor_inherit_logic(): + with mpl.rc_context({'hatch.color': 'edge'}): + # Test for when edgecolor and hatchcolor is set + rect = Rectangle((0, 0), 1, 1, hatch='//', ec='red', + hatchcolor='yellow') + assert mcolors.same_color(rect.get_edgecolor(), 'red') + assert mcolors.same_color(rect.get_hatchcolor(), 'yellow') + + # Test for explicitly setting edgecolor and then hatchcolor + rect = Rectangle((0, 0), 1, 1, hatch='//') + rect.set_edgecolor('orange') + assert mcolors.same_color(rect.get_hatchcolor(), 'orange') + rect.set_hatchcolor('cyan') + assert mcolors.same_color(rect.get_hatchcolor(), 'cyan') + + # Test for explicitly setting hatchcolor and then edgecolor + rect = Rectangle((0, 0), 1, 1, hatch='//') + rect.set_hatchcolor('purple') + assert mcolors.same_color(rect.get_hatchcolor(), 'purple') + rect.set_edgecolor('green') + assert mcolors.same_color(rect.get_hatchcolor(), 'purple') + + # Smoke test for setting with numpy array + rect.set_hatchcolor(np.ones(3)) + + +def test_patch_hatchcolor_fallback_logic(): + # Test for when hatchcolor parameter is passed + rect = Rectangle((0, 0), 1, 1, hatch='//', hatchcolor='green') + assert mcolors.same_color(rect.get_hatchcolor(), 'green') + + # Test that hatchcolor parameter takes precedence over rcParam + # When edgecolor is not set + with mpl.rc_context({'hatch.color': 'blue'}): + rect = Rectangle((0, 0), 1, 1, hatch='//', hatchcolor='green') + assert mcolors.same_color(rect.get_hatchcolor(), 'green') + # When edgecolor is set + with mpl.rc_context({'hatch.color': 'yellow'}): + rect = Rectangle((0, 0), 1, 1, hatch='//', hatchcolor='green', edgecolor='red') + assert mcolors.same_color(rect.get_hatchcolor(), 'green') + + # Test that hatchcolor is not overridden by edgecolor when + # hatchcolor parameter is not passed and hatch.color rcParam is set to a color + # When edgecolor is not set + with mpl.rc_context({'hatch.color': 'blue'}): + rect = Rectangle((0, 0), 1, 1, hatch='//') + assert mcolors.same_color(rect.get_hatchcolor(), 'blue') + # When edgecolor is set + with mpl.rc_context({'hatch.color': 'blue'}): + rect = Rectangle((0, 0), 1, 1, hatch='//', edgecolor='red') + assert mcolors.same_color(rect.get_hatchcolor(), 'blue') + + # Test that hatchcolor matches edgecolor when + # hatchcolor parameter is not passed and hatch.color rcParam is set to 'edge' + with mpl.rc_context({'hatch.color': 'edge'}): + rect = Rectangle((0, 0), 1, 1, hatch='//', edgecolor='red') + assert mcolors.same_color(rect.get_hatchcolor(), 'red') + # hatchcolor parameter is set to 'edge' + rect = Rectangle((0, 0), 1, 1, hatch='//', hatchcolor='edge', edgecolor='orange') + assert mcolors.same_color(rect.get_hatchcolor(), 'orange') + + # Test for default hatchcolor when hatchcolor parameter is not passed and + # hatch.color rcParam is set to 'edge' and edgecolor is not set + rect = Rectangle((0, 0), 1, 1, hatch='//') + assert mcolors.same_color(rect.get_hatchcolor(), mpl.rcParams['patch.edgecolor']) + + +def test_facecolor_none_force_edgecolor_false(): + rcParams['patch.force_edgecolor'] = False # default value + rect = Rectangle((0, 0), 1, 1, facecolor="none") + assert rect.get_edgecolor() == (0.0, 0.0, 0.0, 0.0) + + +def test_facecolor_none_force_edgecolor_true(): + rcParams['patch.force_edgecolor'] = True + rect = Rectangle((0, 0), 1, 1, facecolor="none") + assert rect.get_edgecolor() == (0.0, 0.0, 0.0, 1) + + +def test_facecolor_none_edgecolor_force_edgecolor(): + + # Case 1:force_edgecolor =False -> rcParams['patch.edgecolor'] should NOT be applied + rcParams['patch.force_edgecolor'] = False + rcParams['patch.edgecolor'] = 'red' + rect = Rectangle((0, 0), 1, 1, facecolor="none") + assert not mcolors.same_color(rect.get_edgecolor(), rcParams['patch.edgecolor']) + + # Case 2:force_edgecolor =True -> rcParams['patch.edgecolor'] SHOULD be applied + rcParams['patch.force_edgecolor'] = True + rcParams['patch.edgecolor'] = 'red' + rect = Rectangle((0, 0), 1, 1, facecolor="none") + assert mcolors.same_color(rect.get_edgecolor(), rcParams['patch.edgecolor']) + + +def test_empty_fancyarrow(): + fig, ax = plt.subplots() + arrow = ax.arrow([], [], [], []) + assert arrow is not None + + +def test_patch_edgegapcolor_getter_setter(): + """Test that edgegapcolor can be set and retrieved.""" + patch = Rectangle((0, 0), 1, 1) + # Default is None + assert patch.get_edgegapcolor() is None + + # Set to a color + patch.set_edgegapcolor('red') + assert mcolors.same_color(patch.get_edgegapcolor(), 'red') + + # Set back to None + patch.set_edgegapcolor(None) + assert patch.get_edgegapcolor() is None + + +def test_patch_edgegapcolor_init(): + """Test that edgegapcolor can be passed in __init__.""" + patch = Rectangle((0, 0), 1, 1, edgegapcolor='blue') + assert mcolors.same_color(patch.get_edgegapcolor(), 'blue') + + +def test_patch_has_dashed_edge(): + """Test _has_dashed_edge method for patches.""" + patch = Rectangle((0, 0), 1, 1) + patch.set_linestyle('solid') + assert not patch._has_dashed_edge() + + patch.set_linestyle('--') + assert patch._has_dashed_edge() + + patch.set_linestyle(':') + assert patch._has_dashed_edge() + + patch.set_linestyle('-.') + assert patch._has_dashed_edge() + + # Test custom linestyle + patch.set_linestyle((0, (2, 2, 10, 2))) + assert patch._has_dashed_edge() + + +def test_patch_edgegapcolor_update_from(): + """Test that edgegapcolor is copied in update_from.""" + patch1 = Rectangle((0, 0), 1, 1, edgegapcolor='green') + patch2 = Rectangle((1, 1), 2, 2) + + patch2.update_from(patch1) + assert mcolors.same_color(patch2.get_edgegapcolor(), 'green') + + +@image_comparison(['patch_edgegapcolor.png'], remove_text=True, style='mpl20') +def test_patch_edgegapcolor_visual(): + """Visual test for patch edgegapcolor (striped edges).""" + fig, ax = plt.subplots() + + # Rectangle with edgegapcolor + rect = Rectangle((0.1, 0.1), 0.3, 0.3, fill=False, + edgecolor='blue', edgegapcolor='orange', + linestyle='--', linewidth=3) + ax.add_patch(rect) + + # Ellipse with edgegapcolor + ellipse = Ellipse((0.7, 0.3), 0.3, 0.2, fill=False, + edgecolor='red', edgegapcolor='yellow', + linestyle=':', linewidth=3) + ax.add_patch(ellipse) + + # Polygon with edgegapcolor + polygon = Polygon([[0.1, 0.6], [0.3, 0.9], [0.4, 0.6]], fill=False, + edgecolor='green', edgegapcolor='purple', + linestyle='-.', linewidth=3) + ax.add_patch(polygon) + + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_aspect('equal') diff --git a/lib/matplotlib/tests/test_path.py b/lib/matplotlib/tests/test_path.py index 2c4df6ea3b39..a61f01c0d48a 100644 --- a/lib/matplotlib/tests/test_path.py +++ b/lib/matplotlib/tests/test_path.py @@ -151,12 +151,12 @@ def test_nonlinear_containment(): @image_comparison(['arrow_contains_point.png'], remove_text=True, style='mpl20', - tol=0.027 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.027) def test_arrow_contains_point(): # fix bug (#8384) fig, ax = plt.subplots() - ax.set_xlim((0, 2)) - ax.set_ylim((0, 2)) + ax.set_xlim(0, 2) + ax.set_ylim(0, 2) # create an arrow with Curve style arrow = patches.FancyArrowPatch((0.5, 0.25), (1.5, 0.75), @@ -283,7 +283,7 @@ def test_marker_paths_pdf(): @image_comparison(['nan_path'], style='default', remove_text=True, extensions=['pdf', 'svg', 'eps', 'png'], - tol=0.009 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.009) def test_nan_isolated_points(): y0 = [0, np.nan, 2, np.nan, 4, 5, 6] @@ -355,15 +355,49 @@ def test_path_deepcopy(): # Should not raise any error verts = [[0, 0], [1, 1]] codes = [Path.MOVETO, Path.LINETO] - path1 = Path(verts) - path2 = Path(verts, codes) + path1 = Path(verts, readonly=True) + path2 = Path(verts, codes, readonly=True) path1_copy = path1.deepcopy() path2_copy = path2.deepcopy() assert path1 is not path1_copy assert path1.vertices is not path1_copy.vertices + assert_array_equal(path1.vertices, path1_copy.vertices) + assert path1.readonly + assert not path1_copy.readonly assert path2 is not path2_copy assert path2.vertices is not path2_copy.vertices + assert_array_equal(path2.vertices, path2_copy.vertices) assert path2.codes is not path2_copy.codes + assert_array_equal(path2.codes, path2_copy.codes) + assert path2.readonly + assert not path2_copy.readonly + + +def test_path_deepcopy_cycle(): + class PathWithCycle(Path): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.x = self + + p = PathWithCycle([[0, 0], [1, 1]], readonly=True) + p_copy = p.deepcopy() + assert p_copy is not p + assert p.readonly + assert not p_copy.readonly + assert p_copy.x is p_copy + + class PathWithCycle2(Path): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.x = [self] * 2 + + p2 = PathWithCycle2([[0, 0], [1, 1]], readonly=True) + p2_copy = p2.deepcopy() + assert p2_copy is not p2 + assert p2.readonly + assert not p2_copy.readonly + assert p2_copy.x[0] is p2_copy + assert p2_copy.x[1] is p2_copy def test_path_shallowcopy(): @@ -541,3 +575,84 @@ def test_cleanup_closepoly(): cleaned = p.cleaned(remove_nans=True) assert len(cleaned) == 1 assert cleaned.codes[0] == Path.STOP + + +def test_interpolated_moveto(): + # Initial path has two subpaths with two LINETOs each + vertices = np.array([[0, 0], + [0, 1], + [1, 2], + [4, 4], + [4, 5], + [5, 5]]) + codes = [Path.MOVETO, Path.LINETO, Path.LINETO] * 2 + + path = Path(vertices, codes) + result = path.interpolated(3) + + # Result should have two subpaths with six LINETOs each + expected_subpath_codes = [Path.MOVETO] + [Path.LINETO] * 6 + np.testing.assert_array_equal(result.codes, expected_subpath_codes * 2) + + +def test_interpolated_closepoly(): + codes = [Path.MOVETO] + [Path.LINETO]*2 + [Path.CLOSEPOLY] + vertices = [(4, 3), (5, 4), (5, 3), (0, 0)] + + path = Path(vertices, codes) + result = path.interpolated(2) + + expected_vertices = np.array([[4, 3], + [4.5, 3.5], + [5, 4], + [5, 3.5], + [5, 3], + [4.5, 3], + [4, 3]]) + expected_codes = [Path.MOVETO] + [Path.LINETO]*5 + [Path.CLOSEPOLY] + + np.testing.assert_allclose(result.vertices, expected_vertices) + np.testing.assert_array_equal(result.codes, expected_codes) + + # Usually closepoly is the last vertex but does not have to be. + codes += [Path.LINETO] + vertices += [(2, 1)] + + path = Path(vertices, codes) + result = path.interpolated(2) + + extra_expected_vertices = np.array([[3, 2], + [2, 1]]) + expected_vertices = np.concatenate([expected_vertices, extra_expected_vertices]) + + expected_codes += [Path.LINETO] * 2 + + np.testing.assert_allclose(result.vertices, expected_vertices) + np.testing.assert_array_equal(result.codes, expected_codes) + + +def test_interpolated_moveto_closepoly(): + # Initial path has two closed subpaths + codes = ([Path.MOVETO] + [Path.LINETO]*2 + [Path.CLOSEPOLY]) * 2 + vertices = [(4, 3), (5, 4), (5, 3), (0, 0), (8, 6), (10, 8), (10, 6), (0, 0)] + + path = Path(vertices, codes) + result = path.interpolated(2) + + expected_vertices1 = np.array([[4, 3], + [4.5, 3.5], + [5, 4], + [5, 3.5], + [5, 3], + [4.5, 3], + [4, 3]]) + expected_vertices = np.concatenate([expected_vertices1, expected_vertices1 * 2]) + expected_codes = ([Path.MOVETO] + [Path.LINETO]*5 + [Path.CLOSEPOLY]) * 2 + + np.testing.assert_allclose(result.vertices, expected_vertices) + np.testing.assert_array_equal(result.codes, expected_codes) + + +def test_interpolated_empty_path(): + path = Path(np.zeros((0, 2))) + assert path.interpolated(42) is path diff --git a/lib/matplotlib/tests/test_patheffects.py b/lib/matplotlib/tests/test_patheffects.py index bf067b2abbfd..7095f6b3855b 100644 --- a/lib/matplotlib/tests/test_patheffects.py +++ b/lib/matplotlib/tests/test_patheffects.py @@ -1,8 +1,8 @@ -import platform +import sys import numpy as np -from matplotlib.testing.decorators import image_comparison +from matplotlib.testing.decorators import image_comparison, check_figures_equal import matplotlib.pyplot as plt import matplotlib.patheffects as path_effects from matplotlib.path import Path @@ -11,7 +11,7 @@ from matplotlib.patheffects import PathEffectRenderer -@image_comparison(['patheffect1'], remove_text=True) +@image_comparison(['patheffect1'], remove_text=True, style='mpl20') def test_patheffect1(): ax1 = plt.subplot() ax1.imshow([[1, 2], [2, 3]]) @@ -30,7 +30,7 @@ def test_patheffect1(): @image_comparison(['patheffect2'], remove_text=True, style='mpl20', - tol=0.06 if platform.machine() == 'arm64' else 0) + tol=0.051 if sys.platform == 'darwin' else 0) def test_patheffect2(): ax2 = plt.subplot() @@ -45,8 +45,10 @@ def test_patheffect2(): foreground="w")]) -@image_comparison(['patheffect3'], tol=0.019 if platform.machine() == 'arm64' else 0) +@image_comparison(['patheffect3'], style='mpl20', + tol=0.02 if sys.platform == 'darwin' else 0) def test_patheffect3(): + plt.figure(figsize=(8, 6)) p1, = plt.plot([1, 3, 5, 4, 3], 'o-b', lw=4) p1.set_path_effects([path_effects.SimpleLineShadow(), path_effects.Normal()]) @@ -73,7 +75,7 @@ def test_patheffect3(): t.set_path_effects(pe) -@image_comparison(['stroked_text.png']) +@image_comparison(['stroked_text.png'], style='mpl20') def test_patheffects_stroked_text(): text_chunks = [ 'A B C D E F G H I J K L', @@ -86,7 +88,7 @@ def test_patheffects_stroked_text(): ] font_size = 50 - ax = plt.axes((0, 0, 1, 1)) + ax = plt.figure(figsize=(8, 6)).add_axes((0, 0, 1, 1)) for i, chunk in enumerate(text_chunks): text = ax.text(x=0.01, y=(0.9 - i * 0.13), s=chunk, fontdict={'ha': 'left', 'va': 'center', @@ -119,7 +121,7 @@ def test_SimplePatchShadow_offset(): assert pe._offset == (4, 5) -@image_comparison(['collection'], tol=0.03, style='mpl20') +@image_comparison(['collection'], tol=0.032, style='mpl20') def test_collection(): x, y = np.meshgrid(np.linspace(0, 10, 150), np.linspace(-5, 5, 100)) data = np.sin(x) + np.cos(y) @@ -134,9 +136,8 @@ def test_collection(): 'edgecolor': 'blue'}) -@image_comparison(['tickedstroke'], remove_text=True, extensions=['png'], - tol=0.22) # Increased tolerance due to fixed clipping. -def test_tickedstroke(): +@image_comparison(['tickedstroke.png'], remove_text=True, style='mpl20') +def test_tickedstroke(text_placeholders): fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4)) path = Path.unit_circle() patch = patches.PathPatch(path, facecolor='none', lw=2, path_effects=[ @@ -148,13 +149,13 @@ def test_tickedstroke(): ax1.set_xlim(-2, 2) ax1.set_ylim(-2, 2) - ax2.plot([0, 1], [0, 1], label=' ', + ax2.plot([0, 1], [0, 1], label='C0', path_effects=[path_effects.withTickedStroke(spacing=7, angle=135)]) nx = 101 x = np.linspace(0.0, 1.0, nx) y = 0.3 * np.sin(x * 8) + 0.4 - ax2.plot(x, y, label=' ', path_effects=[path_effects.withTickedStroke()]) + ax2.plot(x, y, label='C1', path_effects=[path_effects.withTickedStroke()]) ax2.legend() @@ -186,7 +187,7 @@ def test_tickedstroke(): ax3.set_ylim(0, 4) -@image_comparison(['spaces_and_newlines.png'], remove_text=True) +@image_comparison(['spaces_and_newlines.png'], remove_text=True, style='mpl20') def test_patheffects_spaces_and_newlines(): ax = plt.subplot() s1 = " " @@ -214,3 +215,19 @@ def close_group(self, s): assert renderer.open_group('s') == "open_group overridden" assert renderer.close_group('s') == "close_group overridden" + + +@check_figures_equal() +def test_simple_line_shadow(fig_test, fig_ref): + ax_ref = fig_ref.add_subplot() + ax_test = fig_test.add_subplot() + + x = np.linspace(-5, 5, 500) + y = np.exp(-x**2) + + line, = ax_test.plot( + x, y, linewidth=5, + path_effects=[ + path_effects.SimpleLineShadow(offset=(0, 0), shadow_color='blue')]) + + ax_ref.plot(x, y, linewidth=5, color='blue', alpha=0.3) diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 7e7ccc14bf8f..1590990cdeb0 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -1,5 +1,7 @@ from io import BytesIO import ast +import os +import sys import pickle import pickletools @@ -8,14 +10,14 @@ import matplotlib as mpl from matplotlib import cm -from matplotlib.testing import subprocess_run_helper +from matplotlib.testing import subprocess_run_helper, is_ci_environment from matplotlib.testing.decorators import check_figures_equal from matplotlib.dates import rrulewrapper from matplotlib.lines import VertexSelector import matplotlib.pyplot as plt import matplotlib.transforms as mtransforms import matplotlib.figure as mfigure -from mpl_toolkits.axes_grid1 import axes_divider, parasite_axes # type: ignore +from mpl_toolkits.axes_grid1 import axes_divider, parasite_axes # type: ignore[import] def test_simple(): @@ -93,11 +95,16 @@ def _generate_complete_test_figure(fig_ref): plt.errorbar(x, x * -0.5, xerr=0.2, yerr=0.4, label='$-.5 x$') plt.legend(draggable=True) + # Ensure subfigure parenting works. + subfigs = fig_ref.subfigures(2) + subfigs[0].subplots(1, 2) + subfigs[1].subplots(1, 2) + fig_ref.align_ylabels() # Test handling of _align_label_groups Groupers. @mpl.style.context("default") -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_complete(fig_test, fig_ref): _generate_complete_test_figure(fig_ref) # plotting is done, now test its pickle-ability @@ -129,7 +136,7 @@ def _pickle_load_subprocess(): @mpl.style.context("default") -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_pickle_load_from_subprocess(fig_test, fig_ref, tmp_path): _generate_complete_test_figure(fig_ref) @@ -143,7 +150,7 @@ def test_pickle_load_from_subprocess(fig_test, fig_ref, tmp_path): proc = subprocess_run_helper( _pickle_load_subprocess, timeout=60, - extra_env={'PICKLE_FILE_PATH': str(fp), 'MPLBACKEND': 'Agg'} + extra_env={"PICKLE_FILE_PATH": str(fp), "MPLBACKEND": "Agg"}, ) loaded_fig = pickle.loads(ast.literal_eval(proc.stdout)) @@ -302,3 +309,23 @@ def test_cycler(): ax = pickle.loads(pickle.dumps(ax)) l, = ax.plot([3, 4]) assert l.get_color() == "m" + + +# Run under an interactive backend to test that we don't try to pickle the +# (interactive and non-picklable) canvas. +def _test_axeswidget_interactive(): + ax = plt.figure().add_subplot() + pickle.dumps(mpl.widgets.Button(ax, "button")) + + +@pytest.mark.xfail( # https://github.com/actions/setup-python/issues/649 + ('TF_BUILD' in os.environ or 'GITHUB_ACTION' in os.environ) and + sys.platform == 'darwin' and sys.version_info[:2] < (3, 11), + reason='Tk version mismatch on Azure macOS CI' + ) +def test_axeswidget_interactive(): + subprocess_run_helper( + _test_axeswidget_interactive, + timeout=120 if is_ci_environment() else 20, + extra_env={'MPLBACKEND': 'tkagg'} + ) diff --git a/lib/matplotlib/tests/test_png.py b/lib/matplotlib/tests/test_png.py index 066eb01c3ae6..e24fe39e9ed1 100644 --- a/lib/matplotlib/tests/test_png.py +++ b/lib/matplotlib/tests/test_png.py @@ -7,7 +7,7 @@ from matplotlib import cm, pyplot as plt -@image_comparison(['pngsuite.png'], tol=0.03) +@image_comparison(['pngsuite.png'], style='default') def test_pngsuite(): files = sorted( (Path(__file__).parent / "baseline_images/pngsuite").glob("basn*.png")) @@ -20,7 +20,7 @@ def test_pngsuite(): if data.ndim == 2: # keep grayscale images gray cmap = cm.gray - plt.imshow(data, extent=(i, i + 1, 0, 1), cmap=cmap) + plt.imshow(data, extent=(i, i + 1, 0, 1), cmap=cmap, interpolation='nearest') plt.gca().patch.set_facecolor("#ddffff") plt.gca().set_xlim(0, len(files)) diff --git a/lib/matplotlib/tests/test_polar.py b/lib/matplotlib/tests/test_polar.py index 6b3c08d2eb3f..a805fb61d238 100644 --- a/lib/matplotlib/tests/test_polar.py +++ b/lib/matplotlib/tests/test_polar.py @@ -1,13 +1,18 @@ +import sys + import numpy as np from numpy.testing import assert_allclose import pytest import matplotlib as mpl +from matplotlib.projections.polar import RadialLocator from matplotlib import pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal +import matplotlib.ticker as mticker -@image_comparison(['polar_axes'], style='default', tol=0.012) +@image_comparison(['polar_axes.png'], style='default', + tol=0.009 if sys.platform == 'darwin' else 0) def test_polar_annotations(): # You can specify the xypoint and the xytext in different positions and # coordinate systems, and optionally turn on a connecting line and mark the @@ -41,8 +46,8 @@ def test_polar_annotations(): ax.tick_params(axis='x', tick1On=True, tick2On=True, direction='out') -@image_comparison(['polar_coords'], style='default', remove_text=True, - tol=0.014) +@image_comparison(['polar_coords.png'], style='default', remove_text=True, + tol=0.013 if sys.platform == 'darwin' else 0) def test_polar_coord_annotations(): # You can also use polar notation on a cartesian axes. Here the native # coordinate system ('data') is cartesian, so you need to specify the @@ -70,7 +75,7 @@ def test_polar_coord_annotations(): ax.set_ylim(-20, 20) -@image_comparison(['polar_alignment.png']) +@image_comparison(['polar_alignment.png'], style='mpl20') def test_polar_alignment(): # Test changing the vertical/horizontal alignment of a polar graph. angles = np.arange(0, 360, 90) @@ -115,7 +120,7 @@ def test_polar_units_1(fig_test, fig_ref): xs = [30.0, 45.0, 60.0, 90.0] ys = [1.0, 2.0, 3.0, 4.0] - plt.figure(fig_test.number) + plt.figure(fig_test) plt.polar([x * units.deg for x in xs], ys) ax = fig_ref.add_subplot(projection="polar") @@ -132,7 +137,7 @@ def test_polar_units_2(fig_test, fig_ref): ys = [1.0, 2.0, 3.0, 4.0] ys_km = [y * units.km for y in ys] - plt.figure(fig_test.number) + plt.figure(fig_test) # test {theta,r}units. plt.polar(xs_deg, ys_km, thetaunits="rad", runits="km") assert isinstance(plt.gca().xaxis.get_major_formatter(), @@ -144,37 +149,37 @@ def test_polar_units_2(fig_test, fig_ref): ax.set(xlabel="rad", ylabel="km") -@image_comparison(['polar_rmin'], style='default') +@image_comparison(['polar_rmin.png'], style='default') def test_polar_rmin(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.plot(theta, r) ax.set_rmax(2.0) ax.set_rmin(0.5) -@image_comparison(['polar_negative_rmin'], style='default') +@image_comparison(['polar_negative_rmin.png'], style='default') def test_polar_negative_rmin(): r = np.arange(-3.0, 0.0, 0.01) theta = 2*np.pi*r fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.plot(theta, r) ax.set_rmax(0.0) ax.set_rmin(-3.0) -@image_comparison(['polar_rorigin'], style='default') +@image_comparison(['polar_rorigin.png'], style='default') def test_polar_rorigin(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.plot(theta, r) ax.set_rmax(2.0) ax.set_rmin(0.5) @@ -184,14 +189,14 @@ def test_polar_rorigin(): @image_comparison(['polar_invertedylim.png'], style='default') def test_polar_invertedylim(): fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.set_ylim(2, 0) @image_comparison(['polar_invertedylim_rorigin.png'], style='default') def test_polar_invertedylim_rorigin(): fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.yaxis.set_inverted(True) # Set the rlims to inverted (2, 0) without calling set_rlim, to check that # viewlims are correctly unstaled before draw()ing. @@ -200,19 +205,19 @@ def test_polar_invertedylim_rorigin(): ax.set_rorigin(3) -@image_comparison(['polar_theta_position'], style='default') +@image_comparison(['polar_theta_position.png'], style='default') def test_polar_theta_position(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r fig = plt.figure() - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax = fig.add_axes((0.1, 0.1, 0.8, 0.8), polar=True) ax.plot(theta, r) ax.set_theta_zero_location("NW", 30) ax.set_theta_direction('clockwise') -@image_comparison(['polar_rlabel_position'], style='default') +@image_comparison(['polar_rlabel_position.png'], style='default') def test_polar_rlabel_position(): fig = plt.figure() ax = fig.add_subplot(projection='polar') @@ -220,7 +225,14 @@ def test_polar_rlabel_position(): ax.tick_params(rotation='auto') -@image_comparison(['polar_theta_wedge'], style='default') +@image_comparison(['polar_title_position.png'], style='mpl20') +def test_polar_title_position(): + fig = plt.figure() + ax = fig.add_subplot(projection='polar') + ax.set_title('foo') + + +@image_comparison(['polar_theta_wedge.png'], style='default') def test_polar_theta_limits(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r @@ -253,7 +265,7 @@ def test_polar_theta_limits(): steps=[1, 2, 2.5, 5, 10]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_polar_rlim(fig_test, fig_ref): ax = fig_test.subplots(subplot_kw={'polar': True}) ax.set_rlim(top=10) @@ -264,7 +276,7 @@ def test_polar_rlim(fig_test, fig_ref): ax.set_rmin(.5) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_polar_rlim_bottom(fig_test, fig_ref): ax = fig_test.subplots(subplot_kw={'polar': True}) ax.set_rlim(bottom=[.5, 10]) @@ -321,10 +333,10 @@ def test_get_tightbbox_polar(): fig.canvas.draw() bb = ax.get_tightbbox(fig.canvas.get_renderer()) assert_allclose( - bb.extents, [107.7778, 29.2778, 539.7847, 450.7222], rtol=1e-03) + bb.extents, [108.27778, 29.1111, 539.7222, 450.8889], rtol=1e-03) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_polar_interpolation_steps_constant_r(fig_test, fig_ref): # Check that an extra half-turn doesn't make any difference -- modulo # antialiasing, which we disable here. @@ -338,7 +350,7 @@ def test_polar_interpolation_steps_constant_r(fig_test, fig_ref): .bar([0], [1], -2*np.pi, edgecolor="none", antialiased=False)) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_polar_interpolation_steps_variable_r(fig_test, fig_ref): l, = fig_test.add_subplot(projection="polar").plot([0, np.pi/2], [1, 2]) l.get_path()._interpolation_steps = 100 @@ -386,7 +398,7 @@ def test_axvspan(): assert span.get_path()._interpolation_steps > 1 -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_remove_shared_polar(fig_ref, fig_test): # Removing shared polar axes used to crash. Test removing them, keeping in # both cases just the lower left axes of a grid to avoid running into a @@ -436,6 +448,33 @@ def test_cursor_precision(): assert ax.format_coord(2, 1) == "θ=0.637π (114.6°), r=1.000" +def test_custom_fmt_data(): + ax = plt.subplot(projection="polar") + def millions(x): + return '$%1.1fM' % (x*1e-6) + + # Test only x formatter + ax.fmt_xdata = None + ax.fmt_ydata = millions + assert ax.format_coord(12, 2e7) == "θ=3.8197186342π (687.54935416°), r=$20.0M" + assert ax.format_coord(1234, 2e6) == "θ=392.794399551π (70702.9919191°), r=$2.0M" + assert ax.format_coord(3, 100) == "θ=0.95493π (171.887°), r=$0.0M" + + # Test only y formatter + ax.fmt_xdata = millions + ax.fmt_ydata = None + assert ax.format_coord(2e5, 1) == "θ=$0.2M, r=1.000" + assert ax.format_coord(1, .1) == "θ=$0.0M, r=0.100" + assert ax.format_coord(1e6, 0.005) == "θ=$1.0M, r=0.005" + + # Test both x and y formatters + ax.fmt_xdata = millions + ax.fmt_ydata = millions + assert ax.format_coord(2e6, 2e4*3e5) == "θ=$2.0M, r=$6000.0M" + assert ax.format_coord(1e18, 12891328123) == "θ=$1000000000000.0M, r=$12891.3M" + assert ax.format_coord(63**7, 1081968*1024) == "θ=$3938980.6M, r=$1107.9M" + + @image_comparison(['polar_log.png'], style='default') def test_polar_log(): fig = plt.figure() @@ -448,9 +487,107 @@ def test_polar_log(): ax.plot(np.linspace(0, 2 * np.pi, n), np.logspace(0, 2, n)) +@check_figures_equal() +def test_polar_log_rorigin(fig_ref, fig_test): + # Test that equivalent linear and log radial settings give the same axes patch + # and spines. + ax_ref = fig_ref.add_subplot(projection='polar', facecolor='red') + ax_ref.set_rlim(0, 2) + ax_ref.set_rorigin(-3) + ax_ref.set_rticks(np.linspace(0, 2, 5)) + + ax_test = fig_test.add_subplot(projection='polar', facecolor='red') + ax_test.set_rscale('log') + ax_test.set_rlim(1, 100) + ax_test.set_rorigin(10**-3) + ax_test.set_rticks(np.logspace(0, 2, 5)) + + for ax in ax_ref, ax_test: + # Radial tick labels should be the only difference, so turn them off. + ax.tick_params(labelleft=False) + + def test_polar_neg_theta_lims(): fig = plt.figure() ax = fig.add_subplot(projection='polar') ax.set_thetalim(-np.pi, np.pi) labels = [l.get_text() for l in ax.xaxis.get_ticklabels()] assert labels == ['-180°', '-135°', '-90°', '-45°', '0°', '45°', '90°', '135°'] + + +@pytest.mark.parametrize("order", ["before", "after"]) +@image_comparison(baseline_images=['polar_errorbar.png'], remove_text=True, + style='mpl20') +def test_polar_errorbar(order): + theta = np.arange(0, 2 * np.pi, np.pi / 8) + r = theta / np.pi / 2 + 0.5 + fig = plt.figure(figsize=(5, 5)) + ax = fig.add_subplot(projection='polar') + if order == "before": + ax.set_theta_zero_location("N") + ax.set_theta_direction(-1) + ax.errorbar(theta, r, xerr=0.1, yerr=0.1, capsize=7, fmt="o", c="seagreen") + else: + ax.errorbar(theta, r, xerr=0.1, yerr=0.1, capsize=7, fmt="o", c="seagreen") + ax.set_theta_zero_location("N") + ax.set_theta_direction(-1) + + +def test_radial_limits_behavior(): + # r=0 is kept as limit if positive data and ticks are used + # negative ticks or data result in negative limits + fig = plt.figure() + ax = fig.add_subplot(projection='polar') + assert ax.get_ylim() == (0, 1) + # upper limit is expanded to include the ticks, but lower limit stays at 0 + ax.set_rticks([1, 2, 3, 4]) + assert ax.get_ylim() == (0, 4) + # upper limit is autoscaled to data, but lower limit limit stays 0 + ax.plot([1, 2], [1, 2]) + assert ax.get_ylim() == (0, 2) + # negative ticks also expand the negative limit + ax.set_rticks([-1, 0, 1, 2]) + assert ax.get_ylim() == (-1, 2) + # negative data also autoscales to negative limits + ax.plot([1, 2], [-1, -2]) + assert ax.get_ylim() == (-2, 2) + + +def test_radial_locator_wrapping(): + # Check that the locator is always wrapped inside a RadialLocator + # and that RaidialAxis.isDefault_majloc is set correctly. + fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + + # set an explicit locator + locator = mticker.MaxNLocator(3) + ax.yaxis.set_major_locator(locator) + assert not ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert ax.yaxis.get_major_locator().base is locator + + ax.clear() # reset to the default locator + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + + ax.set_rticks([0, 1, 2, 3]) # implicitly sets a FixedLocator + assert not ax.yaxis.isDefault_majloc # because of the fixed ticks + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.FixedLocator) + + ax.clear() + + ax.set_rgrids([0, 1, 2, 3]) # implicitly sets a FixedLocator + assert not ax.yaxis.isDefault_majloc # because of the fixed ticks + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.FixedLocator) + + ax.clear() + + ax.set_yscale("log") # implicitly sets a LogLocator + # Note that the LogLocator is still considered the default locator + # for the log scale + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.LogLocator) diff --git a/lib/matplotlib/tests/test_preprocess_data.py b/lib/matplotlib/tests/test_preprocess_data.py index 0684f0dbb9ae..c983d78786e1 100644 --- a/lib/matplotlib/tests/test_preprocess_data.py +++ b/lib/matplotlib/tests/test_preprocess_data.py @@ -267,7 +267,7 @@ class TestPlotTypes: plotters = [Axes.scatter, Axes.bar, Axes.plot] @pytest.mark.parametrize('plotter', plotters) - @check_figures_equal(extensions=['png']) + @check_figures_equal() def test_dict_unpack(self, plotter, fig_test, fig_ref): x = [1, 2, 3] y = [4, 5, 6] @@ -278,7 +278,7 @@ def test_dict_unpack(self, plotter, fig_test, fig_ref): plotter(fig_ref.subplots(), x, y) @pytest.mark.parametrize('plotter', plotters) - @check_figures_equal(extensions=['png']) + @check_figures_equal() def test_data_kwarg(self, plotter, fig_test, fig_ref): x = [1, 2, 3] y = [4, 5, 6] diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 63dc239df2e8..39ffc54bce79 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -1,4 +1,5 @@ import difflib +import inspect import numpy as np import sys @@ -12,7 +13,7 @@ def test_pyplot_up_to_date(tmp_path): - pytest.importorskip("black") + pytest.importorskip("black", minversion="24.1") gen_script = Path(mpl.__file__).parents[2] / "tools/boilerplate.py" if not gen_script.exists(): @@ -163,8 +164,9 @@ def test_close(): try: plt.close(1.1) except TypeError as e: - assert str(e) == "close() argument must be a Figure, an int, " \ - "a string, or None, not " + assert str(e) == ( + "'fig' must be an instance of matplotlib.figure.Figure, int, str " + "or None, not a float") def test_subplot_reuse(): @@ -380,7 +382,7 @@ def extract_documented_functions(lines): :nosignatures: plot - plot_date + errorbar """ functions = [] @@ -439,9 +441,8 @@ def test_switch_backend_no_close(): assert len(plt.get_fignums()) == 2 plt.switch_backend('agg') assert len(plt.get_fignums()) == 2 - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.switch_backend('svg') - assert len(plt.get_fignums()) == 0 + plt.switch_backend('svg') + assert len(plt.get_fignums()) == 2 def figure_hook_example(figure): @@ -460,19 +461,113 @@ def test_figure_hook(): def test_multiple_same_figure_calls(): - fig = mpl.pyplot.figure(1, figsize=(1, 2)) + fig = plt.figure(1, figsize=(1, 2)) with pytest.warns(UserWarning, match="Ignoring specified arguments in this call"): - fig2 = mpl.pyplot.figure(1, figsize=(3, 4)) + fig2 = plt.figure(1, figsize=np.array([3, 4])) with pytest.warns(UserWarning, match="Ignoring specified arguments in this call"): - mpl.pyplot.figure(fig, figsize=(5, 6)) + plt.figure(fig, figsize=np.array([5, 6])) assert fig is fig2 - fig3 = mpl.pyplot.figure(1) # Checks for false warnings + fig3 = plt.figure(1) # Checks for false warnings assert fig is fig3 +def test_register_existing_figure_with_pyplot(): + from matplotlib.figure import Figure + # start with a standalone figure + fig = Figure() + assert fig.canvas.manager is None + with pytest.raises(AttributeError): + # Heads-up: This will change to returning None in the future + # See docstring for the Figure.number property + fig.number + # register the Figure with pyplot + plt.figure(fig) + assert fig.number == 1 + # the figure can now be used in pyplot + plt.suptitle("my title") + assert fig.get_suptitle() == "my title" + # it also has a manager that is properly wired up in the pyplot state + assert plt._pylab_helpers.Gcf.get_fig_manager(fig.number) is fig.canvas.manager + # and we can regularly switch the pyplot state + fig2 = plt.figure() + assert fig2.number == 2 + assert plt.figure(1) is fig + assert plt.gcf() is fig + + def test_close_all_warning(): fig1 = plt.figure() # Check that the warning is issued when 'all' is passed to plt.figure with pytest.warns(UserWarning, match="closes all existing figures"): fig2 = plt.figure("all") + + +def test_matshow(): + fig = plt.figure() + arr = [[0, 1], [1, 2]] + + # Smoke test that matshow does not ask for a new figsize on the existing figure + plt.matshow(arr, fignum=fig.number) + + +def assert_same_signature(func1, func2): + """ + Assert that `func1` and `func2` have the same arguments, + i.e. same parameter count, names and kinds. + + :param func1: First function to check + :param func2: Second function to check + """ + params1 = inspect.signature(func1).parameters + params2 = inspect.signature(func2).parameters + + assert len(params1) == len(params2) + assert all([ + params1[p].name == params2[p].name and + params1[p].kind == params2[p].kind + for p in params1 + ]) + + +def test_setloglevel_signature(): + assert_same_signature(plt.set_loglevel, mpl.set_loglevel) + + +def test_subplots_reuse_existing_figure_error(): + """Test interaction of plt.subplots(num=...) with existing figures.""" + # Create a figure with a specific number first. + fig = plt.figure(1) + + # Case 1: Reusing without clear=True should raise ValueError + with pytest.raises(ValueError, match="already exists"): + plt.subplots(num=1) + + # Case 2: Reusing WITH clear=True should work fine (no error) + fig_new, axs = plt.subplots(num=1, clear=True) + assert fig_new is fig + + # Case 3: Test passing the actual Figure object (The "Narrow Check") + with pytest.raises(ValueError, match="cannot be a FigureBase instance"): + plt.subplots(num=fig) + + plt.close(1) + + +def test_subplot_mosaic_reuse_existing_figure_error(): + """Test that plt.subplot_mosaic raises ValueError when reusing a figure.""" + fig = plt.figure(2) + + # 1. Test passing the existing figure number + with pytest.raises(ValueError, match="already exists"): + plt.subplot_mosaic([['A']], num=2) + + # 2. Test passing the actual Figure object + with pytest.raises(ValueError, match="cannot be a FigureBase instance"): + plt.subplot_mosaic([['A']], num=fig) + + # 3. Test that clear=True allows reuse without error + fig_new, axd = plt.subplot_mosaic([['A']], num=2, clear=True) + assert fig_new is fig + + plt.close(2) diff --git a/lib/matplotlib/tests/test_quiver.py b/lib/matplotlib/tests/test_quiver.py index 7c5a9d343530..4784a7e4dc42 100644 --- a/lib/matplotlib/tests/test_quiver.py +++ b/lib/matplotlib/tests/test_quiver.py @@ -6,6 +6,7 @@ from matplotlib import pyplot as plt from matplotlib.testing.decorators import image_comparison +from matplotlib.testing.decorators import check_figures_equal def draw_quiver(ax, **kwargs): @@ -25,11 +26,12 @@ def test_quiver_memory_leak(): Q = draw_quiver(ax) ttX = Q.X + orig_refcount = sys.getrefcount(ttX) Q.remove() del Q - assert sys.getrefcount(ttX) == 2 + assert sys.getrefcount(ttX) < orig_refcount @pytest.mark.skipif(platform.python_implementation() != 'CPython', @@ -42,9 +44,9 @@ def test_quiver_key_memory_leak(): qk = ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$', labelpos='W', fontproperties={'weight': 'bold'}) - assert sys.getrefcount(qk) == 3 + orig_refcount = sys.getrefcount(qk) qk.remove() - assert sys.getrefcount(qk) == 2 + assert sys.getrefcount(qk) < orig_refcount def test_quiver_number_of_args(): @@ -99,16 +101,16 @@ def test_zero_headlength(): fig.canvas.draw() # Check that no warning is emitted. -@image_comparison(['quiver_animated_test_image.png']) +@image_comparison(['quiver_animated_test_image.png'], style='mpl20') def test_quiver_animate(): # Tests fix for #2616 fig, ax = plt.subplots() Q = draw_quiver(ax, animated=True) - ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$', + ax.quiverkey(Q, 0.5, 0.88, 2, r'$2 \frac{m}{s}$', labelpos='W', fontproperties={'weight': 'bold'}) -@image_comparison(['quiver_with_key_test_image.png']) +@image_comparison(['quiver_with_key_test_image.png'], style='mpl20') def test_quiver_with_key(): fig, ax = plt.subplots() ax.margins(0.1) @@ -136,7 +138,7 @@ def test_quiver_copy(): assert q0.V[0] == 2.0 -@image_comparison(['quiver_key_pivot.png'], remove_text=True) +@image_comparison(['quiver_key_pivot.png'], remove_text=True, style='mpl20') def test_quiver_key_pivot(): fig, ax = plt.subplots() @@ -264,7 +266,7 @@ def test_quiverkey_angles(): qk = ax.quiverkey(q, 1, 1, 2, 'Label') # The arrows are only created when the key is drawn fig.canvas.draw() - assert len(qk.verts) == 1 + assert len(qk.vector.get_paths()) == 1 def test_quiverkey_angles_xy_aitoff(): @@ -293,7 +295,7 @@ def test_quiverkey_angles_xy_aitoff(): qk = ax.quiverkey(q, 0, 0, 1, '1 units') fig.canvas.draw() - assert len(qk.verts) == 1 + assert len(qk.vector.get_paths()) == 1 def test_quiverkey_angles_scale_units_cartesian(): @@ -320,7 +322,7 @@ def test_quiverkey_angles_scale_units_cartesian(): qk = ax.quiverkey(q, 0, 0, 1, '1 units') fig.canvas.draw() - assert len(qk.verts) == 1 + assert len(qk.vector.get_paths()) == 1 def test_quiver_setuvc_numbers(): @@ -333,3 +335,53 @@ def test_quiver_setuvc_numbers(): q = ax.quiver(X, Y, U, V) q.set_UVC(0, 1) + + +def draw_quiverkey_zorder_argument(fig, zorder=None): + """Draw Quiver and QuiverKey using zorder argument""" + x = np.arange(1, 6, 1) + y = np.arange(1, 6, 1) + X, Y = np.meshgrid(x, y) + U, V = 2, 2 + + ax = fig.subplots() + q = ax.quiver(X, Y, U, V, pivot='middle') + ax.set_xlim(0.5, 5.5) + ax.set_ylim(0.5, 5.5) + if zorder is None: + ax.quiverkey(q, 4, 4, 25, coordinates='data', + label='U', color='blue') + ax.quiverkey(q, 5.5, 2, 20, coordinates='data', + label='V', color='blue', angle=90) + else: + ax.quiverkey(q, 4, 4, 25, coordinates='data', + label='U', color='blue', zorder=zorder) + ax.quiverkey(q, 5.5, 2, 20, coordinates='data', + label='V', color='blue', angle=90, zorder=zorder) + + +def draw_quiverkey_setzorder(fig, zorder=None): + """Draw Quiver and QuiverKey using set_zorder""" + x = np.arange(1, 6, 1) + y = np.arange(1, 6, 1) + X, Y = np.meshgrid(x, y) + U, V = 2, 2 + + ax = fig.subplots() + q = ax.quiver(X, Y, U, V, pivot='middle') + ax.set_xlim(0.5, 5.5) + ax.set_ylim(0.5, 5.5) + qk1 = ax.quiverkey(q, 4, 4, 25, coordinates='data', + label='U', color='blue') + qk2 = ax.quiverkey(q, 5.5, 2, 20, coordinates='data', + label='V', color='blue', angle=90) + if zorder is not None: + qk1.set_zorder(zorder) + qk2.set_zorder(zorder) + + +@pytest.mark.parametrize('zorder', [0, 2, 5, None]) +@check_figures_equal() +def test_quiverkey_zorder(fig_test, fig_ref, zorder): + draw_quiverkey_zorder_argument(fig_test, zorder=zorder) + draw_quiverkey_setzorder(fig_ref, zorder=zorder) diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 4823df0ce250..0479fc7a02a8 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -5,6 +5,7 @@ from unittest import mock from cycler import cycler, Cycler +from packaging.version import parse as parse_version import pytest import matplotlib as mpl @@ -12,6 +13,7 @@ import matplotlib.pyplot as plt import matplotlib.colors as mcolors import numpy as np +from matplotlib import rcsetup from matplotlib.rcsetup import ( validate_bool, validate_color, @@ -256,6 +258,8 @@ def generate_validator_testcases(valid): {'validator': validate_cycler, 'success': (('cycler("color", "rgb")', cycler("color", 'rgb')), + ('cycler("color", "Dark2")', + cycler("color", mpl.color_sequences["Dark2"])), (cycler('linestyle', ['-', '--']), cycler('linestyle', ['-', '--'])), ("""(cycler("color", ["r", "g", "b"]) + @@ -271,16 +275,23 @@ def generate_validator_testcases(valid): cycler('linestyle', ['-', '--'])), (cycler(mew=[2, 5]), cycler('markeredgewidth', [2, 5])), + ("2 * cycler('color', 'rgb')", 2 * cycler('color', 'rgb')), + ("2 * cycler('color', 'r' + 'gb')", 2 * cycler('color', 'rgb')), + ("cycler(c='r' + 'gb', lw=[1, 2, 3])", + cycler('color', 'rgb') + cycler('linewidth', [1, 2, 3])), + ("cycler('color', 'rgb') * 2", cycler('color', 'rgb') * 2), + ("concat(cycler('color', 'rgb'), cycler('color', 'cmk'))", + cycler('color', list('rgbcmk'))), + ("cycler('color', 'rgbcmk')[:3]", cycler('color', list('rgb'))), + ("cycler('color', 'rgb')[::-1]", cycler('color', list('bgr'))), ), - # This is *so* incredibly important: validate_cycler() eval's - # an arbitrary string! I think I have it locked down enough, - # and that is what this is testing. - # TODO: Note that these tests are actually insufficient, as it may - # be that they raised errors, but still did an action prior to - # raising the exception. We should devise some additional tests - # for that... + # validate_cycler() parses an arbitrary string using a safe + # AST-based parser (no eval). These tests verify that only valid + # cycler expressions are accepted. 'fail': ((4, ValueError), # Gotta be a string or Cycler object ('cycler("bleh, [])', ValueError), # syntax error + ("cycler('color', 'rgb') * * cycler('color', 'rgb')", # syntax error + ValueError), ('Cycler("linewidth", [1, 2, 3])', ValueError), # only 'cycler()' function is allowed # do not allow dunder in string literals @@ -294,6 +305,9 @@ def generate_validator_testcases(valid): ValueError), ("cycler('c', [j.__class__(j).lower() for j in ['r', 'b']])", ValueError), + # list comprehensions are arbitrary code, even if "safe" + ("cycler('color', [x for x in ['r', 'g', 'b']])", + ValueError), ('1 + 2', ValueError), # doesn't produce a Cycler object ('os.system("echo Gotcha")', ValueError), # os not available ('import os', ValueError), # should not be able to import @@ -454,6 +468,12 @@ def test_validator_invalid(validator, arg, exception_type): validator(arg) +def test_validate_cycler_bad_color_string(): + msg = "'foo' is neither a color sequence name nor can it be interpreted as a list" + with pytest.raises(ValueError, match=msg): + validate_cycler("cycler('color', 'foo')") + + @pytest.mark.parametrize('weight, parsed_weight', [ ('bold', 'bold'), ('BOLD', ValueError), # weight is case-sensitive @@ -520,10 +540,11 @@ def test_rcparams_reset_after_fail(): @pytest.mark.skipif(sys.platform != "linux", reason="Linux only") -def test_backend_fallback_headless(tmp_path): +def test_backend_fallback_headless_invalid_backend(tmp_path): env = {**os.environ, "DISPLAY": "", "WAYLAND_DISPLAY": "", "MPLBACKEND": "", "MPLCONFIGDIR": str(tmp_path)} + # plotting should fail with the tkagg backend selected in a headless environment with pytest.raises(subprocess.CalledProcessError): subprocess_run_for_testing( [sys.executable, "-c", @@ -535,11 +556,38 @@ def test_backend_fallback_headless(tmp_path): env=env, check=True, stderr=subprocess.DEVNULL) +@pytest.mark.skipif(sys.platform != "linux", reason="Linux only") +def test_backend_fallback_headless_auto_backend(tmp_path): + # specify a headless mpl environment, but request a graphical (tk) backend + env = {**os.environ, + "DISPLAY": "", "WAYLAND_DISPLAY": "", + "MPLBACKEND": "TkAgg", "MPLCONFIGDIR": str(tmp_path)} + + # allow fallback to an available interactive backend explicitly in configuration + rc_path = tmp_path / "matplotlibrc" + rc_path.write_text("backend_fallback: true") + + # plotting should succeed, by falling back to use the generic agg backend + backend = subprocess_run_for_testing( + [sys.executable, "-c", + "import matplotlib.pyplot;" + "matplotlib.pyplot.plot(42);" + "print(matplotlib.get_backend());" + ], + env=env, text=True, check=True, capture_output=True).stdout + assert backend.strip().lower() == "agg" + + @pytest.mark.skipif( - sys.platform == "linux" and not _c_internal_utils.display_is_valid(), + sys.platform == "linux" and not _c_internal_utils.xdisplay_is_valid(), reason="headless") def test_backend_fallback_headful(tmp_path): - pytest.importorskip("tkinter") + if parse_version(pytest.__version__) >= parse_version('8.2.0'): + pytest_kwargs = dict(exc_type=ImportError) + else: + pytest_kwargs = {} + + pytest.importorskip("tkinter", **pytest_kwargs) env = {**os.environ, "MPLBACKEND": "", "MPLCONFIGDIR": str(tmp_path)} backend = subprocess_run_for_testing( [sys.executable, "-c", @@ -548,6 +596,7 @@ def test_backend_fallback_headful(tmp_path): # Check that access on another instance does not resolve the sentinel. "assert mpl.RcParams({'backend': sentinel})['backend'] == sentinel; " "assert mpl.rcParams._get('backend') == sentinel; " + "assert mpl.get_backend(auto_select=False) is None; " "import matplotlib.pyplot; " "print(matplotlib.get_backend())"], env=env, text=True, check=True, capture_output=True).stdout @@ -557,40 +606,6 @@ def test_backend_fallback_headful(tmp_path): def test_deprecation(monkeypatch): - monkeypatch.setitem( - mpl._deprecated_map, "patch.linewidth", - ("0.0", "axes.linewidth", lambda old: 2 * old, lambda new: new / 2)) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - assert mpl.rcParams["patch.linewidth"] \ - == mpl.rcParams["axes.linewidth"] / 2 - with pytest.warns(mpl.MatplotlibDeprecationWarning): - mpl.rcParams["patch.linewidth"] = 1 - assert mpl.rcParams["axes.linewidth"] == 2 - - monkeypatch.setitem( - mpl._deprecated_ignore_map, "patch.edgecolor", - ("0.0", "axes.edgecolor")) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - assert mpl.rcParams["patch.edgecolor"] \ - == mpl.rcParams["axes.edgecolor"] - with pytest.warns(mpl.MatplotlibDeprecationWarning): - mpl.rcParams["patch.edgecolor"] = "#abcd" - assert mpl.rcParams["axes.edgecolor"] != "#abcd" - - monkeypatch.setitem( - mpl._deprecated_ignore_map, "patch.force_edgecolor", - ("0.0", None)) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - assert mpl.rcParams["patch.force_edgecolor"] is None - - monkeypatch.setitem( - mpl._deprecated_remain_as_none, "svg.hashsalt", - ("0.0",)) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - mpl.rcParams["svg.hashsalt"] = "foobar" - assert mpl.rcParams["svg.hashsalt"] == "foobar" # Doesn't warn. - mpl.rcParams["svg.hashsalt"] = None # Doesn't warn. - mpl.rcParams.update(mpl.rcParams.copy()) # Doesn't warn. # Note that the warning suppression actually arises from the # iteration over the updater rcParams being protected by @@ -650,3 +665,39 @@ def test_rcparams_path_sketch_from_file(tmp_path, value): rc_path.write_text(f"path.sketch: {value}") with mpl.rc_context(fname=rc_path): assert mpl.rcParams["path.sketch"] == (1, 2, 3) + + +@pytest.mark.parametrize('group, option, alias, value', [ + ('lines', 'linewidth', 'lw', 3), + ('lines', 'linestyle', 'ls', 'dashed'), + ('lines', 'color', 'c', 'white'), + ('axes', 'facecolor', 'fc', 'black'), + ('figure', 'edgecolor', 'ec', 'magenta'), + ('lines', 'markeredgewidth', 'mew', 1.5), + ('patch', 'antialiased', 'aa', False), + ('font', 'sans-serif', 'sans', ["Verdana"]) +]) +def test_rc_aliases(group, option, alias, value): + rc_kwargs = {alias: value,} + mpl.rc(group, **rc_kwargs) + + rcParams_key = f"{group}.{option}" + assert mpl.rcParams[rcParams_key] == value + + +def test_all_params_defined_as_code(): + assert set(p.name for p in rcsetup._params_list()) == set(mpl.rcParams.keys()) + + +def test_validators_defined_as_code(): + for param in rcsetup._params_list(): + validator = rcsetup._convert_validator_spec(param.name, param.validator) + assert validator == rcsetup._validators[param.name] + + +def test_defaults_as_code(): + for param in rcsetup._params_list(): + if param.name == 'backend': + # backend has special handling and no meaningful default + continue + assert param.default == mpl.rcParamsDefault[param.name], param.name diff --git a/lib/matplotlib/tests/test_sankey.py b/lib/matplotlib/tests/test_sankey.py index cbb7f516a65c..745db5f767b2 100644 --- a/lib/matplotlib/tests/test_sankey.py +++ b/lib/matplotlib/tests/test_sankey.py @@ -6,7 +6,7 @@ def test_sankey(): - # lets just create a sankey instance and check the code runs + # let's just create a sankey instance and check the code runs sankey = Sankey() sankey.add() @@ -91,7 +91,7 @@ def test_sankey2(): (0.75, -0.8599479)]) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_sankey3(fig_test, fig_ref): ax_test = fig_test.gca() s_test = Sankey(ax=ax_test, flows=[0.25, -0.25, -0.25, 0.25, 0.5, -0.5], diff --git a/lib/matplotlib/tests/test_scale.py b/lib/matplotlib/tests/test_scale.py index 727397367762..9f882103967e 100644 --- a/lib/matplotlib/tests/test_scale.py +++ b/lib/matplotlib/tests/test_scale.py @@ -6,8 +6,12 @@ LogTransform, InvertedLogTransform, SymmetricalLogTransform) import matplotlib.scale as mscale -from matplotlib.ticker import AsinhLocator, LogFormatterSciNotation +from matplotlib.ticker import ( + AsinhLocator, AutoLocator, LogFormatterSciNotation, + NullFormatter, NullLocator, ScalarFormatter +) from matplotlib.testing.decorators import check_figures_equal, image_comparison +from matplotlib.transforms import IdentityTransform import numpy as np from numpy.testing import assert_allclose @@ -107,7 +111,8 @@ def test_logscale_mask(): fig, ax = plt.subplots() ax.plot(np.exp(-xs**2)) fig.canvas.draw() - ax.set(yscale="log") + ax.set(yscale="log", + yticks=10.**np.arange(-300, 0, 24)) # Backcompat tick selection. def test_extra_kwargs_raise(): @@ -162,6 +167,7 @@ def test_logscale_nonpos_values(): ax4.set_yscale('log') ax4.set_xscale('log') + ax4.set_yticks([1e-2, 1, 1e+2]) # Backcompat tick selection. def test_invalid_log_lims(): @@ -293,3 +299,137 @@ def test_bad_scale(self): AsinhScale(axis=None, linear_width=-1) s0 = AsinhScale(axis=None, ) s1 = AsinhScale(axis=None, linear_width=3.0) + + +def test_custom_scale_without_axis(): + """ + Test that one can register and use custom scales that don't take an *axis* param. + """ + class CustomTransform(IdentityTransform): + pass + + class CustomScale(mscale.ScaleBase): + name = "custom" + + # Important: __init__ has no *axis* parameter + def __init__(self): + self._transform = CustomTransform() + + def get_transform(self): + return self._transform + + def set_default_locators_and_formatters(self, axis): + axis.set_major_locator(AutoLocator()) + axis.set_major_formatter(ScalarFormatter()) + axis.set_minor_locator(NullLocator()) + axis.set_minor_formatter(NullFormatter()) + + try: + mscale.register_scale(CustomScale) + fig, ax = plt.subplots() + ax.set_xscale('custom') + assert isinstance(ax.xaxis.get_transform(), CustomTransform) + finally: + # cleanup - there's no public unregister_scale() + del mscale._scale_mapping["custom"] + del mscale._scale_has_axis_parameter["custom"] + + +def test_custom_scale_with_axis(): + """ + Test that one can still register and use custom scales with an *axis* + parameter, but that registering issues a pending-deprecation warning. + """ + class CustomTransform(IdentityTransform): + pass + + class CustomScale(mscale.ScaleBase): + name = "custom" + + # Important: __init__ still has the *axis* parameter + def __init__(self, axis): + self._transform = CustomTransform() + + def get_transform(self): + return self._transform + + def set_default_locators_and_formatters(self, axis): + axis.set_major_locator(AutoLocator()) + axis.set_major_formatter(ScalarFormatter()) + axis.set_minor_locator(NullLocator()) + axis.set_minor_formatter(NullFormatter()) + + try: + with pytest.warns( + PendingDeprecationWarning, + match=r"'axis' parameter .* is pending-deprecated"): + mscale.register_scale(CustomScale) + fig, ax = plt.subplots() + ax.set_xscale('custom') + assert isinstance(ax.xaxis.get_transform(), CustomTransform) + finally: + # cleanup - there's no public unregister_scale() + del mscale._scale_mapping["custom"] + del mscale._scale_has_axis_parameter["custom"] + + +def test_val_in_range(): + + test_cases = [ + # LinearScale: Always True (even for Inf/NaN) + ('linear', 10.0, True), + ('linear', -10.0, True), + ('linear', 0.0, True), + ('linear', np.inf, False), + ('linear', np.nan, False), + + # LogScale: Only positive values (> 0) + ('log', 1.0, True), + ('log', 1e-300, True), + ('log', 0.0, False), + ('log', -1.0, False), + ('log', np.inf, False), + ('log', np.nan, False), + + # LogitScale: Strictly between 0 and 1 + ('logit', 0.5, True), + ('logit', 0.0, False), + ('logit', 1.0, False), + ('logit', -0.1, False), + ('logit', 1.1, False), + ('logit', np.inf, False), + ('logit', np.nan, False), + + # SymmetricalLogScale: Valid for all real numbers + # Uses ScaleBase fallback. NaN returns False since NaN != NaN + ('symlog', 10.0, True), + ('symlog', -10.0, True), + ('symlog', 0.0, True), + ('symlog', np.inf, False), + ('symlog', np.nan, False), + ] + + for name, val, expected in test_cases: + scale_cls = mscale._scale_mapping[name] + s = scale_cls(axis=None) + + result = s.val_in_range(val) + assert result is expected, ( + f"Failed {name}.val_in_range({val})." + f"Expected {expected}, got {result}" + ) + + +def test_val_in_range_base_fallback(): + # Directly test the ScaleBase fallback for custom scales. + # ScaleBase.limit_range_for_scale returns values unchanged by default + s = mscale.ScaleBase(axis=None) + + # Normal values should be True + assert s.val_in_range(1.0) is True + assert s.val_in_range(-5.5) is True + + # NaN and Inf returns False since they cannot be drawn in a plot + assert s.val_in_range(np.nan) is False + assert s.val_in_range(np.inf) is False + assert s.val_in_range(-np.inf) is False diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index 0a5c215eff30..6b2e5b4cd301 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -25,11 +25,11 @@ def test_clipping(): fig, ax = plt.subplots() ax.plot(t, s, linewidth=1.0) - ax.set_ylim((-0.20, -0.28)) + ax.set_ylim(-0.20, -0.28) @image_comparison(['overflow'], remove_text=True, - tol=0.007 if platform.machine() == 'arm64' else 0) + tol=0 if platform.machine() == 'x86_64' else 0.007) def test_overflow(): x = np.array([1.0, 2.0, 3.0, 2.0e5]) y = np.arange(len(x)) @@ -244,11 +244,11 @@ def test_simplify_curve(): fig, ax = plt.subplots() ax.add_patch(pp1) - ax.set_xlim((0, 2)) - ax.set_ylim((0, 2)) + ax.set_xlim(0, 2) + ax.set_ylim(0, 2) -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_closed_path_nan_removal(fig_test, fig_ref): ax_test = fig_test.subplots(2, 2).flatten() ax_ref = fig_ref.subplots(2, 2).flatten() @@ -356,7 +356,7 @@ def test_closed_path_nan_removal(fig_test, fig_ref): remove_ticks_and_titles(fig_ref) -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_closed_path_clipping(fig_test, fig_ref): vertices = [] for roll in range(8): @@ -401,8 +401,8 @@ def test_closed_path_clipping(fig_test, fig_ref): def test_hatch(): fig, ax = plt.subplots() ax.add_patch(plt.Rectangle((0, 0), 1, 1, fill=False, hatch="/")) - ax.set_xlim((0.45, 0.55)) - ax.set_ylim((0.45, 0.55)) + ax.set_xlim(0.45, 0.55) + ax.set_ylim(0.45, 0.55) @image_comparison(['fft_peaks'], remove_text=True) @@ -455,7 +455,7 @@ def test_start_with_moveto(): assert segs[0][1] == Path.MOVETO -def test_throw_rendering_complexity_exceeded(): +def test_throw_rendering_complexity_exceeded(high_memory): plt.rcParams['path.simplify'] = False xx = np.arange(2_000_000) yy = np.random.rand(2_000_000) @@ -493,7 +493,7 @@ def test_para_equal_perp(): ax.plot(x + 1, y + 1, 'ro') -@image_comparison(['clipping_with_nans']) +@image_comparison(['clipping_with_nans'], style='mpl20') def test_clipping_with_nans(): x = np.linspace(0, 3.14 * 2, 3000) y = np.sin(x) @@ -518,3 +518,54 @@ def test_clipping_full(): simplified = list(p.iter_segments(clip=[0, 0, 100, 100])) assert ([(list(x), y) for x, y in simplified] == [([50, 40], 1)]) + + +def test_simplify_closepoly(): + # The values of the vertices in a CLOSEPOLY should always be ignored, + # in favor of the most recent MOVETO's vertex values + paths = [Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]), + Path([(1, 1), (2, 1), (2, 2), (40, 50)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])] + expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1), (1, 1), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.STOP]) + + for path in paths: + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) + + # test that a compound path also works + path = Path([(1, 1), (2, 1), (2, 2), (np.nan, np.nan), + (-1, 0), (-2, 0), (-2, 1), (np.nan, np.nan)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]) + expected_path = Path([(1, 1), (2, 1), (2, 2), (1, 1), + (-1, 0), (-2, 0), (-2, 1), (-1, 0), (-1, 0), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.STOP]) + + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) + + # test for a path with an invalid MOVETO + # CLOSEPOLY with an invalid MOVETO should be ignored + path = Path([(1, 0), (1, -1), (2, -1), + (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1), + (2, 2), (0, -1)], + [Path.MOVETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.CLOSEPOLY, Path.LINETO]) + expected_path = Path([(1, 0), (1, -1), (2, -1), + (np.nan, np.nan), (-1, -1), (-2, 1), (-1, 1), + (0, -1), (0, -1), (0, 0)], + [Path.MOVETO, Path.LINETO, Path.LINETO, + Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.LINETO, Path.STOP]) + + simplified_path = path.cleaned(simplify=True) + assert_array_equal(expected_path.vertices, simplified_path.vertices) + assert_array_equal(expected_path.codes, simplified_path.codes) diff --git a/lib/matplotlib/tests/test_skew.py b/lib/matplotlib/tests/test_skew.py index fd7e7cebfacb..125ecd7ff606 100644 --- a/lib/matplotlib/tests/test_skew.py +++ b/lib/matplotlib/tests/test_skew.py @@ -25,9 +25,9 @@ def draw(self, renderer): for artist in [self.gridline, self.tick1line, self.tick2line, self.label1, self.label2]: stack.callback(artist.set_visible, artist.get_visible()) - needs_lower = transforms.interval_contains( + needs_lower = transforms._interval_contains( self.axes.lower_xlim, self.get_loc()) - needs_upper = transforms.interval_contains( + needs_upper = transforms._interval_contains( self.axes.upper_xlim, self.get_loc()) self.tick1line.set_visible( self.tick1line.get_visible() and needs_lower) @@ -133,7 +133,7 @@ def upper_xlim(self): register_projection(SkewXAxes) -@image_comparison(['skew_axes'], remove_text=True) +@image_comparison(['skew_axes.png'], remove_text=True) def test_set_line_coll_dash_image(): fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='skewx') @@ -145,8 +145,8 @@ def test_set_line_coll_dash_image(): ax.axvline(0, color='b') -@image_comparison(['skew_rects'], remove_text=True, - tol=0.009 if platform.machine() == 'arm64' else 0) +@image_comparison(['skew_rects.png'], remove_text=True, + tol=0 if platform.machine() == 'x86_64' else 0.009) def test_skew_rectangle(): fix, axes = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(8, 8)) diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index 6624e3b17ba5..c6f4e13c74c2 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -10,8 +10,10 @@ import pytest -pytest.importorskip('sphinx', - minversion=None if sys.version_info < (3, 10) else '4.1.3') +pytest.importorskip('sphinx', minversion='4.1.3') + + +tinypages = Path(__file__).parent / 'data/tinypages' def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): @@ -19,9 +21,13 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): extra_args = [] if extra_args is None else extra_args cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', '-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args] + # On CI, gcov emits warnings (due to agg headers being included with the + # same name in multiple extension modules -- but we don't care about their + # coverage anyways); hide them using GCOV_ERROR_FILE. proc = subprocess_run_for_testing( cmd, capture_output=True, text=True, - env={**os.environ, "MPLBACKEND": ""}) + env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} + ) out = proc.stdout err = proc.stderr @@ -34,24 +40,12 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): def test_tinypages(tmp_path): - shutil.copytree(Path(__file__).parent / 'tinypages', tmp_path, - dirs_exist_ok=True) + shutil.copytree(tinypages, tmp_path, dirs_exist_ok=True, + ignore=shutil.ignore_patterns('_build', 'doctrees', + 'plot_directive')) html_dir = tmp_path / '_build' / 'html' img_dir = html_dir / '_images' doctree_dir = tmp_path / 'doctrees' - # Build the pages with warnings turned into errors - cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', - '-d', str(doctree_dir), - str(Path(__file__).parent / 'tinypages'), str(html_dir)] - # On CI, gcov emits warnings (due to agg headers being included with the - # same name in multiple extension modules -- but we don't care about their - # coverage anyways); hide them using GCOV_ERROR_FILE. - proc = subprocess_run_for_testing( - cmd, capture_output=True, text=True, - env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} - ) - out = proc.stdout - err = proc.stderr # Build the pages with warnings turned into errors build_sphinx_html(tmp_path, doctree_dir, html_dir) @@ -63,7 +57,7 @@ def plot_directive_file(num): # This is always next to the doctree dir. return doctree_dir.parent / 'plot_directive' / f'some_plots-{num}.png' - range_10, range_6, range_4 = [plot_file(i) for i in range(1, 4)] + range_10, range_6, range_4 = (plot_file(i) for i in range(1, 4)) # Plot 5 is range(6) plot assert filecmp.cmp(range_6, plot_file(5)) # Plot 7 is range(4) plot @@ -76,27 +70,35 @@ def plot_directive_file(num): # Plot 13 shows close-figs in action assert filecmp.cmp(range_4, plot_file(13)) # Plot 14 has included source - html_contents = (html_dir / 'some_plots.html').read_bytes() + html_contents = (html_dir / 'some_plots.html').read_text(encoding='utf-8') - assert b'# Only a comment' in html_contents + assert '# Only a comment' in html_contents # check plot defined in external file. assert filecmp.cmp(range_4, img_dir / 'range4.png') assert filecmp.cmp(range_6, img_dir / 'range6_range6.png') # check if figure caption made it into html file - assert b'This is the caption for plot 15.' in html_contents - # check if figure caption using :caption: made it into html file - assert b'Plot 17 uses the caption option.' in html_contents + assert 'This is the caption for plot 15.' in html_contents + # check if figure caption using :caption: made it into html file (because this plot + # doesn't use srcset, the caption preserves newlines in the output.) + assert 'Plot 17 uses the caption option,\nwith multi-line input.' in html_contents + # check if figure alt text using :alt: made it into html file + assert 'Plot 17 uses the alt option, with multi-line input.' in html_contents # check if figure caption made it into html file - assert b'This is the caption for plot 18.' in html_contents + assert 'This is the caption for plot 18.' in html_contents # check if the custom classes made it into the html file - assert b'plot-directive my-class my-other-class' in html_contents + assert 'plot-directive my-class my-other-class' in html_contents # check that the multi-image caption is applied twice - assert html_contents.count(b'This caption applies to both plots.') == 2 + assert html_contents.count('This caption applies to both plots.') == 2 # Plot 21 is range(6) plot via an include directive. But because some of # the previous plots are repeated, the argument to plot_file() is only 17. assert filecmp.cmp(range_6, plot_file(17)) # plot 22 is from the range6.py file again, but a different function assert filecmp.cmp(range_10, img_dir / 'range6_range10.png') + # plots 23--25 use a custom basename + assert filecmp.cmp(range_6, img_dir / 'custom-basename-6.png') + assert filecmp.cmp(range_4, img_dir / 'custom-basename-4.png') + assert filecmp.cmp(range_4, img_dir / 'custom-basename-4-6_00.png') + assert filecmp.cmp(range_6, img_dir / 'custom-basename-4-6_01.png') # Modify the included plot contents = (tmp_path / 'included_plot_21.rst').read_bytes() @@ -123,9 +125,8 @@ def plot_directive_file(num): def test_plot_html_show_source_link(tmp_path): - parent = Path(__file__).parent - shutil.copyfile(parent / 'tinypages/conf.py', tmp_path / 'conf.py') - shutil.copytree(parent / 'tinypages/_static', tmp_path / '_static') + shutil.copyfile(tinypages / 'conf.py', tmp_path / 'conf.py') + shutil.copytree(tinypages / '_static', tmp_path / '_static') doctree_dir = tmp_path / 'doctrees' (tmp_path / 'index.rst').write_text(""" .. plot:: @@ -148,9 +149,8 @@ def test_plot_html_show_source_link(tmp_path): def test_show_source_link_true(tmp_path, plot_html_show_source_link): # Test that a source link is generated if :show-source-link: is true, # whether or not plot_html_show_source_link is true. - parent = Path(__file__).parent - shutil.copyfile(parent / 'tinypages/conf.py', tmp_path / 'conf.py') - shutil.copytree(parent / 'tinypages/_static', tmp_path / '_static') + shutil.copyfile(tinypages / 'conf.py', tmp_path / 'conf.py') + shutil.copytree(tinypages / '_static', tmp_path / '_static') doctree_dir = tmp_path / 'doctrees' (tmp_path / 'index.rst').write_text(""" .. plot:: @@ -168,9 +168,8 @@ def test_show_source_link_true(tmp_path, plot_html_show_source_link): def test_show_source_link_false(tmp_path, plot_html_show_source_link): # Test that a source link is NOT generated if :show-source-link: is false, # whether or not plot_html_show_source_link is true. - parent = Path(__file__).parent - shutil.copyfile(parent / 'tinypages/conf.py', tmp_path / 'conf.py') - shutil.copytree(parent / 'tinypages/_static', tmp_path / '_static') + shutil.copyfile(tinypages / 'conf.py', tmp_path / 'conf.py') + shutil.copytree(tinypages / '_static', tmp_path / '_static') doctree_dir = tmp_path / 'doctrees' (tmp_path / 'index.rst').write_text(""" .. plot:: @@ -184,15 +183,62 @@ def test_show_source_link_false(tmp_path, plot_html_show_source_link): assert len(list(html_dir.glob("**/index-1.py"))) == 0 +def test_plot_html_show_source_link_custom_basename(tmp_path): + # Test that source link filename includes .py extension when using custom basename + shutil.copyfile(tinypages / 'conf.py', tmp_path / 'conf.py') + shutil.copytree(tinypages / '_static', tmp_path / '_static') + doctree_dir = tmp_path / 'doctrees' + (tmp_path / 'index.rst').write_text(""" +.. plot:: + :filename-prefix: custom-name + + plt.plot(range(2)) +""") + html_dir = tmp_path / '_build' / 'html' + build_sphinx_html(tmp_path, doctree_dir, html_dir) + + # Check that source file with .py extension is generated + assert len(list(html_dir.glob("**/custom-name.py"))) == 1 + + # Check that the HTML contains the correct link with .py extension + html_content = (html_dir / 'index.html').read_text() + assert 'custom-name.py' in html_content + + +def test_plot_html_code_caption(tmp_path): + # Test that :code-caption: option adds caption to code block + shutil.copyfile(tinypages / 'conf.py', tmp_path / 'conf.py') + shutil.copytree(tinypages / '_static', tmp_path / '_static') + doctree_dir = tmp_path / 'doctrees' + (tmp_path / 'index.rst').write_text(""" +.. plot:: + :include-source: + :code-caption: Example plotting code + + import matplotlib.pyplot as plt + plt.plot([1, 2, 3], [1, 4, 9]) +""") + html_dir = tmp_path / '_build' / 'html' + build_sphinx_html(tmp_path, doctree_dir, html_dir) + + # Check that the HTML contains the code caption + html_content = (html_dir / 'index.html').read_text(encoding='utf-8') + assert 'Example plotting code' in html_content + # Verify the caption is associated with the code block + # (appears in a caption element) + assert '

info.misses +def test_metrics_cache2(): + # dig into the signature to get the mutable default used as a cache + renderer_cache = inspect.signature( + mpl.text._get_text_metrics_function + ).parameters['_cache'].default + gc.collect() + renderer_cache.clear() + + def helper(): + fig, ax = plt.subplots() + fig.draw_without_rendering() + # show we hit the outer cache + assert len(renderer_cache) == 1 + func = renderer_cache[fig.canvas.get_renderer()] + cache_info = func.cache_info() + # show we hit the inner cache + assert cache_info.currsize > 0 + assert cache_info.currsize == cache_info.misses + assert cache_info.hits > cache_info.misses + plt.close(fig) + + helper() + gc.collect() + # show the outer cache has a lifetime tied to the renderer (via the figure) + assert len(renderer_cache) == 0 + + def test_annotate_offset_fontsize(): # Test that offset_fontsize parameter works and uses accurate values fig, ax = plt.subplots() @@ -916,7 +928,7 @@ def test_annotate_offset_fontsize(): fontsize='10', xycoords='data', textcoords=text_coords[i]) for i in range(2)] - points_coords, fontsize_coords = [ann.get_window_extent() for ann in anns] + points_coords, fontsize_coords = (ann.get_window_extent() for ann in anns) fig.canvas.draw() assert str(points_coords) == str(fontsize_coords) @@ -953,7 +965,7 @@ def test_annotation_antialiased(): assert annot4._antialiased == mpl.rcParams['text.antialiased'] -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_annotate_and_offsetfrom_copy_input(fig_test, fig_ref): # Both approaches place the text (10, 0) pixels away from the center of the line. ax = fig_test.add_subplot() @@ -969,7 +981,7 @@ def test_annotate_and_offsetfrom_copy_input(fig_test, fig_ref): an_xy[:] = 2 -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_text_antialiased_off_default_vs_manual(fig_test, fig_ref): fig_test.text(0.5, 0.5, '6 inches x 2 inches', antialiased=False) @@ -978,7 +990,7 @@ def test_text_antialiased_off_default_vs_manual(fig_test, fig_ref): fig_ref.text(0.5, 0.5, '6 inches x 2 inches') -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_text_antialiased_on_default_vs_manual(fig_test, fig_ref): fig_test.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True) @@ -1005,8 +1017,16 @@ def test_text_annotation_get_window_extent(): _, _, d = renderer.get_text_width_height_descent( 'text', annotation._fontproperties, ismath=False) - _, _, lp_d = renderer.get_text_width_height_descent( - 'lp', annotation._fontproperties, ismath=False) + font = get_font(fontManager._find_fonts_by_props(annotation._fontproperties)) + for name, key in [('OS/2', 'sTypoDescender'), ('hhea', 'descent')]: + if (table := font.get_sfnt_table(name)) is not None: + units_per_em = font.get_sfnt_table('head')['unitsPerEm'] + fontsize = annotation._fontproperties.get_size_in_points() + lp_d = -table[key] / units_per_em * fontsize * figure.dpi / 72 + break + else: + _, _, lp_d = renderer.get_text_width_height_descent( + 'lp', annotation._fontproperties, ismath=False) below_line = max(d, lp_d) # These numbers are specific to the current implementation of Text @@ -1045,7 +1065,7 @@ def test_text_with_arrow_annotation_get_window_extent(): assert bbox.width == text_bbox.width + 50.0 # make sure the annotation text bounding box is same size # as the bounding box of the same string as a Text object - assert ann_txt_bbox.height == text_bbox.height + assert_almost_equal(ann_txt_bbox.height, text_bbox.height) assert ann_txt_bbox.width == text_bbox.width # compute the expected bounding box of arrow + text expected_bbox = mtransforms.Bbox.union([ann_txt_bbox, arrow_bbox]) @@ -1098,10 +1118,9 @@ def test_empty_annotation_get_window_extent(): assert points[0, 1] == 50.0 -@image_comparison(baseline_images=['basictext_wrap'], - extensions=['png']) +@image_comparison(['basictext_wrap.png'], style='mpl20') def test_basic_wrap(): - fig = plt.figure() + fig = plt.figure(figsize=(8, 6)) plt.axis([0, 10, 0, 10]) t = "This is a really long string that I'd rather have wrapped so that" \ " it doesn't go outside of the figure, but if it's long enough it" \ @@ -1115,10 +1134,9 @@ def test_basic_wrap(): plt.text(-1, 0, t, ha='left', rotation=-15, wrap=True) -@image_comparison(baseline_images=['fonttext_wrap'], - extensions=['png']) +@image_comparison(['fonttext_wrap.png'], style='mpl20') def test_font_wrap(): - fig = plt.figure() + fig = plt.figure(figsize=(8, 6)) plt.axis([0, 10, 0, 10]) t = "This is a really long string that I'd rather have wrapped so that" \ " it doesn't go outside of the figure, but if it's long enough it" \ @@ -1130,3 +1148,186 @@ def test_font_wrap(): plt.text(3, 4, t, family='monospace', ha='right', wrap=True) plt.text(-1, 0, t, fontsize=14, style='italic', ha='left', rotation=-15, wrap=True) + + +def test_ha_for_angle(): + text_instance = Text() + angles = np.arange(0, 360.1, 0.1) + for angle in angles: + alignment = text_instance._ha_for_angle(angle) + assert alignment in ['center', 'left', 'right'] + + +def test_va_for_angle(): + text_instance = Text() + angles = np.arange(0, 360.1, 0.1) + for angle in angles: + alignment = text_instance._va_for_angle(angle) + assert alignment in ['center', 'top', 'baseline'] + + +@image_comparison(['xtick_rotation_mode.png'], remove_text=False, style='mpl20') +def test_xtick_rotation_mode(): + fig, ax = plt.subplots(figsize=(12, 1)) + ax.set_yticks([]) + ax2 = ax.twiny() + + ax.set_xticks(range(37), ['foo'] * 37, rotation_mode="xtick") + ax2.set_xticks(range(37), ['foo'] * 37, rotation_mode="xtick") + + angles = np.linspace(0, 360, 37) + + for tick, angle in zip(ax.get_xticklabels(), angles): + tick.set_rotation(angle) + for tick, angle in zip(ax2.get_xticklabels(), angles): + tick.set_rotation(angle) + + plt.subplots_adjust(left=0.01, right=0.99, top=.6, bottom=.4) + + +@image_comparison(['ytick_rotation_mode.png'], remove_text=False, style='mpl20') +def test_ytick_rotation_mode(): + fig, ax = plt.subplots(figsize=(1, 12)) + ax.set_xticks([]) + ax2 = ax.twinx() + + ax.set_yticks(range(37), ['foo'] * 37, rotation_mode="ytick") + ax2.set_yticks(range(37), ['foo'] * 37, rotation_mode='ytick') + + angles = np.linspace(0, 360, 37) + for tick, angle in zip(ax.get_yticklabels(), angles): + tick.set_rotation(angle) + for tick, angle in zip(ax2.get_yticklabels(), angles): + tick.set_rotation(angle) + + plt.subplots_adjust(left=0.4, right=0.6, top=.99, bottom=.01) + + +def test_text_tightbbox_outside_scale_domain(): + # Test that text at positions outside the valid domain of axes scales + # (e.g., negative coordinates with log scale) returns a null bbox. + fig, ax = plt.subplots() + ax.set_yscale('log') + ax.set_ylim(1, 100) + + invalid_text = ax.text(0, -5, 'invalid') + invalid_bbox = invalid_text.get_tightbbox(fig.canvas.get_renderer()) + assert not np.isfinite(invalid_bbox.width) + + +def _test_complex_shaping(fig): + # Raqm is Arabic for writing; note that because Arabic is RTL, the characters here + # may seem to be in a different order than expected, but libraqm will order them + # correctly for us. + text = ( + 'Arabic: \N{Arabic Letter REH}\N{Arabic FATHA}\N{Arabic Letter QAF}' + '\N{Arabic SUKUN}\N{Arabic Letter MEEM}') + math_signs = '\N{N-ary Product}\N{N-ary Coproduct}\N{N-ary summation}\N{Integral}' + text = math_signs + text + math_signs + fig.text(0.5, 0.75, text, size=32, ha='center', va='center') + # Also check fallback behaviour: + # - English should use cmr10 + # - Math signs should use DejaVu Sans Display (and thus be larger than the rest) + # - Arabic should use DejaVu Sans + fig.text(0.5, 0.25, text, size=32, ha='center', va='center', + family=['cmr10', 'DejaVu Sans Display', 'DejaVu Sans']) + + +@image_comparison(['complex'], extensions=['png', 'pdf', 'svg', 'eps'], style='mpl20') +def test_complex_shaping(): + fig = plt.figure(figsize=(6, 2)) + _test_complex_shaping(fig) + + +def _test_text_features(fig): + t = fig.text(1, 0.7, 'Default: fi ffi fl st', + fontsize=32, horizontalalignment='right') + assert t.get_fontfeatures() is None + t = fig.text(1, 0.4, 'Disabled: fi ffi fl st', + fontsize=32, horizontalalignment='right', + fontfeatures=['-liga']) + assert t.get_fontfeatures() == ('-liga', ) + t = fig.text(1, 0.1, 'Discretionary: fi ffi fl st', + fontsize=32, horizontalalignment='right') + t.set_fontfeatures(['dlig']) + assert t.get_fontfeatures() == ('dlig', ) + + +@image_comparison(['features'], remove_text=False, style='mpl20', + extensions=['png', 'pdf', 'svg', 'eps']) +def test_text_features(): + fig = plt.figure(figsize=(5, 1.5)) + _test_text_features(fig) + + +@pytest.mark.parametrize( + 'input, match', + [ + ([1, 2, 3], 'must be list of tuple'), + ([(1, 2)], 'must be list of tuple'), + ([('en', 'foo', 2)], 'start location must be int'), + ([('en', 1, 'foo')], 'end location must be int'), + ], +) +def test_text_language_invalid(input, match): + with pytest.raises(TypeError, match=match): + Text(0, 0, 'foo', language=input) + + +def _test_text_language(fig): + t = fig.text(0, 0.8, 'Default', fontsize=32) + assert t.get_language() is None + t = fig.text(0, 0.55, 'Lang A', fontsize=32) + assert t.get_language() is None + t = fig.text(0, 0.3, 'Lang B', fontsize=32) + assert t.get_language() is None + t = fig.text(0, 0.05, 'Mixed', fontsize=32) + assert t.get_language() is None + + # DejaVu Sans supports language-specific glyphs in the Serbian and Macedonian + # languages in the Cyrillic alphabet. + cyrillic = '\U00000431' + t = fig.text(0.4, 0.8, cyrillic, fontsize=32) + assert t.get_language() is None + t = fig.text(0.4, 0.55, cyrillic, fontsize=32, language='sr') + assert t.get_language() == 'sr' + t = fig.text(0.4, 0.3, cyrillic, fontsize=32) + t.set_language('ru') + assert t.get_language() == 'ru' + t = fig.text(0.4, 0.05, cyrillic * 4, fontsize=32, + language=[('ru', 0, 1), ('sr', 1, 2), ('ru', 2, 3), ('sr', 3, 4)]) + assert t.get_language() == (('ru', 0, 1), ('sr', 1, 2), ('ru', 2, 3), ('sr', 3, 4)) + + # Or the Sámi family of languages in the Latin alphabet. + latin = '\U0000014a' + t = fig.text(0.7, 0.8, latin, fontsize=32) + assert t.get_language() is None + with plt.rc_context({'text.language': 'en'}): + t = fig.text(0.7, 0.55, latin, fontsize=32) + assert t.get_language() == 'en' + t = fig.text(0.7, 0.3, latin, fontsize=32, language='smn') + assert t.get_language() == 'smn' + # Tuples are not documented, but we'll allow it. + t = fig.text(0.7, 0.05, latin * 4, fontsize=32) + t.set_language((('en', 0, 1), ('smn', 1, 2), ('en', 2, 3), ('smn', 3, 4))) + assert t.get_language() == ( + ('en', 0, 1), ('smn', 1, 2), ('en', 2, 3), ('smn', 3, 4)) + + +@image_comparison(['language'], remove_text=False, style='mpl20', + extensions=['png', 'pdf', 'svg', 'eps']) +def test_text_language(): + fig = plt.figure(figsize=(5, 3)) + _test_text_language(fig) + + +@image_comparison(['draw_text_fallback.png'], style='mpl20') +def test_draw_text_as_path_fallback(monkeypatch): + # Delete RendererAgg.draw_text so that we use the RendererBase.draw_text fallback. + monkeypatch.delattr('matplotlib.backends.backend_agg.RendererAgg.draw_text') + heights = [2, 1.5, 3] + fig = plt.figure(figsize=(6, sum(heights))) + subfig = fig.subfigures(3, 1, height_ratios=heights) + _test_complex_shaping(subfig[0]) + _test_text_features(subfig[1]) + _test_text_language(subfig[2]) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index ac68a5d90b14..478a54b8a317 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -6,7 +6,7 @@ from packaging.version import parse as parse_version import numpy as np -from numpy.testing import assert_almost_equal, assert_array_equal +from numpy.testing import assert_almost_equal, assert_array_equal, assert_allclose import pytest import matplotlib as mpl @@ -332,13 +332,11 @@ def test_basic(self): with pytest.raises(ValueError): loc.tick_values(0, 1000) - test_value = np.array([1.00000000e-05, 1.00000000e-03, 1.00000000e-01, - 1.00000000e+01, 1.00000000e+03, 1.00000000e+05, - 1.00000000e+07, 1.000000000e+09]) + test_value = np.array([1e-5, 1e-3, 1e-1, 1e+1, 1e+3, 1e+5, 1e+7]) assert_almost_equal(loc.tick_values(0.001, 1.1e5), test_value) loc = mticker.LogLocator(base=2) - test_value = np.array([0.5, 1., 2., 4., 8., 16., 32., 64., 128., 256.]) + test_value = np.array([.5, 1., 2., 4., 8., 16., 32., 64., 128.]) assert_almost_equal(loc.tick_values(1, 100), test_value) def test_polar_axes(self): @@ -358,19 +356,20 @@ def test_switch_to_autolocator(self): loc = mticker.LogLocator(subs=np.arange(2, 10)) assert 1.0 not in loc.tick_values(0.9, 20.) assert 10.0 not in loc.tick_values(0.9, 20.) + # don't switch if there's already one major and one minor tick (10 & 20) + loc = mticker.LogLocator(subs="auto") + tv = loc.tick_values(10, 20) + assert_array_equal(tv[(10 <= tv) & (tv <= 20)], [20]) def test_set_params(self): """ Create log locator with default value, base=10.0, subs=[1.0], - numdecs=4, numticks=15 and change it to something else. + numticks=15 and change it to something else. See if change was successful. Should not raise exception. """ loc = mticker.LogLocator() - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="numdecs"): - loc.set_params(numticks=7, numdecs=8, subs=[2.0], base=4) + loc.set_params(numticks=7, subs=[2.0], base=4) assert loc.numticks == 7 - with pytest.warns(mpl.MatplotlibDeprecationWarning, match="numdecs"): - assert loc.numdecs == 8 assert loc._base == 4 assert list(loc._subs) == [2.0] @@ -380,7 +379,7 @@ def test_tick_values_correct(self): 1.e+01, 2.e+01, 5.e+01, 1.e+02, 2.e+02, 5.e+02, 1.e+03, 2.e+03, 5.e+03, 1.e+04, 2.e+04, 5.e+04, 1.e+05, 2.e+05, 5.e+05, 1.e+06, 2.e+06, 5.e+06, - 1.e+07, 2.e+07, 5.e+07, 1.e+08, 2.e+08, 5.e+08]) + 1.e+07, 2.e+07, 5.e+07]) assert_almost_equal(ll.tick_values(1, 1e7), test_value) def test_tick_values_not_empty(self): @@ -390,8 +389,7 @@ def test_tick_values_not_empty(self): 1.e+01, 2.e+01, 5.e+01, 1.e+02, 2.e+02, 5.e+02, 1.e+03, 2.e+03, 5.e+03, 1.e+04, 2.e+04, 5.e+04, 1.e+05, 2.e+05, 5.e+05, 1.e+06, 2.e+06, 5.e+06, - 1.e+07, 2.e+07, 5.e+07, 1.e+08, 2.e+08, 5.e+08, - 1.e+09, 2.e+09, 5.e+09]) + 1.e+07, 2.e+07, 5.e+07, 1.e+08, 2.e+08, 5.e+08]) assert_almost_equal(ll.tick_values(1, 1e8), test_value) def test_multiple_shared_axes(self): @@ -606,6 +604,22 @@ def test_set_params(self): assert index._base == 7 assert index.offset == 7 + def test_tick_values_not_exceeding_vmax(self): + """ + Test that tick_values does not return values greater than vmax. + """ + # Test case where offset=0 could cause vmax to be included incorrectly + index = mticker.IndexLocator(base=1, offset=0) + assert_array_equal(index.tick_values(0, 4), [0, 1, 2, 3, 4]) + + # Test case with fractional offset + index = mticker.IndexLocator(base=1, offset=0.5) + assert_array_equal(index.tick_values(0, 4), [0.5, 1.5, 2.5, 3.5]) + + # Test case with base > 1 + index = mticker.IndexLocator(base=2, offset=0) + assert_array_equal(index.tick_values(0, 5), [0, 2, 4]) + class TestSymmetricalLogLocator: def test_set_params(self): @@ -637,7 +651,7 @@ def test_subs(self): sym = mticker.SymmetricalLogLocator(base=10, linthresh=1, subs=[2.0, 4.0]) sym.create_dummy_axis() sym.axis.set_view_interval(-10, 10) - assert (sym() == [-20., -40., -2., -4., 0., 2., 4., 20., 40.]).all() + assert_array_equal(sym(), [-20, -40, -2, -4, 0, 2, 4, 20, 40]) def test_extending(self): sym = mticker.SymmetricalLogLocator(base=10, linthresh=1) @@ -862,6 +876,22 @@ def test_set_use_offset_float(self): assert not tmp_form.get_useOffset() assert tmp_form.offset == 0.5 + def test_set_use_offset_bool(self): + tmp_form = mticker.ScalarFormatter() + tmp_form.set_useOffset(True) + assert tmp_form.get_useOffset() + assert tmp_form.offset == 0 + + tmp_form.set_useOffset(False) + assert not tmp_form.get_useOffset() + assert tmp_form.offset == 0 + + def test_set_use_offset_int(self): + tmp_form = mticker.ScalarFormatter() + tmp_form.set_useOffset(1) + assert not tmp_form.get_useOffset() + assert tmp_form.offset == 1 + def test_use_locale(self): conv = locale.localeconv() sep = conv['thousands_sep'] @@ -1238,11 +1268,16 @@ def test_sublabel(self): ax.set_xlim(1, 80) self._sub_labels(ax.xaxis, subs=[]) - # axis range at 0.4 to 1 decades, label subs 2, 3, 4, 6 + # axis range slightly more than 1 decade, but spanning a single major + # tick, label subs 2, 3, 4, 6 + ax.set_xlim(.8, 9) + self._sub_labels(ax.xaxis, subs=[2, 3, 4, 6]) + + # axis range at 0.4 to 1 decade, label subs 2, 3, 4, 6 ax.set_xlim(1, 8) self._sub_labels(ax.xaxis, subs=[2, 3, 4, 6]) - # axis range at 0 to 0.4 decades, label all + # axis range at 0 to 0.4 decade, label all ax.set_xlim(0.5, 0.9) self._sub_labels(ax.xaxis, subs=np.arange(2, 10, dtype=int)) @@ -1594,6 +1629,73 @@ def test_engformatter_usetex_useMathText(): assert x_tick_label_text == ['$0$', '$500$', '$1$ k'] +@pytest.mark.parametrize( + 'data_offset, noise, oom_center_desired, oom_noise_desired', [ + (271_490_000_000.0, 10, 9, 0), + (27_149_000_000_000.0, 10_000_000, 12, 6), + (27.149, 0.01, 0, -3), + (2_714.9, 0.01, 3, -3), + (271_490.0, 0.001, 3, -3), + (271.49, 0.001, 0, -3), + # The following sets of parameters demonstrates that when + # oom(data_offset)-1 and oom(noise)-2 equal a standard 3*N oom, we get + # that oom_noise_desired < oom(noise) + (27_149_000_000.0, 100, 9, +3), + (27.149, 1e-07, 0, -6), + (271.49, 0.0001, 0, -3), + (27.149, 0.0001, 0, -3), + # Tests where oom(data_offset) <= oom(noise), those are probably + # covered by the part where formatter.offset != 0 + (27_149.0, 10_000, 0, 3), + (27.149, 10_000, 0, 3), + (27.149, 1_000, 0, 3), + (27.149, 100, 0, 0), + (27.149, 10, 0, 0), + ] +) +def test_engformatter_offset_oom( + data_offset, + noise, + oom_center_desired, + oom_noise_desired +): + UNIT = "eV" + fig, ax = plt.subplots() + ydata = data_offset + np.arange(-5, 7, dtype=float)*noise + ax.plot(ydata) + formatter = mticker.EngFormatter(useOffset=True, unit=UNIT) + # So that offset strings will always have the same size + formatter.ENG_PREFIXES[0] = "_" + ax.yaxis.set_major_formatter(formatter) + fig.canvas.draw() + offset_got = formatter.get_offset() + ticks_got = [labl.get_text() for labl in ax.get_yticklabels()] + # Predicting whether offset should be 0 or not is essentially testing + # ScalarFormatter._compute_offset . This function is pretty complex and it + # would be nice to test it, but this is out of scope for this test which + # only makes sure that offset text and the ticks gets the correct unit + # prefixes and the ticks. + if formatter.offset: + prefix_noise_got = offset_got[2] + prefix_noise_desired = formatter.ENG_PREFIXES[oom_noise_desired] + prefix_center_got = offset_got[-1-len(UNIT)] + prefix_center_desired = formatter.ENG_PREFIXES[oom_center_desired] + assert prefix_noise_desired == prefix_noise_got + assert prefix_center_desired == prefix_center_got + # Make sure the ticks didn't get the UNIT + for tick in ticks_got: + assert UNIT not in tick + else: + assert oom_center_desired == 0 + assert offset_got == "" + # Make sure the ticks contain now the prefixes + for tick in ticks_got: + # 0 is zero on all orders of magnitudes, no matter what is + # oom_noise_desired + prefix_idx = 0 if tick[0] == "0" else oom_noise_desired + assert tick.endswith(formatter.ENG_PREFIXES[prefix_idx] + UNIT) + + class TestPercentFormatter: percent_data = [ # Check explicitly set decimals over different intervals and values @@ -1828,14 +1930,57 @@ def test_bad_locator_subs(sub): ll.set_params(subs=sub) -@pytest.mark.parametrize('numticks', [1, 2, 3, 9]) +@pytest.mark.parametrize("numticks, lims, ticks", [ + (1, (.5, 5), [.1, 1, 10]), + (2, (.5, 5), [.1, 1, 10]), + (3, (.5, 5), [.1, 1, 10]), + (9, (.5, 5), [.1, 1, 10]), + (1, (.5, 50), [.1, 10, 1_000]), + (2, (.5, 50), [.1, 1, 10, 100]), + (3, (.5, 50), [.1, 1, 10, 100]), + (9, (.5, 50), [.1, 1, 10, 100]), + (1, (.5, 500), [.1, 10, 1_000]), + (2, (.5, 500), [.01, 1, 100, 10_000]), + (3, (.5, 500), [.1, 1, 10, 100, 1_000]), + (9, (.5, 500), [.1, 1, 10, 100, 1_000]), + (1, (.5, 5000), [.1, 100, 100_000]), + (2, (.5, 5000), [.001, 1, 1_000, 1_000_000]), + (3, (.5, 5000), [.001, 1, 1_000, 1_000_000]), + (9, (.5, 5000), [.1, 1, 10, 100, 1_000, 10_000]), +]) @mpl.style.context('default') -def test_small_range_loglocator(numticks): - ll = mticker.LogLocator() - ll.set_params(numticks=numticks) - for top in [5, 7, 9, 11, 15, 50, 100, 1000]: - ticks = ll.tick_values(.5, top) - assert (np.diff(np.log10(ll.tick_values(6, 150))) == 1).all() +def test_small_range_loglocator(numticks, lims, ticks): + ll = mticker.LogLocator(numticks=numticks) + if parse_version(np.version.version).major < 2: + assert_allclose(ll.tick_values(*lims), ticks, rtol=2e-16) + else: + assert_array_equal(ll.tick_values(*lims), ticks) + + +@mpl.style.context('default') +def test_loglocator_properties(): + # Test that LogLocator returns ticks satisfying basic desirable properties + # for a wide range of inputs. + max_numticks = 8 + pow_end = 20 + for numticks, (lo, hi) in itertools.product( + range(1, max_numticks + 1), itertools.combinations(range(pow_end), 2)): + ll = mticker.LogLocator(numticks=numticks) + decades = np.log10(ll.tick_values(10**lo, 10**hi)).round().astype(int) + # There are no more ticks than the requested number, plus exactly one + # tick below and one tick above the limits. + assert len(decades) <= numticks + 2 + assert decades[0] < lo <= decades[1] + assert decades[-2] <= hi < decades[-1] + stride, = {*np.diff(decades)} # Extract the (constant) stride. + # Either the ticks are on integer multiples of the stride... + if not (decades % stride == 0).all(): + # ... or (for this given stride) no offset would be acceptable, + # i.e. they would either result in fewer ticks than the selected + # solution, or more than the requested number of ticks. + for offset in range(0, stride): + alt_decades = range(lo + offset, hi + 1, stride) + assert len(alt_decades) < len(decades) or len(alt_decades) > numticks def test_NullFormatter(): diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 9c654f4d1f48..98fd5e70cdb9 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -11,6 +11,11 @@ from matplotlib.patches import Rectangle +pytestmark = [ + pytest.mark.usefixtures('text_placeholders') +] + + def example_plot(ax, fontsize=12): ax.plot([1, 2]) ax.locator_params(nbins=3) @@ -19,7 +24,7 @@ def example_plot(ax, fontsize=12): ax.set_title('Title', fontsize=fontsize) -@image_comparison(['tight_layout1'], tol=1.9) +@image_comparison(['tight_layout1'], style='mpl20') def test_tight_layout1(): """Test tight_layout for a single subplot.""" fig, ax = plt.subplots() @@ -27,7 +32,7 @@ def test_tight_layout1(): plt.tight_layout() -@image_comparison(['tight_layout2']) +@image_comparison(['tight_layout2'], style='mpl20') def test_tight_layout2(): """Test tight_layout for multiple subplots.""" fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2) @@ -38,7 +43,7 @@ def test_tight_layout2(): plt.tight_layout() -@image_comparison(['tight_layout3']) +@image_comparison(['tight_layout3'], style='mpl20') def test_tight_layout3(): """Test tight_layout for multiple subplots.""" ax1 = plt.subplot(221) @@ -50,8 +55,7 @@ def test_tight_layout3(): plt.tight_layout() -@image_comparison(['tight_layout4'], freetype_version=('2.5.5', '2.6.1'), - tol=0.015) +@image_comparison(['tight_layout4'], style='mpl20') def test_tight_layout4(): """Test tight_layout for subplot2grid.""" ax1 = plt.subplot2grid((3, 3), (0, 0)) @@ -65,7 +69,7 @@ def test_tight_layout4(): plt.tight_layout() -@image_comparison(['tight_layout5']) +@image_comparison(['tight_layout5'], style='mpl20') def test_tight_layout5(): """Test tight_layout for image.""" ax = plt.subplot() @@ -74,7 +78,7 @@ def test_tight_layout5(): plt.tight_layout() -@image_comparison(['tight_layout6']) +@image_comparison(['tight_layout6'], style='mpl20') def test_tight_layout6(): """Test tight_layout for gridspec.""" @@ -116,7 +120,7 @@ def test_tight_layout6(): h_pad=0.45) -@image_comparison(['tight_layout7'], tol=1.9) +@image_comparison(['tight_layout7'], style='mpl20') def test_tight_layout7(): # tight layout with left and right titles fontsize = 24 @@ -130,7 +134,7 @@ def test_tight_layout7(): plt.tight_layout() -@image_comparison(['tight_layout8'], tol=0.005) +@image_comparison(['tight_layout8'], style='mpl20', tol=0.005) def test_tight_layout8(): """Test automatic use of tight_layout.""" fig = plt.figure() @@ -140,7 +144,7 @@ def test_tight_layout8(): fig.draw_without_rendering() -@image_comparison(['tight_layout9']) +@image_comparison(['tight_layout9'], style='mpl20') def test_tight_layout9(): # Test tight_layout for non-visible subplots # GH 8244 @@ -174,10 +178,10 @@ def test_outward_ticks(): # These values were obtained after visual checking that they correspond # to a tight layouting that did take the ticks into account. expected = [ - [[0.091, 0.607], [0.433, 0.933]], - [[0.579, 0.607], [0.922, 0.933]], - [[0.091, 0.140], [0.433, 0.466]], - [[0.579, 0.140], [0.922, 0.466]], + [[0.092, 0.605], [0.433, 0.933]], + [[0.581, 0.605], [0.922, 0.933]], + [[0.092, 0.138], [0.433, 0.466]], + [[0.581, 0.138], [0.922, 0.466]], ] for nn, ax in enumerate(fig.axes): assert_array_equal(np.round(ax.get_position().get_points(), 3), @@ -190,8 +194,8 @@ def add_offsetboxes(ax, size=10, margin=.1, color='black'): """ m, mp = margin, 1+margin anchor_points = [(-m, -m), (-m, .5), (-m, mp), - (mp, .5), (.5, mp), (mp, mp), - (.5, -m), (mp, -m), (.5, -m)] + (.5, mp), (mp, mp), (mp, .5), + (mp, -m), (.5, -m)] for point in anchor_points: da = DrawingArea(size, size) background = Rectangle((0, 0), width=size, @@ -211,47 +215,78 @@ def add_offsetboxes(ax, size=10, margin=.1, color='black'): bbox_transform=ax.transAxes, borderpad=0.) ax.add_artist(anchored_box) - return anchored_box -@image_comparison(['tight_layout_offsetboxes1', 'tight_layout_offsetboxes2']) def test_tight_layout_offsetboxes(): - # 1. + # 0. # - Create 4 subplots # - Plot a diagonal line on them + # - Use tight_layout + # + # 1. + # - Same 4 subplots # - Surround each plot with 7 boxes # - Use tight_layout - # - See that the squares are included in the tight_layout - # and that the squares in the middle do not overlap + # - See that the squares are included in the tight_layout and that the squares do + # not overlap # # 2. - # - Make the squares around the right side axes invisible - # - See that the invisible squares do not affect the - # tight_layout + # - Make the squares around the Axes invisible + # - See that the invisible squares do not affect the tight_layout rows = cols = 2 colors = ['red', 'blue', 'green', 'yellow'] x = y = [0, 1] - def _subplots(): - _, axs = plt.subplots(rows, cols) - axs = axs.flat - for ax, color in zip(axs, colors): + def _subplots(with_boxes): + fig, axs = plt.subplots(rows, cols) + for ax, color in zip(axs.flat, colors): ax.plot(x, y, color=color) - add_offsetboxes(ax, 20, color=color) - return axs + if with_boxes: + add_offsetboxes(ax, 20, color=color) + return fig, axs + + # 0. + fig0, axs0 = _subplots(False) + fig0.tight_layout() # 1. - axs = _subplots() - plt.tight_layout() + fig1, axs1 = _subplots(True) + fig1.tight_layout() + + # The AnchoredOffsetbox should be added to the bounding of the Axes, causing them to + # be smaller than the plain figure. + for ax0, ax1 in zip(axs0.flat, axs1.flat): + bbox0 = ax0.get_position() + bbox1 = ax1.get_position() + assert bbox1.x0 > bbox0.x0 + assert bbox1.x1 < bbox0.x1 + assert bbox1.y0 > bbox0.y0 + assert bbox1.y1 < bbox0.y1 + + # No AnchoredOffsetbox should overlap with another. + bboxes = [] + for ax1 in axs1.flat: + for child in ax1.get_children(): + if not isinstance(child, AnchoredOffsetbox): + continue + bbox = child.get_window_extent() + for other_bbox in bboxes: + assert not bbox.overlaps(other_bbox) + bboxes.append(bbox) # 2. - axs = _subplots() - for ax in (axs[cols-1::rows]): + fig2, axs2 = _subplots(True) + for ax in axs2.flat: for child in ax.get_children(): if isinstance(child, AnchoredOffsetbox): child.set_visible(False) - - plt.tight_layout() + fig2.tight_layout() + # The invisible AnchoredOffsetbox should not count for tight layout, so it should + # look the same as when they were never added. + for ax0, ax2 in zip(axs0.flat, axs2.flat): + bbox0 = ax0.get_position() + bbox2 = ax2.get_position() + assert_array_equal(bbox2.get_points(), bbox0.get_points()) def test_empty_layout(): @@ -296,8 +331,8 @@ def test_collapsed(): # zero (i.e. margins add up to more than the available width) that a call # to tight_layout will not get applied: fig, ax = plt.subplots(tight_layout=True) - ax.set_xlim([0, 1]) - ax.set_ylim([0, 1]) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) ax.annotate('BIG LONG STRING', xy=(1.25, 2), xytext=(10.5, 1.75), annotation_clip=False) diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index 959814de82db..2b4351a5cfbb 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -9,9 +9,10 @@ import matplotlib.pyplot as plt import matplotlib.patches as mpatches import matplotlib.transforms as mtransforms -from matplotlib.transforms import Affine2D, Bbox, TransformedBbox +from matplotlib.transforms import Affine2D, Bbox, TransformedBbox, _ScaledRotation from matplotlib.path import Path from matplotlib.testing.decorators import image_comparison, check_figures_equal +from unittest.mock import MagicMock class TestAffine2D: @@ -341,6 +342,31 @@ def test_deepcopy(self): assert_array_equal(s.get_matrix(), a.get_matrix()) +class TestAffineDeltaTransform: + def test_invalidate(self): + before = np.array([[1.0, 4.0, 0.0], + [5.0, 1.0, 0.0], + [0.0, 0.0, 1.0]]) + after = np.array([[1.0, 3.0, 0.0], + [5.0, 1.0, 0.0], + [0.0, 0.0, 1.0]]) + + # Translation and skew present + base = mtransforms.Affine2D.from_values(1, 5, 4, 1, 2, 3) + t = mtransforms.AffineDeltaTransform(base) + assert_array_equal(t.get_matrix(), before) + + # Mess with the internal structure of `base` without invalidating + # This should not affect this transform because it's a passthrough: + # it's always invalid + base.get_matrix()[0, 1:] = 3 + assert_array_equal(t.get_matrix(), after) + + # Invalidate the base + base.invalidate() + assert_array_equal(t.get_matrix(), after) + + def test_non_affine_caching(): class AssertingNonAffineTransform(mtransforms.Transform): """ @@ -667,6 +693,13 @@ def test_contains_branch(self): assert not self.stack1.contains_branch(self.tn1 + self.ta2) + blend = mtransforms.BlendedGenericTransform(self.tn2, self.stack2) + x, y = blend.contains_branch_separately(self.stack2_subset) + stack_blend = self.tn3 + blend + sx, sy = stack_blend.contains_branch_separately(self.stack2_subset) + assert x is sx is False + assert y is sy is True + def test_affine_simplification(self): # tests that a transform stack only calls as much is absolutely # necessary "non-affine" allowing the best possible optimization with @@ -802,6 +835,16 @@ def assert_bbox_eq(bbox1, bbox2): assert_array_equal(bbox1.bounds, bbox2.bounds) +def test_bbox_is_finite(): + assert not Bbox([(1, 1), (1, 1)])._is_finite() + assert not Bbox([(0, 0), (np.inf, 1)])._is_finite() + assert not Bbox([(-np.inf, 0), (2, 2)])._is_finite() + assert not Bbox([(np.nan, 0), (2, 2)])._is_finite() + assert Bbox([(0, 0), (0, 2)])._is_finite() + assert Bbox([(0, 0), (2, 0)])._is_finite() + assert Bbox([(0, 0), (1, 2)])._is_finite() + + def test_bbox_frozen_copies_minpos(): bbox = mtransforms.Bbox.from_extents(0.0, 0.0, 1.0, 1.0, minpos=1.0) frozen = bbox.frozen() @@ -858,8 +901,7 @@ def test_str_transform(): Affine2D().scale(1.0))), PolarTransform( PolarAxes(0.125,0.1;0.775x0.8), - use_rmin=True, - apply_theta_transforms=False)), + use_rmin=True)), CompositeGenericTransform( CompositeGenericTransform( PolarAffine( @@ -935,7 +977,7 @@ def test_nonsingular(): zero_expansion = np.array([-0.001, 0.001]) cases = [(0, np.nan), (0, 0), (0, 7.9e-317)] for args in cases: - out = np.array(mtransforms.nonsingular(*args)) + out = np.array(mtransforms._nonsingular(*args)) assert_array_equal(out, zero_expansion) @@ -954,12 +996,6 @@ def test_transformed_path(): [(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)], atol=1e-15) - # Changing the path does not change the result (it's cached). - path.points = [(0, 0)] * 4 - assert_allclose(trans_path.get_fully_transformed_path().vertices, - [(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)], - atol=1e-15) - def test_transformed_patch_path(): trans = mtransforms.Affine2D() @@ -1020,7 +1056,7 @@ def test_transformwrapper(): t.set(scale.LogTransform(10)) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_scale_swapping(fig_test, fig_ref): np.random.seed(19680801) samples = np.random.normal(size=10) @@ -1057,18 +1093,42 @@ def test_transformedbbox_contains(): def test_interval_contains(): - assert mtransforms.interval_contains((0, 1), 0.5) - assert mtransforms.interval_contains((0, 1), 0) - assert mtransforms.interval_contains((0, 1), 1) - assert not mtransforms.interval_contains((0, 1), -1) - assert not mtransforms.interval_contains((0, 1), 2) - assert mtransforms.interval_contains((1, 0), 0.5) + assert mtransforms._interval_contains((0, 1), 0.5) + assert mtransforms._interval_contains((0, 1), 0) + assert mtransforms._interval_contains((0, 1), 1) + assert not mtransforms._interval_contains((0, 1), -1) + assert not mtransforms._interval_contains((0, 1), 2) + assert mtransforms._interval_contains((1, 0), 0.5) def test_interval_contains_open(): - assert mtransforms.interval_contains_open((0, 1), 0.5) - assert not mtransforms.interval_contains_open((0, 1), 0) - assert not mtransforms.interval_contains_open((0, 1), 1) - assert not mtransforms.interval_contains_open((0, 1), -1) - assert not mtransforms.interval_contains_open((0, 1), 2) - assert mtransforms.interval_contains_open((1, 0), 0.5) + assert mtransforms._interval_contains_open((0, 1), 0.5) + assert not mtransforms._interval_contains_open((0, 1), 0) + assert not mtransforms._interval_contains_open((0, 1), 1) + assert not mtransforms._interval_contains_open((0, 1), -1) + assert not mtransforms._interval_contains_open((0, 1), 2) + assert mtransforms._interval_contains_open((1, 0), 0.5) + + +def test_scaledrotation_initialization(): + """Test that the ScaledRotation object is initialized correctly.""" + theta = 1.0 # Arbitrary theta value for testing + trans_shift = MagicMock() # Mock the trans_shift transformation + scaled_rot = _ScaledRotation(theta, trans_shift) + assert scaled_rot._theta == theta + assert scaled_rot._trans_shift == trans_shift + assert scaled_rot._mtx is None + + +def test_scaledrotation_get_matrix_invalid(): + """Test get_matrix when the matrix is invalid and needs recalculation.""" + theta = np.pi / 2 + trans_shift = MagicMock(transform=MagicMock(return_value=[[theta, 0]])) + scaled_rot = _ScaledRotation(theta, trans_shift) + scaled_rot._invalid = True + matrix = scaled_rot.get_matrix() + trans_shift.transform.assert_called_once_with([[theta, 0]]) + expected_rotation = np.array([[0, -1], + [1, 0]]) + assert matrix is not None + assert_allclose(matrix[:2, :2], expected_rotation, atol=1e-15) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 14c591abd4e5..c9187915b5a2 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -232,7 +232,7 @@ def tris_contain_point(triang, xy): triang = mtri.Triangulation(tri_points[1:, 0], tri_points[1:, 1]) -@image_comparison(['tripcolor1.png']) +@image_comparison(['tripcolor1.png'], style='mpl20') def test_tripcolor(): x = np.asarray([0, 0.5, 1, 0, 0.5, 1, 0, 0.5, 1, 0.75]) y = np.asarray([0, 0, 0, 0.5, 0.5, 0.5, 1, 1, 1, 0.75]) @@ -612,7 +612,7 @@ def test_triinterpcubic_cg_solver(): # 1) A commonly used test involves a 2d Poisson matrix. def poisson_sparse_matrix(n, m): """ - Return the sparse, (n*m, n*m) matrix in coo format resulting from the + Return the sparse, (n*m, n*m) matrix in COO format resulting from the discretisation of the 2-dimensional Poisson equation according to a finite difference numerical scheme on a uniform (n, m) grid. """ @@ -1181,43 +1181,44 @@ def test_tricontourf_decreasing_levels(): plt.tricontourf(x, y, z, [1.0, 0.0]) -def test_internal_cpp_api(): +def test_internal_cpp_api() -> None: # Following github issue 8197. - from matplotlib import _tri # noqa: ensure lazy-loaded module *is* loaded. + from matplotlib import _tri # noqa: F401, ensure lazy-loaded module *is* loaded. # C++ Triangulation. with pytest.raises( TypeError, match=r'__init__\(\): incompatible constructor arguments.'): - mpl._tri.Triangulation() + mpl._tri.Triangulation() # type: ignore[call-arg] with pytest.raises( ValueError, match=r'x and y must be 1D arrays of the same length'): - mpl._tri.Triangulation([], [1], [[]], (), (), (), False) + mpl._tri.Triangulation(np.array([]), np.array([1]), np.array([[]]), (), (), (), + False) - x = [0, 1, 1] - y = [0, 0, 1] + x = np.array([0, 1, 1], dtype=np.float64) + y = np.array([0, 0, 1], dtype=np.float64) with pytest.raises( ValueError, match=r'triangles must be a 2D array of shape \(\?,3\)'): - mpl._tri.Triangulation(x, y, [[0, 1]], (), (), (), False) + mpl._tri.Triangulation(x, y, np.array([[0, 1]]), (), (), (), False) - tris = [[0, 1, 2]] + tris = np.array([[0, 1, 2]], dtype=np.int_) with pytest.raises( ValueError, match=r'mask must be a 1D array with the same length as the ' r'triangles array'): - mpl._tri.Triangulation(x, y, tris, [0, 1], (), (), False) + mpl._tri.Triangulation(x, y, tris, np.array([0, 1]), (), (), False) with pytest.raises( ValueError, match=r'edges must be a 2D array with shape \(\?,2\)'): - mpl._tri.Triangulation(x, y, tris, (), [[1]], (), False) + mpl._tri.Triangulation(x, y, tris, (), np.array([[1]]), (), False) with pytest.raises( ValueError, match=r'neighbors must be a 2D array with the same shape as the ' r'triangles array'): - mpl._tri.Triangulation(x, y, tris, (), (), [[-1]], False) + mpl._tri.Triangulation(x, y, tris, (), (), np.array([[-1]]), False) triang = mpl._tri.Triangulation(x, y, tris, (), (), (), False) @@ -1232,9 +1233,9 @@ def test_internal_cpp_api(): ValueError, match=r'mask must be a 1D array with the same length as the ' r'triangles array'): - triang.set_mask(mask) + triang.set_mask(mask) # type: ignore[arg-type] - triang.set_mask([True]) + triang.set_mask(np.array([True])) assert_array_equal(triang.get_edges(), np.empty((0, 2))) triang.set_mask(()) # Equivalent to Python Triangulation mask=None @@ -1244,15 +1245,14 @@ def test_internal_cpp_api(): with pytest.raises( TypeError, match=r'__init__\(\): incompatible constructor arguments.'): - mpl._tri.TriContourGenerator() + mpl._tri.TriContourGenerator() # type: ignore[call-arg] with pytest.raises( ValueError, - match=r'z must be a 1D array with the same length as the x and y ' - r'arrays'): - mpl._tri.TriContourGenerator(triang, [1]) + match=r'z must be a 1D array with the same length as the x and y arrays'): + mpl._tri.TriContourGenerator(triang, np.array([1])) - z = [0, 1, 2] + z = np.array([0, 1, 2]) tcg = mpl._tri.TriContourGenerator(triang, z) with pytest.raises( @@ -1263,13 +1263,13 @@ def test_internal_cpp_api(): with pytest.raises( TypeError, match=r'__init__\(\): incompatible constructor arguments.'): - mpl._tri.TrapezoidMapTriFinder() + mpl._tri.TrapezoidMapTriFinder() # type: ignore[call-arg] trifinder = mpl._tri.TrapezoidMapTriFinder(triang) with pytest.raises( ValueError, match=r'x and y must be array-like with same shape'): - trifinder.find_many([0], [0, 1]) + trifinder.find_many(np.array([0]), np.array([0, 1])) def test_qhull_large_offset(): diff --git a/lib/matplotlib/tests/test_ttconv.py b/lib/matplotlib/tests/test_ttconv.py deleted file mode 100644 index 1d839e7094b0..000000000000 --- a/lib/matplotlib/tests/test_ttconv.py +++ /dev/null @@ -1,17 +0,0 @@ -from pathlib import Path - -import matplotlib -from matplotlib.testing.decorators import image_comparison -import matplotlib.pyplot as plt - - -@image_comparison(["truetype-conversion.pdf"]) -# mpltest.ttf does not have "l"/"p" glyphs so we get a warning when trying to -# get the font extents. -def test_truetype_conversion(recwarn): - matplotlib.rcParams['pdf.fonttype'] = 3 - fig, ax = plt.subplots() - ax.text(0, 0, "ABCDE", - font=Path(__file__).with_name("mpltest.ttf"), fontsize=80) - ax.set_xticks([]) - ax.set_yticks([]) diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index 1e173d5ea84d..b2f93ef28a26 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -5,7 +5,7 @@ def test_Type1Font(): - filename = os.path.join(os.path.dirname(__file__), 'cmr10.pfb') + filename = os.path.join(os.path.dirname(__file__), 'data', 'cmr10.pfb') font = t1f.Type1Font(filename) slanted = font.transform({'slant': 1}) condensed = font.transform({'extend': 0.5}) @@ -78,7 +78,7 @@ def test_Type1Font(): def test_Type1Font_2(): - filename = os.path.join(os.path.dirname(__file__), + filename = os.path.join(os.path.dirname(__file__), 'data', 'Courier10PitchBT-Bold.pfb') font = t1f.Type1Font(filename) assert font.prop['Weight'] == 'Bold' @@ -137,15 +137,15 @@ def test_tokenize_errors(): def test_overprecision(): # We used to output too many digits in FontMatrix entries and # ItalicAngle, which could make Type-1 parsers unhappy. - filename = os.path.join(os.path.dirname(__file__), 'cmr10.pfb') + filename = os.path.join(os.path.dirname(__file__), 'data', 'cmr10.pfb') font = t1f.Type1Font(filename) slanted = font.transform({'slant': .167}) lines = slanted.parts[0].decode('ascii').splitlines() - matrix, = [line[line.index('[')+1:line.index(']')] - for line in lines if '/FontMatrix' in line] - angle, = [word + matrix, = (line[line.index('[')+1:line.index(']')] + for line in lines if '/FontMatrix' in line) + angle, = (word for line in lines if '/ItalicAngle' in line - for word in line.split() if word[0] in '-0123456789'] + for word in line.split() if word[0] in '-0123456789') # the following used to include 0.00016700000000000002 assert matrix == '0.001 0 0.000167 0.001 0 0' # and here we had -9.48090361795083 diff --git a/lib/matplotlib/tests/test_typing.py b/lib/matplotlib/tests/test_typing.py new file mode 100644 index 000000000000..c9fc8e5b162f --- /dev/null +++ b/lib/matplotlib/tests/test_typing.py @@ -0,0 +1,51 @@ +import re +import typing +from pathlib import Path + +import matplotlib.pyplot as plt +from matplotlib.colors import Colormap +from matplotlib.typing import RcKeyType, RcGroupKeyType + + +def test_cm_stub_matches_runtime_colormaps(): + runtime_cm = plt.cm + runtime_cmaps = { + name + for name, value in vars(runtime_cm).items() + if isinstance(value, Colormap) + } + + cm_pyi_path = Path(__file__).parent.parent / "cm.pyi" + assert cm_pyi_path.exists(), f"{cm_pyi_path} does not exist" + + pyi_content = cm_pyi_path.read_text(encoding='utf-8') + + stubbed_cmaps = set( + re.findall(r"^(\w+):\s+colors\.Colormap", pyi_content, re.MULTILINE) + ) + + assert runtime_cmaps, ( + "No colormaps variables found at runtime in matplotlib.colors" + ) + assert stubbed_cmaps, ( + "No colormaps found in cm.pyi" + ) + + assert runtime_cmaps == stubbed_cmaps + + +def test_rcparam_stubs(): + runtime_rc_keys = { + name for name in plt.rcParamsDefault.keys() + if not name.startswith('_') + } + + assert {*typing.get_args(RcKeyType)} == runtime_rc_keys + + runtime_rc_group_keys = set() + for name in runtime_rc_keys: + groups = name.split('.') + for i in range(1, len(groups)): + runtime_rc_group_keys.add('.'.join(groups[:i])) + + assert {*typing.get_args(RcGroupKeyType)} == runtime_rc_group_keys diff --git a/lib/matplotlib/tests/test_units.py b/lib/matplotlib/tests/test_units.py index ae6372fea1e1..d2350667e94f 100644 --- a/lib/matplotlib/tests/test_units.py +++ b/lib/matplotlib/tests/test_units.py @@ -4,8 +4,10 @@ import matplotlib.pyplot as plt from matplotlib.testing.decorators import check_figures_equal, image_comparison +import matplotlib.patches as mpatches import matplotlib.units as munits -from matplotlib.category import UnitData +from matplotlib.category import StrCategoryConverter, UnitData +from matplotlib.dates import DateConverter import numpy as np import pytest @@ -189,7 +191,7 @@ def test_errorbar_mixed_units(): fig.canvas.draw() -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_subclass(fig_test, fig_ref): class subdate(datetime): pass @@ -236,6 +238,39 @@ def test_shared_axis_categorical(): assert "c" in ax2.xaxis.get_units()._mapping.keys() +def test_explicit_converter(): + d1 = {"a": 1, "b": 2} + str_cat_converter = StrCategoryConverter() + str_cat_converter_2 = StrCategoryConverter() + date_converter = DateConverter() + + # Explicit is set + fig1, ax1 = plt.subplots() + ax1.xaxis.set_converter(str_cat_converter) + assert ax1.xaxis.get_converter() == str_cat_converter + # Explicit not overridden by implicit + ax1.plot(d1.keys(), d1.values()) + assert ax1.xaxis.get_converter() == str_cat_converter + # No error when called twice with equivalent input + ax1.xaxis.set_converter(str_cat_converter) + # Error when explicit called twice + with pytest.raises(RuntimeError): + ax1.xaxis.set_converter(str_cat_converter_2) + + fig2, ax2 = plt.subplots() + ax2.plot(d1.keys(), d1.values()) + + # No error when equivalent type is used + ax2.xaxis.set_converter(str_cat_converter) + + fig3, ax3 = plt.subplots() + ax3.plot(d1.keys(), d1.values()) + + # Warn when implicit overridden + with pytest.warns(): + ax3.xaxis.set_converter(date_converter) + + def test_empty_default_limits(quantity_converter): munits.registry[Quantity] = quantity_converter fig, ax1 = plt.subplots() @@ -302,3 +337,17 @@ def test_plot_kernel(): # just a smoketest that fail kernel = Kernel([1, 2, 3, 4, 5]) plt.plot(kernel) + + +def test_connection_patch_units(pd): + # tests that this doesn't raise an error + fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(10, 5)) + x = pd.Timestamp('2017-01-01T12') + ax1.axvline(x) + y = "test test" + ax2.axhline(y) + arr = mpatches.ConnectionPatch((x, 0), (0, y), + coordsA='data', coordsB='data', + axesA=ax1, axesB=ax2) + fig.add_artist(arr) + fig.draw_without_rendering() diff --git a/lib/matplotlib/tests/test_usetex.py b/lib/matplotlib/tests/test_usetex.py index 342face4504f..87277f152789 100644 --- a/lib/matplotlib/tests/test_usetex.py +++ b/lib/matplotlib/tests/test_usetex.py @@ -1,3 +1,4 @@ +import re from tempfile import TemporaryFile import numpy as np @@ -42,13 +43,13 @@ def test_usetex(): ax.set_axis_off() -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_empty(fig_test, fig_ref): mpl.rcParams['text.usetex'] = True fig_test.text(.5, .5, "% a comment") -@check_figures_equal() +@check_figures_equal(extensions=['png', 'pdf', 'svg']) def test_unicode_minus(fig_test, fig_ref): mpl.rcParams['text.usetex'] = True fig_test.text(.5, .5, "$-$") @@ -65,7 +66,7 @@ def test_mathdefault(): fig.canvas.draw() -@image_comparison(['eqnarray.png']) +@image_comparison(['eqnarray.png'], style='mpl20') def test_multiline_eqnarray(): text = ( r'\begin{eqnarray*}' @@ -156,6 +157,69 @@ def test_missing_psfont(fmt, monkeypatch): fig.savefig(tmpfile, format=fmt) +def test_pdf_type1_font_subsetting(): + """Test that fonts in PDF output are properly subset.""" + pikepdf = pytest.importorskip("pikepdf") + + mpl.rcParams["text.usetex"] = True + mpl.rcParams["text.latex.preamble"] = r"\usepackage{amssymb}" + fig, ax = plt.subplots() + ax.text(0.2, 0.7, r"$\int_{-\infty}^{\aleph}\sqrt{\alpha\beta\gamma}\mathrm{d}x$") + ax.text(0.2, 0.5, r"$\mathfrak{x}\circledcirc\mathfrak{y}\in\mathbb{R}$") + + with TemporaryFile() as tmpfile: + fig.savefig(tmpfile, format="pdf") + tmpfile.seek(0) + pdf = pikepdf.Pdf.open(tmpfile) + + length = {} + page = pdf.pages[0] + for font_name, font in page.Resources.Font.items(): + assert font.Subtype == "/Type1", ( + f"Font {font_name}={font} is not a Type 1 font" + ) + + # Subsetted font names have a 6-character tag followed by a '+' + base_font = str(font["/BaseFont"]).removeprefix("/") + assert re.match(r"^[A-Z]{6}\+", base_font), ( + f"Font {font_name}={base_font} lacks a subset indicator tag" + ) + assert "/FontFile" in font.FontDescriptor, ( + f"Type 1 font {font_name}={base_font} is not embedded" + ) + _, original_name = base_font.split("+", 1) + length[original_name] = len(bytes(font["/FontDescriptor"]["/FontFile"])) + + print("Embedded font stream lengths:", length) + # We should have several fonts, each much smaller than the original. + # I get under 10kB on my system for each font, but allow 15kB in case + # of differences in the font files. + assert { + 'CMEX10', + 'CMMI12', + 'CMR12', + 'CMSY10', + 'CMSY8', + 'EUFM10', + 'MSAM10', + 'MSBM10', + }.issubset(length), "Missing expected fonts in the PDF" + for font_name, length in length.items(): + assert length < 15_000, ( + f"Font {font_name}={length} is larger than expected" + ) + + # For comparison, lengths without subsetting on my system: + # 'CMEX10': 29686 + # 'CMMI12': 36176 + # 'CMR12': 32157 + # 'CMSY10': 32004 + # 'CMSY8': 32061 + # 'EUFM10': 20546 + # 'MSAM10': 31199 + # 'MSBM10': 34129 + + try: _old_gs_version = mpl._get_executable_info('gs').version < parse_version('9.55') except mpl.ExecutableNotFoundError: @@ -168,8 +232,8 @@ def test_rotation(): mpl.rcParams['text.usetex'] = True fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1]) - ax.set(xlim=[-0.5, 5], xticks=[], ylim=[-0.5, 3], yticks=[], frame_on=False) + ax = fig.add_axes((0, 0, 1, 1)) + ax.set(xlim=(-0.5, 5), xticks=[], ylim=(-0.5, 3), yticks=[], frame_on=False) text = {val: val[0] for val in ['top', 'center', 'bottom', 'left', 'right']} text['baseline'] = 'B' @@ -185,3 +249,10 @@ def test_rotation(): # 'My' checks full height letters plus descenders. ax.text(x, y, f"$\\mathrm{{My {text[ha]}{text[va]} {angle}}}$", rotation=angle, horizontalalignment=ha, verticalalignment=va) + + +def test_unicode_sizing(): + tp = mpl.textpath.TextToPath() + scale1 = tp.get_glyphs_tex(mpl.font_manager.FontProperties(), "W")[0][0][3] + scale2 = tp.get_glyphs_tex(mpl.font_manager.FontProperties(), r"\textwon")[0][0][3] + assert scale1 == scale2 diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 0f2cc411dbdf..a35e310c2962 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1,15 +1,15 @@ import functools import io +import operator from unittest import mock import matplotlib as mpl -from matplotlib.backend_bases import MouseEvent +from matplotlib.backend_bases import DrawEvent, KeyEvent, MouseEvent import matplotlib.colors as mcolors import matplotlib.widgets as widgets import matplotlib.pyplot as plt from matplotlib.testing.decorators import check_figures_equal, image_comparison -from matplotlib.testing.widgets import (click_and_drag, do_event, get_ax, - mock_event, noop) +from matplotlib.testing.widgets import click_and_drag, get_ax, noop import numpy as np from numpy.testing import assert_allclose @@ -70,12 +70,11 @@ def test_save_blitted_widget_as_pdf(): def test_rectangle_selector(ax, kwargs): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, **kwargs) - do_event(tool, 'press', xdata=100, ydata=100, button=1) - do_event(tool, 'onmove', xdata=199, ydata=199, button=1) - + tool = widgets.RectangleSelector(ax, onselect=onselect, **kwargs) + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (199, 199), 1)._process() # purposely drag outside of axis for release - do_event(tool, 'release', xdata=250, ydata=250, button=1) + MouseEvent._from_ax_coords("button_release_event", ax, (250, 250), 1)._process() if kwargs.get('drawtype', None) not in ['line', 'none']: assert_allclose(tool.geometry, @@ -104,7 +103,7 @@ def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1): minspanx, minspany = (ax.transData.transform((x1, y1)) - ax.transData.transform((x0, y0))) - tool = widgets.RectangleSelector(ax, onselect, interactive=True, + tool = widgets.RectangleSelector(ax, onselect=onselect, interactive=True, spancoords=spancoords, minspanx=minspanx, minspany=minspany) # Too small to create a selector @@ -130,24 +129,14 @@ def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1): assert kwargs == {} -def test_deprecation_selector_visible_attribute(ax): - tool = widgets.RectangleSelector(ax, lambda *args: None) - - assert tool.get_visible() - - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="was deprecated in Matplotlib 3.8"): - tool.visible - - @pytest.mark.parametrize('drag_from_anywhere, new_center', [[True, (60, 75)], [False, (30, 20)]]) def test_rectangle_drag(ax, drag_from_anywhere, new_center): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, drag_from_anywhere=drag_from_anywhere) # Create rectangle - click_and_drag(tool, start=(0, 10), end=(100, 120)) + click_and_drag(tool, start=(10, 10), end=(90, 120)) assert tool.center == (50, 65) # Drag inside rectangle, but away from centre handle # @@ -165,7 +154,7 @@ def test_rectangle_drag(ax, drag_from_anywhere, new_center): def test_rectangle_selector_set_props_handle_props(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, props=dict(facecolor='b', alpha=0.2), handle_props=dict(alpha=0.5)) # Create rectangle @@ -186,10 +175,10 @@ def test_rectangle_selector_set_props_handle_props(ax): def test_rectangle_resize(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle - click_and_drag(tool, start=(0, 10), end=(100, 120)) - assert tool.extents == (0.0, 100.0, 10.0, 120.0) + click_and_drag(tool, start=(10, 10), end=(100, 120)) + assert tool.extents == (10.0, 100.0, 10.0, 120.0) # resize NE handle extents = tool.extents @@ -221,7 +210,7 @@ def test_rectangle_resize(ax): def test_rectangle_add_state(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(125, 130)) @@ -237,7 +226,7 @@ def test_rectangle_add_state(ax): @pytest.mark.parametrize('add_state', [True, False]) def test_rectangle_resize_center(ax, add_state): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(125, 130)) assert tool.extents == (70.0, 125.0, 65.0, 130.0) @@ -311,7 +300,7 @@ def test_rectangle_resize_center(ax, add_state): @pytest.mark.parametrize('add_state', [True, False]) def test_rectangle_resize_square(ax, add_state): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) assert tool.extents == (70.0, 120.0, 65.0, 115.0) @@ -384,7 +373,7 @@ def test_rectangle_resize_square(ax, add_state): def test_rectangle_resize_square_center(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) tool.add_state('square') @@ -449,18 +438,18 @@ def test_rectangle_resize_square_center(ax): @pytest.mark.parametrize('selector_class', [widgets.RectangleSelector, widgets.EllipseSelector]) def test_rectangle_rotate(ax, selector_class): - tool = selector_class(ax, onselect=noop, interactive=True) + tool = selector_class(ax, interactive=True) # Draw rectangle click_and_drag(tool, start=(100, 100), end=(130, 140)) assert tool.extents == (100, 130, 100, 140) assert len(tool._state) == 0 # Rotate anticlockwise using top-right corner - do_event(tool, 'on_key_press', key='r') + KeyEvent("key_press_event", ax.figure.canvas, "r")._process() assert tool._state == {'rotate'} assert len(tool._state) == 1 click_and_drag(tool, start=(130, 140), end=(120, 145)) - do_event(tool, 'on_key_press', key='r') + KeyEvent("key_press_event", ax.figure.canvas, "r")._process() assert len(tool._state) == 0 # Extents shouldn't change (as shape of rectangle hasn't changed) assert tool.extents == (100, 130, 100, 140) @@ -482,7 +471,7 @@ def test_rectangle_rotate(ax, selector_class): def test_rectangle_add_remove_set(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Draw rectangle click_and_drag(tool, start=(100, 100), end=(130, 140)) assert tool.extents == (100, 130, 100, 140) @@ -498,7 +487,7 @@ def test_rectangle_add_remove_set(ax): def test_rectangle_resize_square_center_aspect(ax, use_data_coordinates): ax.set_aspect(0.8) - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, use_data_coordinates=use_data_coordinates) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) @@ -528,10 +517,20 @@ def test_rectangle_resize_square_center_aspect(ax, use_data_coordinates): 46.25, 133.75]) +def test_axeswidget_del_on_failed_init(): + """ + Test that an unraisable exception is not created when initialization + fails. + """ + # Pytest would fail the test if such an exception occurred. + fig, ax = plt.subplots() + with pytest.raises(TypeError, match="unexpected keyword argument 'undefined'"): + widgets.Button(ax, undefined='bar') + + def test_ellipse(ax): """For ellipse, test out the key modifiers""" - tool = widgets.EllipseSelector(ax, onselect=noop, - grab_range=10, interactive=True) + tool = widgets.EllipseSelector(ax, grab_range=10, interactive=True) tool.extents = (100, 150, 100, 150) # drag the rectangle @@ -557,9 +556,7 @@ def test_ellipse(ax): def test_rectangle_handles(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, - grab_range=10, - interactive=True, + tool = widgets.RectangleSelector(ax, grab_range=10, interactive=True, handle_props={'markerfacecolor': 'r', 'markeredgecolor': 'b'}) tool.extents = (100, 150, 100, 150) @@ -594,7 +591,7 @@ def test_rectangle_selector_onselect(ax, interactive): # check when press and release events take place at the same position onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, interactive=interactive) + tool = widgets.RectangleSelector(ax, onselect=onselect, interactive=interactive) # move outside of axis click_and_drag(tool, start=(100, 110), end=(150, 120)) @@ -610,7 +607,7 @@ def test_rectangle_selector_onselect(ax, interactive): def test_rectangle_selector_ignore_outside(ax, ignore_event_outside): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, + tool = widgets.RectangleSelector(ax, onselect=onselect, ignore_event_outside=ignore_event_outside) click_and_drag(tool, start=(100, 110), end=(150, 120)) onselect.assert_called_once() @@ -636,27 +633,36 @@ def test_rectangle_selector_ignore_outside(ax, ignore_event_outside): ('horizontal', False, dict(interactive=True)), ]) def test_span_selector(ax, orientation, onmove_callback, kwargs): - onselect = mock.Mock(spec=noop, return_value=None) - onmove = mock.Mock(spec=noop, return_value=None) - if onmove_callback: - kwargs['onmove_callback'] = onmove - - # While at it, also test that span selectors work in the presence of twin axes on - # top of the axes that contain the selector. Note that we need to unforce the axes - # aspect here, otherwise the twin axes forces the original axes' limits (to respect - # aspect=1) which makes some of the values below go out of bounds. + # Also test that span selectors work in the presence of twin axes or for + # outside-inset axes on top of the axes that contain the selector. Note + # that we need to unforce the axes aspect here, otherwise the twin axes + # forces the original axes' limits (to respect aspect=1) which makes some + # of the values below go out of bounds. ax.set_aspect("auto") - tax = ax.twinx() - - tool = widgets.SpanSelector(ax, onselect, orientation, **kwargs) - do_event(tool, 'press', xdata=100, ydata=100, button=1) - # move outside of axis - do_event(tool, 'onmove', xdata=199, ydata=199, button=1) - do_event(tool, 'release', xdata=250, ydata=250, button=1) - - onselect.assert_called_once_with(100, 199) - if onmove_callback: - onmove.assert_called_once_with(100, 199) + ax.twinx() + child = ax.inset_axes([0, 1, 1, 1], xlim=(0, 200), ylim=(0, 200)) + + for target in [ax, child]: + selected = [] + def onselect(*args): selected.append(args) + moved = [] + def onmove(*args): moved.append(args) + if onmove_callback: + kwargs['onmove_callback'] = onmove + + tool = widgets.SpanSelector(target, onselect, orientation, **kwargs) + MouseEvent._from_ax_coords( + "button_press_event", target, (100, 100), 1)._process() + # move outside of axis + MouseEvent._from_ax_coords( + "motion_notify_event", target, (199, 199), 1)._process() + MouseEvent._from_ax_coords( + "button_release_event", target, (250, 250), 1)._process() + + # tol is set by pixel size (~100 pixels & span of 200 data units) + assert_allclose(selected, [(100, 199)], atol=.5) + if onmove_callback: + assert_allclose(moved, [(100, 199)], atol=.5) @pytest.mark.parametrize('interactive', [True, False]) @@ -772,10 +778,11 @@ def test_span_selector_set_props_handle_props(ax): @pytest.mark.parametrize('selector', ['span', 'rectangle']) def test_selector_clear(ax, selector): - kwargs = dict(ax=ax, onselect=noop, interactive=True) + kwargs = dict(ax=ax, interactive=True) if selector == 'span': Selector = widgets.SpanSelector kwargs['direction'] = 'horizontal' + kwargs['onselect'] = noop else: Selector = widgets.RectangleSelector @@ -795,7 +802,7 @@ def test_selector_clear(ax, selector): click_and_drag(tool, start=(130, 130), end=(130, 130)) assert tool._selection_completed - do_event(tool, 'on_key_press', key='escape') + KeyEvent("key_press_event", ax.figure.canvas, "escape")._process() assert not tool._selection_completed @@ -806,7 +813,7 @@ def test_selector_clear_method(ax, selector): interactive=True, ignore_event_outside=True) else: - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) click_and_drag(tool, start=(10, 10), end=(100, 120)) assert tool._selection_completed assert tool.get_visible() @@ -862,7 +869,7 @@ def test_tool_line_handle(ax): def test_span_selector_bound(direction): fig, ax = plt.subplots(1, 1) ax.plot([10, 20], [10, 30]) - ax.figure.canvas.draw() + fig.canvas.draw() x_bound = ax.get_xbound() y_bound = ax.get_ybound() @@ -917,10 +924,8 @@ def mean(vmin, vmax): # Add span selector and check that the line is draw after it was updated # by the callback - press_data = [1, 2] - move_data = [2, 2] - do_event(span, 'press', xdata=press_data[0], ydata=press_data[1], button=1) - do_event(span, 'onmove', xdata=move_data[0], ydata=move_data[1], button=1) + MouseEvent._from_ax_coords("button_press_event", ax, (1, 2), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (2, 2), 1)._process() assert span._get_animated_artists() == (ln, ln2) assert ln.stale is False assert ln2.stale @@ -930,16 +935,12 @@ def mean(vmin, vmax): # Change span selector and check that the line is drawn/updated after its # value was updated by the callback - press_data = [4, 0] - move_data = [5, 2] - release_data = [5, 2] - do_event(span, 'press', xdata=press_data[0], ydata=press_data[1], button=1) - do_event(span, 'onmove', xdata=move_data[0], ydata=move_data[1], button=1) + MouseEvent._from_ax_coords("button_press_event", ax, (4, 0), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (5, 2), 1)._process() assert ln.stale is False assert ln2.stale assert_allclose(ln2.get_ydata(), -0.9424150707548072) - do_event(span, 'release', xdata=release_data[0], - ydata=release_data[1], button=1) + MouseEvent._from_ax_coords("button_release_event", ax, (5, 2), 1)._process() assert ln2.stale is False @@ -999,10 +1000,10 @@ def test_span_selector_extents(ax): def test_lasso_selector(ax, kwargs): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.LassoSelector(ax, onselect, **kwargs) - do_event(tool, 'press', xdata=100, ydata=100, button=1) - do_event(tool, 'onmove', xdata=125, ydata=125, button=1) - do_event(tool, 'release', xdata=150, ydata=150, button=1) + tool = widgets.LassoSelector(ax, onselect=onselect, **kwargs) + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (125, 125), 1)._process() + MouseEvent._from_ax_coords("button_release_event", ax, (150, 150), 1)._process() onselect.assert_called_once_with([(100, 100), (125, 125), (150, 150)]) @@ -1010,7 +1011,8 @@ def test_lasso_selector(ax, kwargs): def test_lasso_selector_set_props(ax): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.LassoSelector(ax, onselect, props=dict(color='b', alpha=0.2)) + tool = widgets.LassoSelector(ax, onselect=onselect, + props=dict(color='b', alpha=0.2)) artist = tool._selection_artist assert mcolors.same_color(artist.get_color(), 'b') @@ -1077,7 +1079,7 @@ def test_TextBox(ax, toolbar): assert tool.text == '' - do_event(tool, '_click') + MouseEvent._from_ax_coords("button_press_event", ax, (.5, .5), 1)._process() tool.set_val('x**2') @@ -1089,9 +1091,9 @@ def test_TextBox(ax, toolbar): assert submit_event.call_count == 2 - do_event(tool, '_click', xdata=.5, ydata=.5) # Ensure the click is in the axes. - do_event(tool, '_keypress', key='+') - do_event(tool, '_keypress', key='5') + MouseEvent._from_ax_coords("button_press_event", ax, (.5, .5), 1)._process() + KeyEvent("key_press_event", ax.figure.canvas, "+")._process() + KeyEvent("key_press_event", ax.figure.canvas, "5")._process() assert text_change_event.call_count == 3 @@ -1109,7 +1111,7 @@ def test_RadioButtons(ax): @image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True) def test_check_radio_buttons_image(): ax = get_ax() - fig = ax.figure + fig = ax.get_figure(root=False) fig.subplots_adjust(left=0.3) rax1 = fig.add_axes((0.05, 0.7, 0.2, 0.15)) @@ -1137,7 +1139,7 @@ def test_check_radio_buttons_image(): check_props={'color': ['red', 'green', 'blue']}) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_radio_buttons(fig_test, fig_ref): widgets.RadioButtons(fig_test.subplots(), ["tea", "coffee"]) ax = fig_ref.add_subplot(xticks=[], yticks=[]) @@ -1147,7 +1149,7 @@ def test_radio_buttons(fig_test, fig_ref): ax.text(.25, 1/3, "coffee", transform=ax.transAxes, va="center") -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_radio_buttons_props(fig_test, fig_ref): label_props = {'color': ['red'], 'fontsize': [24]} radio_props = {'facecolor': 'green', 'edgecolor': 'blue', 'linewidth': 2} @@ -1162,6 +1164,29 @@ def test_radio_buttons_props(fig_test, fig_ref): cb.set_radio_props({**radio_props, 's': (24 / 2)**2}) +@image_comparison(['check_radio_grid_buttons.png'], style='mpl20', remove_text=True) +def test_radio_grid_buttons(): + fig = plt.figure() + rb_horizontal = widgets.RadioButtons( + fig.add_axes((0.1, 0.05, 0.65, 0.05)), + ["tea", "coffee", "chocolate milk", "water", "soda", "coke"], + layout='horizontal', + active=4, + ) + cb_grid = widgets.CheckButtons( + fig.add_axes((0.1, 0.15, 0.25, 0.05*3)), + ["Chicken", "Salad", "Rice", "Sushi", "Pizza", "Fries"], + layout=(3, 2), + actives=[True, True, False, False, False, True], + ) + rb_vertical = widgets.RadioButtons( + fig.add_axes((0.1, 0.35, 0.2, 0.05*4)), + ["Trinity Cream", "Cake", "Ice Cream", "Muhallebi"], + layout='vertical', + active=3, + ) + + def test_radio_button_active_conflict(ax): with pytest.warns(UserWarning, match=r'Both the \*activecolor\* parameter'): @@ -1171,7 +1196,7 @@ def test_radio_button_active_conflict(ax): assert mcolors.same_color(rb._buttons.get_facecolor(), ['green', 'none']) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_radio_buttons_activecolor_change(fig_test, fig_ref): widgets.RadioButtons(fig_ref.subplots(), ['tea', 'coffee'], activecolor='green') @@ -1182,7 +1207,7 @@ def test_radio_buttons_activecolor_change(fig_test, fig_ref): cb.activecolor = 'green' -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_check_buttons(fig_test, fig_ref): widgets.CheckButtons(fig_test.subplots(), ["tea", "coffee"], [True, True]) ax = fig_ref.add_subplot(xticks=[], yticks=[]) @@ -1194,7 +1219,7 @@ def test_check_buttons(fig_test, fig_ref): ax.text(.25, 1/3, "coffee", transform=ax.transAxes, va="center") -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_check_button_props(fig_test, fig_ref): label_props = {'color': ['red'], 'fontsize': [24]} frame_props = {'facecolor': 'green', 'edgecolor': 'blue', 'linewidth': 2} @@ -1217,6 +1242,24 @@ def test_check_button_props(fig_test, fig_ref): cb.set_check_props({**check_props, 's': (24 / 2)**2}) +@pytest.mark.parametrize("widget", [widgets.RadioButtons, widgets.CheckButtons]) +def test__buttons_callbacks(ax, widget): + """Tests what https://github.com/matplotlib/matplotlib/pull/31031 fixed""" + on_clicked = mock.Mock(spec=noop, return_value=None) + button = widget(ax, ["Test Button"]) + button.on_clicked(on_clicked) + MouseEvent._from_ax_coords( + "button_press_event", + ax, + ax.transData.inverted().transform(ax.transAxes.transform( + # (x, y) of the 0th button defined at `_Buttons._init_layout` + (0.15, 0.5), + )), + 1, + )._process() + on_clicked.assert_called_once() + + def test_slider_slidermin_slidermax_invalid(): fig, ax = plt.subplots() # test min/max with floats @@ -1354,182 +1397,178 @@ def test_range_slider_same_init_values(orientation): assert_allclose(box.get_points().flatten()[idx], [0, 0.25, 0, 0.75]) -def check_polygon_selector(event_sequence, expected_result, selections_count, - **kwargs): +def check_polygon_selector(events, expected, selections_count, **kwargs): """ Helper function to test Polygon Selector. Parameters ---------- - event_sequence : list of tuples (etype, dict()) - A sequence of events to perform. The sequence is a list of tuples - where the first element of the tuple is an etype (e.g., 'onmove', - 'press', etc.), and the second element of the tuple is a dictionary of - the arguments for the event (e.g., xdata=5, key='shift', etc.). - expected_result : list of vertices (xdata, ydata) - The list of vertices that are expected to result from the event - sequence. + events : list[MouseEvent] + A sequence of events to perform. + expected : list of vertices (xdata, ydata) + The list of vertices expected to result from the event sequence. selections_count : int Wait for the tool to call its `onselect` function `selections_count` - times, before comparing the result to the `expected_result` + times, before comparing the result to the `expected` **kwargs Keyword arguments are passed to PolygonSelector. """ - ax = get_ax() - onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.PolygonSelector(ax, onselect, **kwargs) + ax = events[0].canvas.figure.axes[0] + tool = widgets.PolygonSelector(ax, onselect=onselect, **kwargs) - for (etype, event_args) in event_sequence: - do_event(tool, etype, **event_args) + for event in events: + event._process() assert onselect.call_count == selections_count - assert onselect.call_args == ((expected_result, ), {}) + assert onselect.call_args == ((expected, ), {}) -def polygon_place_vertex(xdata, ydata): - return [('onmove', dict(xdata=xdata, ydata=ydata)), - ('press', dict(xdata=xdata, ydata=ydata)), - ('release', dict(xdata=xdata, ydata=ydata))] +def polygon_place_vertex(ax, xy): + return [ + MouseEvent._from_ax_coords("motion_notify_event", ax, xy), + MouseEvent._from_ax_coords("button_press_event", ax, xy, 1), + MouseEvent._from_ax_coords("button_release_event", ax, xy, 1), + ] -def polygon_remove_vertex(xdata, ydata): - return [('onmove', dict(xdata=xdata, ydata=ydata)), - ('press', dict(xdata=xdata, ydata=ydata, button=3)), - ('release', dict(xdata=xdata, ydata=ydata, button=3))] +def polygon_remove_vertex(ax, xy): + return [ + MouseEvent._from_ax_coords("motion_notify_event", ax, xy), + MouseEvent._from_ax_coords("button_press_event", ax, xy, 3), + MouseEvent._from_ax_coords("button_release_event", ax, xy, 3), + ] @pytest.mark.parametrize('draw_bounding_box', [False, True]) -def test_polygon_selector(draw_bounding_box): +def test_polygon_selector(ax, draw_bounding_box): check_selector = functools.partial( check_polygon_selector, draw_bounding_box=draw_bounding_box) # Simple polygon expected_result = [(50, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 50)), ] check_selector(event_sequence, expected_result, 1) # Move first vertex before completing the polygon. expected_result = [(75, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - ('on_key_press', dict(key='control')), - ('onmove', dict(xdata=50, ydata=50)), - ('press', dict(xdata=50, ydata=50)), - ('onmove', dict(xdata=75, ydata=50)), - ('release', dict(xdata=75, ydata=50)), - ('on_key_release', dict(key='control')), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(75, 50), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + KeyEvent("key_press_event", ax.figure.canvas, "control"), + MouseEvent._from_ax_coords("motion_notify_event", ax, (50, 50)), + MouseEvent._from_ax_coords("button_press_event", ax, (50, 50), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (75, 50)), + MouseEvent._from_ax_coords("button_release_event", ax, (75, 50), 1), + KeyEvent("key_release_event", ax.figure.canvas, "control"), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (75, 50)), ] check_selector(event_sequence, expected_result, 1) # Move first two vertices at once before completing the polygon. expected_result = [(50, 75), (150, 75), (50, 150)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - ('on_key_press', dict(key='shift')), - ('onmove', dict(xdata=100, ydata=100)), - ('press', dict(xdata=100, ydata=100)), - ('onmove', dict(xdata=100, ydata=125)), - ('release', dict(xdata=100, ydata=125)), - ('on_key_release', dict(key='shift')), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 75), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + KeyEvent("key_press_event", ax.figure.canvas, "shift"), + MouseEvent._from_ax_coords("motion_notify_event", ax, (100, 100)), + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (100, 125)), + MouseEvent._from_ax_coords("button_release_event", ax, (100, 125), 1), + KeyEvent("key_release_event", ax.figure.canvas, "shift"), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 75)), ] check_selector(event_sequence, expected_result, 1) # Move first vertex after completing the polygon. - expected_result = [(75, 50), (150, 50), (50, 150)] + expected_result = [(85, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), - ('onmove', dict(xdata=50, ydata=50)), - ('press', dict(xdata=50, ydata=50)), - ('onmove', dict(xdata=75, ydata=50)), - ('release', dict(xdata=75, ydata=50)), + *polygon_place_vertex(ax, (60, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (60, 50)), + MouseEvent._from_ax_coords("motion_notify_event", ax, (60, 50)), + MouseEvent._from_ax_coords("button_press_event", ax, (60, 50), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (85, 50)), + MouseEvent._from_ax_coords("button_release_event", ax, (85, 50), 1), ] check_selector(event_sequence, expected_result, 2) # Move all vertices after completing the polygon. expected_result = [(75, 75), (175, 75), (75, 175)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), - ('on_key_press', dict(key='shift')), - ('onmove', dict(xdata=100, ydata=100)), - ('press', dict(xdata=100, ydata=100)), - ('onmove', dict(xdata=125, ydata=125)), - ('release', dict(xdata=125, ydata=125)), - ('on_key_release', dict(key='shift')), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 50)), + KeyEvent("key_press_event", ax.figure.canvas, "shift"), + MouseEvent._from_ax_coords("motion_notify_event", ax, (100, 100)), + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (125, 125)), + MouseEvent._from_ax_coords("button_release_event", ax, (125, 125), 1), + KeyEvent("key_release_event", ax.figure.canvas, "shift"), ] check_selector(event_sequence, expected_result, 2) # Try to move a vertex and move all before placing any vertices. expected_result = [(50, 50), (150, 50), (50, 150)] event_sequence = [ - ('on_key_press', dict(key='control')), - ('onmove', dict(xdata=100, ydata=100)), - ('press', dict(xdata=100, ydata=100)), - ('onmove', dict(xdata=125, ydata=125)), - ('release', dict(xdata=125, ydata=125)), - ('on_key_release', dict(key='control')), - ('on_key_press', dict(key='shift')), - ('onmove', dict(xdata=100, ydata=100)), - ('press', dict(xdata=100, ydata=100)), - ('onmove', dict(xdata=125, ydata=125)), - ('release', dict(xdata=125, ydata=125)), - ('on_key_release', dict(key='shift')), - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), + KeyEvent("key_press_event", ax.figure.canvas, "control"), + MouseEvent._from_ax_coords("motion_notify_event", ax, (100, 100)), + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (125, 125)), + MouseEvent._from_ax_coords("button_release_event", ax, (125, 125), 1), + KeyEvent("key_release_event", ax.figure.canvas, "control"), + KeyEvent("key_press_event", ax.figure.canvas, "shift"), + MouseEvent._from_ax_coords("motion_notify_event", ax, (100, 100)), + MouseEvent._from_ax_coords("button_press_event", ax, (100, 100), 1), + MouseEvent._from_ax_coords("motion_notify_event", ax, (125, 125)), + MouseEvent._from_ax_coords("button_release_event", ax, (125, 125), 1), + KeyEvent("key_release_event", ax.figure.canvas, "shift"), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 50)), ] check_selector(event_sequence, expected_result, 1) # Try to place vertex out-of-bounds, then reset, and start a new polygon. expected_result = [(50, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(250, 50), - ('on_key_press', dict(key='escape')), - ('on_key_release', dict(key='escape')), - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (250, 50)), + KeyEvent("key_press_event", ax.figure.canvas, "escape"), + KeyEvent("key_release_event", ax.figure.canvas, "escape"), + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 50)), ] check_selector(event_sequence, expected_result, 1) @pytest.mark.parametrize('draw_bounding_box', [False, True]) def test_polygon_selector_set_props_handle_props(ax, draw_bounding_box): - tool = widgets.PolygonSelector(ax, onselect=noop, + tool = widgets.PolygonSelector(ax, props=dict(color='b', alpha=0.2), handle_props=dict(alpha=0.5), draw_bounding_box=draw_bounding_box) - event_sequence = [ - *polygon_place_vertex(50, 50), - *polygon_place_vertex(150, 50), - *polygon_place_vertex(50, 150), - *polygon_place_vertex(50, 50), - ] - - for (etype, event_args) in event_sequence: - do_event(tool, etype, **event_args) + for event in [ + *polygon_place_vertex(ax, (50, 50)), + *polygon_place_vertex(ax, (150, 50)), + *polygon_place_vertex(ax, (50, 150)), + *polygon_place_vertex(ax, (50, 50)), + ]: + event._process() artist = tool._selection_artist assert artist.get_color() == 'b' @@ -1553,40 +1592,39 @@ def test_rect_visibility(fig_test, fig_ref): ax_test = fig_test.subplots() _ = fig_ref.subplots() - tool = widgets.RectangleSelector(ax_test, onselect=noop, - props={'visible': False}) + tool = widgets.RectangleSelector(ax_test, props={'visible': False}) tool.extents = (0.2, 0.8, 0.3, 0.7) # Change the order that the extra point is inserted in @pytest.mark.parametrize('idx', [1, 2, 3]) @pytest.mark.parametrize('draw_bounding_box', [False, True]) -def test_polygon_selector_remove(idx, draw_bounding_box): +def test_polygon_selector_remove(ax, idx, draw_bounding_box): verts = [(50, 50), (150, 50), (50, 150)] - event_sequence = [polygon_place_vertex(*verts[0]), - polygon_place_vertex(*verts[1]), - polygon_place_vertex(*verts[2]), + event_sequence = [polygon_place_vertex(ax, verts[0]), + polygon_place_vertex(ax, verts[1]), + polygon_place_vertex(ax, verts[2]), # Finish the polygon - polygon_place_vertex(*verts[0])] + polygon_place_vertex(ax, verts[0])] # Add an extra point - event_sequence.insert(idx, polygon_place_vertex(200, 200)) + event_sequence.insert(idx, polygon_place_vertex(ax, (200, 200))) # Remove the extra point - event_sequence.append(polygon_remove_vertex(200, 200)) + event_sequence.append(polygon_remove_vertex(ax, (200, 200))) # Flatten list of lists - event_sequence = sum(event_sequence, []) + event_sequence = functools.reduce(operator.iadd, event_sequence, []) check_polygon_selector(event_sequence, verts, 2, draw_bounding_box=draw_bounding_box) @pytest.mark.parametrize('draw_bounding_box', [False, True]) -def test_polygon_selector_remove_first_point(draw_bounding_box): +def test_polygon_selector_remove_first_point(ax, draw_bounding_box): verts = [(50, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(*verts[0]), - *polygon_place_vertex(*verts[1]), - *polygon_place_vertex(*verts[2]), - *polygon_place_vertex(*verts[0]), - *polygon_remove_vertex(*verts[0]), + *polygon_place_vertex(ax, verts[0]), + *polygon_place_vertex(ax, verts[1]), + *polygon_place_vertex(ax, verts[2]), + *polygon_place_vertex(ax, verts[0]), + *polygon_remove_vertex(ax, verts[0]), ] check_polygon_selector(event_sequence, verts[1:], 2, draw_bounding_box=draw_bounding_box) @@ -1596,48 +1634,44 @@ def test_polygon_selector_remove_first_point(draw_bounding_box): def test_polygon_selector_redraw(ax, draw_bounding_box): verts = [(50, 50), (150, 50), (50, 150)] event_sequence = [ - *polygon_place_vertex(*verts[0]), - *polygon_place_vertex(*verts[1]), - *polygon_place_vertex(*verts[2]), - *polygon_place_vertex(*verts[0]), + *polygon_place_vertex(ax, verts[0]), + *polygon_place_vertex(ax, verts[1]), + *polygon_place_vertex(ax, verts[2]), + *polygon_place_vertex(ax, verts[0]), # Polygon completed, now remove first two verts. - *polygon_remove_vertex(*verts[1]), - *polygon_remove_vertex(*verts[2]), + *polygon_remove_vertex(ax, verts[1]), + *polygon_remove_vertex(ax, verts[2]), # At this point the tool should be reset so we can add more vertices. - *polygon_place_vertex(*verts[1]), + *polygon_place_vertex(ax, verts[1]), ] - tool = widgets.PolygonSelector(ax, onselect=noop, - draw_bounding_box=draw_bounding_box) - for (etype, event_args) in event_sequence: - do_event(tool, etype, **event_args) + tool = widgets.PolygonSelector(ax, draw_bounding_box=draw_bounding_box) + for event in event_sequence: + event._process() # After removing two verts, only one remains, and the - # selector should be automatically resete + # selector should be automatically reset assert tool.verts == verts[0:2] @pytest.mark.parametrize('draw_bounding_box', [False, True]) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_polygon_selector_verts_setter(fig_test, fig_ref, draw_bounding_box): verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)] ax_test = fig_test.add_subplot() - tool_test = widgets.PolygonSelector( - ax_test, onselect=noop, draw_bounding_box=draw_bounding_box) + tool_test = widgets.PolygonSelector(ax_test, draw_bounding_box=draw_bounding_box) tool_test.verts = verts assert tool_test.verts == verts ax_ref = fig_ref.add_subplot() - tool_ref = widgets.PolygonSelector( - ax_ref, onselect=noop, draw_bounding_box=draw_bounding_box) - event_sequence = [ - *polygon_place_vertex(*verts[0]), - *polygon_place_vertex(*verts[1]), - *polygon_place_vertex(*verts[2]), - *polygon_place_vertex(*verts[0]), - ] - for (etype, event_args) in event_sequence: - do_event(tool_ref, etype, **event_args) + tool_ref = widgets.PolygonSelector(ax_ref, draw_bounding_box=draw_bounding_box) + for event in [ + *polygon_place_vertex(ax_ref, verts[0]), + *polygon_place_vertex(ax_ref, verts[1]), + *polygon_place_vertex(ax_ref, verts[2]), + *polygon_place_vertex(ax_ref, verts[0]), + ]: + event._process() def test_polygon_selector_box(ax): @@ -1645,40 +1679,29 @@ def test_polygon_selector_box(ax): ax.set(xlim=(-10, 50), ylim=(-10, 50)) verts = [(20, 0), (0, 20), (20, 40), (40, 20)] event_sequence = [ - *polygon_place_vertex(*verts[0]), - *polygon_place_vertex(*verts[1]), - *polygon_place_vertex(*verts[2]), - *polygon_place_vertex(*verts[3]), - *polygon_place_vertex(*verts[0]), + *polygon_place_vertex(ax, verts[0]), + *polygon_place_vertex(ax, verts[1]), + *polygon_place_vertex(ax, verts[2]), + *polygon_place_vertex(ax, verts[3]), + *polygon_place_vertex(ax, verts[0]), ] # Create selector - tool = widgets.PolygonSelector(ax, onselect=noop, draw_bounding_box=True) - for (etype, event_args) in event_sequence: - do_event(tool, etype, **event_args) - - # In order to trigger the correct callbacks, trigger events on the canvas - # instead of the individual tools - t = ax.transData - canvas = ax.figure.canvas + tool = widgets.PolygonSelector(ax, draw_bounding_box=True) + for event in event_sequence: + event._process() # Scale to half size using the top right corner of the bounding box - MouseEvent( - "button_press_event", canvas, *t.transform((40, 40)), 1)._process() - MouseEvent( - "motion_notify_event", canvas, *t.transform((20, 20)))._process() - MouseEvent( - "button_release_event", canvas, *t.transform((20, 20)), 1)._process() + MouseEvent._from_ax_coords("button_press_event", ax, (40, 40), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (20, 20))._process() + MouseEvent._from_ax_coords("button_release_event", ax, (20, 20), 1)._process() np.testing.assert_allclose( tool.verts, [(10, 0), (0, 10), (10, 20), (20, 10)]) # Move using the center of the bounding box - MouseEvent( - "button_press_event", canvas, *t.transform((10, 10)), 1)._process() - MouseEvent( - "motion_notify_event", canvas, *t.transform((30, 30)))._process() - MouseEvent( - "button_release_event", canvas, *t.transform((30, 30)), 1)._process() + MouseEvent._from_ax_coords("button_press_event", ax, (10, 10), 1)._process() + MouseEvent._from_ax_coords("motion_notify_event", ax, (30, 30))._process() + MouseEvent._from_ax_coords("button_release_event", ax, (30, 30), 1)._process() np.testing.assert_allclose( tool.verts, [(30, 20), (20, 30), (30, 40), (40, 30)]) @@ -1686,10 +1709,8 @@ def test_polygon_selector_box(ax): np.testing.assert_allclose( tool._box.extents, (20.0, 40.0, 20.0, 40.0)) - MouseEvent( - "button_press_event", canvas, *t.transform((30, 20)), 3)._process() - MouseEvent( - "button_release_event", canvas, *t.transform((30, 20)), 3)._process() + MouseEvent._from_ax_coords("button_press_event", ax, (30, 20), 3)._process() + MouseEvent._from_ax_coords("button_release_event", ax, (30, 20), 3)._process() np.testing.assert_allclose( tool.verts, [(20, 30), (30, 40), (40, 30)]) np.testing.assert_allclose( @@ -1702,9 +1723,9 @@ def test_polygon_selector_clear_method(ax): for result in ([(50, 50), (150, 50), (50, 150), (50, 50)], [(50, 50), (100, 50), (50, 150), (50, 50)]): - for x, y in result: - for etype, event_args in polygon_place_vertex(x, y): - do_event(tool, etype, **event_args) + for xy in result: + for event in polygon_place_vertex(ax, xy): + event._process() artist = tool._selection_artist @@ -1721,26 +1742,30 @@ def test_polygon_selector_clear_method(ax): @pytest.mark.parametrize("horizOn", [False, True]) @pytest.mark.parametrize("vertOn", [False, True]) -def test_MultiCursor(horizOn, vertOn): - (ax1, ax3) = plt.figure().subplots(2, sharex=True) +@pytest.mark.parametrize("with_deprecated_canvas", [False, True]) +def test_MultiCursor(horizOn, vertOn, with_deprecated_canvas): + fig = plt.figure() + (ax1, ax3) = fig.subplots(2, sharex=True) ax2 = plt.figure().subplots() - # useblit=false to avoid having to draw the figure to cache the renderer - multi = widgets.MultiCursor( - None, (ax1, ax2), useblit=False, horizOn=horizOn, vertOn=vertOn - ) + if with_deprecated_canvas: + with pytest.warns(mpl.MatplotlibDeprecationWarning, match=r"canvas.*deprecat"): + multi = widgets.MultiCursor( + None, (ax1, ax2), useblit=False, horizOn=horizOn, vertOn=vertOn + ) + else: + # useblit=false to avoid having to draw the figure to cache the renderer + multi = widgets.MultiCursor( + (ax1, ax2), useblit=False, horizOn=horizOn, vertOn=vertOn + ) # Only two of the axes should have a line drawn on them. assert len(multi.vlines) == 2 assert len(multi.hlines) == 2 - # mock a motion_notify_event - # Can't use `do_event` as that helper requires the widget - # to have a single .ax attribute. - event = mock_event(ax1, xdata=.5, ydata=.25) - multi.onmove(event) + MouseEvent._from_ax_coords("motion_notify_event", ax1, (.5, .25))._process() # force a draw + draw event to exercise clear - ax1.figure.canvas.draw() + fig.canvas.draw() # the lines in the first two ax should both move for l in multi.vlines: @@ -1756,8 +1781,7 @@ def test_MultiCursor(horizOn, vertOn): # After toggling settings, the opposite lines should be visible after move. multi.horizOn = not multi.horizOn multi.vertOn = not multi.vertOn - event = mock_event(ax1, xdata=.5, ydata=.25) - multi.onmove(event) + MouseEvent._from_ax_coords("motion_notify_event", ax1, (.5, .25))._process() assert len([line for line in multi.vlines if line.get_visible()]) == ( 0 if vertOn else 2) assert len([line for line in multi.hlines if line.get_visible()]) == ( @@ -1765,9 +1789,47 @@ def test_MultiCursor(horizOn, vertOn): # test a move event in an Axes not part of the MultiCursor # the lines in ax1 and ax2 should not have moved. - event = mock_event(ax3, xdata=.75, ydata=.75) - multi.onmove(event) + MouseEvent._from_ax_coords("motion_notify_event", ax3, (.75, .75))._process() for l in multi.vlines: assert l.get_xdata() == (.5, .5) for l in multi.hlines: assert l.get_ydata() == (.25, .25) + + +def test_parent_axes_removal(): + + fig, (ax_radio, ax_checks) = plt.subplots(1, 2) + + radio = widgets.RadioButtons(ax_radio, ['1', '2'], 0) + checks = widgets.CheckButtons(ax_checks, ['1', '2'], [True, False]) + + ax_checks.remove() + ax_radio.remove() + with io.BytesIO() as out: + # verify that saving does not raise + fig.savefig(out, format='raw') + + # verify that this method which is triggered by a draw_event callback when + # blitting is enabled does not raise. Calling private methods is simpler + # than trying to force blitting to be enabled with Agg or use a GUI + # framework. + renderer = fig._get_renderer() + evt = DrawEvent('draw_event', fig.canvas, renderer) + radio._clear(evt) + checks._clear(evt) + + +def test_cursor_overlapping_axes_blitting_warning(): + """Test that a warning is raised and useblit is disabled for overlapping axes.""" + fig = plt.figure() + ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) + ax2 = fig.add_axes([0.2, 0.2, 0.6, 0.6]) # Explicitly overlaps ax1 + + match_text = ( + "Cursor blitting is currently not supported on " + "overlapping axes" + ) + with pytest.warns(UserWarning, match=match_text): + cursor = widgets.Cursor(ax1, useblit=True) + + assert cursor.useblit is False diff --git a/lib/matplotlib/tests/tinypages/.gitignore b/lib/matplotlib/tests/tinypages/.gitignore deleted file mode 100644 index 69fa449dd96e..000000000000 --- a/lib/matplotlib/tests/tinypages/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build/ diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 8deb03b3e148..2a7201c87d77 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -23,7 +23,6 @@ import functools import hashlib import logging -import os from pathlib import Path import subprocess from tempfile import TemporaryDirectory @@ -31,7 +30,7 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api, cbook, dviread +from matplotlib import cbook, dviread _log = logging.getLogger(__name__) @@ -63,11 +62,17 @@ class TexManager: Repeated calls to this constructor always return the same instance. """ - texcache = _api.deprecate_privatize_attribute("3.8") - _texcache = os.path.join(mpl.get_cachedir(), 'tex.cache') + _cache_dir = Path(mpl.get_cachedir(), 'tex.cache') _grey_arrayd = {} _font_families = ('serif', 'sans-serif', 'cursive', 'monospace') + # Check for the cm-super package (which registers unicode computer modern + # support just by being installed) without actually loading any package + # (because we already load the incompatible fix-cm). + _check_cmsuper_installed = ( + r'\IfFileExists{type1ec.sty}{}{\PackageError{matplotlib-support}{' + r'Missing cm-super package, required by Matplotlib}{}}' + ) _font_preambles = { 'new century schoolbook': r'\renewcommand{\rmdefault}{pnc}', 'bookman': r'\renewcommand{\rmdefault}{pbk}', @@ -81,13 +86,10 @@ class TexManager: 'helvetica': r'\usepackage{helvet}', 'avant garde': r'\usepackage{avant}', 'courier': r'\usepackage{courier}', - # Loading the type1ec package ensures that cm-super is installed, which - # is necessary for Unicode computer modern. (It also allows the use of - # computer modern at arbitrary sizes, but that's just a side effect.) - 'monospace': r'\usepackage{type1ec}', - 'computer modern roman': r'\usepackage{type1ec}', - 'computer modern sans serif': r'\usepackage{type1ec}', - 'computer modern typewriter': r'\usepackage{type1ec}', + 'monospace': _check_cmsuper_installed, + 'computer modern roman': _check_cmsuper_installed, + 'computer modern sans serif': _check_cmsuper_installed, + 'computer modern typewriter': _check_cmsuper_installed, } _font_types = { 'new century schoolbook': 'serif', @@ -106,7 +108,7 @@ class TexManager: @functools.lru_cache # Always return the same instance. def __new__(cls): - Path(cls._texcache).mkdir(parents=True, exist_ok=True) + cls._cache_dir.mkdir(parents=True, exist_ok=True) return object.__new__(cls) @classmethod @@ -164,20 +166,30 @@ def _get_font_preamble_and_command(cls): return preamble, fontcmd @classmethod - def get_basefile(cls, tex, fontsize, dpi=None): + def _get_base_path(cls, tex, fontsize, dpi=None): """ - Return a filename based on a hash of the string, fontsize, and dpi. + Return a file path based on a hash of the string, fontsize, and dpi. """ src = cls._get_tex_source(tex, fontsize) + str(dpi) - filehash = hashlib.md5(src.encode('utf-8')).hexdigest() - filepath = Path(cls._texcache) + filehash = hashlib.sha256( + src.encode('utf-8'), + usedforsecurity=False + ).hexdigest() + filepath = cls._cache_dir num_letters, num_levels = 2, 2 for i in range(0, num_letters*num_levels, num_letters): - filepath = filepath / Path(filehash[i:i+2]) + filepath = filepath / filehash[i:i+2] filepath.mkdir(parents=True, exist_ok=True) - return os.path.join(filepath, filehash) + return filepath / filehash + + @classmethod + def get_basefile(cls, tex, fontsize, dpi=None): # Kept for backcompat. + """ + Return a filename based on a hash of the string, fontsize, and dpi. + """ + return str(cls._get_base_path(tex, fontsize, dpi)) @classmethod def get_font_preamble(cls): @@ -198,6 +210,7 @@ def _get_tex_source(cls, tex, fontsize): font_preamble, fontcmd = cls._get_font_preamble_and_command() baselineskip = 1.25 * fontsize return "\n".join([ + r"\RequirePackage{fix-cm}", r"\documentclass{article}", r"% Pass-through \mathdefault, which is used in non-usetex mode", r"% to use the default text font but was historically suppressed", @@ -221,8 +234,6 @@ def _get_tex_source(cls, tex, fontsize): r"\begin{document}", r"% The empty hbox ensures that a page is printed even for empty", r"% inputs, except when using psfrag which gets confused by it.", - r"% matplotlibbaselinemarker is used by dviread to detect the", - r"% last line's baseline.", rf"\fontsize{{{fontsize}}}{{{baselineskip}}}%", r"\ifdefined\psfrag\else\hbox{}\fi%", rf"{{{fontcmd} {tex}}}%", @@ -236,22 +247,17 @@ def make_tex(cls, tex, fontsize): Return the file name. """ - texfile = cls.get_basefile(tex, fontsize) + ".tex" - Path(texfile).write_text(cls._get_tex_source(tex, fontsize), - encoding='utf-8') - return texfile + texpath = cls._get_base_path(tex, fontsize).with_suffix(".tex") + texpath.write_text(cls._get_tex_source(tex, fontsize), encoding='utf-8') + return str(texpath) @classmethod def _run_checked_subprocess(cls, command, tex, *, cwd=None): _log.debug(cbook._pformat_subprocess(command)) try: report = subprocess.check_output( - command, cwd=cwd if cwd is not None else cls._texcache, + command, cwd=cwd if cwd is not None else cls._cache_dir, stderr=subprocess.STDOUT) - except FileNotFoundError as exc: - raise RuntimeError( - f'Failed to process string with tex because {command[0]} ' - 'could not be found') from exc except subprocess.CalledProcessError as exc: raise RuntimeError( '{prog} was not able to process the following string:\n' @@ -264,6 +270,10 @@ def _run_checked_subprocess(cls, command, tex, *, cwd=None): tex=tex.encode('unicode_escape'), exc=exc.output.decode('utf-8', 'backslashreplace')) ) from None + except (FileNotFoundError, OSError) as exc: + raise RuntimeError( + f'Failed to process string with tex because {command[0]} ' + 'could not be found') from exc _log.debug(report) return report @@ -274,11 +284,9 @@ def make_dvi(cls, tex, fontsize): Return the file name. """ - basefile = cls.get_basefile(tex, fontsize) - dvifile = '%s.dvi' % basefile - if not os.path.exists(dvifile): - texfile = Path(cls.make_tex(tex, fontsize)) - # Generate the dvi in a temporary directory to avoid race + dvipath = cls._get_base_path(tex, fontsize).with_suffix(".dvi") + if not dvipath.exists(): + # Generate the tex and dvi in a temporary directory to avoid race # conditions e.g. if multiple processes try to process the same tex # string at the same time. Having tmpdir be a subdirectory of the # final output dir ensures that they are on the same filesystem, @@ -287,15 +295,17 @@ def make_dvi(cls, tex, fontsize): # the absolute path may contain characters (e.g. ~) that TeX does # not support; n.b. relative paths cannot traverse parents, or it # will be blocked when `openin_any = p` in texmf.cnf). - cwd = Path(dvifile).parent - with TemporaryDirectory(dir=cwd) as tmpdir: - tmppath = Path(tmpdir) + with TemporaryDirectory(dir=dvipath.parent) as tmpdir: + Path(tmpdir, "file.tex").write_text( + cls._get_tex_source(tex, fontsize), encoding='utf-8') cls._run_checked_subprocess( - ["latex", "-interaction=nonstopmode", "--halt-on-error", - f"--output-directory={tmppath.name}", - f"{texfile.name}"], tex, cwd=cwd) - (tmppath / Path(dvifile).name).replace(dvifile) - return dvifile + ["latex", "-interaction=nonstopmode", "-halt-on-error", + "-no-shell-escape", "file.tex"], tex, cwd=tmpdir) + Path(tmpdir, "file.dvi").replace(dvipath) + # Also move the tex source to the main cache directory, but + # only for backcompat. + Path(tmpdir, "file.tex").replace(dvipath.with_suffix(".tex")) + return str(dvipath) @classmethod def make_png(cls, tex, fontsize, dpi): @@ -304,35 +314,33 @@ def make_png(cls, tex, fontsize, dpi): Return the file name. """ - basefile = cls.get_basefile(tex, fontsize, dpi) - pngfile = '%s.png' % basefile - # see get_rgba for a discussion of the background - if not os.path.exists(pngfile): - dvifile = cls.make_dvi(tex, fontsize) - cmd = ["dvipng", "-bg", "Transparent", "-D", str(dpi), - "-T", "tight", "-o", pngfile, dvifile] - # When testing, disable FreeType rendering for reproducibility; but - # dvipng 1.16 has a bug (fixed in f3ff241) that breaks --freetype0 - # mode, so for it we keep FreeType enabled; the image will be - # slightly off. - if (getattr(mpl, "_called_from_pytest", False) and - mpl._get_executable_info("dvipng").raw_version != "1.16"): - cmd.insert(1, "--freetype0") - cls._run_checked_subprocess(cmd, tex) - return pngfile + pngpath = cls._get_base_path(tex, fontsize, dpi).with_suffix(".png") + if not pngpath.exists(): + dvipath = cls.make_dvi(tex, fontsize) + with TemporaryDirectory(dir=pngpath.parent) as tmpdir: + cmd = ["dvipng", "-bg", "Transparent", "-D", str(dpi), + "-T", "tight", "-o", "file.png", dvipath] + # When testing, disable FreeType rendering for reproducibility; + # but dvipng 1.16 has a bug (fixed in f3ff241) that breaks + # --freetype0 mode, so for it we keep FreeType enabled; the + # image will be slightly off. + if (getattr(mpl, "_called_from_pytest", False) and + mpl._get_executable_info("dvipng").raw_version != "1.16"): + cmd.insert(1, "--freetype0") + cls._run_checked_subprocess(cmd, tex, cwd=tmpdir) + Path(tmpdir, "file.png").replace(pngpath) + return str(pngpath) @classmethod def get_grey(cls, tex, fontsize=None, dpi=None): """Return the alpha channel.""" - if not fontsize: - fontsize = mpl.rcParams['font.size'] - if not dpi: - dpi = mpl.rcParams['savefig.dpi'] + fontsize = mpl._val_or_rc(fontsize, 'font.size') + dpi = mpl._val_or_rc(dpi, 'savefig.dpi') key = cls._get_tex_source(tex, fontsize), dpi alpha = cls._grey_arrayd.get(key) if alpha is None: pngfile = cls.make_png(tex, fontsize, dpi) - rgba = mpl.image.imread(os.path.join(cls._texcache, pngfile)) + rgba = mpl.image.imread(pngfile) cls._grey_arrayd[key] = alpha = rgba[:, :, -1] return alpha @@ -358,9 +366,9 @@ def get_text_width_height_descent(cls, tex, fontsize, renderer=None): """Return width, height and descent of the text.""" if tex.strip() == '': return 0, 0, 0 - dvifile = cls.make_dvi(tex, fontsize) + dvipath = cls.make_dvi(tex, fontsize) dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1 - with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi: + with dviread.Dvi(dvipath, 72 * dpi_fraction) as dvi: page, = dvi # A total height (including the descent) needs to be returned. return page.width, page.height + page.descent, page.descent diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 40cd8c8cd6f7..9c6478f9c7df 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -2,7 +2,9 @@ Classes for including text in a figure. """ +from collections.abc import Sequence import functools +import itertools import logging import math from numbers import Real @@ -11,9 +13,9 @@ import numpy as np import matplotlib as mpl -from . import _api, artist, cbook, _docstring +from . import _api, artist, cbook, _docstring, colors as mcolors from .artist import Artist -from .font_manager import FontProperties +from .font_manager import FontProperties, fontManager, get_font from .patches import FancyArrowPatch, FancyBboxPatch, Rectangle from .textpath import TextPath, TextToPath # noqa # Logically located here from .transforms import ( @@ -23,58 +25,110 @@ _log = logging.getLogger(__name__) -def _get_textbox(text, renderer): +@functools.lru_cache(maxsize=128) +def _rotate(theta): """ - Calculate the bounding box of the text. + Return an Affine2D object that rotates by the given angle in radians. + """ + return Affine2D().rotate(theta) + - The bbox position takes text rotation into account, but the width and - height are those of the unrotated box (unlike `.Text.get_window_extent`). +def _rotate_point(angle, x, y): + """ + Rotate point (x, y) by rotation angle in degrees """ - # TODO : This function may move into the Text class as a method. As a - # matter of fact, the information from the _get_textbox function - # should be available during the Text._get_layout() call, which is - # called within the _get_textbox. So, it would better to move this - # function as a method with some refactoring of _get_layout method. + if angle == 0: + return (x, y) + angle_rad = math.radians(angle) + cos, sin = math.cos(angle_rad), math.sin(angle_rad) + return (cos * x - sin * y, sin * x + cos * y) - projected_xs = [] - projected_ys = [] - theta = np.deg2rad(text.get_rotation()) - tr = Affine2D().rotate(-theta) +def _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi): + """Call ``renderer.get_text_width_height_descent``, caching the results.""" + + # hit the outer cache layer and get the function to compute the metrics + # for this renderer instance + get_text_metrics = _get_text_metrics_function(renderer) + # call the function to compute the metrics and return + # + # We pass a copy of the fontprop because FontProperties is both mutable and + # has a `__hash__` that depends on that mutable state. This is not ideal + # as it means the hash of an object is not stable over time which leads to + # very confusing behavior when used as keys in dictionaries or hashes. + return get_text_metrics(text, fontprop.copy(), ismath, dpi) - _, parts, d = text._get_layout(renderer) - for t, wh, x, y in parts: - w, h = wh +def _get_text_metrics_function(input_renderer, _cache=weakref.WeakKeyDictionary()): + """ + Helper function to provide a two-layered cache for font metrics - xt1, yt1 = tr.transform((x, y)) - yt1 -= d - xt2, yt2 = xt1 + w, yt1 + h - projected_xs.extend([xt1, xt2]) - projected_ys.extend([yt1, yt2]) + To get the rendered size of a size of string we need to know: + - what renderer we are using + - the current dpi of the renderer + - the string + - the font properties + - is it math text or not - xt_box, yt_box = min(projected_xs), min(projected_ys) - w_box, h_box = max(projected_xs) - xt_box, max(projected_ys) - yt_box + We do this as a two-layer cache with the outer layer being tied to a + renderer instance and the inner layer handling everything else. - x_box, y_box = Affine2D().rotate(theta).transform((xt_box, yt_box)) + The outer layer is implemented as `.WeakKeyDictionary` keyed on the + renderer. As long as someone else is holding a hard ref to the renderer + we will keep the cache alive, but it will be automatically dropped when + the renderer is garbage collected. - return x_box, y_box, w_box, h_box + The inner layer is provided by an lru_cache with a large maximum size (such + that we expect very few cache misses in actual use cases). As the + dpi is mutable on the renderer, we need to explicitly include it as part of + the cache key on the inner layer even though we do not directly use it (it is + used in the method call on the renderer). + This function takes a renderer and returns a function that can be used to + get the font metrics. -def _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi): - """Call ``renderer.get_text_width_height_descent``, caching the results.""" - # Cached based on a copy of fontprop so that later in-place mutations of - # the passed-in argument do not mess up the cache. - return _get_text_metrics_with_cache_impl( - weakref.ref(renderer), text, fontprop.copy(), ismath, dpi) + Parameters + ---------- + input_renderer : maplotlib.backend_bases.RendererBase + The renderer to set the cache up for. + _cache : dict, optional + We are using the mutable default value to attach the cache to the function. -@functools.lru_cache(4096) -def _get_text_metrics_with_cache_impl( - renderer_ref, text, fontprop, ismath, dpi): - # dpi is unused, but participates in cache invalidation (via the renderer). - return renderer_ref().get_text_width_height_descent(text, fontprop, ismath) + In principle you could pass a different dict-like to this function to inject + a different cache, but please don't. This is an internal function not meant to + be reused outside of the narrow context we need it for. + + There is a possible race condition here between threads, we may need to drop the + mutable default and switch to a threadlocal variable in the future. + + """ + if (_text_metrics := _cache.get(input_renderer, None)) is None: + # We are going to include this in the closure we put as values in the + # cache. Closing over a hard-ref would create an unbreakable reference + # cycle. + renderer_ref = weakref.ref(input_renderer) + + # define the function locally to get a new lru_cache per renderer + @functools.lru_cache(4096) + # dpi is unused, but participates in cache invalidation (via the renderer). + def _text_metrics(text, fontprop, ismath, dpi): + # this should never happen under normal use, but this is a better error to + # raise than an AttributeError on `None` + if (local_renderer := renderer_ref()) is None: + raise RuntimeError( + "Trying to get text metrics for a renderer that no longer exists. " + "This should never happen and is evidence of a bug elsewhere." + ) + # do the actual method call we need and return the result + return local_renderer.get_text_width_height_descent(text, fontprop, ismath) + + # stash the function for later use. + _cache[input_renderer] = _text_metrics + + # return the inner function + return _text_metrics @_docstring.interpd @@ -136,6 +190,8 @@ def __init__(self, super().__init__() self._x, self._y = x, y self._text = '' + self._features = None + self.set_language(None) self._reset_visual_defaults( text=text, color=color, @@ -185,11 +241,10 @@ def _reset_visual_defaults( self._bbox_patch = None # a FancyBboxPatch instance self._renderer = None if linespacing is None: - linespacing = 1.2 # Maybe use rcParam later. + linespacing = 'normal' # Maybe use rcParam later. self.set_linespacing(linespacing) self.set_rotation_mode(rotation_mode) - self.set_antialiased(antialiased if antialiased is not None else - mpl.rcParams['text.antialiased']) + self.set_antialiased(mpl._val_or_rc(antialiased, 'text.antialiased')) def update(self, kwargs): # docstring inherited @@ -282,10 +337,10 @@ def _char_index_at(self, x): return (np.abs(size_accum - std_x)).argmin() def get_rotation(self): - """Return the text angle in degrees between 0 and 360.""" + """Return the text angle in degrees in the range [0, 360).""" if self.get_transform_rotates_text(): return self.get_transform().transform_angles( - [self._rotation], [self.get_unitless_position()]).item(0) + [self._rotation], [self.get_unitless_position()]).item(0) % 360 else: return self._rotation @@ -301,16 +356,19 @@ def set_rotation_mode(self, m): Parameters ---------- - m : {None, 'default', 'anchor'} + m : {None, 'default', 'anchor', 'xtick', 'ytick'} If ``"default"``, the text will be first rotated, then aligned according to their horizontal and vertical alignments. If ``"anchor"``, then - alignment occurs before rotation. Passing ``None`` will set the rotation - mode to ``"default"``. + alignment occurs before rotation. "xtick" and "ytick" adjust the + horizontal/vertical alignment so that the text is visually pointing + towards its anchor point. This is primarily used for rotated tick + labels and positions them nicely towards their ticks. Passing + ``None`` will set the rotation mode to ``"default"``. """ if m is None: m = "default" else: - _api.check_in_list(("anchor", "default"), rotation_mode=m) + _api.check_in_list(("anchor", "default", "xtick", "ytick"), rotation_mode=m) self._rotation_mode = m self.stale = True @@ -357,52 +415,94 @@ def update_from(self, other): def _get_layout(self, renderer): """ - Return the extent (bbox) of the text together with - multiple-alignment information. Note that it returns an extent - of a rotated text when necessary. + Return + + - the rotated, axis-aligned text bbox; + - a list of ``(line, (width, ascent, descent), xy)`` tuples for each line; + - a ``(xy, (width, height))` pair of the lower-left corner and size of the + rotated, *text-aligned* text box (i.e. describing how to draw the + text-surrounding box). """ thisx, thisy = 0.0, 0.0 lines = self._get_wrapped_text().split("\n") # Ensures lines is not empty. - ws = [] - hs = [] + # Reminder: The ascent (a) goes from the baseline to the top and the + # descent (d) from the baseline to the bottom; both are (typically) + # nonnegative. The height h is the sum, h = a + d. + wads = [] # (width, ascents, descents) xs = [] ys = [] - # Full vertical extent of font, including ascenders and descenders: - _, lp_h, lp_d = _get_text_metrics_with_cache( - renderer, "lp", self._fontproperties, - ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi) - min_dy = (lp_h - lp_d) * self._linespacing - - for i, line in enumerate(lines): + min_ascent = min_descent = line_gap = None + dpi = self.get_figure(root=True).dpi + # Determine full vertical extent of font, including ascenders and descenders: + if not self.get_usetex(): + if hasattr(renderer, '_get_font_height_metrics'): + # TODO: This is a temporary internal method call (for _backend_pdf_ps to + # support AFM files) until we design a proper API for the backends. + min_ascent, min_descent, line_gap = renderer._get_font_height_metrics( + self._fontproperties) + if min_ascent is None: + font = get_font(fontManager._find_fonts_by_props(self._fontproperties)) + possible = [ + ('OS/2', 'sTypoLineGap', 'sTypoAscender', 'sTypoDescender'), + ('hhea', 'lineGap', 'ascent', 'descent') + ] + for table_name, linegap_key, ascent_key, descent_key in possible: + table = font.get_sfnt_table(table_name) + if table is None: + continue + # Rescale to font size/DPI if the metrics were available. + fontsize = self._fontproperties.get_size_in_points() + units_per_em = font.get_sfnt_table('head')['unitsPerEm'] + scale = 1 / units_per_em * fontsize * dpi / 72 + line_gap = table[linegap_key] * scale + min_ascent = table[ascent_key] * scale + min_descent = -table[descent_key] * scale + break + if None in (min_ascent, min_descent): + # Fallback to font measurement. + _, h, min_descent = _get_text_metrics_with_cache( + renderer, "lp", self._fontproperties, + ismath="TeX" if self.get_usetex() else False, + dpi=dpi) + min_ascent = h - min_descent + line_gap = 0 + + # Don't increase text height too much if it's not multiple lines. + if len(lines) == 1: + line_gap = 0 + + for line in lines: clean_line, ismath = self._preprocess_math(line) if clean_line: w, h, d = _get_text_metrics_with_cache( renderer, clean_line, self._fontproperties, - ismath=ismath, dpi=self.figure.dpi) + ismath=ismath, dpi=self.get_figure(root=True).dpi) else: w = h = d = 0 - # For multiline text, increase the line spacing when the text - # net-height (excluding baseline) is larger than that of a "l" - # (e.g., use of superscripts), which seems what TeX does. - h = max(h, lp_h) - d = max(d, lp_d) + a = h - d - ws.append(w) - hs.append(h) + if self.get_usetex() or self._linespacing == 'normal': + # To ensure good linespacing, pretend that the ascent / descent of all + # lines is at least as large as the measured sizes. + a = max(a, min_ascent) + line_gap / 2 + d = max(d, min_descent) + line_gap / 2 + else: + # If using a fixed line spacing, then every line's spacing will be + # determined by the font metrics of the first available font. + line_height = self._linespacing * (min_ascent + min_descent) + leading = line_height - (a + d) + a += leading / 2 + d += leading / 2 # Metrics of the last line that are needed later: - baseline = (h - d) - thisy + baseline = a - thisy - if i == 0: - # position at baseline - thisy = -(h - d) - else: - # put baseline a good distance from bottom of previous line - thisy -= max(min_dy, (h - d) * self._linespacing) + thisy -= a + wads.append((w, a, d)) xs.append(thisx) # == 0. ys.append(thisy) @@ -412,15 +512,13 @@ def _get_layout(self, renderer): descent = d # Bounding box definition: + ws = [w for w, a, d in wads] width = max(ws) xmin = 0 xmax = width ymax = 0 ymin = ys[-1] - descent # baseline of last line minus its descent - # get the rotation matrix - M = Affine2D().rotate_deg(self.get_rotation()) - # now offset the individual text lines within the box malign = self._get_multialignment() if malign == 'left': @@ -433,18 +531,19 @@ def _get_layout(self, renderer): for x, y, w in zip(xs, ys, ws)] # the corners of the unrotated bounding box - corners_horiz = np.array( - [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]) - + corners_horiz = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] + size_horiz = (xmax - xmin, ymax - ymin) # now rotate the bbox - corners_rotated = M.transform(corners_horiz) + angle = self.get_rotation() + rotate = functools.partial(_rotate_point, angle) + corners_rotated = [rotate(x, y) for x, y in corners_horiz] + # compute the bounds of the rotated box - xmin = corners_rotated[:, 0].min() - xmax = corners_rotated[:, 0].max() - ymin = corners_rotated[:, 1].min() - ymax = corners_rotated[:, 1].max() - width = xmax - xmin - height = ymax - ymin + xs, ys = zip(*corners_rotated) + xmin, xmax = min(xs), max(xs) + ymin, ymax = min(ys), max(ys) + width_rot = xmax - xmin + height_rot = ymax - ymin # Now move the box to the target position offset the display # bbox by alignment @@ -453,69 +552,71 @@ def _get_layout(self, renderer): rotation_mode = self.get_rotation_mode() if rotation_mode != "anchor": + if rotation_mode == 'xtick': + halign = self._ha_for_angle(angle) + elif rotation_mode == 'ytick': + valign = self._va_for_angle(angle) # compute the text location in display coords and the offsets # necessary to align the bbox with that location - if halign == 'center': - offsetx = (xmin + xmax) / 2 - elif halign == 'right': - offsetx = xmax - else: - offsetx = xmin - - if valign == 'center': - offsety = (ymin + ymax) / 2 - elif valign == 'top': - offsety = ymax - elif valign == 'baseline': - offsety = ymin + descent - elif valign == 'center_baseline': - offsety = ymin + height - baseline / 2.0 - else: - offsety = ymin + offsetx = ( + xmin if halign == "left" else + xmax if halign == "right" else + (xmin + xmax) / 2 # halign == "center" + ) + offsety = ( + ymin if valign == "bottom" else + ymax if valign == "top" else + (ymin + ymax) / 2 if valign == "center" else + ymin + descent if valign == "baseline" else + ymin + height_rot - baseline / 2 # valign == "center_baseline" + ) else: xmin1, ymin1 = corners_horiz[0] xmax1, ymax1 = corners_horiz[2] - - if halign == 'center': - offsetx = (xmin1 + xmax1) / 2.0 - elif halign == 'right': - offsetx = xmax1 - else: - offsetx = xmin1 - - if valign == 'center': - offsety = (ymin1 + ymax1) / 2.0 - elif valign == 'top': - offsety = ymax1 - elif valign == 'baseline': - offsety = ymax1 - baseline - elif valign == 'center_baseline': - offsety = ymax1 - baseline / 2.0 - else: - offsety = ymin1 - - offsetx, offsety = M.transform((offsetx, offsety)) + offsetx = ( + xmin1 if halign == "left" else + xmax1 if halign == "right" else + (xmin1 + xmax1) / 2 # halign == "center" + ) + offsety = ( + ymin1 if valign == "bottom" else + ymax1 if valign == "top" else + (ymin1 + ymax1) / 2 if valign == "center" else + ymax1 - baseline if valign == "baseline" else + ymax1 - baseline / 2 # valign == "center_baseline" + ) + offsetx, offsety = rotate(offsetx, offsety) xmin -= offsetx ymin -= offsety - bbox = Bbox.from_bounds(xmin, ymin, width, height) + bbox_rot = Bbox.from_bounds(xmin, ymin, width_rot, height_rot) # now rotate the positions around the first (x, y) position - xys = M.transform(offset_layout) - (offsetx, offsety) + xys = [(x - offsetx, y - offsety) + for x, y in itertools.starmap(rotate, offset_layout)] + x, y = corners_rotated[0] + xy_corner = (x - offsetx, y - offsety) - return bbox, list(zip(lines, zip(ws, hs), *xys.T)), descent + return bbox_rot, list(zip(lines, wads, xys)), (xy_corner, size_horiz) def set_bbox(self, rectprops): """ - Draw a bounding box around self. + Draw a box behind/around the text. + + This can be used to set a background and/or a frame around the text. + It's realized through a `.FancyBboxPatch` behind the text (see also + `.Text.get_bbox_patch`). The bbox patch is None by default and only + created when needed. Parameters ---------- - rectprops : dict with properties for `.patches.FancyBboxPatch` + rectprops : dict with properties for `.FancyBboxPatch` or None The default boxstyle is 'square'. The mutation scale of the `.patches.FancyBboxPatch` is set to the fontsize. + Pass ``None`` to remove the bbox patch completely. + Examples -------- :: @@ -550,6 +651,8 @@ def get_bbox_patch(self): """ Return the bbox Patch, or None if the `.patches.FancyBboxPatch` is not made. + + For more details see `.Text.set_bbox`. """ return self._bbox_patch @@ -567,7 +670,7 @@ def update_bbox_position_size(self, renderer): posy = float(self.convert_yunits(self._y)) posx, posy = self.get_transform().transform((posx, posy)) - x_box, y_box, w_box, h_box = _get_textbox(self, renderer) + _, _, ((x_box, y_box), (w_box, h_box)) = self._get_layout(renderer) self._bbox_patch.set_bounds(0., 0., w_box, h_box) self._bbox_patch.set_transform( Affine2D() @@ -606,6 +709,9 @@ def set_wrap(self, wrap): """ Set whether the text can be wrapped. + Wrapping makes sure the text is confined to the (sub)figure box. It + does not take into account any other artists. + Parameters ---------- wrap : bool @@ -631,11 +737,11 @@ def _get_wrap_line_width(self): # Calculate available width based on text alignment alignment = self.get_horizontalalignment() self.set_rotation_mode('anchor') - rotation = self.get_rotation() + angle = self.get_rotation() - left = self._get_dist_to_box(rotation, x0, y0, figure_box) + left = self._get_dist_to_box(angle, x0, y0, figure_box) right = self._get_dist_to_box( - (180 + rotation) % 360, x0, y0, figure_box) + (180 + angle) % 360, x0, y0, figure_box) if alignment == 'left': line_width = left @@ -653,16 +759,16 @@ def _get_dist_to_box(self, rotation, x0, y0, figure_box): """ if rotation > 270: quad = rotation - 270 - h1 = y0 / math.cos(math.radians(quad)) + h1 = (y0 - figure_box.y0) / math.cos(math.radians(quad)) h2 = (figure_box.x1 - x0) / math.cos(math.radians(90 - quad)) elif rotation > 180: quad = rotation - 180 - h1 = x0 / math.cos(math.radians(quad)) - h2 = y0 / math.cos(math.radians(90 - quad)) + h1 = (x0 - figure_box.x0) / math.cos(math.radians(quad)) + h2 = (y0 - figure_box.y0) / math.cos(math.radians(90 - quad)) elif rotation > 90: quad = rotation - 90 h1 = (figure_box.y1 - y0) / math.cos(math.radians(quad)) - h2 = x0 / math.cos(math.radians(90 - quad)) + h2 = (x0 - figure_box.x0) / math.cos(math.radians(90 - quad)) else: h1 = (figure_box.x1 - x0) / math.cos(math.radians(rotation)) h2 = (figure_box.y1 - y0) / math.cos(math.radians(90 - rotation)) @@ -674,10 +780,10 @@ def _get_rendered_text_width(self, text): Return the width of a given text string, in pixels. """ - w, h, d = self._renderer.get_text_width_height_descent( - text, - self.get_fontproperties(), - cbook.is_math_text(text)) + w, h, d = _get_text_metrics_with_cache( + self._renderer, text, self.get_fontproperties(), + cbook.is_math_text(text), + self.get_figure(root=True).dpi) return math.ceil(w) def _get_wrapped_text(self): @@ -744,59 +850,65 @@ def draw(self, renderer): renderer.open_group('text', self.get_gid()) - with self._cm_set(text=self._get_wrapped_text()): - bbox, info, descent = self._get_layout(renderer) - trans = self.get_transform() + bbox, info, _ = self._get_layout(renderer) + trans = self.get_transform() + + # don't use self.get_position here, which refers to text + # position in Text: + x, y = self._x, self._y + if np.ma.is_masked(x): + x = np.nan + if np.ma.is_masked(y): + y = np.nan + posx = float(self.convert_xunits(x)) + posy = float(self.convert_yunits(y)) + posx, posy = trans.transform((posx, posy)) + if np.isnan(posx) or np.isnan(posy): + return # don't throw a warning here + if not np.isfinite(posx) or not np.isfinite(posy): + _log.warning("posx and posy should be finite values") + return + canvasw, canvash = renderer.get_canvas_width_height() - # don't use self.get_position here, which refers to text - # position in Text: - posx = float(self.convert_xunits(self._x)) - posy = float(self.convert_yunits(self._y)) - posx, posy = trans.transform((posx, posy)) - if not np.isfinite(posx) or not np.isfinite(posy): - _log.warning("posx and posy should be finite values") - return - canvasw, canvash = renderer.get_canvas_width_height() - - # Update the location and size of the bbox - # (`.patches.FancyBboxPatch`), and draw it. - if self._bbox_patch: - self.update_bbox_position_size(renderer) - self._bbox_patch.draw(renderer) - - gc = renderer.new_gc() - gc.set_foreground(self.get_color()) - gc.set_alpha(self.get_alpha()) - gc.set_url(self._url) - gc.set_antialiased(self._antialiased) - self._set_gc_clip(gc) - - angle = self.get_rotation() - - for line, wh, x, y in info: - - mtext = self if len(info) == 1 else None - x = x + posx - y = y + posy - if renderer.flipy(): - y = canvash - y - clean_line, ismath = self._preprocess_math(line) - - if self.get_path_effects(): - from matplotlib.patheffects import PathEffectRenderer - textrenderer = PathEffectRenderer( - self.get_path_effects(), renderer) - else: - textrenderer = renderer - - if self.get_usetex(): - textrenderer.draw_tex(gc, x, y, clean_line, - self._fontproperties, angle, - mtext=mtext) - else: - textrenderer.draw_text(gc, x, y, clean_line, - self._fontproperties, angle, - ismath=ismath, mtext=mtext) + # Update the location and size of the bbox + # (`.patches.FancyBboxPatch`), and draw it. + if self._bbox_patch: + self.update_bbox_position_size(renderer) + self._bbox_patch.draw(renderer) + + gc = renderer.new_gc() + gc.set_foreground(mcolors.to_rgba(self.get_color()), isRGBA=True) + gc.set_alpha(self.get_alpha()) + gc.set_url(self._url) + gc.set_antialiased(self._antialiased) + gc.set_snap(self.get_snap()) + self._set_gc_clip(gc) + + angle = self.get_rotation() + + for line, wad, (x, y) in info: + + mtext = self if len(info) == 1 else None + x = x + posx + y = y + posy + if renderer.flipy(): + y = canvash - y + clean_line, ismath = self._preprocess_math(line) + + if self.get_path_effects(): + from matplotlib.patheffects import PathEffectRenderer + textrenderer = PathEffectRenderer(self.get_path_effects(), renderer) + else: + textrenderer = renderer + + if self.get_usetex(): + textrenderer.draw_tex(gc, x, y, clean_line, + self._fontproperties, angle, + mtext=mtext) + else: + textrenderer.draw_text(gc, x, y, clean_line, + self._fontproperties, angle, + ismath=ismath, mtext=mtext) gc.restore() renderer.close_group('text') @@ -820,6 +932,12 @@ def get_fontfamily(self): """ return self._fontproperties.get_family() + def get_fontfeatures(self): + """ + Return a tuple of font feature tags to enable. + """ + return self._features + def get_fontname(self): """ Return the font name as a string. @@ -931,37 +1049,53 @@ def get_window_extent(self, renderer=None, dpi=None): dpi : float, optional The dpi value for computing the bbox, defaults to - ``self.figure.dpi`` (*not* the renderer dpi); should be set e.g. if - to match regions with a figure saved with a custom dpi value. + ``self.get_figure(root=True).dpi`` (*not* the renderer dpi); should be set + e.g. if to match regions with a figure saved with a custom dpi value. """ if not self.get_visible(): return Bbox.unit() + + fig = self.get_figure(root=True) if dpi is None: - dpi = self.figure.dpi + dpi = fig.dpi if self.get_text() == '': - with cbook._setattr_cm(self.figure, dpi=dpi): + with cbook._setattr_cm(fig, dpi=dpi): tx, ty = self._get_xy_display() return Bbox.from_bounds(tx, ty, 0, 0) if renderer is not None: self._renderer = renderer if self._renderer is None: - self._renderer = self.figure._get_renderer() + self._renderer = fig._get_renderer() if self._renderer is None: raise RuntimeError( "Cannot get window extent of text w/o renderer. You likely " "want to call 'figure.draw_without_rendering()' first.") - with cbook._setattr_cm(self.figure, dpi=dpi): - bbox, info, descent = self._get_layout(self._renderer) + with cbook._setattr_cm(fig, dpi=dpi): + bbox, _, _ = self._get_layout(self._renderer) x, y = self.get_unitless_position() x, y = self.get_transform().transform((x, y)) bbox = bbox.translated(x, y) return bbox + def get_tightbbox(self, renderer=None): + if not self.get_visible() or self.get_text() == "": + return Bbox.null() + # Exclude text at data coordinates outside the valid domain of the axes + # scales (e.g., negative coordinates with a log scale). + if (self.axes + and self.get_transform() == self.axes.transData + and not self.axes._point_in_data_domain(*self.get_unitless_position())): + return Bbox.null() + return super().get_tightbbox(renderer) + def set_backgroundcolor(self, color): """ - Set the background color of the text by updating the bbox. + Set the background color of the text. + + This is realized through the bbox (see `.set_bbox`), + creating the bbox patch if needed. Parameters ---------- @@ -1026,18 +1160,26 @@ def set_multialignment(self, align): def set_linespacing(self, spacing): """ - Set the line spacing as a multiple of the font size. - - The default line spacing is 1.2. + Set the line spacing. Parameters ---------- - spacing : float (multiple of font size) + spacing : 'normal' or float, default: 'normal' + If 'normal', then the line spacing is automatically determined by font + metrics for each line individually. + + If a float, then line spacing will be fixed to this multiple of the font + size for every line. """ - _api.check_isinstance(Real, spacing=spacing) + if not cbook._str_equal(spacing, 'normal'): + _api.check_isinstance(Real, spacing=spacing) self._linespacing = spacing self.stale = True + def get_linespacing(self): + """Get the line spacing.""" + return self._linespacing + def set_fontfamily(self, fontname): """ Set the font family. Can be either a single string, or a list of @@ -1062,6 +1204,39 @@ def set_fontfamily(self, fontname): self._fontproperties.set_family(fontname) self.stale = True + def set_fontfeatures(self, features): + """ + Set the feature tags to enable on the font. + + Parameters + ---------- + features : list of str, or tuple of str, or None + A list of feature tags to be used with the associated font. These strings + are eventually passed to HarfBuzz, and so all `string formats supported by + hb_feature_from_string() + `__ + are supported. Note though that subranges are not explicitly supported and + behaviour may change in the future. + + For example, if your desired font includes Stylistic Sets which enable + various typographic alternates including one that you do not wish to use + (e.g., Contextual Ligatures), then you can pass the following to enable one + and not the other:: + + fp.set_features([ + 'ss01', # Use Stylistic Set 1. + '-clig', # But disable Contextural Ligatures. + ]) + + Available font feature tags may be found at + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + """ + _api.check_isinstance((Sequence, None), features=features) + if features is not None: + features = tuple(features) + self._features = features + self.stale = True + def set_fontvariant(self, variant): """ Set the font variant. @@ -1323,10 +1498,7 @@ def set_usetex(self, usetex): Whether to render using TeX, ``None`` means to use :rc:`text.usetex`. """ - if usetex is None: - self._usetex = mpl.rcParams['text.usetex'] - else: - self._usetex = bool(usetex) + self._usetex = bool(mpl._val_or_rc(usetex, 'text.usetex')) self.stale = True def get_usetex(self): @@ -1367,6 +1539,68 @@ def set_fontname(self, fontname): """ self.set_fontfamily(fontname) + def _ha_for_angle(self, angle): + """ + Determines horizontal alignment ('ha') for rotation_mode "xtick" based on + the angle of rotation in degrees and the vertical alignment. + """ + anchor_at_bottom = self.get_verticalalignment() == 'bottom' + if (angle <= 10 or 85 <= angle <= 95 or 350 <= angle or + 170 <= angle <= 190 or 265 <= angle <= 275): + return 'center' + elif 10 < angle < 85 or 190 < angle < 265: + return 'left' if anchor_at_bottom else 'right' + return 'right' if anchor_at_bottom else 'left' + + def _va_for_angle(self, angle): + """ + Determines vertical alignment ('va') for rotation_mode "ytick" based on + the angle of rotation in degrees and the horizontal alignment. + """ + anchor_at_left = self.get_horizontalalignment() == 'left' + if (angle <= 10 or 350 <= angle or 170 <= angle <= 190 + or 80 <= angle <= 100 or 260 <= angle <= 280): + return 'center' + elif 190 < angle < 260 or 10 < angle < 80: + return 'baseline' if anchor_at_left else 'top' + return 'top' if anchor_at_left else 'baseline' + + def get_language(self): + """Return the language this Text is in.""" + return self._language + + def set_language(self, language): + """ + Set the language of the text. + + Parameters + ---------- + language : str or None + The language of the text in a format accepted by libraqm, namely `a BCP47 + language code `_. + + If None, then defaults to :rc:`text.language`. + """ + _api.check_isinstance((Sequence, str, None), language=language) + language = mpl._val_or_rc(language, 'text.language') + + if not cbook.is_scalar_or_string(language): + language = tuple(language) + for val in language: + if not isinstance(val, tuple) or len(val) != 3: + raise TypeError('language must be list of tuple, not {language!r}') + sublang, start, end = val + if not isinstance(sublang, str): + raise TypeError( + 'sub-language specification must be str, not {sublang!r}') + if not isinstance(start, int): + raise TypeError('start location must be int, not {start!r}') + if not isinstance(end, int): + raise TypeError('end location must be int, not {end!r}') + + self._language = language + self.stale = True + class OffsetFrom: """Callable helper class for working with `Annotation`.""" @@ -1498,9 +1732,7 @@ def _get_xy_transform(self, renderer, coords): return self.axes.transData elif coords == 'polar': from matplotlib.projections import PolarAxes - tr = PolarAxes.PolarTransform(apply_theta_transforms=False) - trans = tr + self.axes.transData - return trans + return PolarAxes.PolarTransform() + self.axes.transData try: bbox_name, unit = coords.split() @@ -1511,9 +1743,9 @@ def _get_xy_transform(self, renderer, coords): # if unit is offset-like if bbox_name == "figure": - bbox0 = self.figure.figbbox + bbox0 = self.get_figure(root=False).figbbox elif bbox_name == "subfigure": - bbox0 = self.figure.bbox + bbox0 = self.get_figure(root=False).bbox elif bbox_name == "axes": bbox0 = self.axes.bbox @@ -1526,11 +1758,13 @@ def _get_xy_transform(self, renderer, coords): raise ValueError(f"{coords!r} is not a valid coordinate") if unit == "points": - tr = Affine2D().scale(self.figure.dpi / 72) # dpi/72 dots per point + tr = Affine2D().scale( + self.get_figure(root=True).dpi / 72) # dpi/72 dots per point elif unit == "pixels": tr = Affine2D() elif unit == "fontsize": - tr = Affine2D().scale(self.get_size() * self.figure.dpi / 72) + tr = Affine2D().scale( + self.get_size() * self.get_figure(root=True).dpi / 72) elif unit == "fraction": tr = Affine2D().scale(*bbox0.size) else: @@ -1568,7 +1802,7 @@ def _get_position_xy(self, renderer): def _check_xy(self, renderer=None): """Check whether the annotation at *xy_pixel* should be drawn.""" if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() b = self.get_annotation_clip() if b or (b is None and self.xycoords == "data"): # check if self.xy is inside the Axes. @@ -1834,10 +2068,6 @@ def transform(renderer) -> Transform # modified YAArrow API to be used with FancyArrowPatch for key in ['width', 'headwidth', 'headlength', 'shrink']: arrowprops.pop(key, None) - if 'frac' in arrowprops: - _api.warn_deprecated( - "3.8", name="the (unused) 'frac' key in 'arrowprops'") - arrowprops.pop("frac") self.arrow_patch = FancyArrowPatch((0, 0), (1, 1), **arrowprops) else: self.arrow_patch = None @@ -1845,7 +2075,6 @@ def transform(renderer) -> Transform # Must come last, as some kwargs may be propagated to arrow_patch. Text.__init__(self, x, y, text, **kwargs) - @_api.rename_parameter("3.8", "event", "mouseevent") def contains(self, mouseevent): if self._different_canvas(mouseevent): return False, {} @@ -1984,8 +2213,9 @@ def draw(self, renderer): self.update_positions(renderer) self.update_bbox_position_size(renderer) if self.arrow_patch is not None: # FancyArrowPatch - if self.arrow_patch.figure is None and self.figure is not None: - self.arrow_patch.figure = self.figure + if (self.arrow_patch.get_figure(root=False) is None and + (fig := self.get_figure(root=False)) is not None): + self.arrow_patch.set_figure(fig) self.arrow_patch.draw(renderer) # Draw text, including FancyBboxPatch, after FancyArrowPatch. # Otherwise, a wedge arrowstyle can land partly on top of the Bbox. @@ -2000,7 +2230,7 @@ def get_window_extent(self, renderer=None): if renderer is not None: self._renderer = renderer if self._renderer is None: - self._renderer = self.figure._get_renderer() + self._renderer = self.get_figure(root=True)._get_renderer() if self._renderer is None: raise RuntimeError('Cannot get window extent without renderer') @@ -2021,4 +2251,4 @@ def get_tightbbox(self, renderer=None): return super().get_tightbbox(renderer) -_docstring.interpd.update(Annotation=Annotation.__init__.__doc__) +_docstring.interpd.register(Annotation=Annotation.__init__.__doc__) diff --git a/lib/matplotlib/text.pyi b/lib/matplotlib/text.pyi index 6a83b1bbbed9..15811462224a 100644 --- a/lib/matplotlib/text.pyi +++ b/lib/matplotlib/text.pyi @@ -2,9 +2,9 @@ from .artist import Artist from .backend_bases import RendererBase from .font_manager import FontProperties from .offsetbox import DraggableAnnotation -from .path import Path +from pathlib import Path from .patches import FancyArrowPatch, FancyBboxPatch -from .textpath import ( # noqa: reexported API +from .textpath import ( # noqa: F401, reexported API TextPath as TextPath, TextToPath as TextToPath, ) @@ -14,9 +14,9 @@ from .transforms import ( Transform, ) -from collections.abc import Callable, Iterable +from collections.abc import Iterable, Sequence from typing import Any, Literal -from .typing import ColorType +from .typing import ColorType, CoordsType class Text(Artist): zorder: float @@ -34,7 +34,7 @@ class Text(Artist): multialignment: Literal["left", "center", "right"] | None = ..., fontproperties: str | Path | FontProperties | None = ..., rotation: float | Literal["vertical", "horizontal"] | None = ..., - linespacing: float | None = ..., + linespacing: Literal["normal"] | float | None = ..., rotation_mode: Literal["default", "anchor"] | None = ..., usetex: bool | None = ..., wrap: bool = ..., @@ -46,9 +46,9 @@ class Text(Artist): def update(self, kwargs: dict[str, Any]) -> list[Any]: ... def get_rotation(self) -> float: ... def get_transform_rotates_text(self) -> bool: ... - def set_rotation_mode(self, m: None | Literal["default", "anchor"]) -> None: ... - def get_rotation_mode(self) -> Literal["default", "anchor"]: ... - def set_bbox(self, rectprops: dict[str, Any]) -> None: ... + def set_rotation_mode(self, m: None | Literal["default", "anchor", "xtick", "ytick"]) -> None: ... + def get_rotation_mode(self) -> Literal["default", "anchor", "xtick", "ytick"]: ... + def set_bbox(self, rectprops: dict[str, Any] | None) -> None: ... def get_bbox_patch(self) -> None | FancyBboxPatch: ... def update_bbox_position_size(self, renderer: RendererBase) -> None: ... def get_wrap(self) -> bool: ... @@ -56,6 +56,7 @@ class Text(Artist): def get_color(self) -> ColorType: ... def get_fontproperties(self) -> FontProperties: ... def get_fontfamily(self) -> list[str]: ... + def get_fontfeatures(self) -> tuple[str, ...] | None: ... def get_fontname(self) -> str: ... def get_fontstyle(self) -> Literal["normal", "italic", "oblique"]: ... def get_fontsize(self) -> float | str: ... @@ -78,8 +79,10 @@ class Text(Artist): self, align: Literal["left", "center", "right"] ) -> None: ... def set_multialignment(self, align: Literal["left", "center", "right"]) -> None: ... - def set_linespacing(self, spacing: float) -> None: ... + def set_linespacing(self, spacing: Literal["normal"] | float) -> None: ... + def get_linespacing(self) -> Literal["normal"] | float: ... def set_fontfamily(self, fontname: str | Iterable[str]) -> None: ... + def set_fontfeatures(self, features: Sequence[str] | None) -> None: ... def set_fontvariant(self, variant: Literal["normal", "small-caps"]) -> None: ... def set_fontstyle( self, fontstyle: Literal["normal", "italic", "oblique"] @@ -106,6 +109,10 @@ class Text(Artist): def set_fontname(self, fontname: str | Iterable[str]) -> None: ... def get_antialiased(self) -> bool: ... def set_antialiased(self, antialiased: bool) -> None: ... + def _ha_for_angle(self, angle: Any) -> Literal['center', 'right', 'left'] | None: ... + def _va_for_angle(self, angle: Any) -> Literal['center', 'top', 'baseline'] | None: ... + def get_language(self) -> str | tuple[tuple[str, int, int], ...] | None: ... + def set_language(self, language: str | Sequence[tuple[str, int, int]] | None) -> None: ... class OffsetFrom: def __init__( @@ -120,17 +127,11 @@ class OffsetFrom: class _AnnotationBase: xy: tuple[float, float] - xycoords: str | tuple[str, str] | Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ] + xycoords: CoordsType def __init__( self, xy, - xycoords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] = ..., + xycoords: CoordsType = ..., annotation_clip: bool | None = ..., ) -> None: ... def set_annotation_clip(self, b: bool | None) -> None: ... @@ -147,17 +148,8 @@ class Annotation(Text, _AnnotationBase): text: str, xy: tuple[float, float], xytext: tuple[float, float] | None = ..., - xycoords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] = ..., - textcoords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform] - | None = ..., + xycoords: CoordsType = ..., + textcoords: CoordsType | None = ..., arrowprops: dict[str, Any] | None = ..., annotation_clip: bool | None = ..., **kwargs @@ -165,17 +157,11 @@ class Annotation(Text, _AnnotationBase): @property def xycoords( self, - ) -> str | tuple[str, str] | Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ]: ... + ) -> CoordsType: ... @xycoords.setter def xycoords( self, - xycoords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform], + xycoords: CoordsType, ) -> None: ... @property def xyann(self) -> tuple[float, float]: ... @@ -183,31 +169,19 @@ class Annotation(Text, _AnnotationBase): def xyann(self, xytext: tuple[float, float]) -> None: ... def get_anncoords( self, - ) -> str | tuple[str, str] | Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ]: ... + ) -> CoordsType: ... def set_anncoords( self, - coords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform], + coords: CoordsType, ) -> None: ... @property def anncoords( self, - ) -> str | tuple[str, str] | Artist | Transform | Callable[ - [RendererBase], Bbox | Transform - ]: ... + ) -> CoordsType: ... @anncoords.setter def anncoords( self, - coords: str - | tuple[str, str] - | Artist - | Transform - | Callable[[RendererBase], Bbox | Transform], + coords: CoordsType, ) -> None: ... def update_positions(self, renderer: RendererBase) -> None: ... # Drops `dpi` parameter from superclass diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index c00966d6e6c3..d7c1cdf1622f 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -8,7 +8,7 @@ from matplotlib.font_manager import ( FontProperties, get_font, fontManager as _fontManager ) -from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_TARGET_LIGHT +from matplotlib.ft2font import LoadFlags from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.texmanager import TexManager @@ -37,13 +37,11 @@ def _get_font(self, prop): return font def _get_hinting_flag(self): - return LOAD_NO_HINTING + return LoadFlags.NO_HINTING - def _get_char_id(self, font, ccode): - """ - Return a unique id for the given font and character-code set. - """ - return urllib.parse.quote(f"{font.postscript_name}-{ccode:x}") + def _get_glyph_repr(self, font, glyph): + """Return a unique id for the given font and glyph index.""" + return urllib.parse.quote(f"{font.postscript_name}-{glyph:x}") def get_text_width_height_descent(self, s, prop, ismath): fontsize = prop.get_size_in_points() @@ -61,7 +59,7 @@ def get_text_width_height_descent(self, s, prop, ismath): return width * scale, height * scale, descent * scale font = self._get_font(prop) - font.set_text(s, 0.0, flags=LOAD_NO_HINTING) + font.set_text(s, 0.0, flags=LoadFlags.NO_HINTING) w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 @@ -69,7 +67,7 @@ def get_text_width_height_descent(self, s, prop, ismath): d /= 64.0 return w * scale, h * scale, d * scale - def get_text_path(self, prop, s, ismath=False): + def get_text_path(self, prop, s, ismath=False, *, features=None, language=None): """ Convert text *s* to path (a tuple of vertices and codes for matplotlib.path.Path). @@ -82,6 +80,9 @@ def get_text_path(self, prop, s, ismath=False): The text to be converted. ismath : {False, True, "TeX"} If True, use mathtext parser. If "TeX", use tex for rendering. + language : str, optional + The language of the text in a format accepted by libraqm, namely `a BCP47 + language code `_. Returns ------- @@ -109,13 +110,14 @@ def get_text_path(self, prop, s, ismath=False): glyph_info, glyph_map, rects = self.get_glyphs_tex(prop, s) elif not ismath: font = self._get_font(prop) - glyph_info, glyph_map, rects = self.get_glyphs_with_font(font, s) + glyph_info, glyph_map, rects = self.get_glyphs_with_font( + font, s, features=features, language=language) else: glyph_info, glyph_map, rects = self.get_glyphs_mathtext(prop, s) verts, codes = [], [] - for glyph_id, xposition, yposition, scale in glyph_info: - verts1, codes1 = glyph_map[glyph_id] + for glyph_repr, xposition, yposition, scale in glyph_info: + verts1, codes1 = glyph_map[glyph_repr] verts.extend(verts1 * scale + [xposition, yposition]) codes.extend(codes1) for verts1, codes1 in rects: @@ -130,7 +132,8 @@ def get_text_path(self, prop, s, ismath=False): return verts, codes def get_glyphs_with_font(self, font, s, glyph_map=None, - return_new_glyphs_only=False): + return_new_glyphs_only=False, *, features=None, + language=None): """ Convert string *s* to vertices and codes using the provided ttf font. """ @@ -144,20 +147,21 @@ def get_glyphs_with_font(self, font, s, glyph_map=None, glyph_map_new = glyph_map xpositions = [] - glyph_ids = [] - for item in _text_helpers.layout(s, font): - char_id = self._get_char_id(item.ft_object, ord(item.char)) - glyph_ids.append(char_id) + ypositions = [] + glyph_reprs = [] + for item in _text_helpers.layout(s, font, features=features, language=language): + glyph_repr = self._get_glyph_repr(item.ft_object, item.glyph_index) + glyph_reprs.append(glyph_repr) xpositions.append(item.x) - if char_id not in glyph_map: - glyph_map_new[char_id] = item.ft_object.get_path() + ypositions.append(item.y) + if glyph_repr not in glyph_map: + glyph_map_new[glyph_repr] = item.ft_object.get_path() - ypositions = [0] * len(xpositions) sizes = [1.] * len(xpositions) rects = [] - return (list(zip(glyph_ids, xpositions, ypositions, sizes)), + return (list(zip(glyph_reprs, xpositions, ypositions, sizes)), glyph_map_new, rects) def get_glyphs_mathtext(self, prop, s, glyph_map=None, @@ -182,20 +186,20 @@ def get_glyphs_mathtext(self, prop, s, glyph_map=None, xpositions = [] ypositions = [] - glyph_ids = [] + glyph_reprs = [] sizes = [] - for font, fontsize, ccode, ox, oy in glyphs: - char_id = self._get_char_id(font, ccode) - if char_id not in glyph_map: + for font, fontsize, ccode, glyph_index, ox, oy in glyphs: + glyph_repr = self._get_glyph_repr(font, glyph_index) + if glyph_repr not in glyph_map: font.clear() font.set_size(self.FONT_SCALE, self.DPI) - font.load_char(ccode, flags=LOAD_NO_HINTING) - glyph_map_new[char_id] = font.get_path() + font.load_glyph(glyph_index, flags=LoadFlags.NO_HINTING) + glyph_map_new[glyph_repr] = font.get_path() xpositions.append(ox) ypositions.append(oy) - glyph_ids.append(char_id) + glyph_reprs.append(glyph_repr) size = fontsize / self.FONT_SCALE sizes.append(size) @@ -208,7 +212,7 @@ def get_glyphs_mathtext(self, prop, s, glyph_map=None, Path.CLOSEPOLY] myrects.append((vert1, code1)) - return (list(zip(glyph_ids, xpositions, ypositions, sizes)), + return (list(zip(glyph_reprs, xpositions, ypositions, sizes)), glyph_map_new, myrects) def get_glyphs_tex(self, prop, s, glyph_map=None, @@ -228,30 +232,22 @@ def get_glyphs_tex(self, prop, s, glyph_map=None, else: glyph_map_new = glyph_map - glyph_ids, xpositions, ypositions, sizes = [], [], [], [] + glyph_reprs, xpositions, ypositions, sizes = [], [], [], [] # Gather font information and do some setup for combining # characters into strings. for text in page.text: - font = get_font(text.font_path) - char_id = self._get_char_id(font, text.glyph) - if char_id not in glyph_map: + font = get_font(text.font.resolve_path()) + if text.font.subfont: + raise NotImplementedError("Indexing TTC fonts is not supported yet") + glyph_repr = self._get_glyph_repr(font, text.index) + if glyph_repr not in glyph_map: font.clear() font.set_size(self.FONT_SCALE, self.DPI) - glyph_name_or_index = text.glyph_name_or_index - if isinstance(glyph_name_or_index, str): - index = font.get_name_index(glyph_name_or_index) - font.load_glyph(index, flags=LOAD_TARGET_LIGHT) - elif isinstance(glyph_name_or_index, int): - self._select_native_charmap(font) - font.load_char( - glyph_name_or_index, flags=LOAD_TARGET_LIGHT) - else: # Should not occur. - raise TypeError(f"Glyph spec of unexpected type: " - f"{glyph_name_or_index!r}") - glyph_map_new[char_id] = font.get_path() - - glyph_ids.append(char_id) + font.load_glyph(text.index, flags=LoadFlags.TARGET_LIGHT) + glyph_map_new[glyph_repr] = font.get_path() + + glyph_reprs.append(glyph_repr) xpositions.append(text.x) ypositions.append(text.y) sizes.append(text.font_size / self.FONT_SCALE) @@ -266,26 +262,9 @@ def get_glyphs_tex(self, prop, s, glyph_map=None, Path.CLOSEPOLY] myrects.append((vert1, code1)) - return (list(zip(glyph_ids, xpositions, ypositions, sizes)), + return (list(zip(glyph_reprs, xpositions, ypositions, sizes)), glyph_map_new, myrects) - @staticmethod - def _select_native_charmap(font): - # Select the native charmap. (we can't directly identify it but it's - # typically an Adobe charmap). - for charmap_code in [ - 1094992451, # ADOBE_CUSTOM. - 1094995778, # ADOBE_STANDARD. - ]: - try: - font.select_charmap(charmap_code) - except (ValueError, RuntimeError): - pass - else: - break - else: - _log.warning("No supported encoding in font (%s).", font.fname) - text_to_path = TextToPath() diff --git a/lib/matplotlib/textpath.pyi b/lib/matplotlib/textpath.pyi index 34d4e92ac47e..07f81598aa75 100644 --- a/lib/matplotlib/textpath.pyi +++ b/lib/matplotlib/textpath.pyi @@ -16,7 +16,13 @@ class TextToPath: self, s: str, prop: FontProperties, ismath: bool | Literal["TeX"] ) -> tuple[float, float, float]: ... def get_text_path( - self, prop: FontProperties, s: str, ismath: bool | Literal["TeX"] = ... + self, + prop: FontProperties, + s: str, + ismath: bool | Literal["TeX"] = ..., + *, + features: tuple[str] | None = ..., + language: str | list[tuple[str, int, int]] | None = ..., ) -> list[np.ndarray]: ... def get_glyphs_with_font( self, @@ -24,6 +30,9 @@ class TextToPath: s: str, glyph_map: dict[str, tuple[np.ndarray, np.ndarray]] | None = ..., return_new_glyphs_only: bool = ..., + *, + features: tuple[str] | None = ..., + language: str | list[tuple[str, int, int]] | None = ..., ) -> tuple[ list[tuple[str, float, float, float]], dict[str, tuple[np.ndarray, np.ndarray]], diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 2b00937f9e29..cec84e1f3f36 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -199,6 +199,17 @@ def create_dummy_axis(self, **kwargs): class Formatter(TickHelper): """ Create a string based on a tick value and location. + + The Formatter provides four formatting methods for different use cases: + + - `format_ticks`: The public API for generating tick labels from a set of + tick values. + - `__call__`: The low-level primitive for formatting a single tick value, + potentially in the context of multiple values. + - `format_data`: Context-independent representation of a single value. + Used internally, e.g. for offset and scientific-notation strings. + - `format_data_short`: Concise plain-text representation of a single value + for the interactive mouseover tooltip. """ # some classes want to see all the locs to help format # individual ones @@ -206,28 +217,69 @@ class Formatter(TickHelper): def __call__(self, x, pos=None): """ - Return the format for tick value *x* at position pos. - ``pos=None`` indicates an unspecified location. + Return the tick label strings for value *x* at tick index *pos*. + + This is the low-level formatting primitive for a single tick in + the context of multiple ticks. Any context-dependent state + (e.g. locs, offset, order of magnitude) must already be configured, + typically by a prior call to ``format_ticks`` or ``set_locs``. + + *pos* defines the index into ``self.locs`` so that the format can + depend on the location. ``pos=None`` indicates an unspecified + location. + + The output may contain mathtext or LaTeX markup. + + Subclasses must override this method. """ raise NotImplementedError('Derived must override') def format_ticks(self, values): - """Return the tick labels for all the ticks at once.""" + """ + Return the tick label strings for all *values*. + + This is the public API for generating tick labels. It calls + ``set_locs`` to configure context-dependent formatting state before + delegating to ``__call__`` for each individual value. + + The output may contain mathtext or LaTeX markup. + + Use this method (rather than ``__call__``) whenever formatting a + complete set of tick values, so that formatters which need to see + all tick locations (e.g. to determine precision, offsets, or which + date components to display) can work correctly. + """ self.set_locs(values) return [self(value, i) for i, value in enumerate(values)] def format_data(self, value): """ - Return the full string representation of the value with the - position unspecified. + Return the context-independent string representation of a single *value*. + + This is used internally, e.g. for constructing offset and + scientific-notation strings. It always formats with ``pos=None`` + and should return a context-independent representation + rather than a concise tick label. + + The output may contain mathtext or LaTeX markup. """ return self.__call__(value) def format_data_short(self, value): """ - Return a short string version of the tick value. + Return a short string representation of *value* for the mouseover + tooltip (the coordinate display in the interactive figure window). + + This should return concise, plain text (no mathtext / LaTeX). + The precision is typically adapted to the current axis resolution + so that neighbouring pixels produce distinguishable labels. + + Defaults to `.Formatter.format_data`; subclasses should override + this to provide a plain-text representation that is independent + of the current tick locations. - Defaults to the position-independent long value. + Note: The mouseover text can be customized by setting the + ``Axes.fmt_xdata`` and ``Axes.fmt_ydata`` attributes. """ return self.format_data(value) @@ -379,6 +431,12 @@ class StrMethodFormatter(Formatter): It is typically unnecessary to explicitly construct `.StrMethodFormatter` objects, as `~.Axis.set_major_formatter` directly accepts the format string itself. + + Examples + -------- + >>> formatter = StrMethodFormatter("{x} km") + >>> formatter(10) + "10 km" """ def __init__(self, fmt): @@ -407,6 +465,11 @@ class ScalarFormatter(Formatter): useLocale : bool, default: :rc:`axes.formatter.use_locale`. Whether to use locale settings for decimal sign and positive sign. See `.set_useLocale`. + usetex : bool, default: :rc:`text.usetex` + To enable/disable the use of TeX's math mode for rendering the + numbers in the formatter. + + .. versionadded:: 3.10 Notes ----- @@ -435,22 +498,21 @@ class ScalarFormatter(Formatter): lim = (1_000_000, 1_000_010) fig, (ax1, ax2, ax3) = plt.subplots(3, 1, gridspec_kw={'hspace': 2}) - ax1.set(title='offset_notation', xlim=lim) + ax1.set(title='offset notation', xlim=lim) ax2.set(title='scientific notation', xlim=lim) ax2.xaxis.get_major_formatter().set_useOffset(False) - ax3.set(title='floating point notation', xlim=lim) + ax3.set(title='floating-point notation', xlim=lim) ax3.xaxis.get_major_formatter().set_useOffset(False) ax3.xaxis.get_major_formatter().set_scientific(False) """ - def __init__(self, useOffset=None, useMathText=None, useLocale=None): - if useOffset is None: - useOffset = mpl.rcParams['axes.formatter.useoffset'] - self._offset_threshold = \ - mpl.rcParams['axes.formatter.offset_threshold'] + def __init__(self, useOffset=None, useMathText=None, useLocale=None, *, + usetex=None): + useOffset = mpl._val_or_rc(useOffset, 'axes.formatter.useoffset') + self._offset_threshold = mpl.rcParams['axes.formatter.offset_threshold'] self.set_useOffset(useOffset) - self._usetex = mpl.rcParams['text.usetex'] + self.set_usetex(usetex) self.set_useMathText(useMathText) self.orderOfMagnitude = 0 self.format = '' @@ -458,6 +520,16 @@ def __init__(self, useOffset=None, useMathText=None, useLocale=None): self._powerlimits = mpl.rcParams['axes.formatter.limits'] self.set_useLocale(useLocale) + def get_usetex(self): + """Return whether TeX's math mode is enabled for rendering.""" + return self._usetex + + def set_usetex(self, val): + """Set whether to use TeX's math mode for rendering numbers in the formatter.""" + self._usetex = mpl._val_or_rc(val, 'text.usetex') + + usetex = property(fget=get_usetex, fset=set_usetex) + def get_useOffset(self): """ Return whether automatic mode for offset notation is active. @@ -498,7 +570,7 @@ def set_useOffset(self, val): will be formatted as ``0, 2, 4, 6, 8`` plus an offset ``+1e5``, which is written to the edge of the axis. """ - if val in [True, False]: + if isinstance(val, bool): self.offset = 0 self._useOffset = val else: @@ -526,10 +598,7 @@ def set_useLocale(self, val): val : bool or None *None* resets to :rc:`axes.formatter.use_locale`. """ - if val is None: - self._useLocale = mpl.rcParams['axes.formatter.use_locale'] - else: - self._useLocale = val + self._useLocale = mpl._val_or_rc(val, 'axes.formatter.use_locale') useLocale = property(fget=get_useLocale, fset=set_useLocale) @@ -574,7 +643,7 @@ def set_useMathText(self, val): from matplotlib import font_manager ufont = font_manager.findfont( font_manager.FontProperties( - mpl.rcParams["font.family"] + family=mpl.rcParams["font.family"] ), fallback_to_default=False, ) @@ -847,20 +916,23 @@ class LogFormatter(Formatter): labelOnlyBase : bool, default: False If True, label ticks only at integer powers of base. - This is normally True for major ticks and False for - minor ticks. + This is normally True for major ticks and False for minor ticks. minor_thresholds : (subset, all), default: (1, 0.4) If labelOnlyBase is False, these two numbers control the labeling of ticks that are not at integer powers of - base; normally these are the minor ticks. The controlling - parameter is the log of the axis data range. In the typical - case where base is 10 it is the number of decades spanned - by the axis, so we can call it 'numdec'. If ``numdec <= all``, - all minor ticks will be labeled. If ``all < numdec <= subset``, - then only a subset of minor ticks will be labeled, so as to - avoid crowding. If ``numdec > subset`` then no minor ticks will - be labeled. + base; normally these are the minor ticks. + + The first number (*subset*) is the largest number of major ticks for + which minor ticks are labeled; e.g., the default, 1, means that minor + ticks are labeled as long as there is no more than 1 major tick. (It + is assumed that major ticks are at integer powers of *base*.) + + The second number (*all*) is a threshold, in log-units of the axis + limit range, over which only a subset of the minor ticks are labeled, + so as to avoid crowding; e.g., with the default value (0.4) and the + usual ``base=10``, all minor ticks are shown only if the axis limit + range spans less than 0.4 decades. linthresh : None or float, default: None If a symmetric log scale is in use, its ``linthresh`` @@ -884,12 +956,9 @@ class LogFormatter(Formatter): Examples -------- - To label a subset of minor ticks when the view limits span up - to 2 decades, and all of the ticks when zoomed in to 0.5 decades - or less, use ``minor_thresholds=(2, 0.5)``. - - To label all minor ticks when the view limits span up to 1.5 - decades, use ``minor_thresholds=(1.5, 1.5)``. + To label a subset of minor ticks when there are up to 2 major ticks, + and all of the ticks when zoomed in to 0.5 decades or less, use + ``minor_thresholds=(2, 0.5)``. """ def __init__(self, base=10.0, labelOnlyBase=False, @@ -957,22 +1026,32 @@ def set_locs(self, locs=None): return b = self._base + if linthresh is not None: # symlog - # Only compute the number of decades in the logarithmic part of the - # axis - numdec = 0 + # Only count ticks and decades in the logarithmic part of the axis. + numdec = numticks = 0 if vmin < -linthresh: rhs = min(vmax, -linthresh) - numdec += math.log(vmin / rhs) / math.log(b) + numticks += ( + math.floor(math.log(abs(rhs), b)) + - math.floor(math.nextafter(math.log(abs(vmin), b), -math.inf))) + numdec += math.log(vmin / rhs, b) if vmax > linthresh: lhs = max(vmin, linthresh) - numdec += math.log(vmax / lhs) / math.log(b) + numticks += ( + math.floor(math.log(vmax, b)) + - math.floor(math.nextafter(math.log(lhs, b), -math.inf))) + numdec += math.log(vmax / lhs, b) else: - vmin = math.log(vmin) / math.log(b) - vmax = math.log(vmax) / math.log(b) - numdec = abs(vmax - vmin) - - if numdec > self.minor_thresholds[0]: + lmin = math.log(vmin, b) + lmax = math.log(vmax, b) + # The nextafter call handles the case where vmin is exactly at a + # decade (e.g. there's one major tick between 1 and 5). + numticks = (math.floor(lmax) + - math.floor(math.nextafter(lmin, -math.inf))) + numdec = abs(lmax - lmin) + + if numticks > self.minor_thresholds[0]: # Label only bases self._sublabels = {1} elif numdec > self.minor_thresholds[1]: @@ -987,13 +1066,7 @@ def set_locs(self, locs=None): self._sublabels = set(np.arange(1, b + 1)) def _num_to_string(self, x, vmin, vmax): - if x > 10000: - s = '%1.0e' % x - elif x < 1: - s = '%1.0e' % x - else: - s = self._pprint_val(x, vmax - vmin) - return s + return self._pprint_val(x, vmax - vmin) if 1 <= x <= 10000 else f"{x:1.0e}" def __call__(self, x, pos=None): # docstring inherited @@ -1014,7 +1087,7 @@ def __call__(self, x, pos=None): return '' vmin, vmax = self.axis.get_view_interval() - vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05) + vmin, vmax = mtransforms._nonsingular(vmin, vmax, expander=0.05) s = self._num_to_string(x, vmin, vmax) return self.fix_minus(s) @@ -1053,15 +1126,14 @@ class LogFormatterExponent(LogFormatter): """ Format values for log axis using ``exponent = log_base(value)``. """ + def _num_to_string(self, x, vmin, vmax): fx = math.log(x) / math.log(self._base) - if abs(fx) > 10000: - s = '%1.0g' % fx - elif abs(fx) < 1: - s = '%1.0g' % fx - else: + if 1 <= abs(fx) <= 10000: fd = math.log(vmax - vmin) / math.log(self._base) s = self._pprint_val(fx, fd) + else: + s = f"{fx:1.0g}" return s @@ -1146,10 +1218,10 @@ def __init__( Parameters ---------- use_overline : bool, default: False - If x > 1/2, with x = 1-v, indicate if x should be displayed as - $\overline{v}$. The default is to display $1-v$. + If x > 1/2, with x = 1 - v, indicate if x should be displayed as + $\overline{v}$. The default is to display $1 - v$. - one_half : str, default: r"\frac{1}{2}" + one_half : str, default: r"\\frac{1}{2}" The string used to represent 1/2. minor : bool, default: False @@ -1179,9 +1251,9 @@ def use_overline(self, use_overline): Parameters ---------- - use_overline : bool, default: False - If x > 1/2, with x = 1-v, indicate if x should be displayed as - $\overline{v}$. The default is to display $1-v$. + use_overline : bool + If x > 1/2, with x = 1 - v, indicate if x should be displayed as + $\overline{v}$. The default is to display $1 - v$. """ self._use_overline = use_overline @@ -1189,7 +1261,7 @@ def set_one_half(self, one_half): r""" Set the way one half is displayed. - one_half : str, default: r"\frac{1}{2}" + one_half : str The string used to represent 1/2. """ self._one_half = one_half @@ -1324,7 +1396,7 @@ def format_data_short(self, value): return f"1-{1 - value:e}" -class EngFormatter(Formatter): +class EngFormatter(ScalarFormatter): """ Format axis values using engineering prefixes to represent powers of 1000, plus a specified unit, e.g., 10 MHz instead of 1e7. @@ -1356,7 +1428,7 @@ class EngFormatter(Formatter): } def __init__(self, unit="", places=None, sep=" ", *, usetex=None, - useMathText=None): + useMathText=None, useOffset=False): r""" Parameters ---------- @@ -1390,76 +1462,124 @@ def __init__(self, unit="", places=None, sep=" ", *, usetex=None, useMathText : bool, default: :rc:`axes.formatter.use_mathtext` To enable/disable the use mathtext for rendering the numbers in the formatter. + useOffset : bool or float, default: False + Whether to use offset notation with :math:`10^{3*N}` based prefixes. + This features allows showing an offset with standard SI order of + magnitude prefix near the axis. Offset is computed similarly to + how `ScalarFormatter` computes it internally, but here you are + guaranteed to get an offset which will make the tick labels exceed + 3 digits. See also `.set_useOffset`. + + .. versionadded:: 3.10 """ self.unit = unit self.places = places self.sep = sep - self.set_usetex(usetex) - self.set_useMathText(useMathText) - - def get_usetex(self): - return self._usetex - - def set_usetex(self, val): - if val is None: - self._usetex = mpl.rcParams['text.usetex'] - else: - self._usetex = val - - usetex = property(fget=get_usetex, fset=set_usetex) + super().__init__( + useOffset=useOffset, + useMathText=useMathText, + useLocale=False, + usetex=usetex, + ) - def get_useMathText(self): - return self._useMathText + def __call__(self, x, pos=None): + """ + Return the format for tick value *x* at position *pos*. - def set_useMathText(self, val): - if val is None: - self._useMathText = mpl.rcParams['axes.formatter.use_mathtext'] + If there is no currently offset in the data, it returns the best + engineering formatting that fits the given argument, independently. + """ + if len(self.locs) == 0 or self.offset == 0: + return self.fix_minus(self.format_data(x)) else: - self._useMathText = val + xp = (x - self.offset) / (10. ** self.orderOfMagnitude) + if abs(xp) < 1e-8: + xp = 0 + return self._format_maybe_minus_and_locale(self.format, xp) - useMathText = property(fget=get_useMathText, fset=set_useMathText) + def set_locs(self, locs): + # docstring inherited + self.locs = locs + if len(self.locs) > 0: + vmin, vmax = sorted(self.axis.get_view_interval()) + if self._useOffset: + self._compute_offset() + if self.offset != 0: + # We don't want to use the offset computed by + # self._compute_offset because it rounds the offset unaware + # of our engineering prefixes preference, and this can + # cause ticks with 4+ digits to appear. These ticks are + # slightly less readable, so if offset is justified + # (decided by self._compute_offset) we set it to better + # value: + self.offset = round((vmin + vmax)/2, 3) + # Use log1000 to use engineers' oom standards + self.orderOfMagnitude = math.floor(math.log(vmax - vmin, 1000))*3 + self._set_format() - def __call__(self, x, pos=None): - s = f"{self.format_eng(x)}{self.unit}" - # Remove the trailing separator when there is neither prefix nor unit - if self.sep and s.endswith(self.sep): - s = s[:-len(self.sep)] - return self.fix_minus(s) + # Simplify a bit ScalarFormatter.get_offset: We always want to use + # self.format_data. Also we want to return a non-empty string only if there + # is an offset, no matter what is self.orderOfMagnitude. If there _is_ an + # offset, self.orderOfMagnitude is consulted. This behavior is verified + # in `test_ticker.py`. + def get_offset(self): + # docstring inherited + if len(self.locs) == 0: + return '' + if self.offset: + offsetStr = '' + if self.offset: + offsetStr = self.format_data(self.offset) + if self.offset > 0: + offsetStr = '+' + offsetStr + sciNotStr = self.format_data(10 ** self.orderOfMagnitude) + if self._useMathText or self._usetex: + if sciNotStr != '': + sciNotStr = r'\times%s' % sciNotStr + s = f'${sciNotStr}{offsetStr}$' + else: + s = sciNotStr + offsetStr + return self.fix_minus(s) + return '' def format_eng(self, num): + """Alias to EngFormatter.format_data""" + return self.format_data(num) + + def format_data(self, value): """ Format a number in engineering notation, appending a letter representing the power of 1000 of the original number. Some examples: - >>> format_eng(0) # for self.places = 0 + >>> format_data(0) # for self.places = 0 '0' - >>> format_eng(1000000) # for self.places = 1 + >>> format_data(1000000) # for self.places = 1 '1.0 M' - >>> format_eng(-1e-6) # for self.places = 2 + >>> format_data(-1e-6) # for self.places = 2 '-1.00 \N{MICRO SIGN}' """ sign = 1 fmt = "g" if self.places is None else f".{self.places:d}f" - if num < 0: + if value < 0: sign = -1 - num = -num + value = -value - if num != 0: - pow10 = int(math.floor(math.log10(num) / 3) * 3) + if value != 0: + pow10 = int(math.floor(math.log10(value) / 3) * 3) else: pow10 = 0 - # Force num to zero, to avoid inconsistencies like + # Force value to zero, to avoid inconsistencies like # format_eng(-0) = "0" and format_eng(0.0) = "0" # but format_eng(-0.0) = "-0.0" - num = 0.0 + value = 0.0 pow10 = np.clip(pow10, min(self.ENG_PREFIXES), max(self.ENG_PREFIXES)) - mant = sign * num / (10.0 ** pow10) + mant = sign * value / (10.0 ** pow10) # Taking care of the cases like 999.9..., which may be rounded to 1000 # instead of 1 k. Beware of the corner case of values that are beyond # the range of SI prefixes (i.e. > 'Y'). @@ -1468,13 +1588,15 @@ def format_eng(self, num): mant /= 1000 pow10 += 3 - prefix = self.ENG_PREFIXES[int(pow10)] + unit_prefix = self.ENG_PREFIXES[int(pow10)] + if self.unit or unit_prefix: + suffix = f"{self.sep}{unit_prefix}{self.unit}" + else: + suffix = "" if self._usetex or self._useMathText: - formatted = f"${mant:{fmt}}${self.sep}{prefix}" + return f"${mant:{fmt}}${suffix}" else: - formatted = f"{mant:{fmt}}{self.sep}{prefix}" - - return formatted + return f"{mant:{fmt}}{suffix}" class PercentFormatter(Formatter): @@ -1666,7 +1788,7 @@ def nonsingular(self, v0, v1): default view limits. - Otherwise, ``(v0, v1)`` is returned without modification. """ - return mtransforms.nonsingular(v0, v1, expander=.05) + return mtransforms._nonsingular(v0, v1, expander=.05) def view_limits(self, vmin, vmax): """ @@ -1674,7 +1796,7 @@ def view_limits(self, vmin, vmax): Subclasses should override this method to change locator behaviour. """ - return mtransforms.nonsingular(vmin, vmax) + return mtransforms._nonsingular(vmin, vmax) class IndexLocator(Locator): @@ -1684,6 +1806,7 @@ class IndexLocator(Locator): IndexLocator assumes index plotting; i.e., that the ticks are placed at integer values in the range between 0 and len(data) inclusive. """ + def __init__(self, base, offset): """Place ticks every *base* data point, starting at *offset*.""" self._base = base @@ -1702,19 +1825,22 @@ def __call__(self): return self.tick_values(dmin, dmax) def tick_values(self, vmin, vmax): - return self.raise_if_exceeds( - np.arange(vmin + self.offset, vmax + 1, self._base)) + # We want tick values in the closed interval [vmin, vmax]. + # Since np.arange(start, stop) returns values in the semi-open interval + # [start, stop), we add a minimal offset so that stop = vmax + eps + tick_values = np.arange(vmin + self.offset, vmax + 1e-12, self._base) + return self.raise_if_exceeds(tick_values) class FixedLocator(Locator): - """ + r""" Place ticks at a set of fixed values. If *nbins* is None ticks are placed at all values. Otherwise, the *locs* array of - possible positions will be subsampled to keep the number of ticks <= - :math:`nbins* +1`. The subsampling will be done to include the smallest absolute - value; for example, if zero is included in the array of possibilities, then it of - the chosen ticks. + possible positions will be subsampled to keep the number of ticks + :math:`\leq nbins + 1`. The subsampling will be done to include the smallest + absolute value; for example, if zero is included in the array of possibilities, then + it will be included in the chosen ticks. """ def __init__(self, locs, nbins=None): @@ -1736,9 +1862,7 @@ def tick_values(self, vmin, vmax): .. note:: - Because the values are fixed, vmin and vmax are not used in this - method. - + Because the values are fixed, *vmin* and *vmax* are not used. """ if self.nbins is None: return self.locs @@ -1753,7 +1877,7 @@ def tick_values(self, vmin, vmax): class NullLocator(Locator): """ - No ticks + Place no ticks. """ def __call__(self): @@ -1765,8 +1889,7 @@ def tick_values(self, vmin, vmax): .. note:: - Because the values are Null, vmin and vmax are not used in this - method. + Because there are no ticks, *vmin* and *vmax* are not used. """ return [] @@ -1775,12 +1898,11 @@ class LinearLocator(Locator): """ Place ticks at evenly spaced values. - The first time this function is called it will try to set the - number of ticks to make a nice tick partitioning. Thereafter, the - number of ticks will be fixed so that interactive navigation will - be nice - + The first time this function is called, it will try to set the number of + ticks to make a nice tick partitioning. Thereafter, the number of ticks + will be fixed to avoid jumping during interactive navigation. """ + def __init__(self, numticks=None, presets=None): """ Parameters @@ -1820,7 +1942,7 @@ def __call__(self): return self.tick_values(vmin, vmax) def tick_values(self, vmin, vmax): - vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05) + vmin, vmax = mtransforms._nonsingular(vmin, vmax, expander=0.05) if (vmin, vmax) in self.presets: return self.presets[(vmin, vmax)] @@ -1849,7 +1971,7 @@ def view_limits(self, vmin, vmax): vmin = math.floor(scale * vmin) / scale vmax = math.ceil(scale * vmax) / scale - return mtransforms.nonsingular(vmin, vmax) + return mtransforms._nonsingular(vmin, vmax) class MultipleLocator(Locator): @@ -1861,9 +1983,9 @@ def __init__(self, base=1.0, offset=0.0): """ Parameters ---------- - base : float > 0 + base : float > 0, default: 1.0 Interval between ticks. - offset : float + offset : float, default: 0.0 Value added to each multiple of *base*. .. versionadded:: 3.8 @@ -1877,9 +1999,9 @@ def set_params(self, base=None, offset=None): Parameters ---------- - base : float > 0 + base : float > 0, optional Interval between ticks. - offset : float + offset : float, optional Value added to each multiple of *base*. .. versionadded:: 3.8 @@ -1919,7 +2041,7 @@ def view_limits(self, dmin, dmax): vmin = dmin vmax = dmax - return mtransforms.nonsingular(vmin, vmax) + return mtransforms._nonsingular(vmin, vmax) def scale_range(vmin, vmax, n=1, threshold=100): @@ -1940,6 +2062,7 @@ class _Edge_integer: Take floating-point precision limitations into account when calculating tick locations as integer multiples of a step. """ + def __init__(self, step, offset): """ Parameters @@ -2174,7 +2297,7 @@ def tick_values(self, vmin, vmax): if self._symmetric: vmax = max(abs(vmin), abs(vmax)) vmin = -vmax - vmin, vmax = mtransforms.nonsingular( + vmin, vmax = mtransforms._nonsingular( vmin, vmax, expander=1e-13, tiny=1e-14) locs = self._raw_ticks(vmin, vmax) @@ -2192,7 +2315,7 @@ def view_limits(self, dmin, dmax): dmax = max(abs(dmin), abs(dmax)) dmin = -dmax - dmin, dmax = mtransforms.nonsingular( + dmin, dmax = mtransforms._nonsingular( dmin, dmax, expander=1e-12, tiny=1e-13) if mpl.rcParams['axes.autolimit_mode'] == 'round_numbers': @@ -2275,8 +2398,7 @@ class LogLocator(Locator): Places ticks at the values ``subs[j] * base**i``. """ - @_api.delete_parameter("3.8", "numdecs") - def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None): + def __init__(self, base=10.0, subs=(1.0,), *, numticks=None): """ Parameters ---------- @@ -2305,24 +2427,17 @@ def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None): numticks = 'auto' self._base = float(base) self._set_subs(subs) - self._numdecs = numdecs self.numticks = numticks - @_api.delete_parameter("3.8", "numdecs") - def set_params(self, base=None, subs=None, numdecs=None, numticks=None): + def set_params(self, base=None, subs=None, *, numticks=None): """Set parameters within this locator.""" if base is not None: self._base = float(base) if subs is not None: self._set_subs(subs) - if numdecs is not None: - self._numdecs = numdecs if numticks is not None: self.numticks = numticks - numdecs = _api.deprecate_privatize_attribute( - "3.8", addendum="This attribute has no effect.") - def _set_subs(self, subs): """ Set the minor ticks for the log scaling every ``base**i*subs[j]``. @@ -2349,14 +2464,19 @@ def __call__(self): vmin, vmax = self.axis.get_view_interval() return self.tick_values(vmin, vmax) + def _log_b(self, x): + # Use specialized logs if possible, as they can be more accurate; e.g. + # log(.001) / log(10) = -2.999... (whether math.log or np.log) due to + # floating point error. + return (np.log10(x) if self._base == 10 else + np.log2(x) if self._base == 2 else + np.log(x) / np.log(self._base)) + def tick_values(self, vmin, vmax): - if self.numticks == 'auto': - if self.axis is not None: - numticks = np.clip(self.axis.get_tick_space(), 2, 9) - else: - numticks = 9 - else: - numticks = self.numticks + n_request = ( + self.numticks if self.numticks != "auto" else + np.clip(self.axis.get_tick_space(), 2, 9) if self.axis is not None else + 9) b = self._base if vmin <= 0.0: @@ -2365,19 +2485,19 @@ def tick_values(self, vmin, vmax): if vmin <= 0.0 or not np.isfinite(vmin): raise ValueError( - "Data has no positive values, and therefore cannot be log-scaled.") - - _log.debug('vmin %s vmax %s', vmin, vmax) + "Data cannot be log-scaled because all values are <= 0.") if vmax < vmin: vmin, vmax = vmax, vmin - log_vmin = math.log(vmin) / math.log(b) - log_vmax = math.log(vmax) / math.log(b) - - numdec = math.floor(log_vmax) - math.ceil(log_vmin) + # Min and max exponents, float and int versions; e.g., if vmin=10^0.3, + # vmax=10^6.9, then efmin=0.3, emin=1, emax=6, efmax=6.9, n_avail=6. + efmin, efmax = self._log_b([vmin, vmax]) + emin = math.ceil(efmin) + emax = math.floor(efmax) + n_avail = emax - emin + 1 # Total number of decade ticks available. if isinstance(self._subs, str): - if numdec > 10 or b < 3: + if n_avail >= 10 or b < 3: if self._subs == 'auto': return np.array([]) # no minor or major ticks else: @@ -2388,41 +2508,87 @@ def tick_values(self, vmin, vmax): else: subs = self._subs - # Get decades between major ticks. - stride = (max(math.ceil(numdec / (numticks - 1)), 1) - if mpl.rcParams['_internal.classic_mode'] else - numdec // numticks + 1) - - # if we have decided that the stride is as big or bigger than - # the range, clip the stride back to the available range - 1 - # with a floor of 1. This prevents getting axis with only 1 tick - # visible. - if stride >= numdec: - stride = max(1, numdec - 1) - - # Does subs include anything other than 1? Essentially a hack to know - # whether we're a major or a minor locator. - have_subs = len(subs) > 1 or (len(subs) == 1 and subs[0] != 1.0) - - decades = np.arange(math.floor(log_vmin) - stride, - math.ceil(log_vmax) + 2 * stride, stride) - - if have_subs: - if stride == 1: - ticklocs = np.concatenate( - [subs * decade_start for decade_start in b ** decades]) + # Get decades between major ticks. Include an extra tick outside the + # lower and the upper limit: QuadContourSet._autolev relies on this. + if mpl.rcParams["_internal.classic_mode"]: # keep historic formulas + stride = max(math.ceil((n_avail - 1) / (n_request - 1)), 1) + decades = np.arange(emin - stride, emax + stride + 1, stride) + else: + # *Determine the actual number of ticks*: Find the largest number + # of ticks, no more than the requested number, that can actually + # be drawn (e.g., with 9 decades ticks, no stride yields 7 + # ticks). For a given value of the stride *s*, there are either + # floor(n_avail/s) or ceil(n_avail/s) ticks depending on the + # offset. Pick the smallest stride such that floor(n_avail/s) < + # n_request, i.e. n_avail/s < n_request+1, then re-set n_request + # to ceil(...) if acceptable, else to floor(...) (which must then + # equal the original n_request, i.e. n_request is kept unchanged). + stride = n_avail // (n_request + 1) + 1 + nr = math.ceil(n_avail / stride) + if nr <= n_request: + n_request = nr + else: + assert nr == n_request + 1 + if n_request == 0: # No tick in bounds; two ticks just outside. + decades = [emin - 1, emax + 1] + stride = decades[1] - decades[0] + elif n_request == 1: # A single tick close to center. + mid = round((efmin + efmax) / 2) + stride = max(mid - (emin - 1), (emax + 1) - mid) + decades = [mid - stride, mid, mid + stride] + else: + # *Determine the stride*: Pick the largest stride that yields + # this actual n_request (e.g., with 15 decades, strides of + # 5, 6, or 7 *can* yield 3 ticks; picking a larger stride + # minimizes unticked space at the ends). First try for + # ceil(n_avail/stride) == n_request + # i.e. + # n_avail/n_request <= stride < n_avail/(n_request-1) + # else fallback to + # floor(n_avail/stride) == n_request + # i.e. + # n_avail/(n_request+1) < stride <= n_avail/n_request + # One of these cases must have an integer solution (given the + # choice of n_request above). + stride = (n_avail - 1) // (n_request - 1) + if stride < n_avail / n_request: # fallback to second case + stride = n_avail // n_request + # *Determine the offset*: For a given stride *and offset* + # (0 <= offset < stride), the actual number of ticks is + # ceil((n_avail - offset) / stride), which must be equal to + # n_request. This leads to olo <= offset < ohi, with the + # values defined below. + olo = max(n_avail - stride * n_request, 0) + ohi = min(n_avail - stride * (n_request - 1), stride) + # Try to see if we can pick an offset so that ticks are at + # integer multiples of the stride while satisfying the bounds + # above; if not, fallback to the smallest acceptable offset. + offset = (-emin) % stride + if not olo <= offset < ohi: + offset = olo + decades = range(emin + offset - stride, emax + stride + 1, stride) + + # Guess whether we're a minor locator, based on whether subs include + # anything other than 1. + is_minor = len(subs) > 1 or (len(subs) == 1 and subs[0] != 1.0) + if is_minor: + if stride == 1 or n_avail <= 1: + # Minor ticks start in the decade preceding the first major tick. + ticklocs = np.concatenate([ + subs * b**decade for decade in range(emin - 1, emax + 1)]) else: ticklocs = np.array([]) else: - ticklocs = b ** decades + ticklocs = b ** np.array(decades) - _log.debug('ticklocs %r', ticklocs) if (len(subs) > 1 and stride == 1 - and ((vmin <= ticklocs) & (ticklocs <= vmax)).sum() <= 1): + and (len(decades) - 2 # major + + ((vmin <= ticklocs) & (ticklocs <= vmax)).sum()) # minor + <= 1): # If we're a minor locator *that expects at least two ticks per # decade* and the major locator stride is 1 and there's no more - # than one minor tick, switch to AutoLocator. + # than one major or minor tick, switch to AutoLocator. return AutoLocator().tick_values(vmin, vmax) else: return self.raise_if_exceeds(ticklocs) @@ -2613,7 +2779,7 @@ def view_limits(self, vmin, vmax): vmin = _decade_less(vmin, b) vmax = _decade_greater(vmax, b) - return mtransforms.nonsingular(vmin, vmax) + return mtransforms._nonsingular(vmin, vmax) class AsinhLocator(Locator): @@ -2881,20 +3047,21 @@ class AutoMinorLocator(Locator): Place evenly spaced minor ticks, with the step size and maximum number of ticks chosen automatically. - The Axis scale must be linear with evenly spaced major ticks . + The Axis must use a linear scale and have evenly spaced major ticks. """ def __init__(self, n=None): """ - *n* is the number of subdivisions of the interval between - major ticks; e.g., n=2 will place a single minor tick midway - between major ticks. - - If *n* is omitted or None, the value stored in rcParams will be used. - In case *n* is set to 'auto', it will be set to 4 or 5. If the distance - between the major ticks equals 1, 2.5, 5 or 10 it can be perfectly - divided in 5 equidistant sub-intervals with a length multiple of - 0.05. Otherwise it is divided in 4 sub-intervals. + Parameters + ---------- + n : int or 'auto', default: :rc:`xtick.minor.ndivs` or :rc:`ytick.minor.ndivs` + The number of subdivisions of the interval between major ticks; + e.g., n=2 will place a single minor tick midway between major ticks. + + If *n* is 'auto', it will be set to 4 or 5: if the distance + between the major ticks equals 1, 2.5, 5 or 10 it can be perfectly + divided in 5 equidistant sub-intervals with a length multiple of + 0.05; otherwise, it is divided in 4 sub-intervals. """ self.ndivs = n diff --git a/lib/matplotlib/ticker.pyi b/lib/matplotlib/ticker.pyi index f026b4943c94..bed288658909 100644 --- a/lib/matplotlib/ticker.pyi +++ b/lib/matplotlib/ticker.pyi @@ -64,8 +64,16 @@ class ScalarFormatter(Formatter): useOffset: bool | float | None = ..., useMathText: bool | None = ..., useLocale: bool | None = ..., + *, + usetex: bool | None = ..., ) -> None: ... offset: float + def get_usetex(self) -> bool: ... + def set_usetex(self, val: bool) -> None: ... + @property + def usetex(self) -> bool: ... + @usetex.setter + def usetex(self, val: bool) -> None: ... def get_useOffset(self) -> bool: ... def set_useOffset(self, val: bool | float) -> None: ... @property @@ -125,7 +133,7 @@ class LogitFormatter(Formatter): def set_minor_number(self, minor_number: int) -> None: ... def format_data_short(self, value: float) -> str: ... -class EngFormatter(Formatter): +class EngFormatter(ScalarFormatter): ENG_PREFIXES: dict[int, str] unit: str places: int | None @@ -137,20 +145,9 @@ class EngFormatter(Formatter): sep: str = ..., *, usetex: bool | None = ..., - useMathText: bool | None = ... + useMathText: bool | None = ..., + useOffset: bool | float | None = ..., ) -> None: ... - def get_usetex(self) -> bool: ... - def set_usetex(self, val: bool | None) -> None: ... - @property - def usetex(self) -> bool: ... - @usetex.setter - def usetex(self, val: bool | None) -> None: ... - def get_useMathText(self) -> bool: ... - def set_useMathText(self, val: bool | None) -> None: ... - @property - def useMathText(self) -> bool: ... - @useMathText.setter - def useMathText(self, val: bool | None) -> None: ... def format_eng(self, num: float) -> str: ... class PercentFormatter(Formatter): @@ -231,20 +228,19 @@ class MaxNLocator(Locator): def view_limits(self, dmin: float, dmax: float) -> tuple[float, float]: ... class LogLocator(Locator): - numdecs: float numticks: int | None def __init__( self, base: float = ..., subs: None | Literal["auto", "all"] | Sequence[float] = ..., - numdecs: float = ..., + *, numticks: int | None = ..., ) -> None: ... def set_params( self, base: float | None = ..., subs: Literal["auto", "all"] | Sequence[float] | None = ..., - numdecs: float | None = ..., + *, numticks: int | None = ..., ) -> None: ... @@ -299,3 +295,14 @@ class AutoLocator(MaxNLocator): class AutoMinorLocator(Locator): ndivs: int def __init__(self, n: int | None = ...) -> None: ... + +__all__ = ('TickHelper', 'Formatter', 'FixedFormatter', + 'NullFormatter', 'FuncFormatter', 'FormatStrFormatter', + 'StrMethodFormatter', 'ScalarFormatter', 'LogFormatter', + 'LogFormatterExponent', 'LogFormatterMathtext', + 'LogFormatterSciNotation', + 'LogitFormatter', 'EngFormatter', 'PercentFormatter', + 'Locator', 'IndexLocator', 'FixedLocator', 'NullLocator', + 'LinearLocator', 'LogLocator', 'AutoLocator', + 'MultipleLocator', 'MaxNLocator', 'AutoMinorLocator', + 'SymmetricalLogLocator', 'AsinhLocator', 'LogitLocator') diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 5003e2113930..a279de0dfd8b 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1,7 +1,6 @@ """ -Matplotlib includes a framework for arbitrary geometric -transformations that is used determine the final position of all -elements drawn on the canvas. +Matplotlib includes a framework for arbitrary geometric transformations that is used to +determine the final position of all elements drawn on the canvas. Transforms are composed into trees of `TransformNode` objects whose actual value depends on their children. When the contents of @@ -11,10 +10,10 @@ unnecessary recomputations of transforms, and contributes to better interactive performance. -For example, here is a graph of the transform tree used to plot data -to the graph: +For example, here is a graph of the transform tree used to plot data to the figure: -.. image:: ../_static/transforms.png +.. graphviz:: /api/transforms.dot + :alt: Diagram of transform tree from data to figure coordinates. The framework can be used for both affine and non-affine transformations. However, for speed, we want to use the backend @@ -36,8 +35,8 @@ # `np.minimum` instead of the builtin `min`, and likewise for `max`. This is # done so that `nan`s are propagated, instead of being silently dropped. -import copy import functools +import itertools import textwrap import weakref import math @@ -45,9 +44,8 @@ import numpy as np from numpy.linalg import inv -from matplotlib import _api -from matplotlib._path import ( - affine_transform, count_bboxes_overlapping_bbox, update_path_extents) +from matplotlib import _api, _docstring +from matplotlib._path import affine_transform, count_bboxes_overlapping_bbox from .path import Path DEBUG = False @@ -92,9 +90,6 @@ class TransformNode: # Invalidation may affect only the affine part. If the # invalidation was "affine-only", the _invalid member is set to # INVALID_AFFINE_ONLY - INVALID_NON_AFFINE = _api.deprecated("3.8")(_api.classproperty(lambda cls: 1)) - INVALID_AFFINE = _api.deprecated("3.8")(_api.classproperty(lambda cls: 2)) - INVALID = _api.deprecated("3.8")(_api.classproperty(lambda cls: 3)) # Possible values for the _invalid attribute. _VALID, _INVALID_AFFINE_ONLY, _INVALID_FULL = range(3) @@ -102,7 +97,6 @@ class TransformNode: # Some metadata about the transform, used to determine whether an # invalidation is affine-only is_affine = False - is_bbox = _api.deprecated("3.9")(_api.classproperty(lambda cls: False)) pass_through = False """ @@ -144,7 +138,9 @@ def __setstate__(self, data_dict): for k, v in self._parents.items() if v is not None} def __copy__(self): - other = copy.copy(super()) + cls = type(self) + other = cls.__new__(cls) + other.__dict__.update(self.__dict__) # If `c = a + b; a1 = copy(a)`, then modifications to `a1` do not # propagate back to `c`, i.e. we need to clear the parents of `a1`. other._parents = {} @@ -220,7 +216,6 @@ class BboxBase(TransformNode): and height, but these are not stored explicitly. """ - is_bbox = _api.deprecated("3.9")(_api.classproperty(lambda cls: True)) is_affine = True if DEBUG: @@ -245,7 +240,7 @@ def x0(self): The first of the pair of *x* coordinates that define the bounding box. This is not guaranteed to be less than :attr:`x1` (for that, use - :attr:`xmin`). + :attr:`~BboxBase.xmin`). """ return self.get_points()[0, 0] @@ -255,7 +250,7 @@ def y0(self): The first of the pair of *y* coordinates that define the bounding box. This is not guaranteed to be less than :attr:`y1` (for that, use - :attr:`ymin`). + :attr:`~BboxBase.ymin`). """ return self.get_points()[0, 1] @@ -265,7 +260,7 @@ def x1(self): The second of the pair of *x* coordinates that define the bounding box. This is not guaranteed to be greater than :attr:`x0` (for that, use - :attr:`xmax`). + :attr:`~BboxBase.xmax`). """ return self.get_points()[1, 0] @@ -275,7 +270,7 @@ def y1(self): The second of the pair of *y* coordinates that define the bounding box. This is not guaranteed to be greater than :attr:`y0` (for that, use - :attr:`ymax`). + :attr:`~BboxBase.ymax`). """ return self.get_points()[1, 1] @@ -285,7 +280,7 @@ def p0(self): The first pair of (*x*, *y*) coordinates that define the bounding box. This is not guaranteed to be the bottom-left corner (for that, use - :attr:`min`). + :attr:`~BboxBase.min`). """ return self.get_points()[0] @@ -295,7 +290,7 @@ def p1(self): The second pair of (*x*, *y*) coordinates that define the bounding box. This is not guaranteed to be the top-right corner (for that, use - :attr:`max`). + :attr:`~BboxBase.max`). """ return self.get_points()[1] @@ -367,7 +362,10 @@ def size(self): @property def bounds(self): - """Return (:attr:`x0`, :attr:`y0`, :attr:`width`, :attr:`height`).""" + """ + Return (:attr:`x0`, :attr:`y0`, :attr:`~BboxBase.width`, + :attr:`~BboxBase.height`). + """ (x0, y0), (x1, y1) = self.get_points() return (x0, y0, x1 - x0, y1 - y0) @@ -379,6 +377,29 @@ def extents(self): def get_points(self): raise NotImplementedError + def _is_finite(self): + """ + Return whether the bounding box is finite and not degenerate to a + single point. + + We count the box as finite if neither width nor height are infinite + and at least one direction is non-zero; i.e. a point is not finite, + but a horizontal or vertical line is. + + .. versionadded:: 3.11 + + Notes + ----- + We keep this private for now because concise naming is hard and + because we are not sure how universal the concept is. It is + currently used only for filtering bboxes to be included in + tightbbox calculation, but I'm unsure whether single points + should be included there as well. + """ + width = self.width + height = self.height + return (width > 0 or height > 0) and width < np.inf and height < np.inf + def containsx(self, x): """ Return whether *x* is in the closed (:attr:`x0`, :attr:`x1`) interval. @@ -479,7 +500,7 @@ def transformed(self, transform): 'NW': (0, 1.0), 'W': (0, 0.5)} - def anchored(self, c, container=None): + def anchored(self, c, container): """ Return a copy of the `Bbox` anchored to *c* within *container*. @@ -489,19 +510,13 @@ def anchored(self, c, container=None): Either an (*x*, *y*) pair of relative coordinates (0 is left or bottom, 1 is right or top), 'C' (center), or a cardinal direction ('SW', southwest, is bottom left, etc.). - container : `Bbox`, optional + container : `Bbox` The box within which the `Bbox` is positioned. See Also -------- .Axes.set_anchor """ - if container is None: - _api.warn_deprecated( - "3.8", message="Calling anchored() with no container bbox " - "returns a frozen copy of the original bbox and is deprecated " - "since %(since)s.") - container = self l, b, w, h = container.bounds L, B, W, H = self.bounds cx, cy = self.coefs[c] if isinstance(c, str) else c @@ -553,7 +568,7 @@ def splitx(self, *args): x0, y0, x1, y1 = self.extents w = x1 - x0 return [Bbox([[x0 + xf0 * w, y0], [x0 + xf1 * w, y1]]) - for xf0, xf1 in zip(xf[:-1], xf[1:])] + for xf0, xf1 in itertools.pairwise(xf)] def splity(self, *args): """ @@ -564,7 +579,7 @@ def splity(self, *args): x0, y0, x1, y1 = self.extents h = y1 - y0 return [Bbox([[x0, y0 + yf0 * h], [x1, y0 + yf1 * h]]) - for yf0, yf1 in zip(yf[:-1], yf[1:])] + for yf0, yf1 in itertools.pairwise(yf)] def count_contains(self, vertices): """ @@ -605,7 +620,6 @@ def expanded(self, sw, sh): a = np.array([[-deltaw, -deltah], [deltaw, deltah]]) return Bbox(self._points + a) - @_api.rename_parameter("3.8", "p", "w_pad") def padded(self, w_pad, h_pad=None): """ Construct a `Bbox` by padding this one on all four sides. @@ -827,7 +841,7 @@ def from_extents(*args, minpos=None): set. This is useful when dealing with logarithmic scales and other scales where negative bounds result in floating point errors. """ - bbox = Bbox(np.reshape(args, (2, 2))) + bbox = Bbox(np.asarray(args, dtype=float).reshape((2, 2))) if minpos is not None: bbox._minpos[:] = minpos return bbox @@ -875,13 +889,39 @@ def update_from_path(self, path, ignore=None, updatex=True, updatey=True): if ignore is None: ignore = self._ignore - if path.vertices.size == 0: + if path.vertices.size == 0 or not (updatex or updatey): return - points, minpos, changed = update_path_extents( - path, None, self._points, self._minpos, ignore) + if ignore: + points = np.array([[np.inf, np.inf], [-np.inf, -np.inf]]) + minpos = np.array([np.inf, np.inf]) + else: + points = self._points.copy() + minpos = self._minpos.copy() + + valid_points = (np.isfinite(path.vertices[..., 0]) + & np.isfinite(path.vertices[..., 1])) + + if updatex: + x = path.vertices[..., 0][valid_points] + minx = np.min(x, initial=np.inf) + points[0, 0] = min(points[0, 0], minx) + points[1, 0] = max(points[1, 0], np.max(x, initial=-np.inf)) + if minx > 0: # Fast path for all-positive x values + minpos[0] = min(minpos[0], minx) + else: + minpos[0] = min(minpos[0], np.min(x[x > 0], initial=np.inf)) + if updatey: + y = path.vertices[..., 1][valid_points] + miny = np.min(y, initial=np.inf) + points[0, 1] = min(points[0, 1], miny) + points[1, 1] = max(points[1, 1], np.max(y, initial=-np.inf)) + if miny > 0: # Fast path for all-positive y values + minpos[1] = min(minpos[1], miny) + else: + minpos[1] = min(minpos[1], np.min(y[y > 0], initial=np.inf)) - if changed: + if np.any(points != self._points) or np.any(minpos != self._minpos): self.invalidate() if updatex: self._points[:, 0] = points[:, 0] @@ -906,8 +946,9 @@ def update_from_data_x(self, x, ignore=None): - When ``None``, use the last value passed to :meth:`ignore`. """ x = np.ravel(x) - self.update_from_data_xy(np.column_stack([x, np.ones(x.size)]), - ignore=ignore, updatey=False) + # The y-component in np.array([x, *y*]).T is not used. We simply pass + # x again to not spend extra time on creating an array of unused data + self.update_from_data_xy(np.array([x, x]).T, ignore=ignore, updatey=False) def update_from_data_y(self, y, ignore=None): """ @@ -925,8 +966,9 @@ def update_from_data_y(self, y, ignore=None): - When ``None``, use the last value passed to :meth:`ignore`. """ y = np.ravel(y) - self.update_from_data_xy(np.column_stack([np.ones(y.size), y]), - ignore=ignore, updatex=False) + # The x-component in np.array([*x*, y]).T is not used. We simply pass + # y again to not spend extra time on creating an array of unused data + self.update_from_data_xy(np.array([y, y]).T, ignore=ignore, updatex=False) def update_from_data_xy(self, xy, ignore=None, updatex=True, updatey=True): """ @@ -1407,7 +1449,7 @@ def contains_branch(self, other): return True return False - def contains_branch_seperately(self, other_transform): + def contains_branch_separately(self, other_transform): """ Return whether the given branch is a sub-tree of this transform on each separate dimension. @@ -1415,15 +1457,20 @@ def contains_branch_seperately(self, other_transform): A common use for this method is to identify if a transform is a blended transform containing an Axes' data transform. e.g.:: - x_isdata, y_isdata = trans.contains_branch_seperately(ax.transData) + x_isdata, y_isdata = trans.contains_branch_separately(ax.transData) """ if self.output_dims != 2: - raise ValueError('contains_branch_seperately only supports ' + raise ValueError('contains_branch_separately only supports ' 'transforms with 2 output dimensions') # for a non-blended transform each separate dimension is the same, so # just return the appropriate shape. - return [self.contains_branch(other_transform)] * 2 + return (self.contains_branch(other_transform), ) * 2 + + # Permanent alias for backwards compatibility (historical typo) + def contains_branch_seperately(self, other_transform): + """:meta private:""" + return self.contains_branch_separately(other_transform) def __sub__(self, other): """ @@ -1486,14 +1533,14 @@ def transform(self, values): Parameters ---------- values : array-like - The input values as an array of length :attr:`input_dims` or - shape (N, :attr:`input_dims`). + The input values as an array of length :attr:`~Transform.input_dims` or + shape (N, :attr:`~Transform.input_dims`). Returns ------- array - The output values as an array of length :attr:`output_dims` or - shape (N, :attr:`output_dims`), depending on the input. + The output values as an array of length :attr:`~Transform.output_dims` or + shape (N, :attr:`~Transform.output_dims`), depending on the input. """ # Ensure that values is a 2d array (but remember whether # we started with a 1d or 2d array). @@ -1531,14 +1578,14 @@ def transform_affine(self, values): Parameters ---------- values : array - The input values as an array of length :attr:`input_dims` or - shape (N, :attr:`input_dims`). + The input values as an array of length :attr:`~Transform.input_dims` or + shape (N, :attr:`~Transform.input_dims`). Returns ------- array - The output values as an array of length :attr:`output_dims` or - shape (N, :attr:`output_dims`), depending on the input. + The output values as an array of length :attr:`~Transform.output_dims` or + shape (N, :attr:`~Transform.output_dims`), depending on the input. """ return self.get_affine().transform(values) @@ -1556,14 +1603,17 @@ def transform_non_affine(self, values): Parameters ---------- values : array - The input values as an array of length :attr:`input_dims` or - shape (N, :attr:`input_dims`). + The input values as an array of length + :attr:`~matplotlib.transforms.Transform.input_dims` or + shape (N, :attr:`~matplotlib.transforms.Transform.input_dims`). Returns ------- array - The output values as an array of length :attr:`output_dims` or - shape (N, :attr:`output_dims`), depending on the input. + The output values as an array of length + :attr:`~matplotlib.transforms.Transform.output_dims` or shape + (N, :attr:`~matplotlib.transforms.Transform.output_dims`), + depending on the input. """ return values @@ -1798,7 +1848,6 @@ def transform_affine(self, values): raise NotImplementedError('Affine subclasses should override this ' 'method.') - @_api.rename_parameter("3.8", "points", "values") def transform_non_affine(self, values): # docstring inherited return values @@ -1856,7 +1905,6 @@ def to_values(self): mtx = self.get_matrix() return tuple(mtx[:2].swapaxes(0, 1).flat) - @_api.rename_parameter("3.8", "points", "values") def transform_affine(self, values): mtx = self.get_matrix() if isinstance(values, np.ma.MaskedArray): @@ -1867,7 +1915,6 @@ def transform_affine(self, values): if DEBUG: _transform_affine = transform_affine - @_api.rename_parameter("3.8", "points", "values") def transform_affine(self, values): # docstring inherited # The major speed trap here is just converting to the @@ -2130,17 +2177,14 @@ def get_matrix(self): # docstring inherited return self._mtx - @_api.rename_parameter("3.8", "points", "values") def transform(self, values): # docstring inherited return np.asanyarray(values) - @_api.rename_parameter("3.8", "points", "values") def transform_affine(self, values): # docstring inherited return np.asanyarray(values) - @_api.rename_parameter("3.8", "points", "values") def transform_non_affine(self, values): # docstring inherited return np.asanyarray(values) @@ -2177,7 +2221,7 @@ def __eq__(self, other): else: return NotImplemented - def contains_branch_seperately(self, transform): + def contains_branch_separately(self, transform): return (self._x.contains_branch(transform), self._y.contains_branch(transform)) @@ -2229,7 +2273,6 @@ def frozen(self): # docstring inherited return blended_transform_factory(self._x.frozen(), self._y.frozen()) - @_api.rename_parameter("3.8", "points", "values") def transform_non_affine(self, values): # docstring inherited if self._x.is_affine and self._y.is_affine: @@ -2404,6 +2447,15 @@ def _iter_break_from_left_to_right(self): for left, right in self._b._iter_break_from_left_to_right(): yield self._a + left, right + def contains_branch_separately(self, other_transform): + # docstring inherited + if self.output_dims != 2: + raise ValueError('contains_branch_separately only supports ' + 'transforms with 2 output dimensions') + if self == other_transform: + return (True, True) + return self._b.contains_branch_separately(other_transform) + depth = property(lambda self: self._a.depth + self._b.depth) is_affine = property(lambda self: self._a.is_affine and self._b.is_affine) is_separable = property( @@ -2413,12 +2465,10 @@ def _iter_break_from_left_to_right(self): __str__ = _make_str_method("_a", "_b") - @_api.rename_parameter("3.8", "points", "values") def transform_affine(self, values): # docstring inherited return self.get_affine().transform(values) - @_api.rename_parameter("3.8", "points", "values") def transform_non_affine(self, values): # docstring inherited if self._a.is_affine and self._b.is_affine: @@ -2565,9 +2615,9 @@ def get_matrix(self): if DEBUG and (x_scale == 0 or y_scale == 0): raise ValueError( "Transforming from or to a singular bounding box") - self._mtx = np.array([[x_scale, 0.0 , (-inl*x_scale+outl)], - [0.0 , y_scale, (-inb*y_scale+outb)], - [0.0 , 0.0 , 1.0 ]], + self._mtx = np.array([[x_scale, 0.0, -inl*x_scale+outl], + [ 0.0, y_scale, -inb*y_scale+outb], + [ 0.0, 0.0, 1.0]], float) self._inverted = None self._invalid = 0 @@ -2612,27 +2662,6 @@ def get_matrix(self): return self._mtx -@_api.deprecated("3.9") -class BboxTransformToMaxOnly(BboxTransformTo): - """ - `BboxTransformToMaxOnly` is a transformation that linearly transforms points from - the unit bounding box to a given `Bbox` with a fixed upper left of (0, 0). - """ - def get_matrix(self): - # docstring inherited - if self._invalid: - xmax, ymax = self._boxout.max - if DEBUG and (xmax == 0 or ymax == 0): - raise ValueError("Transforming to a singular bounding box.") - self._mtx = np.array([[xmax, 0.0, 0.0], - [ 0.0, ymax, 0.0], - [ 0.0, 0.0, 1.0]], - float) - self._inverted = None - self._invalid = 0 - return self._mtx - - class BboxTransformFrom(Affine2DBase): """ `BboxTransformFrom` linearly transforms points from a given `Bbox` to the @@ -2659,9 +2688,9 @@ def get_matrix(self): raise ValueError("Transforming from a singular bounding box.") x_scale = 1.0 / inw y_scale = 1.0 / inh - self._mtx = np.array([[x_scale, 0.0 , (-inl*x_scale)], - [0.0 , y_scale, (-inb*y_scale)], - [0.0 , 0.0 , 1.0 ]], + self._mtx = np.array([[x_scale, 0.0, -inl*x_scale], + [ 0.0, y_scale, -inb*y_scale], + [ 0.0, 0.0, 1.0]], float) self._inverted = None self._invalid = 0 @@ -2694,6 +2723,25 @@ def get_matrix(self): return self._mtx +class _ScaledRotation(Affine2DBase): + """ + A transformation that applies rotation by *theta*, after transform by *trans_shift*. + """ + def __init__(self, theta, trans_shift): + super().__init__() + self._theta = theta + self._trans_shift = trans_shift + self._mtx = None + + def get_matrix(self): + if self._invalid: + transformed_coords = self._trans_shift.transform([[self._theta, 0]])[0] + adjusted_theta = transformed_coords[0] + rotation = Affine2D().rotate(adjusted_theta) + self._mtx = rotation.get_matrix() + return self._mtx + + class AffineDeltaTransform(Affine2DBase): r""" A transform wrapper for transforming displacements between pairs of points. @@ -2711,9 +2759,12 @@ class AffineDeltaTransform(Affine2DBase): This class is experimental as of 3.3, and the API may change. """ + pass_through = True + def __init__(self, transform, **kwargs): super().__init__(**kwargs) self._base_transform = transform + self.set_children(transform) __str__ = _make_str_method("_base_transform") @@ -2822,7 +2873,7 @@ def _revalidate(self): super()._revalidate() -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): +def _nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): """ Modify the endpoints of a range as needed to avoid singularities. @@ -2880,7 +2931,13 @@ def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): return vmin, vmax -def interval_contains(interval, val): +@_api.deprecated("3.11") +@_docstring.copy(_nonsingular) +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + return _nonsingular(vmin, vmax, expander, tiny, increasing) + + +def _interval_contains(interval, val): """ Check, inclusively, whether an interval includes a given value. @@ -2902,6 +2959,12 @@ def interval_contains(interval, val): return a <= val <= b +@_api.deprecated("3.11") +@_docstring.copy(_interval_contains) +def interval_contains(interval, val): + return _interval_contains(interval, val) + + def _interval_contains_close(interval, val, rtol=1e-10): """ Check, inclusively, whether an interval includes a given value, with the @@ -2931,7 +2994,7 @@ def _interval_contains_close(interval, val, rtol=1e-10): return a - rtol <= val <= b + rtol -def interval_contains_open(interval, val): +def _interval_contains_open(interval, val): """ Check, excluding endpoints, whether an interval includes a given value. @@ -2951,6 +3014,12 @@ def interval_contains_open(interval, val): return a < val < b or a > val > b +@_api.deprecated("3.11") +@_docstring.copy(_interval_contains_open) +def interval_contains_open(interval, val): + return _interval_contains_open(interval, val) + + def offset_copy(trans, fig=None, x=0.0, y=0.0, units='inches'): """ Return a new transform with an added offset. diff --git a/lib/matplotlib/transforms.pyi b/lib/matplotlib/transforms.pyi index 90a527e5bfc5..ebee3954a3a7 100644 --- a/lib/matplotlib/transforms.pyi +++ b/lib/matplotlib/transforms.pyi @@ -12,7 +12,6 @@ class TransformNode: INVALID_NON_AFFINE: int INVALID_AFFINE: int INVALID: int - is_bbox: bool # Implemented as a standard attr in base class, but functionally readonly and some subclasses implement as such @property def is_affine(self) -> bool: ... @@ -24,7 +23,6 @@ class TransformNode: def frozen(self) -> TransformNode: ... class BboxBase(TransformNode): - is_bbox: bool is_affine: bool def frozen(self) -> Bbox: ... def __array__(self, *args, **kwargs): ... @@ -67,6 +65,7 @@ class BboxBase(TransformNode): @property def extents(self) -> tuple[float, float, float, float]: ... def get_points(self) -> np.ndarray: ... + def _is_finite(self) -> bool: ... def containsx(self, x: float) -> bool: ... def containsy(self, y: float) -> bool: ... def contains(self, x: float, y: float) -> bool: ... @@ -77,9 +76,10 @@ class BboxBase(TransformNode): def fully_overlaps(self, other: BboxBase) -> bool: ... def transformed(self, transform: Transform) -> Bbox: ... coefs: dict[str, tuple[float, float]] - # anchored type can be s/str/Literal["C", "SW", "S", "SE", "E", "NE", "N", "NW", "W"] def anchored( - self, c: tuple[float, float] | str, container: BboxBase | None = ... + self, + c: tuple[float, float] | Literal['C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'], + container: BboxBase, ) -> Bbox: ... def shrunk(self, mx: float, my: float) -> Bbox: ... def shrunk_to_aspect( @@ -190,9 +190,10 @@ class Transform(TransformNode): @property def depth(self) -> int: ... def contains_branch(self, other: Transform) -> bool: ... - def contains_branch_seperately( + def contains_branch_separately( self, other_transform: Transform ) -> Sequence[bool]: ... + contains_branch_seperately = contains_branch_separately # Alias (historical typo) def __sub__(self, other: Transform) -> Transform: ... def __array__(self, *args, **kwargs) -> np.ndarray: ... def transform(self, values: ArrayLike) -> np.ndarray: ... @@ -253,7 +254,7 @@ class IdentityTransform(Affine2DBase): ... class _BlendedMixin: def __eq__(self, other: object) -> bool: ... - def contains_branch_seperately(self, transform: Transform) -> Sequence[bool]: ... + def contains_branch_separately(self, transform: Transform) -> Sequence[bool]: ... class BlendedGenericTransform(_BlendedMixin, Transform): input_dims: Literal[2] @@ -294,8 +295,6 @@ class BboxTransform(Affine2DBase): class BboxTransformTo(Affine2DBase): def __init__(self, boxout: BboxBase, **kwargs) -> None: ... -class BboxTransformToMaxOnly(BboxTransformTo): ... - class BboxTransformFrom(Affine2DBase): def __init__(self, boxin: BboxBase, **kwargs) -> None: ... @@ -317,6 +316,13 @@ class TransformedPath(TransformNode): class TransformedPatchPath(TransformedPath): def __init__(self, patch: Patch) -> None: ... +def _nonsingular( + vmin: float, + vmax: float, + expander: float = ..., + tiny: float = ..., + increasing: bool = ..., +) -> tuple[float, float]: ... def nonsingular( vmin: float, vmax: float, @@ -324,7 +330,9 @@ def nonsingular( tiny: float = ..., increasing: bool = ..., ) -> tuple[float, float]: ... +def _interval_contains(interval: tuple[float, float], val: float) -> bool: ... def interval_contains(interval: tuple[float, float], val: float) -> bool: ... +def _interval_contains_open(interval: tuple[float, float], val: float) -> bool: ... def interval_contains_open(interval: tuple[float, float], val: float) -> bool: ... def offset_copy( trans: Transform, @@ -333,3 +341,8 @@ def offset_copy( y: float = ..., units: Literal["inches", "points", "dots"] = ..., ) -> Transform: ... + + +class _ScaledRotation(Affine2DBase): + def __init__(self, theta: float, trans_shift: Transform) -> None: ... + def get_matrix(self) -> np.ndarray: ... diff --git a/lib/matplotlib/tri/_tricontour.py b/lib/matplotlib/tri/_tricontour.py index 1db3715d01af..8250515f3ef8 100644 --- a/lib/matplotlib/tri/_tricontour.py +++ b/lib/matplotlib/tri/_tricontour.py @@ -5,7 +5,7 @@ from matplotlib.tri._triangulation import Triangulation -@_docstring.dedent_interpd +@_docstring.interpd class TriContourSet(ContourSet): """ Create and store a set of contour lines or filled regions for @@ -79,7 +79,7 @@ def _contour_args(self, args, kwargs): return (tri, z) -_docstring.interpd.update(_tricontour_doc=""" +_docstring.interpd.register(_tricontour_doc=""" Draw contour %%(type)s on an unstructured triangular grid. Call signatures:: @@ -218,7 +218,7 @@ def _contour_args(self, args, kwargs): @_docstring.Substitution(func='tricontour', type='lines') -@_docstring.dedent_interpd +@_docstring.interpd def tricontour(ax, *args, **kwargs): """ %(_tricontour_doc)s @@ -247,7 +247,7 @@ def tricontour(ax, *args, **kwargs): @_docstring.Substitution(func='tricontourf', type='regions') -@_docstring.dedent_interpd +@_docstring.interpd def tricontourf(ax, *args, **kwargs): """ %(_tricontour_doc)s diff --git a/lib/matplotlib/tri/_triinterpolate.py b/lib/matplotlib/tri/_triinterpolate.py index 90ad6cf3a76c..2dc62770c7ed 100644 --- a/lib/matplotlib/tri/_triinterpolate.py +++ b/lib/matplotlib/tri/_triinterpolate.py @@ -928,7 +928,7 @@ def get_Kff_and_Ff(self, J, ecc, triangles, Uc): Returns ------- - (Kff_rows, Kff_cols, Kff_vals) Kff matrix in coo format - Duplicate + (Kff_rows, Kff_cols, Kff_vals) Kff matrix in COO format - Duplicate (row, col) entries must be summed. Ff: force vector - dim npts * 3 """ @@ -961,12 +961,12 @@ def get_Kff_and_Ff(self, J, ecc, triangles, Uc): # [ Kcf Kff ] # * As F = K x U one gets straightforwardly: Ff = - Kfc x Uc - # Computing Kff stiffness matrix in sparse coo format + # Computing Kff stiffness matrix in sparse COO format Kff_vals = np.ravel(K_elem[np.ix_(vec_range, f_dof, f_dof)]) Kff_rows = np.ravel(f_row_indices[np.ix_(vec_range, f_dof, f_dof)]) Kff_cols = np.ravel(f_col_indices[np.ix_(vec_range, f_dof, f_dof)]) - # Computing Ff force vector in sparse coo format + # Computing Ff force vector in sparse COO format Kfc_elem = K_elem[np.ix_(vec_range, f_dof, c_dof)] Uc_elem = np.expand_dims(Uc, axis=2) Ff_elem = -(Kfc_elem @ Uc_elem)[:, :, 0] @@ -1178,7 +1178,7 @@ def compute_dz(self): triangles = self._triangles Uc = self.z[self._triangles] - # Building stiffness matrix and force vector in coo format + # Building stiffness matrix and force vector in COO format Kff_rows, Kff_cols, Kff_vals, Ff = reference_element.get_Kff_and_Ff( J, eccs, triangles, Uc) @@ -1215,7 +1215,7 @@ def compute_dz(self): class _Sparse_Matrix_coo: def __init__(self, vals, rows, cols, shape): """ - Create a sparse matrix in coo format. + Create a sparse matrix in COO format. *vals*: arrays of values of non-null entries of the matrix *rows*: int arrays of rows of non-null entries of the matrix *cols*: int arrays of cols of non-null entries of the matrix diff --git a/lib/matplotlib/tri/_triinterpolate.pyi b/lib/matplotlib/tri/_triinterpolate.pyi index 8a56b22acdb2..33b2fd8be4cd 100644 --- a/lib/matplotlib/tri/_triinterpolate.pyi +++ b/lib/matplotlib/tri/_triinterpolate.pyi @@ -28,3 +28,5 @@ class CubicTriInterpolator(TriInterpolator): trifinder: TriFinder | None = ..., dz: tuple[ArrayLike, ArrayLike] | None = ..., ) -> None: ... + +__all__ = ('TriInterpolator', 'LinearTriInterpolator', 'CubicTriInterpolator') diff --git a/lib/matplotlib/tri/_tripcolor.py b/lib/matplotlib/tri/_tripcolor.py index 1ac6c48a2d7c..5a5b24522d17 100644 --- a/lib/matplotlib/tri/_tripcolor.py +++ b/lib/matplotlib/tri/_tripcolor.py @@ -1,10 +1,11 @@ import numpy as np -from matplotlib import _api +from matplotlib import _api, _docstring from matplotlib.collections import PolyCollection, TriMesh from matplotlib.tri._triangulation import Triangulation +@_docstring.interpd def tripcolor(ax, *args, alpha=1.0, norm=None, cmap=None, vmin=None, vmax=None, shading='flat', facecolors=None, **kwargs): """ @@ -54,8 +55,25 @@ def tripcolor(ax, *args, alpha=1.0, norm=None, cmap=None, vmin=None, values used for each triangle are from the mean c of the triangle's three points. If *shading* is 'gouraud' then color values must be defined at points. - other_parameters - All other parameters are the same as for `~.Axes.pcolor`. + %(cmap_doc)s + + %(norm_doc)s + + %(vmin_vmax_doc)s + + %(colorizer_doc)s + + Returns + ------- + `~matplotlib.collections.PolyCollection` or `~matplotlib.collections.TriMesh` + The result depends on *shading*: For ``shading='flat'`` the result is a + `.PolyCollection`, for ``shading='gouraud'`` the result is a `.TriMesh`. + + Other Parameters + ---------------- + **kwargs : `~matplotlib.collections.Collection` properties + + %(Collection:kwdoc)s """ _api.check_in_list(['flat', 'gouraud'], shading=shading) @@ -145,5 +163,7 @@ def tripcolor(ax, *args, alpha=1.0, norm=None, cmap=None, vmin=None, corners = (minx, miny), (maxx, maxy) ax.update_datalim(corners) ax.autoscale_view() - ax.add_collection(collection) + # TODO: check whether the above explicit limit handling can be + # replaced by autolim=True + ax.add_collection(collection, autolim=False) return collection diff --git a/lib/matplotlib/tri/_trirefine.py b/lib/matplotlib/tri/_trirefine.py index 7f5110ab9e21..6a7037ad74fd 100644 --- a/lib/matplotlib/tri/_trirefine.py +++ b/lib/matplotlib/tri/_trirefine.py @@ -64,7 +64,7 @@ def __init__(self, triangulation): def refine_triangulation(self, return_tri_index=False, subdiv=3): """ Compute a uniformly refined triangulation *refi_triangulation* of - the encapsulated :attr:`triangulation`. + the encapsulated :attr:`!triangulation`. This function refines the encapsulated triangulation by splitting each father triangle into 4 child sub-triangles built on the edges midside diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py index 02059be94ba2..e194874e13ff 100644 --- a/lib/matplotlib/typing.py +++ b/lib/matplotlib/typing.py @@ -4,57 +4,576 @@ This module contains Type aliases which are useful for Matplotlib and potentially downstream libraries. -.. admonition:: Provisional status of typing +.. warning:: + **Provisional status of typing** The ``typing`` module and type stub files are considered provisional and may change at any time without a deprecation period. """ from collections.abc import Hashable, Sequence import pathlib -from typing import Any, Literal, TypeVar, Union +from typing import Any, Literal, TypeAlias, TypeVar, Union +from collections.abc import Callable from . import path from ._enums import JoinStyle, CapStyle +from .artist import Artist +from .backend_bases import RendererBase from .markers import MarkerStyle +from .transforms import Bbox, Transform -# The following are type aliases. Once python 3.9 is dropped, they should be annotated -# using ``typing.TypeAlias`` and Unions should be converted to using ``|`` syntax. +RGBColorType: TypeAlias = tuple[float, float, float] | str +"""Any RGB color specification accepted by Matplotlib.""" -RGBColorType = Union[tuple[float, float, float], str] -RGBAColorType = Union[ - str, # "none" or "#RRGGBBAA"/"#RGBA" hex strings - tuple[float, float, float, float], +RGBAColorType: TypeAlias = ( + str | # "none" or "#RRGGBBAA"/"#RGBA" hex strings + tuple[float, float, float, float] | # 2 tuple (color, alpha) representations, not infinitely recursive # RGBColorType includes the (str, float) tuple, even for RGBA strings - tuple[RGBColorType, float], + tuple[RGBColorType, float] | # (4-tuple, float) is odd, but accepted as the outer float overriding A of 4-tuple - tuple[tuple[float, float, float, float], float], -] + tuple[tuple[float, float, float, float], float] +) +"""Any RGBA color specification accepted by Matplotlib.""" -ColorType = Union[RGBColorType, RGBAColorType] +ColorType: TypeAlias = RGBColorType | RGBAColorType +"""Any color specification accepted by Matplotlib. See :mpltype:`color`.""" -RGBColourType = RGBColorType -RGBAColourType = RGBAColorType -ColourType = ColorType +RGBColourType: TypeAlias = RGBColorType +"""Alias of `.RGBColorType`.""" -LineStyleType = Union[str, tuple[float, Sequence[float]]] -DrawStyleType = Literal["default", "steps", "steps-pre", "steps-mid", "steps-post"] -MarkEveryType = Union[ - None, int, tuple[int, int], slice, list[int], float, tuple[float, float], list[bool] -] +RGBAColourType: TypeAlias = RGBAColorType +"""Alias of `.RGBAColorType`.""" + +ColourType: TypeAlias = ColorType +"""Alias of `.ColorType`.""" + +LineStyleType: TypeAlias = ( + Literal["-", "solid", "--", "dashed", "-.", "dashdot", ":", "dotted", + "", "none", " ", "None"] | + tuple[float, Sequence[float]] +) +""" +Any line style specification accepted by Matplotlib. +See :doc:`/gallery/lines_bars_and_markers/linestyles`. +""" + +DrawStyleType: TypeAlias = Literal["default", "steps", "steps-pre", "steps-mid", + "steps-post"] +"""See :doc:`/gallery/lines_bars_and_markers/step_demo`.""" + +MarkEveryType: TypeAlias = ( + None | + int | tuple[int, int] | slice | list[int] | + float | tuple[float, float] | + list[bool] +) +"""See :doc:`/gallery/lines_bars_and_markers/markevery_demo`.""" + +MarkerType: TypeAlias = ( + path.Path | MarkerStyle | str | # str required for "$...$" marker + Literal[ + ".", ",", "o", "v", "^", "<", ">", + "1", "2", "3", "4", "8", "s", "p", + "P", "*", "h", "H", "+", "x", "X", + "D", "d", "|", "_", "none", " ", + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + ] | list[tuple[int, int]] | tuple[int, Literal[0, 1, 2], int] +) +""" +Marker specification. See :doc:`/gallery/lines_bars_and_markers/marker_reference`. +""" + +FillStyleType: TypeAlias = Literal["full", "left", "right", "bottom", "top", "none"] +"""Marker fill styles. See :doc:`/gallery/lines_bars_and_markers/marker_reference`.""" + +JoinStyleType: TypeAlias = JoinStyle | Literal["miter", "round", "bevel"] +"""Line join styles. See :doc:`/gallery/lines_bars_and_markers/joinstyle`.""" + +CapStyleType: TypeAlias = CapStyle | Literal["butt", "projecting", "round"] +"""Line cap styles. See :doc:`/gallery/lines_bars_and_markers/capstyle`.""" -MarkerType = Union[str, path.Path, MarkerStyle] -FillStyleType = Literal["full", "left", "right", "bottom", "top", "none"] -JoinStyleType = Union[JoinStyle, Literal["miter", "round", "bevel"]] -CapStyleType = Union[CapStyle, Literal["butt", "projecting", "round"]] +LogLevel: TypeAlias = Literal["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] +"""Literal type for valid logging levels accepted by `set_loglevel()`.""" -RcStyleType = Union[ +CoordsBaseType = Union[ str, - dict[str, Any], - pathlib.Path, - Sequence[Union[str, pathlib.Path, dict[str, Any]]], + Artist, + Transform, + Callable[ + [RendererBase], + Union[Bbox, Transform] + ] ] +CoordsType = Union[ + CoordsBaseType, + tuple[CoordsBaseType, CoordsBaseType] +] + +RcStyleType: TypeAlias = ( + str | + dict[str, Any] | + pathlib.Path | + Sequence[str | pathlib.Path | dict[str, Any]] +) _HT = TypeVar("_HT", bound=Hashable) -HashableList = list[Union[_HT, "HashableList[_HT]"]] +HashableList: TypeAlias = list[_HT | "HashableList[_HT]"] """A nested list of Hashable values.""" + +MouseEventType: TypeAlias = Literal[ + "button_press_event", + "button_release_event", + "motion_notify_event", + "scroll_event", + "figure_enter_event", + "figure_leave_event", + "axes_enter_event", + "axes_leave_event", +] + +KeyEventType: TypeAlias = Literal[ + "key_press_event", + "key_release_event" +] + +DrawEventType: TypeAlias = Literal["draw_event"] +PickEventType: TypeAlias = Literal["pick_event"] +ResizeEventType: TypeAlias = Literal["resize_event"] +CloseEventType: TypeAlias = Literal["close_event"] + +EventType: TypeAlias = Literal[ + MouseEventType, + KeyEventType, + DrawEventType, + PickEventType, + ResizeEventType, + CloseEventType, +] + +LegendLocType: TypeAlias = ( + Literal[ + # for simplicity, we don't distinguish the between allowed positions for + # Axes legend and figure legend. It's still better to limit the allowed + # range to the union of both rather than to accept arbitrary strings + "upper right", "upper left", "lower left", "lower right", + "right", "center left", "center right", "lower center", "upper center", + "center", + # Axes only + "best", + # Figure only + "outside upper left", "outside upper center", "outside upper right", + "outside right upper", "outside right center", "outside right lower", + "outside lower right", "outside lower center", "outside lower left", + "outside left lower", "outside left center", "outside left upper", + ] | + tuple[float, float] | + int +) + +RcKeyType: TypeAlias = Literal[ + "agg.path.chunksize", + "animation.bitrate", + "animation.codec", + "animation.convert_args", + "animation.convert_path", + "animation.embed_limit", + "animation.ffmpeg_args", + "animation.ffmpeg_path", + "animation.frame_format", + "animation.html", + "animation.writer", + "axes.autolimit_mode", + "axes.axisbelow", + "axes.edgecolor", + "axes.facecolor", + "axes.formatter.limits", + "axes.formatter.min_exponent", + "axes.formatter.offset_threshold", + "axes.formatter.use_locale", + "axes.formatter.use_mathtext", + "axes.formatter.useoffset", + "axes.grid", + "axes.grid.axis", + "axes.grid.which", + "axes.labelcolor", + "axes.labelpad", + "axes.labelsize", + "axes.labelweight", + "axes.linewidth", + "axes.prop_cycle", + "axes.spines.bottom", + "axes.spines.left", + "axes.spines.right", + "axes.spines.top", + "axes.titlecolor", + "axes.titlelocation", + "axes.titlepad", + "axes.titlesize", + "axes.titleweight", + "axes.titley", + "axes.unicode_minus", + "axes.xmargin", + "axes.ymargin", + "axes.zmargin", + "axes3d.automargin", + "axes3d.depthshade", + "axes3d.depthshade_minalpha", + "axes3d.grid", + "axes3d.mouserotationstyle", + "axes3d.trackballborder", + "axes3d.snap_rotation", + "axes3d.trackballsize", + "axes3d.xaxis.panecolor", + "axes3d.yaxis.panecolor", + "axes3d.zaxis.panecolor", + "backend", + "backend_fallback", + "boxplot.bootstrap", + "boxplot.boxprops.color", + "boxplot.boxprops.linestyle", + "boxplot.boxprops.linewidth", + "boxplot.capprops.color", + "boxplot.capprops.linestyle", + "boxplot.capprops.linewidth", + "boxplot.flierprops.color", + "boxplot.flierprops.linestyle", + "boxplot.flierprops.linewidth", + "boxplot.flierprops.marker", + "boxplot.flierprops.markeredgecolor", + "boxplot.flierprops.markeredgewidth", + "boxplot.flierprops.markerfacecolor", + "boxplot.flierprops.markersize", + "boxplot.meanline", + "boxplot.meanprops.color", + "boxplot.meanprops.linestyle", + "boxplot.meanprops.linewidth", + "boxplot.meanprops.marker", + "boxplot.meanprops.markeredgecolor", + "boxplot.meanprops.markerfacecolor", + "boxplot.meanprops.markersize", + "boxplot.medianprops.color", + "boxplot.medianprops.linestyle", + "boxplot.medianprops.linewidth", + "boxplot.notch", + "boxplot.patchartist", + "boxplot.showbox", + "boxplot.showcaps", + "boxplot.showfliers", + "boxplot.showmeans", + "boxplot.vertical", + "boxplot.whiskerprops.color", + "boxplot.whiskerprops.linestyle", + "boxplot.whiskerprops.linewidth", + "boxplot.whiskers", + "contour.algorithm", + "contour.corner_mask", + "contour.linewidth", + "contour.negative_linestyle", + "date.autoformatter.day", + "date.autoformatter.hour", + "date.autoformatter.microsecond", + "date.autoformatter.minute", + "date.autoformatter.month", + "date.autoformatter.second", + "date.autoformatter.year", + "date.converter", + "date.epoch", + "date.interval_multiples", + "docstring.hardcopy", + "errorbar.capsize", + "errorbar.capthick", + "errorbar.elinewidth", + "figure.autolayout", + "figure.constrained_layout.h_pad", + "figure.constrained_layout.hspace", + "figure.constrained_layout.use", + "figure.constrained_layout.w_pad", + "figure.constrained_layout.wspace", + "figure.dpi", + "figure.edgecolor", + "figure.facecolor", + "figure.figsize", + "figure.frameon", + "figure.hooks", + "figure.labelsize", + "figure.labelweight", + "figure.max_open_warning", + "figure.raise_window", + "figure.subplot.bottom", + "figure.subplot.hspace", + "figure.subplot.left", + "figure.subplot.right", + "figure.subplot.top", + "figure.subplot.wspace", + "figure.titlesize", + "figure.titleweight", + "font.cursive", + "font.enable_last_resort", + "font.family", + "font.fantasy", + "font.monospace", + "font.sans-serif", + "font.serif", + "font.size", + "font.stretch", + "font.style", + "font.variant", + "font.weight", + "grid.alpha", + "grid.color", + "grid.linestyle", + "grid.linewidth", + "grid.major.alpha", + "grid.major.color", + "grid.major.linestyle", + "grid.major.linewidth", + "grid.minor.alpha", + "grid.minor.color", + "grid.minor.linestyle", + "grid.minor.linewidth", + "hatch.color", + "hatch.linewidth", + "hist.bins", + "image.aspect", + "image.cmap", + "image.composite_image", + "image.interpolation", + "image.interpolation_stage", + "image.lut", + "image.origin", + "image.resample", + "interactive", + "keymap.back", + "keymap.copy", + "keymap.forward", + "keymap.fullscreen", + "keymap.grid", + "keymap.grid_minor", + "keymap.help", + "keymap.home", + "keymap.pan", + "keymap.quit", + "keymap.quit_all", + "keymap.save", + "keymap.xscale", + "keymap.yscale", + "keymap.zoom", + "legend.borderaxespad", + "legend.borderpad", + "legend.columnspacing", + "legend.edgecolor", + "legend.facecolor", + "legend.fancybox", + "legend.fontsize", + "legend.framealpha", + "legend.frameon", + "legend.handleheight", + "legend.handlelength", + "legend.handletextpad", + "legend.labelcolor", + "legend.labelspacing", + "legend.linewidth", + "legend.loc", + "legend.markerscale", + "legend.numpoints", + "legend.scatterpoints", + "legend.shadow", + "legend.title_fontsize", + "lines.antialiased", + "lines.color", + "lines.dash_capstyle", + "lines.dash_joinstyle", + "lines.dashdot_pattern", + "lines.dashed_pattern", + "lines.dotted_pattern", + "lines.linestyle", + "lines.linewidth", + "lines.marker", + "lines.markeredgecolor", + "lines.markeredgewidth", + "lines.markerfacecolor", + "lines.markersize", + "lines.scale_dashes", + "lines.solid_capstyle", + "lines.solid_joinstyle", + "macosx.window_mode", + "markers.fillstyle", + "mathtext.bf", + "mathtext.bfit", + "mathtext.cal", + "mathtext.default", + "mathtext.fallback", + "mathtext.fontset", + "mathtext.it", + "mathtext.rm", + "mathtext.sf", + "mathtext.tt", + "patch.antialiased", + "patch.edgecolor", + "patch.facecolor", + "patch.force_edgecolor", + "patch.linewidth", + "path.effects", + "path.simplify", + "path.simplify_threshold", + "path.sketch", + "path.snap", + "pcolor.shading", + "pcolormesh.snap", + "pdf.compression", + "pdf.fonttype", + "pdf.inheritcolor", + "pdf.use14corefonts", + "pgf.preamble", + "pgf.rcfonts", + "pgf.texsystem", + "polaraxes.grid", + "ps.distiller.res", + "ps.fonttype", + "ps.papersize", + "ps.useafm", + "ps.usedistiller", + "savefig.bbox", + "savefig.directory", + "savefig.dpi", + "savefig.edgecolor", + "savefig.facecolor", + "savefig.format", + "savefig.orientation", + "savefig.pad_inches", + "savefig.transparent", + "scatter.edgecolors", + "scatter.marker", + "svg.fonttype", + "svg.hashsalt", + "svg.id", + "svg.image_inline", + "text.antialiased", + "text.color", + "text.hinting", + "text.hinting_factor", + "text.kerning_factor", + "text.language", + "text.latex.engine", + "text.latex.preamble", + "text.parse_math", + "text.usetex", + "timezone", + "tk.window_focus", + "toolbar", + "webagg.address", + "webagg.open_in_browser", + "webagg.port", + "webagg.port_retries", + "xaxis.labellocation", + "xtick.alignment", + "xtick.bottom", + "xtick.color", + "xtick.direction", + "xtick.labelbottom", + "xtick.labelcolor", + "xtick.labelsize", + "xtick.labeltop", + "xtick.major.bottom", + "xtick.major.pad", + "xtick.major.size", + "xtick.major.top", + "xtick.major.width", + "xtick.minor.bottom", + "xtick.minor.ndivs", + "xtick.minor.pad", + "xtick.minor.size", + "xtick.minor.top", + "xtick.minor.visible", + "xtick.minor.width", + "xtick.top", + "yaxis.labellocation", + "ytick.alignment", + "ytick.color", + "ytick.direction", + "ytick.labelcolor", + "ytick.labelleft", + "ytick.labelright", + "ytick.labelsize", + "ytick.left", + "ytick.major.left", + "ytick.major.pad", + "ytick.major.right", + "ytick.major.size", + "ytick.major.width", + "ytick.minor.left", + "ytick.minor.ndivs", + "ytick.minor.pad", + "ytick.minor.right", + "ytick.minor.size", + "ytick.minor.visible", + "ytick.minor.width", + "ytick.right", +] + +RcGroupKeyType: TypeAlias = Literal[ + "agg", + "agg.path", + "animation", + "axes", + "axes.formatter", + "axes.grid", + "axes.spines", + "axes3d", + "axes3d.xaxis", + "axes3d.yaxis", + "axes3d.zaxis", + "boxplot", + "boxplot.boxprops", + "boxplot.capprops", + "boxplot.flierprops", + "boxplot.meanprops", + "boxplot.medianprops", + "boxplot.whiskerprops", + "contour", + "date", + "date.autoformatter", + "docstring", + "errorbar", + "figure", + "figure.constrained_layout", + "figure.subplot", + "font", + "grid", + "grid.major", + "grid.minor", + "hatch", + "hist", + "image", + "keymap", + "legend", + "lines", + "macosx", + "markers", + "mathtext", + "patch", + "path", + "pcolor", + "pcolormesh", + "pdf", + "pgf", + "polaraxes", + "ps", + "ps.distiller", + "savefig", + "scatter", + "svg", + "text", + "text.latex", + "tk", + "webagg", + "xaxis", + "xtick", + "xtick.major", + "xtick.minor", + "yaxis", + "ytick", + "ytick.major", + "ytick.minor", +] diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index eaa35e25440b..9ab4548157fd 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1,8 +1,6 @@ """ -GUI neutral widgets -=================== - Widgets that are designed to work for any of the GUI backends. + All of these widgets require you to predefine an `~.axes.Axes` instance and pass that as the first parameter. Matplotlib doesn't try to be too smart with respect to layout -- you will have to figure out how @@ -11,7 +9,10 @@ from contextlib import ExitStack import copy +import enum +import functools import itertools +import weakref from numbers import Integral, Number from cycler import cycler @@ -90,22 +91,6 @@ def ignore(self, event): """ return not self.active - def _changed_canvas(self): - """ - Someone has switched the canvas on us! - - This happens if `savefig` needs to save to a format the previous - backend did not support (e.g. saving a figure using an Agg based - backend saved to a vector format). - - Returns - ------- - bool - True if the canvas has been changed. - - """ - return self.canvas is not self.ax.figure.canvas - class AxesWidget(Widget): """ @@ -131,8 +116,12 @@ class AxesWidget(Widget): def __init__(self, ax): self.ax = ax - self.canvas = ax.figure.canvas self._cids = [] + self._blit_background_id = None + + canvas = property( + lambda self: getattr(self.ax.get_figure(root=True), 'canvas', None) + ) def connect_event(self, event, callback): """ @@ -149,15 +138,61 @@ def disconnect_events(self): for c in self._cids: self.canvas.mpl_disconnect(c) - def _get_data_coords(self, event): - """Return *event*'s data coordinates in this widget's Axes.""" - # This method handles the possibility that event.inaxes != self.ax (which may - # occur if multiple Axes are overlaid), in which case event.xdata/.ydata will - # be wrong. Note that we still special-case the common case where - # event.inaxes == self.ax and avoid re-running the inverse data transform, - # because that can introduce floating point errors for synthetic events. - return ((event.xdata, event.ydata) if event.inaxes is self.ax - else self.ax.transData.inverted().transform((event.x, event.y))) + def ignore(self, event): + # docstring inherited + return super().ignore(event) or self.canvas is None + + def _set_cursor(self, cursor): + """Update the canvas cursor.""" + self.ax.get_figure(root=True).canvas.set_cursor(cursor) + + def _save_blit_background(self, background): + """ + Save a blit background. + + The background is stored on the canvas in a uniquely identifiable way. + It should be read back via `._load_blit_background`. Be prepared that + some events may invalidate the background, in which case + `._load_blit_background` will return None. + + This currently allows at most one background per widget, which is + good enough for all existing widgets. + """ + if self._blit_background_id is None: + bbid = self.canvas._get_blit_background_id() + weakref.finalize(self, self.canvas._release_blit_background_id, bbid) + self._blit_background_id = bbid + self.canvas._blit_backgrounds[self._blit_background_id] = background + + def _load_blit_background(self): + """Load a blit background; may be None at any time.""" + return self.canvas._blit_backgrounds.get(self._blit_background_id) + + +def _call_with_reparented_event(func): + """ + Event callback decorator ensuring that the callback is called with an event + that has been reparented to the widget's axes. + """ + # This decorator handles the possibility that event.inaxes != self.ax + # (e.g. if multiple Axes are overlaid), in which case event.xdata/.ydata + # will be wrong. Note that we still special-case the common case where + # event.inaxes == self.ax and avoid re-running the inverse data transform, + # because that can introduce floating point errors for synthetic events. + @functools.wraps(func) + def wrapper(self, event): + if event.inaxes is not self.ax: + event = copy.copy(event) + event.guiEvent = None + event.inaxes = self.ax + try: + event.xdata, event.ydata = ( + self.ax.transData.inverted().transform((event.x, event.y))) + except ValueError: # cf LocationEvent._set_inaxes. + event.xdata = event.ydata = None + return func(self, event) + + return wrapper class Button(AxesWidget): @@ -210,7 +245,7 @@ def __init__(self, ax, label, image=None, horizontalalignment='center', transform=ax.transAxes) - self._useblit = useblit and self.canvas.supports_blit + self._useblit = useblit self._observers = cbook.CallbackRegistry(signals=["clicked"]) @@ -224,12 +259,14 @@ def __init__(self, ax, label, image=None, self.color = color self.hovercolor = hovercolor + @_call_with_reparented_event def _click(self, event): if not self.eventson or self.ignore(event) or not self.ax.contains(event)[0]: return if event.canvas.mouse_grabber != self.ax: event.canvas.grab_mouse(self.ax) + @_call_with_reparented_event def _release(self, event): if self.ignore(event) or event.canvas.mouse_grabber != self.ax: return @@ -237,6 +274,7 @@ def _release(self, event): if self.eventson and self.ax.contains(event)[0]: self._observers.process('clicked', event) + @_call_with_reparented_event def _motion(self, event): if self.ignore(event): return @@ -244,7 +282,7 @@ def _motion(self, event): if not colors.same_color(c, self.ax.get_facecolor()): self.ax.set_facecolor(c) if self.drawon: - if self._useblit: + if self._useblit and self.canvas.supports_blit: self.ax.draw_artist(self.ax) self.canvas.blit(self.ax.bbox) else: @@ -288,10 +326,10 @@ def __init__(self, ax, orientation, closedmin, closedmax, self.valfmt = valfmt if orientation == "vertical": - ax.set_ylim((valmin, valmax)) + ax.set_ylim(valmin, valmax) axis = ax.yaxis else: - ax.set_xlim((valmin, valmax)) + ax.set_xlim(valmin, valmax) axis = ax.xaxis self._fmt = axis.get_major_formatter() @@ -379,8 +417,9 @@ def __init__(self, ax, label, valmin, valmax, *, valinit=0.5, valfmt=None, The slider initial position. valfmt : str, default: None - %-format string used to format the slider value. If None, a - `.ScalarFormatter` is used instead. + The way to format the slider value. If a string, it must be in %-format. + If a callable, it must have the signature ``valfmt(val: float) -> str``. + If None, a `.ScalarFormatter` is used. closedmin : bool, default: True Whether the slider interval is closed on the bottom. @@ -535,6 +574,7 @@ def _value_in_bounds(self, val): val = self.slidermax.val return val + @_call_with_reparented_event def _update(self, event): """Update the slider position.""" if self.ignore(event) or event.button != 1: @@ -553,16 +593,18 @@ def _update(self, event): event.canvas.release_mouse(self.ax) return - xdata, ydata = self._get_data_coords(event) val = self._value_in_bounds( - xdata if self.orientation == 'horizontal' else ydata) + event.xdata if self.orientation == 'horizontal' else event.ydata) if val not in [None, self.val]: self.set_val(val) def _format(self, val): """Pretty-print *val*.""" if self.valfmt is not None: - return self.valfmt % val + if callable(self.valfmt): + return self.valfmt(val) + else: + return self.valfmt % val else: _, s, _ = self._fmt.format_ticks([self.valmin, val, self.valmax]) # fmt.get_offset is actually the multiplicative factor, if any. @@ -584,7 +626,7 @@ def set_val(self, val): self._handle.set_xdata([val]) self.valtext.set_text(self._format(val)) if self.drawon: - self.ax.figure.canvas.draw_idle() + self.ax.get_figure(root=True).canvas.draw_idle() self.val = val if self.eventson: self._observers.process('changed', val) @@ -659,9 +701,11 @@ def __init__( The initial positions of the slider. If None the initial positions will be at the 25th and 75th percentiles of the range. - valfmt : str, default: None - %-format string used to format the slider values. If None, a - `.ScalarFormatter` is used instead. + valfmt : str or callable, default: None + The way to format the range's minimal and maximal values. If a + string, it must be in %-format. If a callable, it must have the + signature ``valfmt(val: float) -> str``. If None, a + `.ScalarFormatter` is used. closedmin : bool, default: True Whether the slider interval is closed on the bottom. @@ -868,6 +912,7 @@ def _update_val_from_pos(self, pos): else: self._active_handle.set_xdata([val]) + @_call_with_reparented_event def _update(self, event): """Update the slider position.""" if self.ignore(event) or event.button != 1: @@ -888,11 +933,10 @@ def _update(self, event): return # determine which handle was grabbed - xdata, ydata = self._get_data_coords(event) handle_index = np.argmin(np.abs( - [h.get_xdata()[0] - xdata for h in self._handles] + [h.get_xdata()[0] - event.xdata for h in self._handles] if self.orientation == "horizontal" else - [h.get_ydata()[0] - ydata for h in self._handles])) + [h.get_ydata()[0] - event.ydata for h in self._handles])) handle = self._handles[handle_index] # these checks ensure smooth behavior if the handles swap which one @@ -900,12 +944,16 @@ def _update(self, event): if handle is not self._active_handle: self._active_handle = handle - self._update_val_from_pos(xdata if self.orientation == "horizontal" else ydata) + self._update_val_from_pos( + event.xdata if self.orientation == "horizontal" else event.ydata) def _format(self, val): """Pretty-print *val*.""" if self.valfmt is not None: - return f"({self.valfmt % val[0]}, {self.valfmt % val[1]})" + if callable(self.valfmt): + return f"({self.valfmt(val[0])}, {self.valfmt(val[1])})" + else: + return f"({self.valfmt % val[0]}, {self.valfmt % val[1]})" else: _, s1, s2, _ = self._fmt.format_ticks( [self.valmin, *val, self.valmax] @@ -960,7 +1008,7 @@ def set_val(self, val): self.valtext.set_text(self._format((vmin, vmax))) if self.drawon: - self.ax.figure.canvas.draw_idle() + self.ax.get_figure(root=True).canvas.draw_idle() self.val = (vmin, vmax) if self.eventson: self._observers.process("changed", (vmin, vmax)) @@ -988,30 +1036,218 @@ def _expand_text_props(props): return cycler(**props)() if props else itertools.repeat({}) -class CheckButtons(AxesWidget): +class _Buttons(AxesWidget): + """ + The base class for `CheckButtons` and `RadioButtons`. + + This class provides common functionality for button widgets, + such as handling click events, managing button labels, and connecting callbacks. + + The class itself is private and may be changed or removed without prior warning. + However, the public API it provides to subclasses is stable and considered + public on the subclasses. + """ + + def __init__(self, ax, labels, *, useblit=True, label_props=None, layout=None, + **kwargs): + super().__init__(ax) + + ax.set_xticks([]) + ax.set_yticks([]) + ax.set_navigate(False) + + self._useblit = useblit + + self._init_layout(layout, labels, label_props) + text_size = np.array([text.get_fontsize() for text in self.labels]) / 2 + + self._init_props(text_size, **kwargs) + + self.connect_event('button_press_event', self._clicked) + if self._useblit: + self.connect_event('draw_event', self._clear) + + self._observers = cbook.CallbackRegistry(signals=["clicked"]) + + def _init_layout(self, layout, labels, label_props): + + label_props = _expand_text_props(label_props) + + if layout is None: + # legacy hard-coded vertical layout + self._buttons_xs = [0.15] * len(labels) + self._buttons_ys = np.linspace(1, 0, len(labels)+2)[1:-1] + self.labels = [ + self.ax.text(0.25, y, label, transform=self.ax.transAxes, + horizontalalignment="left", verticalalignment="center", + **props) + for y, label, props in zip(self._buttons_ys, labels, label_props)] + return + + # New layout algorithm with text measurement + # Parse layout parameter + n_labels = len(labels) + match layout: + case "vertical": + n_rows, n_cols = n_labels, 1 + case "horizontal": + n_rows, n_cols = 1, n_labels + case (int() as n_rows, int() as n_cols): + if n_rows * n_cols < n_labels: + raise ValueError( + f"layout {layout} has {n_rows * n_cols} positions but " + f"{n_labels} labels were provided" + ) + case _: + raise ValueError( + "layout must be None, 'vertical', 'horizontal', or a (rows, cols) " + f"tuple; got {layout!r}") + + # Define spacing in points for DPI-independent sizing + fig = self.ax.get_figure(root=False) + axes_width_display = 72 * self.ax.bbox.transformed( + fig.dpi_scale_trans.inverted() + ).width + left_margin_display = 11 # points + button_text_offset_display = 5.5 # points + col_spacing_display = 11 # points + + # Convert to axes coordinates + left_margin = left_margin_display / axes_width_display + button_text_offset = button_text_offset_display / axes_width_display + col_spacing = col_spacing_display / axes_width_display + + # Create text objects to measure widths. + # We create Text objects directly rather than using ax.text() since we're + # only measuring them and only later add them to the axes. + self.labels = [ + mtext.Text(0, 0, text=label, transform=self.ax.transAxes, + horizontalalignment="left", verticalalignment="center", + **props) + for label, props in zip(labels, label_props) + ] + # Set figure reference so Text objects can access figure properties + for text in self.labels: + text.set_figure(fig) + # Calculate max text width per column (in axes coordinates) + renderer = self.ax.figure.canvas.get_renderer() + inv_trans = fig.dpi_scale_trans.inverted() + col_widths = [ + max( + ( + text.get_window_extent(renderer).transformed(inv_trans).width * 72 + for text in self.labels[col_idx::n_cols] + ), + default=0, + ) + / axes_width_display + for col_idx in range(n_cols) + ] + + # Center rows vertically in the axes + ys_per_row = np.linspace(1, 0, n_rows + 2)[1:-1] + # Calculate x positions based on text widths + col_x_positions = [left_margin] # First column starts at left margin + for col_idx in range(n_cols - 1): + col_x_positions.append( + col_x_positions[-1] + + button_text_offset + + col_widths[col_idx] + + col_spacing + ) + label_idx = np.arange(n_labels) + self._buttons_xs = np.take(col_x_positions, label_idx % n_cols) + self._buttons_ys = ys_per_row[label_idx // n_cols] + for text, x, y in zip(self.labels, self._buttons_xs, self._buttons_ys): + text.set_position((x + button_text_offset, y)) + self.ax.add_artist(text) + + def _init_props(self, text_size, **kwargs): + raise NotImplementedError("This method should be defined in subclasses") + + def _clear(self, event): + """Internal event handler to clear the buttons.""" + if self.ignore(event) or self.canvas.is_saving(): + return + if self._useblit and self.canvas.supports_blit: + self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox)) + self.ax.draw_artist(self._buttons) + + def set_label_props(self, props): + """ + Set properties of the `.Text` labels. + + .. versionadded:: 3.7 + + Parameters + ---------- + props : dict + Dictionary of `.Text` properties to be used for the labels. Same + format as label_props argument of :class:`RadioButtons` or + :class:`CheckButtons`. + """ + _api.check_isinstance(dict, props=props) + props = _expand_text_props(props) + for text, prop in zip(self.labels, props): + text.update(prop) + + @_call_with_reparented_event + def _clicked(self, event): + if self.ignore(event) or event.button != 1 or not self.ax.contains(event)[0]: + return + idxs = [ # Indices of frames and of texts that contain the event. + *self._buttons.contains(event)[1]["ind"], + *[i for i, text in enumerate(self.labels) if text.contains(event)[0]]] + if idxs: + coords = self._buttons.get_offset_transform().transform( + self._buttons.get_offsets()) + self.set_active( # Closest index, only looking in idxs. + idxs[(((event.x, event.y) - coords[idxs]) ** 2).sum(-1).argmin()]) + + def on_clicked(self, func): + """ + Connect the callback function *func* to button click events. + + Parameters + ---------- + func : callable + When the button is clicked, call *func* with button label. + When all buttons are cleared, call *func* with None. + The callback func must have the signature:: + + def func(label: str | None) -> Any + + Return values may exist, but are ignored. + + Returns + ------- + A connection id, which can be used to disconnect the callback. + """ + return self._observers.connect('clicked', func) + + def disconnect(self, cid): + """Remove the observer with connection id *cid*.""" + self._observers.disconnect(cid) + + +class CheckButtons(_Buttons): r""" A GUI neutral set of check buttons. For the check buttons to remain responsive you must keep a reference to this object. - Connect to the CheckButtons with the `.on_clicked` method. + Connect to the CheckButtons with the `~._Buttons.on_clicked` method. Attributes ---------- ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - labels : list of `~matplotlib.text.Text` - - rectangles : list of `~matplotlib.patches.Rectangle` - - lines : list of (`.Line2D`, `.Line2D`) pairs - List of lines for the x's in the checkboxes. These lines exist for - each box, but have ``set_visible(False)`` when its box is not checked. + The text label objects of the check buttons. """ - def __init__(self, ax, labels, actives=None, *, useblit=True, + def __init__(self, ax, labels, actives=None, *, layout=None, useblit=True, label_props=None, frame_props=None, check_props=None): """ Add check buttons to `~.axes.Axes` instance *ax*. @@ -1025,14 +1261,41 @@ def __init__(self, ax, labels, actives=None, *, useblit=True, actives : list of bool, optional The initial check states of the buttons. The list must have the same length as *labels*. If not given, all buttons are unchecked. + layout : None or "vertical" or "horizontal" or (int, int), default: None + The layout of the check buttons. Options are: + + - ``None``: Use legacy vertical layout (default). + - ``"vertical"``: Arrange buttons in a single column with + dynamic positioning based on text widths. + - ``"horizontal"``: Arrange buttons in a single row with + dynamic positioning based on text widths. + - ``(rows, cols)`` tuple: Arrange buttons in a grid with the + specified number of rows and columns. Buttons are placed + left-to-right, top-to-bottom with dynamic positioning. + + The layout options "vertical", "horizontal" and ``(rows, cols)`` + create ``mtext.Text`` objects to determine exact text sizes, and + then they are added to the Axes. This is usually okay, but may cause + side-effects and has a slight performance impact. Therefore the + default ``None`` value avoids this. + + .. admonition:: Provisional + The new layout options are provisional. Their algorithmic + behavior, including the exact positions of buttons and labels, + may still change without prior warning. + + .. versionadded:: 3.11 useblit : bool, default: True Use blitting for faster drawing if supported by the backend. See the tutorial :ref:`blitting` for details. .. versionadded:: 3.7 - label_props : dict, optional - Dictionary of `.Text` properties to be used for the labels. + label_props : dict of lists, optional + Dictionary of `.Text` properties to be used for the labels. Each + dictionary value should be a list of at least a single element. If + the list is of length M, its values are cycled such that the Nth + label gets the (N mod M) property. .. versionadded:: 3.7 frame_props : dict, optional @@ -1048,97 +1311,52 @@ def __init__(self, ax, labels, actives=None, *, useblit=True, .. versionadded:: 3.7 """ - super().__init__(ax) - _api.check_isinstance((dict, None), label_props=label_props, frame_props=frame_props, check_props=check_props) - ax.set_xticks([]) - ax.set_yticks([]) - ax.set_navigate(False) - - if actives is None: - actives = [False] * len(labels) - - self._useblit = useblit and self.canvas.supports_blit - self._background = None - - ys = np.linspace(1, 0, len(labels)+2)[1:-1] - - label_props = _expand_text_props(label_props) - self.labels = [ - ax.text(0.25, y, label, transform=ax.transAxes, - horizontalalignment="left", verticalalignment="center", - **props) - for y, label, props in zip(ys, labels, label_props)] - text_size = np.array([text.get_fontsize() for text in self.labels]) / 2 + super().__init__(ax, labels, layout=layout, useblit=useblit, + label_props=label_props, actives=actives, + frame_props=frame_props, check_props=check_props) + def _init_props(self, text_size, actives, frame_props, check_props): frame_props = { 's': text_size**2, 'linewidth': 1, **cbook.normalize_kwargs(frame_props, collections.PathCollection), 'marker': 's', - 'transform': ax.transAxes, + 'transform': self.ax.transAxes, } frame_props.setdefault('facecolor', frame_props.get('color', 'none')) frame_props.setdefault('edgecolor', frame_props.pop('color', 'black')) - self._frames = ax.scatter([0.15] * len(ys), ys, **frame_props) + self._frames = self.ax.scatter( + self._buttons_xs, + self._buttons_ys, + **frame_props, + ) check_props = { 'linewidth': 1, 's': text_size**2, **cbook.normalize_kwargs(check_props, collections.PathCollection), 'marker': 'x', - 'transform': ax.transAxes, - 'animated': self._useblit, + 'transform': self.ax.transAxes, + 'animated': self._useblit and self.canvas.supports_blit, + # TODO: This may need an update when switching out the canvas. + # Can set this to `_useblit` only and live with the animated=True + # overhead on unsupported backends. } check_props.setdefault('facecolor', check_props.pop('color', 'black')) - self._checks = ax.scatter([0.15] * len(ys), ys, **check_props) + self._buttons = self.ax.scatter( + self._buttons_xs, + self._buttons_ys, + **check_props + ) + if actives is None: + actives = [False] * len(self.labels) # The user may have passed custom colours in check_props, so we need to # create the checks (above), and modify the visibility after getting # whatever the user set. self._init_status(actives) - self.connect_event('button_press_event', self._clicked) - if self._useblit: - self.connect_event('draw_event', self._clear) - - self._observers = cbook.CallbackRegistry(signals=["clicked"]) - - def _clear(self, event): - """Internal event handler to clear the buttons.""" - if self.ignore(event) or self._changed_canvas(): - return - self._background = self.canvas.copy_from_bbox(self.ax.bbox) - self.ax.draw_artist(self._checks) - - def _clicked(self, event): - if self.ignore(event) or event.button != 1 or not self.ax.contains(event)[0]: - return - idxs = [ # Indices of frames and of texts that contain the event. - *self._frames.contains(event)[1]["ind"], - *[i for i, text in enumerate(self.labels) if text.contains(event)[0]]] - if idxs: - coords = self._frames.get_offset_transform().transform( - self._frames.get_offsets()) - self.set_active( # Closest index, only looking in idxs. - idxs[(((event.x, event.y) - coords[idxs]) ** 2).sum(-1).argmin()]) - - def set_label_props(self, props): - """ - Set properties of the `.Text` labels. - - .. versionadded:: 3.7 - - Parameters - ---------- - props : dict - Dictionary of `.Text` properties to be used for the labels. - """ - _api.check_isinstance(dict, props=props) - props = _expand_text_props(props) - for text, prop in zip(self.labels, props): - text.update(prop) - def set_frame_props(self, props): """ Set properties of the check button frames. @@ -1172,7 +1390,7 @@ def set_check_props(self, props): if 's' in props: # Keep API consistent with constructor. props['sizes'] = np.broadcast_to(props.pop('s'), len(self.labels)) actives = self.get_status() - self._checks.update(props) + self._buttons.update(props) # If new colours are supplied, then we must re-apply the status. self._init_status(actives) @@ -1180,7 +1398,7 @@ def set_active(self, index, state=None): """ Modify the state of a check button by index. - Callbacks will be triggered if :attr:`eventson` is True. + Callbacks will be triggered if :attr:`!eventson` is True. Parameters ---------- @@ -1204,17 +1422,18 @@ def set_active(self, index, state=None): invisible = colors.to_rgba('none') - facecolors = self._checks.get_facecolor() + facecolors = self._buttons.get_facecolor() if state is None: state = colors.same_color(facecolors[index], invisible) facecolors[index] = self._active_check_colors[index] if state else invisible - self._checks.set_facecolor(facecolors) + self._buttons.set_facecolor(facecolors) if self.drawon: - if self._useblit: - if self._background is not None: - self.canvas.restore_region(self._background) - self.ax.draw_artist(self._checks) + if self._useblit and self.canvas.supports_blit: + background = self._load_blit_background() + if background is not None: + self.canvas.restore_region(background) + self.ax.draw_artist(self._buttons) self.canvas.blit(self.ax.bbox) else: self.canvas.draw() @@ -1230,18 +1449,18 @@ def _init_status(self, actives): constructor, or to `.set_check_props`, so we need to modify the visibility after getting whatever the user set. """ - self._active_check_colors = self._checks.get_facecolor() + self._active_check_colors = self._buttons.get_facecolor() if len(self._active_check_colors) == 1: self._active_check_colors = np.repeat(self._active_check_colors, len(actives), axis=0) - self._checks.set_facecolor( + self._buttons.set_facecolor( [ec if active else "none" for ec, active in zip(self._active_check_colors, actives)]) def clear(self): """Uncheck all checkboxes.""" - self._checks.set_facecolor(['none'] * len(self._active_check_colors)) + self._buttons.set_facecolor(['none'] * len(self._active_check_colors)) if hasattr(self, '_lines'): for l1, l2 in self._lines: @@ -1260,7 +1479,7 @@ def get_status(self): Return a list of the status (True/False) of all of the check buttons. """ return [not colors.same_color(color, colors.to_rgba("none")) - for color in self._checks.get_facecolors()] + for color in self._buttons.get_facecolors()] def get_checked_labels(self): """Return a list of labels currently checked by user.""" @@ -1269,31 +1488,6 @@ def get_checked_labels(self): zip(self.labels, self.get_status()) if box_checked] - def on_clicked(self, func): - """ - Connect the callback function *func* to button click events. - - Parameters - ---------- - func : callable - When the button is clicked, call *func* with button label. - When all buttons are cleared, call *func* with None. - The callback func must have the signature:: - - def func(label: str | None) -> Any - - Return values may exist, but are ignored. - - Returns - ------- - A connection id, which can be used to disconnect the callback. - """ - return self._observers.connect('clicked', lambda text: func(text)) - - def disconnect(self, cid): - """Remove the observer with connection id *cid*.""" - self._observers.disconnect(cid) - class TextBox(AxesWidget): """ @@ -1341,7 +1535,7 @@ def __init__(self, ax, label, initial='', *, """ super().__init__(ax) - self._text_position = _api.check_getitem( + self._text_position = _api.getitem_checked( {"left": 0.05, "center": 0.5, "right": 0.95}, textalignment=textalignment) @@ -1391,8 +1585,9 @@ def _rendercursor(self): # This causes a single extra draw if the figure has never been rendered # yet, which should be fine as we're going to repeatedly re-render the # figure later anyways. - if self.ax.figure._get_renderer() is None: - self.ax.figure.canvas.draw() + fig = self.ax.get_figure(root=True) + if fig._get_renderer() is None: + fig.canvas.draw() text = self.text_disp.get_text() # Save value before overwriting it. widthtext = text[:self.cursor_index] @@ -1414,8 +1609,9 @@ def _rendercursor(self): visible=True) self.text_disp.set_text(text) - self.ax.figure.canvas.draw() + fig.canvas.draw() + @_call_with_reparented_event def _release(self, event): if self.ignore(event): return @@ -1423,6 +1619,7 @@ def _release(self, event): return event.canvas.release_mouse(self.ax) + @_call_with_reparented_event def _keypress(self, event): if self.ignore(event): return @@ -1477,7 +1674,7 @@ def begin_typing(self): stack = ExitStack() # Register cleanup actions when user stops typing. self._on_stop_typing = stack.close toolmanager = getattr( - self.ax.figure.canvas.manager, "toolmanager", None) + self.ax.get_figure(root=True).canvas.manager, "toolmanager", None) if toolmanager is not None: # If using toolmanager, lock keypresses, and plan to release the # lock when typing stops. @@ -1499,12 +1696,13 @@ def stop_typing(self): notifysubmit = False self.capturekeystrokes = False self.cursor.set_visible(False) - self.ax.figure.canvas.draw() + self.ax.get_figure(root=True).canvas.draw() if notifysubmit and self.eventson: # Because process() might throw an error in the user's code, only # call it once we've already done our cleanup. self._observers.process('submit', self.text) + @_call_with_reparented_event def _click(self, event): if self.ignore(event): return @@ -1520,9 +1718,11 @@ def _click(self, event): self.cursor_index = self.text_disp._char_index_at(event.x) self._rendercursor() + @_call_with_reparented_event def _resize(self, event): self.stop_typing() + @_call_with_reparented_event def _motion(self, event): if self.ignore(event): return @@ -1530,7 +1730,7 @@ def _motion(self, event): if not colors.same_color(c, self.ax.get_facecolor()): self.ax.set_facecolor(c) if self.drawon: - self.ax.figure.canvas.draw() + self.ax.get_figure(root=True).canvas.draw() def on_text_change(self, func): """ @@ -1554,14 +1754,14 @@ def disconnect(self, cid): self._observers.disconnect(cid) -class RadioButtons(AxesWidget): +class RadioButtons(_Buttons): """ A GUI neutral radio button. For the buttons to remain responsive you must keep a reference to this object. - Connect to the RadioButtons with the `.on_clicked` method. + Connect to the RadioButtons with the `~._Buttons.on_clicked` method. Attributes ---------- @@ -1571,15 +1771,13 @@ class RadioButtons(AxesWidget): The color of the selected button. labels : list of `.Text` The button labels. - circles : list of `~.patches.Circle` - The buttons. value_selected : str The label text of the currently selected button. index_selected : int The index of the selected button. """ - def __init__(self, ax, labels, active=0, activecolor=None, *, + def __init__(self, ax, labels, active=0, activecolor=None, *, layout=None, useblit=True, label_props=None, radio_props=None): """ Add radio buttons to an `~.axes.Axes`. @@ -1595,14 +1793,41 @@ def __init__(self, ax, labels, active=0, activecolor=None, *, activecolor : :mpltype:`color` The color of the selected button. The default is ``'blue'`` if not specified here or in *radio_props*. + layout : None or "vertical" or "horizontal" or (int, int), default: None + The layout of the radio buttons. Options are: + + - ``None``: Use legacy vertical layout (default). + - ``"vertical"``: Arrange buttons in a single column with + dynamic positioning based on text widths. + - ``"horizontal"``: Arrange buttons in a single row with + dynamic positioning based on text widths. + - ``(rows, cols)`` tuple: Arrange buttons in a grid with the + specified number of rows and columns. Buttons are placed + left-to-right, top-to-bottom with dynamic positioning. + + The layout options "vertical", "horizontal" and ``(rows, cols)`` + create ``mtext.Text`` objects to determine exact text sizes, and + then they are added to the Axes. This is usually okay, but may cause + side-effects and has a slight performance impact. Therefore the + default ``None`` value avoids this. + + .. admonition:: Provisional + The new layout options are provisional. Their algorithmic + behavior, including the exact positions of buttons and labels, + may still change without prior warning. + + .. versionadded:: 3.11 useblit : bool, default: True Use blitting for faster drawing if supported by the backend. See the tutorial :ref:`blitting` for details. .. versionadded:: 3.7 - label_props : dict or list of dict, optional - Dictionary of `.Text` properties to be used for the labels. + label_props : dict of lists, optional + Dictionary of `.Text` properties to be used for the labels. Each + dictionary value should be a list of at least a single element. If + the list is of length M, its values are cycled such that the Nth + label gets the (N mod M) property. .. versionadded:: 3.7 radio_props : dict, optional @@ -1617,8 +1842,6 @@ def __init__(self, ax, labels, active=0, activecolor=None, *, .. versionadded:: 3.7 """ - super().__init__(ax) - _api.check_isinstance((dict, None), label_props=label_props, radio_props=radio_props) @@ -1632,92 +1855,46 @@ def __init__(self, ax, labels, active=0, activecolor=None, *, '*activecolor* will be ignored.') else: activecolor = 'blue' # Default. + super().__init__(ax, labels, useblit=useblit, label_props=label_props, + active=active, layout=layout, activecolor=activecolor, + radio_props=radio_props) self._activecolor = activecolor self._initial_active = active self.value_selected = labels[active] self.index_selected = active - ax.set_xticks([]) - ax.set_yticks([]) - ax.set_navigate(False) - - ys = np.linspace(1, 0, len(labels) + 2)[1:-1] - - self._useblit = useblit and self.canvas.supports_blit - self._background = None - - label_props = _expand_text_props(label_props) - self.labels = [ - ax.text(0.25, y, label, transform=ax.transAxes, - horizontalalignment="left", verticalalignment="center", - **props) - for y, label, props in zip(ys, labels, label_props)] - text_size = np.array([text.get_fontsize() for text in self.labels]) / 2 - + def _init_props(self, text_size, active, activecolor, radio_props): radio_props = { 's': text_size**2, **radio_props, 'marker': 'o', - 'transform': ax.transAxes, - 'animated': self._useblit, + 'transform': self.ax.transAxes, + 'animated': self._useblit and self.canvas.supports_blit, + # TODO: This may need an update when switching out the canvas. + # Can set this to `_useblit` only and live with the animated=True + # overhead on unsupported backends. + } radio_props.setdefault('edgecolor', radio_props.get('color', 'black')) radio_props.setdefault('facecolor', radio_props.pop('color', activecolor)) - self._buttons = ax.scatter([.15] * len(ys), ys, **radio_props) + self._buttons = self.ax.scatter( + self._buttons_xs, + self._buttons_ys, + **radio_props, + ) # The user may have passed custom colours in radio_props, so we need to # create the radios, and modify the visibility after getting whatever # the user set. self._active_colors = self._buttons.get_facecolor() if len(self._active_colors) == 1: - self._active_colors = np.repeat(self._active_colors, len(labels), + self._active_colors = np.repeat(self._active_colors, len(self.labels), axis=0) self._buttons.set_facecolor( [activecolor if i == active else "none" for i, activecolor in enumerate(self._active_colors)]) - self.connect_event('button_press_event', self._clicked) - if self._useblit: - self.connect_event('draw_event', self._clear) - - self._observers = cbook.CallbackRegistry(signals=["clicked"]) - - def _clear(self, event): - """Internal event handler to clear the buttons.""" - if self.ignore(event) or self._changed_canvas(): - return - self._background = self.canvas.copy_from_bbox(self.ax.bbox) - self.ax.draw_artist(self._buttons) - - def _clicked(self, event): - if self.ignore(event) or event.button != 1 or not self.ax.contains(event)[0]: - return - idxs = [ # Indices of buttons and of texts that contain the event. - *self._buttons.contains(event)[1]["ind"], - *[i for i, text in enumerate(self.labels) if text.contains(event)[0]]] - if idxs: - coords = self._buttons.get_offset_transform().transform( - self._buttons.get_offsets()) - self.set_active( # Closest index, only looking in idxs. - idxs[(((event.x, event.y) - coords[idxs]) ** 2).sum(-1).argmin()]) - - def set_label_props(self, props): - """ - Set properties of the `.Text` labels. - - .. versionadded:: 3.7 - - Parameters - ---------- - props : dict - Dictionary of `.Text` properties to be used for the labels. - """ - _api.check_isinstance(dict, props=props) - props = _expand_text_props(props) - for text, prop in zip(self.labels, props): - text.update(prop) - def set_radio_props(self, props): """ Set properties of the `.Text` labels. @@ -1751,17 +1928,12 @@ def activecolor(self, activecolor): colors._check_color_like(activecolor=activecolor) self._activecolor = activecolor self.set_radio_props({'facecolor': activecolor}) - # Make sure the deprecated version is updated. - # Remove once circles is removed. - labels = [label.get_text() for label in self.labels] - with cbook._setattr_cm(self, eventson=False): - self.set_active(labels.index(self.value_selected)) def set_active(self, index): """ Select button with number *index*. - Callbacks will be triggered if :attr:`eventson` is True. + Callbacks will be triggered if :attr:`!eventson` is True. Parameters ---------- @@ -1783,9 +1955,10 @@ def set_active(self, index): self._buttons.set_facecolor(button_facecolors) if self.drawon: - if self._useblit: - if self._background is not None: - self.canvas.restore_region(self._background) + if self._useblit and self.canvas.supports_blit: + background = self._load_blit_background() + if background is not None: + self.canvas.restore_region(background) self.ax.draw_artist(self._buttons) self.canvas.blit(self.ax.bbox) else: @@ -1798,31 +1971,6 @@ def clear(self): """Reset the active button to the initially active one.""" self.set_active(self._initial_active) - def on_clicked(self, func): - """ - Connect the callback function *func* to button click events. - - Parameters - ---------- - func : callable - When the button is clicked, call *func* with button label. - When all buttons are cleared, call *func* with None. - The callback func must have the signature:: - - def func(label: str | None) -> Any - - Return values may exist, but are ignored. - - Returns - ------- - A connection id, which can be used to disconnect the callback. - """ - return self._observers.connect('clicked', func) - - def disconnect(self, cid): - """Remove the observer with connection id *cid*.""" - self._observers.disconnect(cid) - class SubplotTool(Widget): """ @@ -1868,7 +2016,7 @@ def __init__(self, targetfig, toolfig): self.sliderbottom.slidermax = self.slidertop self.slidertop.slidermin = self.sliderbottom - bax = toolfig.add_axes([0.8, 0.05, 0.15, 0.075]) + bax = toolfig.add_axes((0.8, 0.05, 0.15, 0.075)) self.buttonreset = Button(bax, 'Reset') self.buttonreset.on_clicked(self._on_reset) @@ -1934,23 +2082,33 @@ def __init__(self, ax, *, horizOn=True, vertOn=True, useblit=False, self.visible = True self.horizOn = horizOn self.vertOn = vertOn - self.useblit = useblit and self.canvas.supports_blit + self.useblit = useblit and self.canvas.supports_blit # TODO: make dynamic + + if self.useblit: + for ax_ in ax.get_figure(root=True).get_axes(): + if ax_ is not ax and ax.bbox.overlaps(ax_.bbox): + _api.warn_external( + "Cursor blitting is currently not supported on " + "overlapping axes; falling back to useblit=False." + ) + self.useblit = False + break if self.useblit: lineprops['animated'] = True self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **lineprops) self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops) - self.background = None self.needclear = False def clear(self, event): """Internal event handler to clear the cursor.""" - if self.ignore(event) or self._changed_canvas(): + if self.ignore(event) or self.canvas.is_saving(): return if self.useblit: - self.background = self.canvas.copy_from_bbox(self.ax.bbox) + self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox)) + @_call_with_reparented_event def onmove(self, event): """Internal event handler to draw the cursor when the mouse moves.""" if self.ignore(event): @@ -1965,17 +2123,17 @@ def onmove(self, event): self.needclear = False return self.needclear = True - xdata, ydata = self._get_data_coords(event) - self.linev.set_xdata((xdata, xdata)) + self.linev.set_xdata((event.xdata, event.xdata)) self.linev.set_visible(self.visible and self.vertOn) - self.lineh.set_ydata((ydata, ydata)) + self.lineh.set_ydata((event.ydata, event.ydata)) self.lineh.set_visible(self.visible and self.horizOn) if not (self.visible and (self.vertOn or self.horizOn)): return # Redraw. if self.useblit: - if self.background is not None: - self.canvas.restore_region(self.background) + background = self._load_blit_background() + if background is not None: + self.canvas.restore_region(background) self.ax.draw_artist(self.linev) self.ax.draw_artist(self.lineh) self.canvas.blit(self.ax.bbox) @@ -1988,12 +2146,19 @@ class MultiCursor(Widget): Provide a vertical (default) and/or horizontal line cursor shared between multiple Axes. + Call signatures:: + + MultiCursor(axes, *, ...) + MultiCursor(canvas, axes, *, ...) # deprecated + For the cursor to remain responsive you must keep a reference to it. Parameters ---------- canvas : object - This parameter is entirely unused and only kept for back-compatibility. + This parameter is entirely unused. + + .. deprecated:: 3.11 axes : list of `~matplotlib.axes.Axes` The `~.axes.Axes` to attach the cursor to. @@ -2020,18 +2185,33 @@ class MultiCursor(Widget): See :doc:`/gallery/widgets/multicursor`. """ - def __init__(self, canvas, axes, *, useblit=True, horizOn=False, vertOn=True, + def __init__(self, *args, useblit=True, horizOn=False, vertOn=True, **lineprops): - # canvas is stored only to provide the deprecated .canvas attribute; - # once it goes away the unused argument won't need to be stored at all. - self._canvas = canvas + # Deprecation of canvas as the first attribute. When the deprecation expires: + # - change the signature to __init__(self, axes, *, ...) + # - delete the "Call signatures" block in the docstring + # - delete this block + kwargs = {k: lineprops.pop(k) + for k in list(lineprops) if k in ("canvas", "axes")} + params = _api.select_matching_signature( + [lambda axes: locals(), lambda canvas, axes: locals()], *args, **kwargs) + if "canvas" in params: + _api.warn_deprecated( + "3.11", + message="The canvas parameter in MultiCursor is unused and deprecated " + "since %(since)s. Please remove it and call MultiCursor(axes) " + "instead of MultiCursor(canvas, axes). The latter will start raising " + "an error in %(removal)s" + ) + axes = params["axes"] self.axes = axes self.horizOn = horizOn self.vertOn = vertOn self._canvas_infos = { - ax.figure.canvas: {"cids": [], "background": None} for ax in axes} + ax.get_figure(root=True).canvas: + {"cids": [], "background": None} for ax in axes} xmin, xmax = axes[-1].get_xlim() ymin, ymax = axes[-1].get_ylim() @@ -2042,6 +2222,7 @@ def __init__(self, canvas, axes, *, useblit=True, horizOn=False, vertOn=True, self.useblit = ( useblit and all(canvas.supports_blit for canvas in self._canvas_infos)) + # TODO: make dynamic if self.useblit: lineprops['animated'] = True @@ -2116,14 +2297,27 @@ def onmove(self, event): class _SelectorWidget(AxesWidget): + """ + The base class for selector widgets. - def __init__(self, ax, onselect, useblit=False, button=None, + This class provides common functionality for selector widgets, + such as handling mouse and keyboard events, managing state modifier keys, etc. + + The class itself is private and may be changed or removed without prior warning. + However, the public API it provides to subclasses is stable and considered + public on the subclasses. + """ + + def __init__(self, ax, onselect=None, useblit=False, button=None, state_modifier_keys=None, use_data_coordinates=False): super().__init__(ax) self._visible = True - self.onselect = onselect - self.useblit = useblit and self.canvas.supports_blit + if onselect is None: + self.onselect = lambda *args: None + else: + self.onselect = onselect + self._useblit = useblit self.connect_default_events() self._state_modifier_keys = dict(move=' ', clear='escape', @@ -2132,8 +2326,6 @@ def __init__(self, ax, onselect, useblit=False, button=None, self._state_modifier_keys.update(state_modifier_keys or {}) self._use_data_coordinates = use_data_coordinates - self.background = None - if isinstance(button, Integral): self.validButtons = [button] else: @@ -2149,6 +2341,11 @@ def __init__(self, ax, onselect, useblit=False, button=None, self._prev_event = None self._state = set() + @property + def useblit(self): + """Return whether blitting is used (requested and supported by canvas).""" + return self._useblit and self.canvas.supports_blit + def set_active(self, active): super().set_active(active) if active: @@ -2172,6 +2369,8 @@ def update_background(self, event): # `release` can call a draw event even when `ignore` is True. if not self.useblit: return + if self.canvas.is_saving(): + return # saving does not use blitting # Make sure that widget artists don't get accidentally included in the # background, by re-rendering the background if needed (and then # re-re-rendering the canvas with the visible widget artists). @@ -2187,7 +2386,7 @@ def update_background(self, event): for artist in artists: stack.enter_context(artist._cm_set(visible=False)) self.canvas.draw() - self.background = self.canvas.copy_from_bbox(self.ax.bbox) + self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox)) if needs_redraw: for artist in artists: self.ax.draw_artist(artist) @@ -2204,7 +2403,9 @@ def connect_default_events(self): def ignore(self, event): # docstring inherited - if not self.active or not self.ax.get_visible(): + if super().ignore(event): + return True + if not self.ax.get_visible(): return True # If canvas was locked if not self.canvas.widgetlock.available(self): @@ -2229,11 +2430,12 @@ def ignore(self, event): def update(self): """Draw using blit() or draw_idle(), depending on ``self.useblit``.""" if (not self.ax.get_visible() or - self.ax.figure._get_renderer() is None): + self.ax.get_figure(root=True)._get_renderer() is None): return if self.useblit: - if self.background is not None: - self.canvas.restore_region(self.background) + background = self._load_blit_background() + if background is not None: + self.canvas.restore_region(background) else: self.update_background(None) # We need to draw all artists, which are not included in the @@ -2251,9 +2453,8 @@ def _get_data(self, event): """Get the xdata and ydata for event, with limits.""" if event.xdata is None: return None, None - xdata, ydata = self._get_data_coords(event) - xdata = np.clip(xdata, *self.ax.get_xbound()) - ydata = np.clip(ydata, *self.ax.get_ybound()) + xdata = np.clip(event.xdata, *self.ax.get_xbound()) + ydata = np.clip(event.ydata, *self.ax.get_ybound()) return xdata, ydata def _clean_event(self, event): @@ -2273,6 +2474,7 @@ def _clean_event(self, event): self._prev_event = event return event + @_call_with_reparented_event def press(self, event): """Button press handler and validator.""" if not self.ignore(event): @@ -2291,6 +2493,7 @@ def press(self, event): def _press(self, event): """Button press event handler.""" + @_call_with_reparented_event def release(self, event): """Button release event handler and validator.""" if not self.ignore(event) and self._eventpress: @@ -2306,6 +2509,7 @@ def release(self, event): def _release(self, event): """Button release event handler.""" + @_call_with_reparented_event def onmove(self, event): """Cursor move event handler and validator.""" if not self.ignore(event) and self._eventpress: @@ -2317,6 +2521,7 @@ def onmove(self, event): def _onmove(self, event): """Cursor move event handler.""" + @_call_with_reparented_event def on_scroll(self, event): """Mouse scroll event handler and validator.""" if not self.ignore(event): @@ -2325,6 +2530,7 @@ def on_scroll(self, event): def _on_scroll(self, event): """Mouse scroll event handler.""" + @_call_with_reparented_event def on_key_press(self, event): """Key press event handler and validator for all selection widgets.""" if self.active: @@ -2349,6 +2555,7 @@ def on_key_press(self, event): def _on_key_press(self, event): """Key press event handler - for widget-specific key press actions.""" + @_call_with_reparented_event def on_key_release(self, event): """Key release event handler and validator.""" if self.active: @@ -2373,11 +2580,6 @@ def get_visible(self): """Get the visibility of the selector artists.""" return self._visible - @property - def visible(self): - _api.warn_deprecated("3.8", alternative="get_visible") - return self.get_visible() - def clear(self): """Clear the selection and set the selector ready to make a new one.""" self._clear_without_update() @@ -2409,7 +2611,7 @@ def set_props(self, **props): def set_handle_props(self, **handle_props): """ Set the properties of the handles selector artist. See the - `handle_props` argument in the selector docstring to know which + *handle_props* argument in the selector docstring to know which properties are supported. """ if not hasattr(self, '_handles_artists'): @@ -2433,13 +2635,15 @@ def _validate_state(self, state): def add_state(self, state): """ Add a state to define the widget's behavior. See the - `state_modifier_keys` parameters for details. + *state_modifier_keys* parameter in the constructor of the concrete + selector class for details. Parameters ---------- state : str Must be a supported state of the selector. See the - `state_modifier_keys` parameters for details. + *state_modifier_keys* parameter in the constructor of the concrete + selector class for details. Raises ------ @@ -2453,13 +2657,15 @@ def add_state(self, state): def remove_state(self, state): """ Remove a state to define the widget's behavior. See the - `state_modifier_keys` parameters for details. + *state_modifier_keys* parameter in the constructor of the concrete + selector class for details. Parameters ---------- state : str Must be a supported state of the selector. See the - `state_modifier_keys` parameters for details. + *state_modifier_keys* parameter in the constructor of the concrete + selector class for details. Raises ------ @@ -2571,7 +2777,14 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False, if props is None: props = dict(facecolor='red', alpha=0.5) - props['animated'] = self.useblit + # Note: We set this based on the user setting during ínitialization, + # not on the actual capability of blitting. But the value is + # irrelevant if the backend does not support blitting, so that + # we don't have to dynamically update this on the backend. + # This relies on the current behavior that the request for + # useblit is fixed during initialization and cannot be changed + # afterwards. + props['animated'] = self._useblit self.direction = direction self._extents_on_press = None @@ -2586,9 +2799,7 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False, self.drag_from_anywhere = drag_from_anywhere self.ignore_event_outside = ignore_event_outside - # Reset canvas so that `new_axes` connects events. - self.canvas = None - self.new_axes(ax, _props=props) + self.new_axes(ax, _props=props, _init=True) # Setup handles self._handle_props = { @@ -2601,14 +2812,15 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False, self._active_handle = None - def new_axes(self, ax, *, _props=None): + def new_axes(self, ax, *, _props=None, _init=False): """Set SpanSelector to operate on a new Axes.""" - self.ax = ax - if self.canvas is not ax.figure.canvas: + reconnect = False + if _init or self.canvas is not ax.get_figure(root=True).canvas: if self.canvas is not None: self.disconnect_events() - - self.canvas = ax.figure.canvas + reconnect = True + self.ax = ax + if reconnect: self.connect_default_events() # Reset @@ -2638,7 +2850,7 @@ def _setup_edge_handles(self, props): self._edge_handles = ToolLineHandles(self.ax, positions, direction=self.direction, line_props=props, - useblit=self.useblit) + useblit=self._useblit) @property def _handles_artists(self): @@ -2647,7 +2859,7 @@ def _handles_artists(self): else: return () - def _set_cursor(self, enabled): + def _set_span_cursor(self, *, enabled): """Update the canvas cursor based on direction of the selector.""" if enabled: cursor = (backend_tools.Cursors.RESIZE_HORIZONTAL @@ -2656,7 +2868,7 @@ def _set_cursor(self, enabled): else: cursor = backend_tools.Cursors.POINTER - self.ax.figure.canvas.set_cursor(cursor) + self._set_cursor(cursor) def connect_default_events(self): # docstring inherited @@ -2666,7 +2878,7 @@ def connect_default_events(self): def _press(self, event): """Button press event handler.""" - self._set_cursor(True) + self._set_span_cursor(enabled=True) if self._interactive and self._selection_artist.get_visible(): self._set_active_handle(event) else: @@ -2676,8 +2888,7 @@ def _press(self, event): # Clear previous rectangle before drawing new rectangle. self.update() - xdata, ydata = self._get_data_coords(event) - v = xdata if self.direction == 'horizontal' else ydata + v = event.xdata if self.direction == 'horizontal' else event.ydata if self._active_handle is None and not self.ignore_event_outside: # when the press event outside the span, we initially set the @@ -2714,9 +2925,10 @@ def direction(self, direction): else: self._direction = direction + @_call_with_reparented_event def _release(self, event): """Button release event handler.""" - self._set_cursor(False) + self._set_span_cursor(enabled=False) if not self._interactive: self._selection_artist.set_visible(False) @@ -2745,6 +2957,7 @@ def _release(self, event): return False + @_call_with_reparented_event def _hover(self, event): """Update the canvas cursor if it's over a handle.""" if self.ignore(event): @@ -2758,17 +2971,16 @@ def _hover(self, event): return _, e_dist = self._edge_handles.closest(event.x, event.y) - self._set_cursor(e_dist <= self.grab_range) + self._set_span_cursor(enabled=e_dist <= self.grab_range) def _onmove(self, event): """Motion notify event handler.""" - xdata, ydata = self._get_data_coords(event) if self.direction == 'horizontal': - v = xdata + v = event.xdata vpress = self._eventpress.xdata else: - v = ydata + v = event.ydata vpress = self._eventpress.ydata # move existing span @@ -3021,7 +3233,7 @@ def __init__(self, ax, x, y, *, marker='o', marker_props=None, useblit=True): props = {'marker': marker, 'markersize': 7, 'markerfacecolor': 'w', 'linestyle': 'none', 'alpha': 0.5, 'visible': False, 'label': '_nolegend_', - **cbook.normalize_kwargs(marker_props, Line2D._alias_map)} + **cbook.normalize_kwargs(marker_props, Line2D)} self._markers = Line2D(x, y, animated=useblit, **props) self.ax.add_line(self._markers) @@ -3068,7 +3280,7 @@ def closest(self, x, y): ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional A callback function that is called after a release event and the selection is created, changed or removed. It must have the signature:: @@ -3083,7 +3295,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) (when already existing) or cancelled. minspany : float, default: 0 - Selections with an y-span less than or equal to *minspanx* are removed + Selections with a y-span less than or equal to *minspanx* are removed (when already existing) or cancelled. useblit : bool, default: False @@ -3149,6 +3361,13 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) """ +class _RectangleSelectorAction(enum.Enum): + ROTATE = enum.auto() + MOVE = enum.auto() + RESIZE = enum.auto() + CREATE = enum.auto() + + @_docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING.replace( '__ARTIST_NAME__', 'rectangle')) class RectangleSelector(_SelectorWidget): @@ -3181,7 +3400,8 @@ class RectangleSelector(_SelectorWidget): See also: :doc:`/gallery/widgets/rectangle_selector` """ - def __init__(self, ax, onselect, *, minspanx=0, minspany=0, useblit=False, + def __init__(self, ax, onselect=None, *, minspanx=0, + minspany=0, useblit=False, props=None, spancoords='data', button=None, grab_range=10, handle_props=None, interactive=False, state_modifier_keys=None, drag_from_anywhere=False, @@ -3204,7 +3424,7 @@ def __init__(self, ax, onselect, *, minspanx=0, minspany=0, useblit=False, if props is None: props = dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True) - props = {**props, 'animated': self.useblit} + props = {**props, 'animated': self._useblit} self._visible = props.pop('visible', self._visible) to_draw = self._init_shape(**props) self.ax.add_patch(to_draw) @@ -3229,18 +3449,18 @@ def __init__(self, ax, onselect, *, minspanx=0, minspany=0, useblit=False, xc, yc = self.corners self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=self._handle_props, - useblit=self.useblit) + useblit=self._useblit) self._edge_order = ['W', 'S', 'E', 'N'] xe, ye = self.edge_centers self._edge_handles = ToolHandles(self.ax, xe, ye, marker='s', marker_props=self._handle_props, - useblit=self.useblit) + useblit=self._useblit) xc, yc = self.center self._center_handle = ToolHandles(self.ax, [xc], [yc], marker='s', marker_props=self._handle_props, - useblit=self.useblit) + useblit=self._useblit) self._active_handle = None @@ -3270,9 +3490,8 @@ def _press(self, event): if (self._active_handle is None and not self.ignore_event_outside and self._allow_creation): - x, y = self._get_data_coords(event) self._visible = False - self.extents = x, x, y, y + self.extents = event.xdata, event.xdata, event.ydata, event.ydata self._visible = True else: self.set_visible(True) @@ -3281,10 +3500,24 @@ def _press(self, event): self._rotation_on_press = self._rotation self._set_aspect_ratio_correction() + match self._get_action(): + case _RectangleSelectorAction.ROTATE: + # TODO: set to a rotate cursor if possible? + pass + case _RectangleSelectorAction.MOVE: + self._set_cursor(backend_tools.cursors.MOVE) + case _RectangleSelectorAction.RESIZE: + # TODO: set to a resize cursor if possible? + pass + case _RectangleSelectorAction.CREATE: + self._set_cursor(backend_tools.cursors.SELECT_REGION) + return False + @_call_with_reparented_event def _release(self, event): """Button release event handler.""" + self._set_cursor(backend_tools.Cursors.POINTER) if not self._interactive: self._selection_artist.set_visible(False) @@ -3328,9 +3561,20 @@ def _release(self, event): self.update() self._active_handle = None self._extents_on_press = None - return False + def _get_action(self): + state = self._state + if 'rotate' in state and self._active_handle in self._corner_order: + return _RectangleSelectorAction.ROTATE + elif self._active_handle == 'C': + return _RectangleSelectorAction.MOVE + elif self._active_handle: + return _RectangleSelectorAction.RESIZE + + return _RectangleSelectorAction.CREATE + + def _onmove(self, event): """ Motion notify event handler. @@ -3345,12 +3589,10 @@ def _onmove(self, event): # The calculations are done for rotation at zero: we apply inverse # transformation to events except when we rotate and move state = self._state - rotate = 'rotate' in state and self._active_handle in self._corner_order - move = self._active_handle == 'C' - resize = self._active_handle and not move + action = self._get_action() - xdata, ydata = self._get_data_coords(event) - if resize: + xdata, ydata = event.xdata, event.ydata + if action == _RectangleSelectorAction.RESIZE: inv_tr = self._get_rotation_transform().inverted() xdata, ydata = inv_tr.transform([xdata, ydata]) eventpress.xdata, eventpress.ydata = inv_tr.transform( @@ -3370,7 +3612,7 @@ def _onmove(self, event): x0, x1, y0, y1 = self._extents_on_press # rotate an existing shape - if rotate: + if action == _RectangleSelectorAction.ROTATE: # calculate angle abc a = (eventpress.xdata, eventpress.ydata) b = self.center @@ -3379,7 +3621,7 @@ def _onmove(self, event): np.arctan2(a[1]-b[1], a[0]-b[0])) self.rotation = np.rad2deg(self._rotation_on_press + angle) - elif resize: + elif action == _RectangleSelectorAction.RESIZE: size_on_press = [x1 - x0, y1 - y0] center = (x0 + size_on_press[0] / 2, y0 + size_on_press[1] / 2) @@ -3430,7 +3672,7 @@ def _onmove(self, event): sign = np.sign(xdata - x0) x1 = x0 + sign * abs(y1 - y0) * self._aspect_ratio_correction - elif move: + elif action == _RectangleSelectorAction.MOVE: x0, x1, y0, y1 = self._extents_on_press dx = xdata - eventpress.xdata dy = ydata - eventpress.ydata @@ -3703,7 +3945,7 @@ def onselect(verts): ---------- ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional Whenever the lasso is released, the *onselect* function is called and passed the vertices of the selected path. useblit : bool, default: True @@ -3718,14 +3960,14 @@ def onselect(verts): which corresponds to all buttons. """ - def __init__(self, ax, onselect, *, useblit=True, props=None, button=None): + def __init__(self, ax, onselect=None, *, useblit=True, props=None, button=None): super().__init__(ax, onselect, useblit=useblit, button=button) self.verts = None props = { **(props if props is not None else {}), # Note that self.useblit may be != useblit, if the canvas doesn't # support blitting. - 'animated': self.useblit, 'visible': False, + 'animated': self._useblit, 'visible': False, } line = Line2D([], [], **props) self.ax.add_line(line) @@ -3735,6 +3977,7 @@ def _press(self, event): self.verts = [self._get_data(event)] self._selection_artist.set_visible(True) + @_call_with_reparented_event def _release(self, event): if self.verts is not None: self.verts.append(self._get_data(event)) @@ -3776,7 +4019,7 @@ class PolygonSelector(_SelectorWidget): ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional When a polygon is completed or modified after completion, the *onselect* function is called and passed a list of the vertices as ``(xdata, ydata)`` tuples. @@ -3828,7 +4071,7 @@ class PolygonSelector(_SelectorWidget): point. """ - def __init__(self, ax, onselect, *, useblit=False, + def __init__(self, ax, onselect=None, *, useblit=False, props=None, handle_props=None, grab_range=10, draw_bounding_box=False, box_handle_props=None, box_props=None): @@ -3849,7 +4092,7 @@ def __init__(self, ax, onselect, *, useblit=False, if props is None: props = dict(color='k', linestyle='-', linewidth=2, alpha=0.5) - props = {**props, 'animated': self.useblit} + props = {**props, 'animated': self._useblit} self._selection_artist = line = Line2D([], [], **props) self.ax.add_line(line) @@ -3858,7 +4101,7 @@ def __init__(self, ax, onselect, *, useblit=False, markerfacecolor=props.get('color', 'k')) self._handle_props = handle_props self._polygon_handles = ToolHandles(self.ax, [], [], - useblit=self.useblit, + useblit=self._useblit, marker_props=self._handle_props) self._active_handle_idx = -1 @@ -3878,8 +4121,7 @@ def _get_bbox(self): def _add_box(self): self._box = RectangleSelector(self.ax, - onselect=lambda *args, **kwargs: None, - useblit=self.useblit, + useblit=self._useblit, grab_range=self.grab_range, handle_props=self._box_handle_props, props=self._box_props, @@ -3906,6 +4148,7 @@ def _update_box(self): # Save a copy self._old_box_extents = self._box.extents + @_call_with_reparented_event def _scale_polygon(self, event): """ Scale the polygon selector points when the bounding box is moved or @@ -3970,6 +4213,7 @@ def _press(self, event): # support the 'move_all' state modifier). self._xys_at_press = self._xys.copy() + @_call_with_reparented_event def _release(self, event): """Button release event handler.""" # Release active tool handle. @@ -3989,39 +4233,45 @@ def _release(self, event): elif (not self._selection_completed and 'move_all' not in self._state and 'move_vertex' not in self._state): - self._xys.insert(-1, self._get_data_coords(event)) + self._xys.insert(-1, (event.xdata, event.ydata)) if self._selection_completed: self.onselect(self.verts) + @_call_with_reparented_event def onmove(self, event): """Cursor move event handler and validator.""" # Method overrides _SelectorWidget.onmove because the polygon selector # needs to process the move callback even if there is no button press. # _SelectorWidget.onmove include logic to ignore move event if # _eventpress is None. - if not self.ignore(event): + if self.ignore(event): + # Hide the cursor when interactive zoom/pan is active + if not self.canvas.widgetlock.available(self) and self._xys: + self._xys[-1] = (np.nan, np.nan) + self._draw_polygon() + return False + + else: event = self._clean_event(event) self._onmove(event) return True - return False def _onmove(self, event): """Cursor move event handler.""" # Move the active vertex (ToolHandle). if self._active_handle_idx >= 0: idx = self._active_handle_idx - self._xys[idx] = self._get_data_coords(event) + self._xys[idx] = (event.xdata, event.ydata) # Also update the end of the polygon line if the first vertex is # the active handle and the polygon is completed. if idx == 0 and self._selection_completed: - self._xys[-1] = self._get_data_coords(event) + self._xys[-1] = (event.xdata, event.ydata) # Move all vertices. elif 'move_all' in self._state and self._eventpress: - xdata, ydata = self._get_data_coords(event) - dx = xdata - self._eventpress.xdata - dy = ydata - self._eventpress.ydata + dx = event.xdata - self._eventpress.xdata + dy = event.ydata - self._eventpress.ydata for k in range(len(self._xys)): x_at_press, y_at_press = self._xys_at_press[k] self._xys[k] = x_at_press + dx, y_at_press + dy @@ -4041,7 +4291,7 @@ def _onmove(self, event): if len(self._xys) > 3 and v0_dist < self.grab_range: self._xys[-1] = self._xys[0] else: - self._xys[-1] = self._get_data_coords(event) + self._xys[-1] = (event.xdata, event.ydata) self._draw_polygon() @@ -4063,12 +4313,12 @@ def _on_key_release(self, event): and (event.key == self._state_modifier_keys.get('move_vertex') or event.key == self._state_modifier_keys.get('move_all'))): - self._xys.append(self._get_data_coords(event)) + self._xys.append((event.xdata, event.ydata)) self._draw_polygon() # Reset the polygon if the released key is the 'clear' key. elif event.key == self._state_modifier_keys.get('clear'): event = self._clean_event(event) - self._xys = [self._get_data_coords(event)] + self._xys = [(event.xdata, event.ydata)] self._selection_completed = False self._remove_box() self.set_visible(True) @@ -4153,7 +4403,7 @@ class Lasso(AxesWidget): def __init__(self, ax, xy, callback, *, useblit=True, props=None): super().__init__(ax) - self.useblit = useblit and self.canvas.supports_blit + self.useblit = useblit and self.canvas.supports_blit # TODO: Make dynamic if self.useblit: self.background = self.canvas.copy_from_bbox(self.ax.bbox) @@ -4170,24 +4420,26 @@ def __init__(self, ax, xy, callback, *, useblit=True, props=None): self.connect_event('button_release_event', self.onrelease) self.connect_event('motion_notify_event', self.onmove) + @_call_with_reparented_event def onrelease(self, event): if self.ignore(event): return if self.verts is not None: - self.verts.append(self._get_data_coords(event)) + self.verts.append((event.xdata, event.ydata)) if len(self.verts) > 2: self.callback(self.verts) self.line.remove() self.verts = None self.disconnect_events() + @_call_with_reparented_event def onmove(self, event): if (self.ignore(event) or self.verts is None or event.button != 1 or not self.ax.contains(event)[0]): return - self.verts.append(self._get_data_coords(event)) + self.verts.append((event.xdata, event.ydata)) self.line.set_data(list(zip(*self.verts))) if self.useblit: diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi index c85ad2158ee7..a80ed8bf8274 100644 --- a/lib/matplotlib/widgets.pyi +++ b/lib/matplotlib/widgets.pyi @@ -4,8 +4,9 @@ from .backend_bases import FigureCanvasBase, Event, MouseEvent, MouseButton from .collections import LineCollection from .figure import Figure from .lines import Line2D -from .patches import Circle, Polygon, Rectangle +from .patches import Polygon, Rectangle from .text import Text +from .backend_tools import Cursors import PIL.Image @@ -33,10 +34,12 @@ class Widget: class AxesWidget(Widget): ax: Axes - canvas: FigureCanvasBase | None def __init__(self, ax: Axes) -> None: ... + @property + def canvas(self) -> FigureCanvasBase | None: ... def connect_event(self, event: Event, callback: Callable) -> None: ... def disconnect_events(self) -> None: ... + def _set_cursor(self, cursor: Cursors) -> None: ... class Button(AxesWidget): label: Text @@ -63,7 +66,7 @@ class SliderBase(AxesWidget): valmax: float valstep: float | ArrayLike | None drag_active: bool - valfmt: str + valfmt: str | Callable[[float], str] | None def __init__( self, ax: Axes, @@ -72,7 +75,7 @@ class SliderBase(AxesWidget): closedmax: bool, valmin: float, valmax: float, - valfmt: str, + valfmt: str | Callable[[float], str] | None, dragging: Slider | None, valstep: float | ArrayLike | None, ) -> None: ... @@ -129,7 +132,7 @@ class RangeSlider(SliderBase): valmax: float, *, valinit: tuple[float, float] | None = ..., - valfmt: str | None = ..., + valfmt: str | Callable[[float], str] | None = ..., closedmin: bool = ..., closedmax: bool = ..., dragging: bool = ..., @@ -152,12 +155,13 @@ class CheckButtons(AxesWidget): labels: Sequence[str], actives: Iterable[bool] | None = ..., *, + layout: None | Literal["vertical", "horizontal"] | tuple[int, int] = None, useblit: bool = ..., - label_props: dict[str, Any] | None = ..., + label_props: dict[str, Sequence[Any]] | None = ..., frame_props: dict[str, Any] | None = ..., check_props: dict[str, Any] | None = ..., ) -> None: ... - def set_label_props(self, props: dict[str, Any]) -> None: ... + def set_label_props(self, props: dict[str, Sequence[Any]]) -> None: ... def set_frame_props(self, props: dict[str, Any]) -> None: ... def set_check_props(self, props: dict[str, Any]) -> None: ... def set_active(self, index: int, state: bool | None = ...) -> None: ... # type: ignore[override] @@ -198,6 +202,7 @@ class TextBox(AxesWidget): class RadioButtons(AxesWidget): activecolor: ColorType value_selected: str + index_selected: int labels: list[Text] def __init__( self, @@ -206,11 +211,12 @@ class RadioButtons(AxesWidget): active: int = ..., activecolor: ColorType | None = ..., *, + layout: None | Literal["vertical", "horizontal"] | tuple[int, int] = None, useblit: bool = ..., - label_props: dict[str, Any] | Sequence[dict[str, Any]] | None = ..., + label_props: dict[str, Sequence[Any]] | None = ..., radio_props: dict[str, Any] | None = ..., ) -> None: ... - def set_label_props(self, props: dict[str, Any]) -> None: ... + def set_label_props(self, props: dict[str, Sequence[Any]]) -> None: ... def set_radio_props(self, props: dict[str, Any]) -> None: ... def set_active(self, index: int) -> None: ... def clear(self) -> None: ... @@ -269,18 +275,20 @@ class MultiCursor(Widget): class _SelectorWidget(AxesWidget): onselect: Callable[[float, float], Any] - useblit: bool + _useblit: bool background: Any validButtons: list[MouseButton] def __init__( self, ax: Axes, - onselect: Callable[[float, float], Any], + onselect: Callable[[float, float], Any] | None = ..., useblit: bool = ..., button: MouseButton | Collection[MouseButton] | None = ..., state_modifier_keys: dict[str, str] | None = ..., use_data_coordinates: bool = ..., ) -> None: ... + @property + def useblit(self) -> bool: ... def update_background(self, event: Event) -> None: ... def connect_default_events(self) -> None: ... def ignore(self, event: Event) -> bool: ... @@ -293,8 +301,6 @@ class _SelectorWidget(AxesWidget): def on_key_release(self, event: Event) -> None: ... def set_visible(self, visible: bool) -> None: ... def get_visible(self) -> bool: ... - @property - def visible(self) -> bool: ... def clear(self) -> None: ... @property def artists(self) -> tuple[Artist]: ... @@ -310,7 +316,6 @@ class SpanSelector(_SelectorWidget): grab_range: float drag_from_anywhere: bool ignore_event_outside: bool - canvas: FigureCanvasBase | None def __init__( self, ax: Axes, @@ -330,7 +335,14 @@ class SpanSelector(_SelectorWidget): ignore_event_outside: bool = ..., snap_values: ArrayLike | None = ..., ) -> None: ... - def new_axes(self, ax: Axes, *, _props: dict[str, Any] | None = ...) -> None: ... + def new_axes( + self, + ax: Axes, + *, + _props: dict[str, Any] | None = ..., + _init: bool = ..., + ) -> None: ... + def _set_span_cursor(self, *, enabled: bool) -> None: ... def connect_default_events(self) -> None: ... @property def direction(self) -> Literal["horizontal", "vertical"]: ... @@ -394,10 +406,11 @@ class RectangleSelector(_SelectorWidget): minspany: float spancoords: Literal["data", "pixels"] grab_range: float + _active_handle: None | Literal["C", "N", "NE", "E", "SE", "S", "SW", "W", "NW"] def __init__( self, ax: Axes, - onselect: Callable[[MouseEvent, MouseEvent], Any], + onselect: Callable[[MouseEvent, MouseEvent], Any] | None = ..., *, minspanx: float = ..., minspany: float = ..., @@ -437,7 +450,7 @@ class LassoSelector(_SelectorWidget): def __init__( self, ax: Axes, - onselect: Callable[[list[tuple[float, float]]], Any], + onselect: Callable[[list[tuple[float, float]]], Any] | None = ..., *, useblit: bool = ..., props: dict[str, Any] | None = ..., @@ -449,7 +462,7 @@ class PolygonSelector(_SelectorWidget): def __init__( self, ax: Axes, - onselect: Callable[[ArrayLike, ArrayLike], Any], + onselect: Callable[[ArrayLike, ArrayLike], Any] | None = ..., *, useblit: bool = ..., props: dict[str, Any] | None = ..., diff --git a/lib/mpl_toolkits/axes_grid1/anchored_artists.py b/lib/mpl_toolkits/axes_grid1/anchored_artists.py index 1238310b462b..a8be06800a07 100644 --- a/lib/mpl_toolkits/axes_grid1/anchored_artists.py +++ b/lib/mpl_toolkits/axes_grid1/anchored_artists.py @@ -1,12 +1,12 @@ -from matplotlib import _api, transforms +from matplotlib import transforms from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox, DrawingArea, TextArea, VPacker) -from matplotlib.patches import (Rectangle, Ellipse, ArrowStyle, +from matplotlib.patches import (Rectangle, ArrowStyle, FancyArrowPatch, PathPatch) from matplotlib.text import TextPath __all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox', - 'AnchoredEllipse', 'AnchoredSizeBar', 'AnchoredDirectionArrows'] + 'AnchoredSizeBar', 'AnchoredDirectionArrows'] class AnchoredDrawingArea(AnchoredOffsetbox): @@ -83,7 +83,7 @@ def __init__(self, transform, loc, ---------- transform : `~matplotlib.transforms.Transform` The transformation object for the coordinate system in use, i.e., - :attr:`matplotlib.axes.Axes.transData`. + :attr:`!matplotlib.axes.Axes.transData`. loc : str Location of this artist. Valid locations are 'upper left', 'upper center', 'upper right', @@ -124,54 +124,6 @@ def __init__(self, transform, loc, **kwargs) -@_api.deprecated("3.8") -class AnchoredEllipse(AnchoredOffsetbox): - def __init__(self, transform, width, height, angle, loc, - pad=0.1, borderpad=0.1, prop=None, frameon=True, **kwargs): - """ - Draw an anchored ellipse of a given size. - - Parameters - ---------- - transform : `~matplotlib.transforms.Transform` - The transformation object for the coordinate system in use, i.e., - :attr:`matplotlib.axes.Axes.transData`. - width, height : float - Width and height of the ellipse, given in coordinates of - *transform*. - angle : float - Rotation of the ellipse, in degrees, anti-clockwise. - loc : str - Location of the ellipse. Valid locations are - 'upper left', 'upper center', 'upper right', - 'center left', 'center', 'center right', - 'lower left', 'lower center', 'lower right'. - For backward compatibility, numeric values are accepted as well. - See the parameter *loc* of `.Legend` for details. - pad : float, default: 0.1 - Padding around the ellipse, in fraction of the font size. - borderpad : float, default: 0.1 - Border padding, in fraction of the font size. - frameon : bool, default: True - If True, draw a box around the ellipse. - prop : `~matplotlib.font_manager.FontProperties`, optional - Font property used as a reference for paddings. - **kwargs - Keyword arguments forwarded to `.AnchoredOffsetbox`. - - Attributes - ---------- - ellipse : `~matplotlib.patches.Ellipse` - Ellipse patch drawn. - """ - self._box = AuxTransformBox(transform) - self.ellipse = Ellipse((0, 0), width, height, angle=angle) - self._box.add_artist(self.ellipse) - - super().__init__(loc, pad=pad, borderpad=borderpad, child=self._box, - prop=prop, frameon=frameon, **kwargs) - - class AnchoredSizeBar(AnchoredOffsetbox): def __init__(self, transform, size, label, loc, pad=0.1, borderpad=0.1, sep=2, @@ -185,7 +137,7 @@ def __init__(self, transform, size, label, loc, ---------- transform : `~matplotlib.transforms.Transform` The transformation object for the coordinate system in use, i.e., - :attr:`matplotlib.axes.Axes.transData`. + :attr:`!matplotlib.axes.Axes.transData`. size : float Horizontal length of the size bar, given in coordinates of *transform*. @@ -304,7 +256,7 @@ def __init__(self, transform, label_x, label_y, length=0.15, ---------- transform : `~matplotlib.transforms.Transform` The transformation object for the coordinate system in use, i.e., - :attr:`matplotlib.axes.Axes.transAxes`. + :attr:`!matplotlib.axes.Axes.transAxes`. label_x, label_y : str Label text for the x and y arrows length : float, default: 0.15 diff --git a/lib/mpl_toolkits/axes_grid1/axes_divider.py b/lib/mpl_toolkits/axes_grid1/axes_divider.py index f6c38f35dbc4..f88b69dddea0 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_divider.py +++ b/lib/mpl_toolkits/axes_grid1/axes_divider.py @@ -199,31 +199,6 @@ def new_locator(self, nx, ny, nx1=None, ny1=None): locator.get_subplotspec = self.get_subplotspec return locator - @_api.deprecated( - "3.8", alternative="divider.new_locator(...)(ax, renderer)") - def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): - """ - Implementation of ``divider.new_locator().__call__``. - - Parameters - ---------- - nx, nx1 : int - Integers specifying the column-position of the cell. When *nx1* is - None, a single *nx*-th column is specified. Otherwise, the - location of columns spanning between *nx* to *nx1* (but excluding - *nx1*-th column) is specified. - ny, ny1 : int - Same as *nx* and *nx1*, but for row positions. - axes - renderer - """ - xref = self._xrefindex - yref = self._yrefindex - return self._locate( - nx - xref, (nx + 1 if nx1 is None else nx1) - xref, - ny - yref, (ny + 1 if ny1 is None else ny1) - yref, - axes, renderer) - def _locate(self, nx, ny, nx1, ny1, axes, renderer): """ Implementation of ``divider.new_locator().__call__``. @@ -305,57 +280,6 @@ def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None): self.append_size(d, Size._AxesDecorationsSize(use_axes, d) + pad) -@_api.deprecated("3.8") -class AxesLocator: - """ - A callable object which returns the position and size of a given - `.AxesDivider` cell. - """ - - def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): - """ - Parameters - ---------- - axes_divider : `~mpl_toolkits.axes_grid1.axes_divider.AxesDivider` - nx, nx1 : int - Integers specifying the column-position of the - cell. When *nx1* is None, a single *nx*-th column is - specified. Otherwise, location of columns spanning between *nx* - to *nx1* (but excluding *nx1*-th column) is specified. - ny, ny1 : int - Same as *nx* and *nx1*, but for row positions. - """ - self._axes_divider = axes_divider - - _xrefindex = axes_divider._xrefindex - _yrefindex = axes_divider._yrefindex - - self._nx, self._ny = nx - _xrefindex, ny - _yrefindex - - if nx1 is None: - nx1 = len(self._axes_divider) - if ny1 is None: - ny1 = len(self._axes_divider[0]) - - self._nx1 = nx1 - _xrefindex - self._ny1 = ny1 - _yrefindex - - def __call__(self, axes, renderer): - - _xrefindex = self._axes_divider._xrefindex - _yrefindex = self._axes_divider._yrefindex - - return self._axes_divider.locate(self._nx + _xrefindex, - self._ny + _yrefindex, - self._nx1 + _xrefindex, - self._ny1 + _yrefindex, - axes, - renderer) - - def get_subplotspec(self): - return self._axes_divider.get_subplotspec() - - class SubplotDivider(Divider): """ The Divider class whose rectangle area is specified as a subplot geometry. @@ -515,7 +439,7 @@ def append_axes(self, position, size, pad=None, *, axes_class=None, **kwargs All extra keywords arguments are passed to the created axes. """ - create_axes, pack_start = _api.check_getitem({ + create_axes, pack_start = _api.getitem_checked({ "left": (self.new_horizontal, True), "right": (self.new_horizontal, False), "bottom": (self.new_vertical, True), diff --git a/lib/mpl_toolkits/axes_grid1/axes_grid.py b/lib/mpl_toolkits/axes_grid1/axes_grid.py index 315a7bccd668..b26c87edce1c 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_grid.py +++ b/lib/mpl_toolkits/axes_grid1/axes_grid.py @@ -17,14 +17,9 @@ def __init__(self, *args, orientation, **kwargs): super().__init__(*args, **kwargs) def colorbar(self, mappable, **kwargs): - return self.figure.colorbar( + return self.get_figure(root=False).colorbar( mappable, cax=self, location=self.orientation, **kwargs) - @_api.deprecated("3.8", alternative="ax.tick_params and colorbar.set_label") - def toggle_label(self, b): - axis = self.axis[self.orientation] - axis.toggle(ticklabels=b, label=b) - _cbaraxes_class_factory = cbook._make_class_factory(CbarAxesBase, "Cbar{}") @@ -56,16 +51,17 @@ class Grid: in the usage pattern ``grid.axes_row[row][col]``. axes_llc : Axes The Axes in the lower left corner. - ngrids : int + n_axes : int Number of Axes in the grid. """ _defaultAxesClass = Axes + @_api.rename_parameter("3.11", "ngrids", "n_axes") def __init__(self, fig, rect, nrows_ncols, - ngrids=None, + n_axes=None, direction="row", axes_pad=0.02, *, @@ -88,8 +84,8 @@ def __init__(self, fig, ``121``), or as a `~.SubplotSpec`. nrows_ncols : (int, int) Number of rows and columns in the grid. - ngrids : int or None, default: None - If not None, only the first *ngrids* axes in the grid are created. + n_axes : int, optional + If given, only the first *n_axes* axes in the grid are created. direction : {"row", "column"}, default: "row" Whether axes are created in row-major ("row by row") or column-major order ("column by column"). This also affects the @@ -121,14 +117,12 @@ def __init__(self, fig, """ self._nrows, self._ncols = nrows_ncols - if ngrids is None: - ngrids = self._nrows * self._ncols + if n_axes is None: + n_axes = self._nrows * self._ncols else: - if not 0 < ngrids <= self._nrows * self._ncols: + if not 0 < n_axes <= self._nrows * self._ncols: raise ValueError( - "ngrids must be positive and not larger than nrows*ncols") - - self.ngrids = ngrids + "n_axes must be positive and not larger than nrows*ncols") self._horiz_pad_size, self._vert_pad_size = map( Size.Fixed, np.broadcast_to(axes_pad, 2)) @@ -155,7 +149,7 @@ def __init__(self, fig, rect = self._divider.get_position() axes_array = np.full((self._nrows, self._ncols), None, dtype=object) - for i in range(self.ngrids): + for i in range(n_axes): col, row = self._get_col_row(i) if share_all: sharex = sharey = axes_array[0, 0] @@ -165,9 +159,9 @@ def __init__(self, fig, axes_array[row, col] = axes_class( fig, rect, sharex=sharex, sharey=sharey) self.axes_all = axes_array.ravel( - order="C" if self._direction == "row" else "F").tolist() - self.axes_column = axes_array.T.tolist() - self.axes_row = axes_array.tolist() + order="C" if self._direction == "row" else "F").tolist()[:n_axes] + self.axes_row = [[ax for ax in row if ax] for row in axes_array] + self.axes_column = [[ax for ax in col if ax] for col in axes_array.T] self.axes_llc = self.axes_column[0][-1] self._init_locators() @@ -182,7 +176,7 @@ def _init_locators(self): [Size.Scaled(1), self._horiz_pad_size] * (self._ncols-1) + [Size.Scaled(1)]) self._divider.set_vertical( [Size.Scaled(1), self._vert_pad_size] * (self._nrows-1) + [Size.Scaled(1)]) - for i in range(self.ngrids): + for i in range(self.n_axes): col, row = self._get_col_row(i) self.axes_all[i].set_axes_locator( self._divider.new_locator(nx=2 * col, ny=2 * (self._nrows - 1 - row))) @@ -195,6 +189,9 @@ def _get_col_row(self, n): return col, row + n_axes = property(lambda self: len(self.axes_all)) + ngrids = _api.deprecated('3.11')(property(lambda self: len(self.axes_all))) + # Good to propagate __len__ if we have __getitem__ def __len__(self): return len(self.axes_all) @@ -256,28 +253,27 @@ def set_label_mode(self, mode): - "keep": Do not do anything. """ _api.check_in_list(["all", "L", "1", "keep"], mode=mode) - is_last_row, is_first_col = ( - np.mgrid[:self._nrows, :self._ncols] == [[[self._nrows - 1]], [[0]]]) - if mode == "all": - bottom = left = np.full((self._nrows, self._ncols), True) - elif mode == "L": - bottom = is_last_row - left = is_first_col - elif mode == "1": - bottom = left = is_last_row & is_first_col - else: + if mode == "keep": return - for i in range(self._nrows): - for j in range(self._ncols): + for i, j in np.ndindex(self._nrows, self._ncols): + try: ax = self.axes_row[i][j] - if isinstance(ax.axis, MethodType): - bottom_axis = SimpleAxisArtist(ax.xaxis, 1, ax.spines["bottom"]) - left_axis = SimpleAxisArtist(ax.yaxis, 1, ax.spines["left"]) - else: - bottom_axis = ax.axis["bottom"] - left_axis = ax.axis["left"] - bottom_axis.toggle(ticklabels=bottom[i, j], label=bottom[i, j]) - left_axis.toggle(ticklabels=left[i, j], label=left[i, j]) + except IndexError: + continue + if isinstance(ax.axis, MethodType): + bottom_axis = SimpleAxisArtist(ax.xaxis, 1, ax.spines["bottom"]) + left_axis = SimpleAxisArtist(ax.yaxis, 1, ax.spines["left"]) + else: + bottom_axis = ax.axis["bottom"] + left_axis = ax.axis["left"] + display_at_bottom = (i == self._nrows - 1 if mode == "L" else + i == self._nrows - 1 and j == 0 if mode == "1" else + True) # if mode == "all" + display_at_left = (j == 0 if mode == "L" else + i == self._nrows - 1 and j == 0 if mode == "1" else + True) # if mode == "all" + bottom_axis.toggle(ticklabels=display_at_bottom, label=display_at_bottom) + left_axis.toggle(ticklabels=display_at_left, label=display_at_left) def get_divider(self): return self._divider @@ -302,7 +298,7 @@ class ImageGrid(Grid): def __init__(self, fig, rect, nrows_ncols, - ngrids=None, + n_axes=None, direction="row", axes_pad=0.02, *, @@ -326,8 +322,8 @@ def __init__(self, fig, as a three-digit subplot position code (e.g., "121"). nrows_ncols : (int, int) Number of rows and columns in the grid. - ngrids : int or None, default: None - If not None, only the first *ngrids* axes in the grid are created. + n_axes : int, optional + If given, only the first *n_axes* axes in the grid are created. direction : {"row", "column"}, default: "row" Whether axes are created in row-major ("row by row") or column-major order ("column by column"). This also affects the @@ -354,16 +350,21 @@ def __init__(self, fig, Whether to create a colorbar for "each" axes, a "single" colorbar for the entire grid, colorbars only for axes on the "edge" determined by *cbar_location*, or no colorbars. The colorbars are - stored in the :attr:`cbar_axes` attribute. + stored in the :attr:`!cbar_axes` attribute. cbar_location : {"left", "right", "bottom", "top"}, default: "right" cbar_pad : float, default: None Padding between the image axes and the colorbar axes. - cbar_size : size specification (see `.Size.from_any`), default: "5%" + + .. versionchanged:: 3.10 + ``cbar_mode="single"`` no longer adds *axes_pad* between the axes + and the colorbar if the *cbar_location* is "left" or "bottom". + + cbar_size : size specification (see `!.Size.from_any`), default: "5%" Colorbar size. cbar_set_cax : bool, default: True If True, each axes in the grid has a *cax* attribute that is bound to associated *cbar_axes*. - axes_class : subclass of `matplotlib.axes.Axes`, default: None + axes_class : subclass of `matplotlib.axes.Axes`, default: `.mpl_axes.Axes` """ _api.check_in_list(["each", "single", "edge", None], cbar_mode=cbar_mode) @@ -376,7 +377,7 @@ def __init__(self, fig, # The colorbar axes are created in _init_locators(). super().__init__( - fig, rect, nrows_ncols, ngrids, + fig, rect, nrows_ncols, n_axes, direction=direction, axes_pad=axes_pad, share_all=share_all, share_x=True, share_y=True, aspect=aspect, label_mode=label_mode, axes_class=axes_class) @@ -410,9 +411,9 @@ def _init_locators(self): self._colorbar_pad = self._vert_pad_size.fixed_size self.cbar_axes = [ _cbaraxes_class_factory(self._defaultAxesClass)( - self.axes_all[0].figure, self._divider.get_position(), + self.axes_all[0].get_figure(root=False), self._divider.get_position(), orientation=self._colorbar_location) - for _ in range(self.ngrids)] + for _ in range(self.n_axes)] cb_mode = self._colorbar_mode cb_location = self._colorbar_location @@ -433,13 +434,13 @@ def _init_locators(self): v.append(Size.from_any(self._colorbar_size, sz)) v.append(Size.from_any(self._colorbar_pad, sz)) locator = self._divider.new_locator(nx=0, nx1=-1, ny=0) - for i in range(self.ngrids): + for i in range(self.n_axes): self.cbar_axes[i].set_visible(False) self.cbar_axes[0].set_axes_locator(locator) self.cbar_axes[0].set_visible(True) for col, ax in enumerate(self.axes_row[0]): - if h: + if col != 0: h.append(self._horiz_pad_size) if ax: @@ -468,7 +469,7 @@ def _init_locators(self): v_ax_pos = [] v_cb_pos = [] for row, ax in enumerate(self.axes_column[0][::-1]): - if v: + if row != 0: v.append(self._vert_pad_size) if ax: @@ -494,7 +495,7 @@ def _init_locators(self): v_cb_pos.append(len(v)) v.append(Size.from_any(self._colorbar_size, sz)) - for i in range(self.ngrids): + for i in range(self.n_axes): col, row = self._get_col_row(i) locator = self._divider.new_locator(nx=h_ax_pos[col], ny=v_ax_pos[self._nrows-1-row]) @@ -534,12 +535,12 @@ def _init_locators(self): v.append(Size.from_any(self._colorbar_size, sz)) locator = self._divider.new_locator(nx=0, nx1=-1, ny=-2) if cb_location in ("right", "top"): - for i in range(self.ngrids): + for i in range(self.n_axes): self.cbar_axes[i].set_visible(False) self.cbar_axes[0].set_axes_locator(locator) self.cbar_axes[0].set_visible(True) elif cb_mode == "each": - for i in range(self.ngrids): + for i in range(self.n_axes): self.cbar_axes[i].set_visible(True) elif cb_mode == "edge": if cb_location in ("right", "left"): @@ -548,10 +549,10 @@ def _init_locators(self): count = self._ncols for i in range(count): self.cbar_axes[i].set_visible(True) - for j in range(i + 1, self.ngrids): + for j in range(i + 1, self.n_axes): self.cbar_axes[j].set_visible(False) else: - for i in range(self.ngrids): + for i in range(self.n_axes): self.cbar_axes[i].set_visible(False) self.cbar_axes[i].set_position([1., 1., 0.001, 0.001], which="active") diff --git a/lib/mpl_toolkits/axes_grid1/axes_size.py b/lib/mpl_toolkits/axes_grid1/axes_size.py index e417c1a899ac..55820827cd6a 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_size.py +++ b/lib/mpl_toolkits/axes_grid1/axes_size.py @@ -1,12 +1,16 @@ """ Provides classes of simple units that will be used with `.AxesDivider` class (or others) to determine the size of each Axes. The unit -classes define `get_size` method that returns a tuple of two floats, +classes define `!get_size` method that returns a tuple of two floats, meaning relative and absolute sizes, respectively. Note that this class is nothing more than a simple tuple of two floats. Take a look at the Divider class to see how these two values are used. + +Once created, the unit classes can be modified by simple arithmetic +operations: addition /subtraction with another unit type or a real number and scaling +(multiplication or division) by a real number. """ from numbers import Real @@ -17,14 +21,33 @@ class (or others) to determine the size of each Axes. The unit class _Base: def __rmul__(self, other): + return self * other + + def __mul__(self, other): + if not isinstance(other, Real): + return NotImplemented return Fraction(other, self) + def __div__(self, other): + return (1 / other) * self + def __add__(self, other): if isinstance(other, _Base): return Add(self, other) else: return Add(self, Fixed(other)) + def __neg__(self): + return -1 * self + + def __radd__(self, other): + # other cannot be a _Base instance, because A + B would trigger + # A.__add__(B) first. + return Add(self, Fixed(other)) + + def __sub__(self, other): + return self + (-other) + def get_size(self, renderer): """ Return two-float tuple with relative and absolute sizes. diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 6d591a45311b..a1a9cc8df591 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -6,58 +6,13 @@ from matplotlib.offsetbox import AnchoredOffsetbox from matplotlib.patches import Patch, Rectangle from matplotlib.path import Path -from matplotlib.transforms import Bbox, BboxTransformTo +from matplotlib.transforms import Bbox from matplotlib.transforms import IdentityTransform, TransformedBbox from . import axes_size as Size from .parasite_axes import HostAxes -@_api.deprecated("3.8", alternative="Axes.inset_axes") -class InsetPosition: - @_docstring.dedent_interpd - def __init__(self, parent, lbwh): - """ - An object for positioning an inset axes. - - This is created by specifying the normalized coordinates in the axes, - instead of the figure. - - Parameters - ---------- - parent : `~matplotlib.axes.Axes` - Axes to use for normalizing coordinates. - - lbwh : iterable of four floats - The left edge, bottom edge, width, and height of the inset axes, in - units of the normalized coordinate of the *parent* axes. - - See Also - -------- - :meth:`matplotlib.axes.Axes.set_axes_locator` - - Examples - -------- - The following bounds the inset axes to a box with 20%% of the parent - axes height and 40%% of the width. The size of the axes specified - ([0, 0, 1, 1]) ensures that the axes completely fills the bounding box: - - >>> parent_axes = plt.gca() - >>> ax_ins = plt.axes([0, 0, 1, 1]) - >>> ip = InsetPosition(parent_axes, [0.5, 0.1, 0.4, 0.2]) - >>> ax_ins.set_axes_locator(ip) - """ - self.parent = parent - self.lbwh = lbwh - - def __call__(self, ax, renderer): - bbox_parent = self.parent.get_position(original=False) - trans = BboxTransformTo(bbox_parent) - bbox_inset = Bbox.from_bounds(*self.lbwh) - bb = TransformedBbox(bbox_inset, trans) - return bb - - class AnchoredLocatorBase(AnchoredOffsetbox): def __init__(self, bbox_to_anchor, offsetbox, loc, borderpad=0.5, bbox_transform=None): @@ -70,13 +25,14 @@ def draw(self, renderer): raise RuntimeError("No draw method should be called") def __call__(self, ax, renderer): + fig = ax.get_figure(root=False) if renderer is None: - renderer = ax.figure._get_renderer() + renderer = fig._get_renderer() self.axes = ax bbox = self.get_window_extent(renderer) px, py = self.get_offset(bbox.width, bbox.height, 0, 0, renderer) bbox_canvas = Bbox.from_bounds(px, py, bbox.width, bbox.height) - tr = ax.figure.transSubfigure.inverted() + tr = fig.transSubfigure.inverted() return TransformedBbox(bbox_canvas, tr) @@ -130,7 +86,7 @@ def get_bbox(self, renderer): class BboxPatch(Patch): - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, bbox, **kwargs): """ Patch showing the shape bounded by a Bbox. @@ -192,7 +148,7 @@ def connect_bbox(bbox1, bbox2, loc1, loc2=None): x2, y2 = BboxConnector.get_bbox_edge_pos(bbox2, loc2) return Path([[x1, y1], [x2, y2]]) - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, bbox1, bbox2, loc1, loc2=None, **kwargs): """ Connect two bboxes with a straight line. @@ -236,7 +192,7 @@ def get_path(self): class BboxConnectorPatch(BboxConnector): - @_docstring.dedent_interpd + @_docstring.interpd def __init__(self, bbox1, bbox2, loc1a, loc2a, loc1b, loc2b, **kwargs): """ Connect two bboxes with a quadrilateral. @@ -287,13 +243,14 @@ def _add_inset_axes(parent_axes, axes_class, axes_kwargs, axes_locator): axes_class = HostAxes if axes_kwargs is None: axes_kwargs = {} + fig = parent_axes.get_figure(root=False) inset_axes = axes_class( - parent_axes.figure, parent_axes.get_position(), + fig, parent_axes.get_position(), **{"navigate": False, **axes_kwargs, "axes_locator": axes_locator}) - return parent_axes.figure.add_axes(inset_axes) + return fig.add_axes(inset_axes) -@_docstring.dedent_interpd +@_docstring.interpd def inset_axes(parent_axes, width, height, loc='upper right', bbox_to_anchor=None, bbox_transform=None, axes_class=None, axes_kwargs=None, @@ -384,18 +341,24 @@ def inset_axes(parent_axes, width, height, loc='upper right', %(Axes:kwdoc)s - borderpad : float, default: 0.5 + borderpad : float or (float, float), default: 0.5 Padding between inset axes and the bbox_to_anchor. + If a float, the same padding is used for both x and y. + If a tuple of two floats, it specifies the (x, y) padding. The units are axes font size, i.e. for a default font size of 10 points *borderpad = 0.5* is equivalent to a padding of 5 points. + .. versionadded:: 3.11 + The *borderpad* parameter now accepts a tuple of (x, y) paddings. + Returns ------- inset_axes : *axes_class* Inset axes object created. """ - if (bbox_transform in [parent_axes.transAxes, parent_axes.figure.transFigure] + if (bbox_transform in [parent_axes.transAxes, + parent_axes.get_figure(root=False).transFigure] and bbox_to_anchor is None): _api.warn_external("Using the axes or figure transform requires a " "bounding box in the respective coordinates. " @@ -416,7 +379,7 @@ def inset_axes(parent_axes, width, height, loc='upper right', bbox_transform=bbox_transform, borderpad=borderpad)) -@_docstring.dedent_interpd +@_docstring.interpd def zoomed_inset_axes(parent_axes, zoom, loc='upper right', bbox_to_anchor=None, bbox_transform=None, axes_class=None, axes_kwargs=None, @@ -509,7 +472,7 @@ def get_points(self): return super().get_points() -@_docstring.dedent_interpd +@_docstring.interpd def mark_inset(parent_axes, inset_axes, loc1, loc2, **kwargs): """ Draw a box to mark the location of an area represented by an inset axes. diff --git a/lib/mpl_toolkits/axes_grid1/parasite_axes.py b/lib/mpl_toolkits/axes_grid1/parasite_axes.py index 2a2b5957e844..fbc6e8141272 100644 --- a/lib/mpl_toolkits/axes_grid1/parasite_axes.py +++ b/lib/mpl_toolkits/axes_grid1/parasite_axes.py @@ -13,7 +13,8 @@ def __init__(self, parent_axes, aux_transform=None, self.transAux = aux_transform self.set_viewlim_mode(viewlim_mode) kwargs["frameon"] = False - super().__init__(parent_axes.figure, parent_axes._position, **kwargs) + super().__init__(parent_axes.get_figure(root=False), + parent_axes._position, **kwargs) def clear(self): super().clear() @@ -24,6 +25,9 @@ def clear(self): self._parent_axes.callbacks._connect_picklable( "ylim_changed", self._sync_lims) + def get_axes_locator(self): + return self._parent_axes.get_axes_locator() + def pick(self, mouseevent): # This most likely goes to Artist.pick (depending on axes_class given # to the factory), which only handles pick events registered on the @@ -215,8 +219,7 @@ def _remove_any_twin(self, ax): self.axis[tuple(restore)].set_visible(True) self.axis[tuple(restore)].toggle(ticklabels=False, label=False) - @_api.make_keyword_only("3.8", "call_axes_locator") - def get_tightbbox(self, renderer=None, call_axes_locator=True, + def get_tightbbox(self, renderer=None, *, call_axes_locator=True, bbox_extra_artists=None): bbs = [ *[ax.get_tightbbox(renderer, call_axes_locator=call_axes_locator) diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_artists.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_artists.png index 8729ba90f148..fd5bd33f9716 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_artists.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_artists.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows.png index cb3a57b30e24..db8339da9d98 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows_many_args.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows_many_args.png index a0fbc804f140..0cdb7a9a45d1 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows_many_args.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_direction_arrows_many_args.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_locator_base_call.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_locator_base_call.png index 31c63d7df718..ad293669c14c 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_locator_base_call.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/anchored_locator_base_call.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid.png index a696787a0248..5ba5f11a4876 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_each_left_label_mode_all.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_each_left_label_mode_all.png index f9a4524b5812..f1412b124f34 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_each_left_label_mode_all.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_each_left_label_mode_all.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_single_bottom_label_mode_1.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_single_bottom_label_mode_1.png index 1a0f4cd1fc9a..4cce01ba1b86 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_single_bottom_label_mode_1.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/image_grid_single_bottom_label_mode_1.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/imagegrid_cbar_mode.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/imagegrid_cbar_mode.png index 9cb576faa49a..e855c0cc7b0c 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/imagegrid_cbar_mode.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/imagegrid_cbar_mode.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_axes.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_axes.png index 90498f5d441b..b3ab335cb09a 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_axes.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_axes.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_locator.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_locator.png index c7ad1e64b84d..7246cc162d43 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_locator.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inset_locator.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png deleted file mode 100644 index e8676cfd6c95..000000000000 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png and /dev/null differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inverted_zoomed_axes.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inverted_zoomed_axes.png index 2b7b3ea5b627..9fd039cfeb79 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inverted_zoomed_axes.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/inverted_zoomed_axes.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/rgb_axes.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/rgb_axes.png index 5cf6dc7e35c0..0159cf22c62d 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/rgb_axes.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/rgb_axes.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/twin_axes_empty_and_removed.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/twin_axes_empty_and_removed.png index 096104fa0533..34cf84e75efe 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/twin_axes_empty_and_removed.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/twin_axes_empty_and_removed.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/zoomed_axes.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/zoomed_axes.png index 7545bb05d50d..750dfbbd1b5a 100644 Binary files a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/zoomed_axes.png and b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/zoomed_axes.png differ diff --git a/lib/mpl_toolkits/axes_grid1/tests/conftest.py b/lib/mpl_toolkits/axes_grid1/tests/conftest.py index 61c2de3e07ba..12eaac9ce2f4 100644 --- a/lib/mpl_toolkits/axes_grid1/tests/conftest.py +++ b/lib/mpl_toolkits/axes_grid1/tests/conftest.py @@ -1,2 +1,3 @@ -from matplotlib.testing.conftest import (mpl_test_settings, # noqa - pytest_configure, pytest_unconfigure) +from matplotlib.testing.conftest import ( # noqa + pytest_configure, pytest_unconfigure, + high_memory, mpl_test_settings) diff --git a/lib/mpl_toolkits/axes_grid1/tests/test_axes_grid1.py b/lib/mpl_toolkits/axes_grid1/tests/test_axes_grid1.py index 7c444f6ae178..5a6a229f3c59 100644 --- a/lib/mpl_toolkits/axes_grid1/tests/test_axes_grid1.py +++ b/lib/mpl_toolkits/axes_grid1/tests/test_axes_grid1.py @@ -1,6 +1,7 @@ from itertools import product import io import platform +import sys import matplotlib as mpl import matplotlib.pyplot as plt @@ -9,7 +10,7 @@ from matplotlib.backend_bases import MouseEvent from matplotlib.colors import LogNorm from matplotlib.patches import Circle, Ellipse -from matplotlib.transforms import Bbox, TransformedBbox +from matplotlib.transforms import Affine2D, Bbox, TransformedBbox from matplotlib.testing.decorators import ( check_figures_equal, image_comparison, remove_ticks_and_titles) @@ -18,15 +19,15 @@ host_subplot, make_axes_locatable, Grid, AxesGrid, ImageGrid) from mpl_toolkits.axes_grid1.anchored_artists import ( - AnchoredAuxTransformBox, AnchoredDrawingArea, AnchoredEllipse, + AnchoredAuxTransformBox, AnchoredDrawingArea, AnchoredDirectionArrows, AnchoredSizeBar) from mpl_toolkits.axes_grid1.axes_divider import ( Divider, HBoxDivider, make_axes_area_auto_adjustable, SubplotDivider, VBoxDivider) from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes from mpl_toolkits.axes_grid1.inset_locator import ( - zoomed_inset_axes, mark_inset, inset_axes, BboxConnectorPatch, - InsetPosition) + zoomed_inset_axes, mark_inset, inset_axes, BboxConnectorPatch) +from mpl_toolkits.axes_grid1.parasite_axes import HostAxes import mpl_toolkits.axes_grid1.mpl_axes import pytest @@ -61,16 +62,14 @@ def test_divider_append_axes(): assert bboxes["top"].x1 == bboxes["main"].x1 == bboxes["bottom"].x1 -# Update style when regenerating the test image -@image_comparison(['twin_axes_empty_and_removed'], extensions=["png"], tol=1, - style=('classic', '_classic_test_patch')) +@image_comparison(['twin_axes_empty_and_removed.png'], style='mpl20') def test_twin_axes_empty_and_removed(): # Purely cosmetic font changes (avoid overlap) - mpl.rcParams.update( - {"font.size": 8, "xtick.labelsize": 8, "ytick.labelsize": 8}) + mpl.rcParams.update({"font.size": 8, "xtick.labelsize": 8, "ytick.labelsize": 8}) generators = ["twinx", "twiny", "twin"] modifiers = ["", "host invisible", "twin removed", "twin invisible", "twin removed\nhost invisible"] + plt.figure(figsize=(8, 6)) # Unmodified host subplot at the beginning for reference h = host_subplot(len(modifiers)+1, len(generators), 2) h.text(0.5, 0.5, "host_subplot", @@ -93,8 +92,8 @@ def test_twin_axes_empty_and_removed(): def test_twin_axes_both_with_units(): host = host_subplot(111) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - host.plot_date([0, 1, 2], [0, 1, 2], xdate=False, ydate=True) + host.yaxis.axis_date() + host.plot([0, 1, 2], [0, 1, 2]) twin = host.twinx() twin.plot(["a", "b", "c"]) assert host.get_yticklabels()[0].get_text() == "00:00:00" @@ -105,7 +104,6 @@ def test_axesgrid_colorbar_log_smoketest(): fig = plt.figure() grid = AxesGrid(fig, 111, # modified to be only subplot nrows_ncols=(1, 1), - ngrids=1, label_mode="L", cbar_location="top", cbar_mode="single", @@ -344,10 +342,8 @@ def test_fill_facecolor(): mark_inset(ax[3], axins, loc1=2, loc2=4, fc="g", ec="0.5", fill=False) -# Update style when regenerating the test image -@image_comparison(['zoomed_axes.png', 'inverted_zoomed_axes.png'], - style=('classic', '_classic_test_patch'), - tol=0.02 if platform.machine() == 'arm64' else 0) +@image_comparison(['zoomed_axes.png', 'inverted_zoomed_axes.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.03) def test_zooming_with_inverted_axes(): fig, ax = plt.subplots() ax.plot([1, 2, 3], [1, 2, 3]) @@ -362,10 +358,8 @@ def test_zooming_with_inverted_axes(): inset_ax.axis([1.4, 1.1, 1.4, 1.1]) -# Update style when regenerating the test image -@image_comparison(['anchored_direction_arrows.png'], - tol=0 if platform.machine() == 'x86_64' else 0.01, - style=('classic', '_classic_test_patch')) +@image_comparison(['anchored_direction_arrows.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.006) def test_anchored_direction_arrows(): fig, ax = plt.subplots() ax.imshow(np.zeros((10, 10)), interpolation='nearest') @@ -374,9 +368,8 @@ def test_anchored_direction_arrows(): ax.add_artist(simple_arrow) -# Update style when regenerating the test image -@image_comparison(['anchored_direction_arrows_many_args.png'], - style=('classic', '_classic_test_patch')) +@image_comparison(['anchored_direction_arrows_many_args.png'], style='mpl20', + tol=0.002 if sys.platform == 'win32' else 0) def test_anchored_direction_arrows_many_args(): fig, ax = plt.subplots() ax.imshow(np.ones((10, 10))) @@ -424,7 +417,7 @@ def test_image_grid_single_bottom(): fig = plt.figure(1, (2.5, 1.5)) grid = ImageGrid(fig, (0, 0, 1, 1), nrows_ncols=(1, 3), - axes_pad=(0.2, 0.15), cbar_mode="single", + axes_pad=(0.2, 0.15), cbar_mode="single", cbar_pad=0.3, cbar_location="bottom", cbar_size="10%", label_mode="1") # 4-tuple rect => Divider, isinstance will give True for SubplotDivider assert type(grid.get_divider()) is Divider @@ -469,6 +462,26 @@ def test_gettightbbox(): [-17.7, -13.9, 7.2, 5.4]) +def test_gettightbbox_parasite(): + fig = plt.figure() + + y0 = 0.3 + horiz = [Size.Scaled(1.0)] + vert = [Size.Scaled(1.0)] + ax0_div = Divider(fig, [0.1, y0, 0.8, 0.2], horiz, vert) + ax1_div = Divider(fig, [0.1, 0.5, 0.8, 0.4], horiz, vert) + + ax0 = fig.add_subplot( + xticks=[], yticks=[], axes_locator=ax0_div.new_locator(nx=0, ny=0)) + ax1 = fig.add_subplot( + axes_class=HostAxes, axes_locator=ax1_div.new_locator(nx=0, ny=0)) + aux_ax = ax1.get_aux_axes(Affine2D()) + + fig.canvas.draw() + rdr = fig.canvas.get_renderer() + assert rdr.get_canvas_width_height()[1] * y0 / fig.dpi == fig.get_tightbbox(rdr).y0 + + @pytest.mark.parametrize("click_on", ["big", "small"]) @pytest.mark.parametrize("big_on_axes,small_on_axes", [ ("gca", "gca"), @@ -515,7 +528,7 @@ def on_pick(event): if click_axes is axes["parasite"]: click_axes = axes["host"] (x, y) = click_axes.transAxes.transform(axes_coords) - m = MouseEvent("button_press_event", click_axes.figure.canvas, x, y, + m = MouseEvent("button_press_event", click_axes.get_figure(root=True).canvas, x, y, button=1) click_axes.pick(m) # Checks @@ -543,13 +556,6 @@ def test_anchored_artists(): box.drawing_area.add_artist(el) ax.add_artist(box) - # Manually construct the ellipse instead, once the deprecation elapses. - with pytest.warns(mpl.MatplotlibDeprecationWarning): - ae = AnchoredEllipse(ax.transData, width=0.1, height=0.25, angle=-60, - loc='lower left', pad=0.5, borderpad=0.4, - frameon=True) - ax.add_artist(ae) - asb = AnchoredSizeBar(ax.transData, 0.2, r"0.2 units", loc='lower right', pad=0.3, borderpad=0.4, sep=4, fill_bar=True, frameon=False, label_top=True, prop={'size': 20}, @@ -637,15 +643,15 @@ def test_grid_axes_position(direction): assert loc[3].args[1] == loc[2].args[1] -@pytest.mark.parametrize('rect, ngrids, error, message', ( +@pytest.mark.parametrize('rect, n_axes, error, message', ( ((1, 1), None, TypeError, "Incorrect rect format"), - (111, -1, ValueError, "ngrids must be positive"), - (111, 7, ValueError, "ngrids must be positive"), + (111, -1, ValueError, "n_axes must be positive"), + (111, 7, ValueError, "n_axes must be positive"), )) -def test_grid_errors(rect, ngrids, error, message): +def test_grid_errors(rect, n_axes, error, message): fig = plt.figure() with pytest.raises(error, match=message): - Grid(fig, rect, (2, 3), ngrids=ngrids) + Grid(fig, rect, (2, 3), n_axes=n_axes) @pytest.mark.parametrize('anchor, error, message', ( @@ -660,7 +666,7 @@ def test_divider_errors(anchor, error, message): anchor=anchor) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_mark_inset_unstales_viewlim(fig_test, fig_ref): inset, full = fig_test.subplots(1, 2) full.plot([0, 5], [0, 5]) @@ -678,7 +684,7 @@ def test_mark_inset_unstales_viewlim(fig_test, fig_ref): def test_auto_adjustable(): fig = plt.figure() - ax = fig.add_axes([0, 0, 1, 1]) + ax = fig.add_axes((0, 0, 1, 1)) pad = 0.1 make_axes_area_auto_adjustable(ax, pad=pad) fig.canvas.draw() @@ -702,17 +708,6 @@ def test_rgb_axes(): ax.imshow_rgb(r, g, b, interpolation='none') -# Update style when regenerating the test image -@image_comparison(['insetposition.png'], remove_text=True, - style=('classic', '_classic_test_patch')) -def test_insetposition(): - fig, ax = plt.subplots(figsize=(2, 2)) - ax_ins = plt.axes([0, 0, 1, 1]) - with pytest.warns(mpl.MatplotlibDeprecationWarning): - ip = InsetPosition(ax, [0.2, 0.25, 0.5, 0.4]) - ax_ins.set_axes_locator(ip) - - # The original version of this test relied on mpl_toolkits's slightly different # colorbar implementation; moving to matplotlib's own colorbar implementation # caused the small image comparison error. @@ -790,3 +785,11 @@ def test_anchored_locator_base_call(): def test_grid_with_axes_class_not_overriding_axis(): Grid(plt.figure(), 111, (2, 2), axes_class=mpl.axes.Axes) RGBAxes(plt.figure(), 111, axes_class=mpl.axes.Axes) + + +def test_grid_n_axes(): + fig = plt.figure() + grid = Grid(fig, 111, (3, 3), n_axes=5) + assert len(fig.axes) == grid.n_axes == 5 + with pytest.warns(mpl.MatplotlibDeprecationWarning, match="ngrids attribute"): + assert grid.ngrids == 5 diff --git a/lib/mpl_toolkits/axisartist/angle_helper.py b/lib/mpl_toolkits/axisartist/angle_helper.py index 1786cd70bcdb..56b461e4a1d3 100644 --- a/lib/mpl_toolkits/axisartist/angle_helper.py +++ b/lib/mpl_toolkits/axisartist/angle_helper.py @@ -1,6 +1,7 @@ import numpy as np import math +from matplotlib.transforms import Bbox from mpl_toolkits.axisartist.grid_finder import ExtremeFinderSimple @@ -347,11 +348,12 @@ def __init__(self, nx, ny, self.lon_minmax = lon_minmax self.lat_minmax = lat_minmax - def __call__(self, transform_xy, x1, y1, x2, y2): + def _find_transformed_bbox(self, trans, bbox): # docstring inherited - x, y = np.meshgrid( - np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny)) - lon, lat = transform_xy(np.ravel(x), np.ravel(y)) + grid = np.reshape(np.meshgrid(np.linspace(bbox.x0, bbox.x1, self.nx), + np.linspace(bbox.y0, bbox.y1, self.ny)), + (2, -1)).T + lon, lat = trans.transform(grid).T # iron out jumps, but algorithm should be improved. # This is just naive way of doing and my fail for some cases. @@ -367,11 +369,10 @@ def __call__(self, transform_xy, x1, y1, x2, y2): lat0 = np.nanmin(lat) lat -= 360. * ((lat - lat0) > 180.) - lon_min, lon_max = np.nanmin(lon), np.nanmax(lon) - lat_min, lat_max = np.nanmin(lat), np.nanmax(lat) - - lon_min, lon_max, lat_min, lat_max = \ - self._add_pad(lon_min, lon_max, lat_min, lat_max) + tbbox = Bbox.null() + tbbox.update_from_data_xy(np.column_stack([lon, lat])) + tbbox = tbbox.expanded(1 + 2 / self.nx, 1 + 2 / self.ny) + lon_min, lat_min, lon_max, lat_max = tbbox.extents # check cycle if self.lon_cycle: @@ -391,4 +392,4 @@ def __call__(self, transform_xy, x1, y1, x2, y2): max0 = self.lat_minmax[1] lat_max = min(max0, lat_max) - return lon_min, lon_max, lat_min, lat_max + return Bbox.from_extents(lon_min, lat_min, lon_max, lat_max) diff --git a/lib/mpl_toolkits/axisartist/axes_divider.py b/lib/mpl_toolkits/axisartist/axes_divider.py index a01d4e27df93..d0392be782d9 100644 --- a/lib/mpl_toolkits/axisartist/axes_divider.py +++ b/lib/mpl_toolkits/axisartist/axes_divider.py @@ -1,2 +1,2 @@ from mpl_toolkits.axes_grid1.axes_divider import ( # noqa - Divider, AxesLocator, SubplotDivider, AxesDivider, make_axes_locatable) + Divider, SubplotDivider, AxesDivider, make_axes_locatable) diff --git a/lib/mpl_toolkits/axisartist/axes_grid.py b/lib/mpl_toolkits/axisartist/axes_grid.py deleted file mode 100644 index ecb3e9d92c18..000000000000 --- a/lib/mpl_toolkits/axisartist/axes_grid.py +++ /dev/null @@ -1,23 +0,0 @@ -from matplotlib import _api - -import mpl_toolkits.axes_grid1.axes_grid as axes_grid_orig -from .axislines import Axes - - -_api.warn_deprecated( - "3.8", name=__name__, obj_type="module", alternative="axes_grid1.axes_grid") - - -@_api.deprecated("3.8", alternative=( - "axes_grid1.axes_grid.Grid(..., axes_class=axislines.Axes")) -class Grid(axes_grid_orig.Grid): - _defaultAxesClass = Axes - - -@_api.deprecated("3.8", alternative=( - "axes_grid1.axes_grid.ImageGrid(..., axes_class=axislines.Axes")) -class ImageGrid(axes_grid_orig.ImageGrid): - _defaultAxesClass = Axes - - -AxesGrid = ImageGrid diff --git a/lib/mpl_toolkits/axisartist/axes_rgb.py b/lib/mpl_toolkits/axisartist/axes_rgb.py deleted file mode 100644 index 2195747469a1..000000000000 --- a/lib/mpl_toolkits/axisartist/axes_rgb.py +++ /dev/null @@ -1,18 +0,0 @@ -from matplotlib import _api -from mpl_toolkits.axes_grid1.axes_rgb import ( # noqa - make_rgb_axes, RGBAxes as _RGBAxes) -from .axislines import Axes - - -_api.warn_deprecated( - "3.8", name=__name__, obj_type="module", alternative="axes_grid1.axes_rgb") - - -@_api.deprecated("3.8", alternative=( - "axes_grid1.axes_rgb.RGBAxes(..., axes_class=axislines.Axes")) -class RGBAxes(_RGBAxes): - """ - Subclass of `~.axes_grid1.axes_rgb.RGBAxes` with - ``_defaultAxesClass`` = `.axislines.Axes`. - """ - _defaultAxesClass = Axes diff --git a/lib/mpl_toolkits/axisartist/axis_artist.py b/lib/mpl_toolkits/axisartist/axis_artist.py index 407ad07a3dc2..3ba70c3d7d3b 100644 --- a/lib/mpl_toolkits/axisartist/axis_artist.py +++ b/lib/mpl_toolkits/axisartist/axis_artist.py @@ -7,7 +7,7 @@ There is one `AxisArtist` per Axis; it can be accessed through the ``axis`` dictionary of the parent Axes (which should be a -`mpl_toolkits.axislines.Axes`), e.g. ``ax.axis["bottom"]``. +`~mpl_toolkits.axisartist.axislines.Axes`), e.g. ``ax.axis["bottom"]``. Children of the AxisArtist are accessed as attributes: ``.line`` and ``.label`` for the axis line and label, ``.major_ticks``, ``.major_ticklabels``, @@ -55,14 +55,14 @@ axislabel ha right center right center =================== ====== ======== ====== ======== -Ticks are by default direct opposite side of the ticklabels. To make ticks to -the same side of the ticklabels, :: +Tick orientation is controlled by :rc:`xtick.direction` and +:rc:`ytick.direction`; they can be manually adjusted using :: - ax.axis["bottom"].major_ticks.set_tick_out(True) + ax.axis["bottom"].major_ticks.set_tick_direction("in") # or "out", "inout" The following attributes can be customized (use the ``set_xxx`` methods): -* `Ticks`: ticksize, tick_out +* `Ticks`: ticksize, tick_direction * `TickLabels`: pad * `AxisLabel`: pad """ @@ -109,16 +109,16 @@ class Ticks(AttributeCopier, Line2D): Ticks are derived from `.Line2D`, and note that ticks themselves are markers. Thus, you should use set_mec, set_mew, etc. - To change the tick size (length), you need to use - `set_ticksize`. To change the direction of the ticks (ticks are - in opposite direction of ticklabels by default), use - ``set_tick_out(False)`` + To change the tick size (length), use `set_ticksize`. + To change the direction of the ticks, use ``set_tick_direction("in")`` (or + "out", or "inout"). """ + locs_angles_labels = _api.deprecated("3.11")(property(lambda self: [])) + + @_api.delete_parameter("3.11", "tick_out", alternative="tick_direction") def __init__(self, ticksize, tick_out=False, *, axis=None, **kwargs): self._ticksize = ticksize - self.locs_angles_labels = [] - self.set_tick_out(tick_out) self._axis = axis @@ -152,13 +152,33 @@ def get_markeredgecolor(self): def get_markeredgewidth(self): return self.get_attribute_from_ref_artist("markeredgewidth") + def set_tick_direction(self, direction): + _api.check_in_list(["in", "out", "inout"], direction=direction) + self._tick_dir = direction + + def get_tick_direction(self): + return self._tick_dir + def set_tick_out(self, b): - """Set whether ticks are drawn inside or outside the axes.""" - self._tick_out = b + """ + Set whether ticks are drawn inside or outside the axes. + .. admonition:: Discouraged + Consider using the more general method `.set_tick_direction` instead. + """ + self._tick_dir = "out" if b else "in" + + @_api.deprecated("3.11", alternative="get_tick_direction") def get_tick_out(self): """Return whether ticks are drawn inside or outside the axes.""" - return self._tick_out + if self._tick_dir == "in": + return False + elif self._tick_dir == "out": + return True + else: + raise ValueError( + f"Tick direction ({self._tick_dir!r}) not supported by get_tick_out, " + f"use get_tick_direction instead") def set_ticksize(self, ticksize): """Set length of the ticks in points.""" @@ -171,29 +191,33 @@ def get_ticksize(self): def set_locs_angles(self, locs_angles): self.locs_angles = locs_angles - _tickvert_path = Path([[0., 0.], [1., 0.]]) + _tick_paths = { + "in": Path([[0, 0], [+1, 0]]), + "inout": Path([[-1/2, 0.], [+1/2, 0]]), + "out": Path([[-1, 0], [0, 0]]), + } def draw(self, renderer): if not self.get_visible(): return gc = renderer.new_gc() - gc.set_foreground(self.get_markeredgecolor()) + edgecolor = mcolors.to_rgba(self.get_markeredgecolor()) + gc.set_foreground(edgecolor, isRGBA=True) gc.set_linewidth(self.get_markeredgewidth()) gc.set_alpha(self._alpha) + tickvert_path = self._tick_paths[self._tick_dir] path_trans = self.get_transform() marker_transform = (Affine2D() .scale(renderer.points_to_pixels(self._ticksize))) - if self.get_tick_out(): - marker_transform.rotate_deg(180) for loc, angle in self.locs_angles: locs = path_trans.transform_non_affine(np.array([loc])) if self.axes and not self.axes.viewLim.contains(*locs[0]): continue renderer.draw_markers( - gc, self._tickvert_path, + gc, tickvert_path, marker_transform + Affine2D().rotate_deg(angle), Path(locs), path_trans.get_affine()) @@ -207,8 +231,9 @@ class LabelBase(mtext.Text): text_ref_angle, and offset_radius attributes. """ + locs_angles_labels = _api.deprecated("3.11")(property(lambda self: [])) + def __init__(self, *args, **kwargs): - self.locs_angles_labels = [] self._ref_angle = 0 self._offset_radius = 0. @@ -253,7 +278,7 @@ def draw(self, renderer): def get_window_extent(self, renderer=None): if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() # save original and adjust some properties tr = self.get_transform() @@ -312,13 +337,13 @@ def get_pad(self): def get_ref_artist(self): # docstring inherited - return self._axis.get_label() + return self._axis.label def get_text(self): # docstring inherited t = super().get_text() if t == "__from_axes__": - return self._axis.get_label().get_text() + return self._axis.label.get_text() return self._text _default_alignments = dict(left=("bottom", "center"), @@ -334,7 +359,7 @@ def set_default_alignment(self, d): ---------- d : {"left", "bottom", "right", "top"} """ - va, ha = _api.check_getitem(self._default_alignments, d=d) + va, ha = _api.getitem_checked(self._default_alignments, d=d) self.set_va(va) self.set_ha(ha) @@ -351,7 +376,7 @@ def set_default_angle(self, d): ---------- d : {"left", "bottom", "right", "top"} """ - self.set_rotation(_api.check_getitem(self._default_angles, d=d)) + self.set_rotation(_api.getitem_checked(self._default_angles, d=d)) def set_axis_direction(self, d): """ @@ -391,7 +416,7 @@ def draw(self, renderer): def get_window_extent(self, renderer=None): if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() if not self.get_visible(): return @@ -550,7 +575,7 @@ def set_locs_angles_labels(self, locs_angles_labels): def get_window_extents(self, renderer=None): if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() if not self.get_visible(): self._axislabel_pad = self._external_pad @@ -588,8 +613,9 @@ def get_texts_widths_heights_descents(self, renderer): if not label.strip(): continue clean_line, ismath = self._preprocess_math(label) - whd = renderer.get_text_width_height_descent( - clean_line, self._fontproperties, ismath=ismath) + whd = mtext._get_text_metrics_with_cache( + renderer, clean_line, self._fontproperties, ismath=ismath, + dpi=self.get_figure(root=True).dpi) whd_list.append(whd) return whd_list @@ -691,7 +717,7 @@ def __init__(self, axes, self.offset_transform = ScaledTranslation( *offset, Affine2D().scale(1 / 72) # points to inches. - + self.axes.figure.dpi_scale_trans) + + self.axes.get_figure(root=False).dpi_scale_trans) if axis_direction in ["left", "right"]: self.axis = axes.yaxis @@ -764,7 +790,7 @@ def set_ticklabel_direction(self, tick_direction): ---------- tick_direction : {"+", "-"} """ - self._ticklabel_add_angle = _api.check_getitem( + self._ticklabel_add_angle = _api.getitem_checked( {"+": 0, "-": 180}, tick_direction=tick_direction) def invert_ticklabel_direction(self): @@ -783,7 +809,7 @@ def set_axislabel_direction(self, label_direction): ---------- label_direction : {"+", "-"} """ - self._axislabel_add_angle = _api.check_getitem( + self._axislabel_add_angle = _api.getitem_checked( {"+": 0, "-": 180}, label_direction=label_direction) def get_transform(self): @@ -865,21 +891,23 @@ def _init_ticks(self, **kwargs): + self.offset_transform) self.major_ticks = Ticks( - kwargs.get( + ticksize=kwargs.get( "major_tick_size", mpl.rcParams[f"{axis_name}tick.major.size"]), + tick_direction=mpl.rcParams[f"{axis_name}tick.direction"], axis=self.axis, transform=trans) self.minor_ticks = Ticks( - kwargs.get( + ticksize=kwargs.get( "minor_tick_size", mpl.rcParams[f"{axis_name}tick.minor.size"]), + tick_direction=mpl.rcParams[f"{axis_name}tick.direction"], axis=self.axis, transform=trans) size = mpl.rcParams[f"{axis_name}tick.labelsize"] self.major_ticklabels = TickLabels( axis=self.axis, axis_direction=self._axis_direction, - figure=self.axes.figure, + figure=self.axes.get_figure(root=False), transform=trans, fontsize=size, pad=kwargs.get( @@ -888,7 +916,7 @@ def _init_ticks(self, **kwargs): self.minor_ticklabels = TickLabels( axis=self.axis, axis_direction=self._axis_direction, - figure=self.axes.figure, + figure=self.axes.get_figure(root=False), transform=trans, fontsize=size, pad=kwargs.get( @@ -922,16 +950,15 @@ def _update_ticks(self, renderer=None): # majorticks even for minor ticks. not clear what is best. if renderer is None: - renderer = self.figure._get_renderer() + renderer = self.get_figure(root=True)._get_renderer() - dpi_cor = renderer.points_to_pixels(1.) - if self.major_ticks.get_visible() and self.major_ticks.get_tick_out(): - ticklabel_pad = self.major_ticks._ticksize * dpi_cor - self.major_ticklabels._external_pad = ticklabel_pad - self.minor_ticklabels._external_pad = ticklabel_pad - else: - self.major_ticklabels._external_pad = 0 - self.minor_ticklabels._external_pad = 0 + self.major_ticklabels._external_pad = \ + self.minor_ticklabels._external_pad = ( + renderer.points_to_pixels(self.major_ticks._ticksize) + * {"in": 0, "inout": 1/2, "out": 1}[ + self.major_ticks.get_tick_direction()] + * self.major_ticks.get_visible() # 0 if invisible. + ) majortick_iter, minortick_iter = \ self._axis_artist_helper.get_tick_iterators(self.axes) @@ -997,7 +1024,7 @@ def _init_label(self, **kwargs): transform=tr, axis_direction=self._axis_direction, ) - self.label.set_figure(self.axes.figure) + self.label.set_figure(self.axes.get_figure(root=False)) labelpad = kwargs.get("labelpad", 5) self.label.set_pad(labelpad) @@ -1006,13 +1033,18 @@ def _update_label(self, renderer): return if self._ticklabel_add_angle != self._axislabel_add_angle: - if ((self.major_ticks.get_visible() - and not self.major_ticks.get_tick_out()) - or (self.minor_ticks.get_visible() - and not self.major_ticks.get_tick_out())): - axislabel_pad = self.major_ticks._ticksize - else: - axislabel_pad = 0 + axislabel_pad = max( + # major pad: + self.major_ticks._ticksize + * {"in": 1, "inout": 1/2, "out": 0}[ + self.major_ticks.get_tick_direction()] + * self.major_ticks.get_visible(), # 0 if invisible. + # minor pad: + self.minor_ticks._ticksize + * {"in": 1, "inout": 1/2, "out": 0}[ + self.minor_ticks.get_tick_direction()] + * self.minor_ticks.get_visible(), # 0 if invisible. + ) else: axislabel_pad = max(self.major_ticklabels._axislabel_pad, self.minor_ticklabels._axislabel_pad) diff --git a/lib/mpl_toolkits/axisartist/axisline_style.py b/lib/mpl_toolkits/axisartist/axisline_style.py index 7f25b98082ef..ac89603e0844 100644 --- a/lib/mpl_toolkits/axisartist/axisline_style.py +++ b/lib/mpl_toolkits/axisartist/axisline_style.py @@ -177,8 +177,7 @@ def __init__(self, size=1, facecolor=None): .. versionadded:: 3.7 """ - if facecolor is None: - facecolor = mpl.rcParams['axes.edgecolor'] + facecolor = mpl._val_or_rc(facecolor, 'axes.edgecolor') self.size = size self._facecolor = facecolor super().__init__(size=size) diff --git a/lib/mpl_toolkits/axisartist/axislines.py b/lib/mpl_toolkits/axisartist/axislines.py index 1d695c129ae2..68091c25ec3f 100644 --- a/lib/mpl_toolkits/axisartist/axislines.py +++ b/lib/mpl_toolkits/axisartist/axislines.py @@ -16,7 +16,7 @@ In the new axes class, xaxis and yaxis is set to not visible by default, and new set of artist (AxisArtist) are defined to draw axis line, ticks, ticklabels and axis label. Axes.axis attribute serves as -a dictionary of these artists, i.e., ax.axis["left"] is a AxisArtist +a dictionary of these artists, i.e., ax.axis["left"] is an AxisArtist instance responsible to draw left y-axis. The default Axes.axis contains "bottom", "left", "top" and "right". @@ -45,6 +45,8 @@ from matplotlib import _api import matplotlib.axes as maxes from matplotlib.path import Path +from matplotlib.transforms import Bbox + from mpl_toolkits.axes_grid1 import mpl_axes from .axisline_style import AxislineStyle # noqa from .axis_artist import AxisArtist, GridlinesCollection @@ -118,10 +120,9 @@ def _to_xy(self, values, const): class _FixedAxisArtistHelperBase(_AxisArtistHelperBase): """Helper class for a fixed (in the axes coordinate) axis.""" - @_api.delete_parameter("3.9", "nth_coord") - def __init__(self, loc, nth_coord=None): + def __init__(self, loc): """``nth_coord = 0``: x-axis; ``nth_coord = 1``: y-axis.""" - super().__init__(_api.check_getitem( + super().__init__(_api.getitem_checked( {"bottom": 0, "top": 0, "left": 1, "right": 1}, loc=loc)) self._loc = loc self._pos = {"bottom": 0, "top": 1, "left": 0, "right": 1}[loc] @@ -169,12 +170,7 @@ def get_line(self, axes): class FixedAxisArtistHelperRectilinear(_FixedAxisArtistHelperBase): - @_api.delete_parameter("3.9", "nth_coord") - def __init__(self, axes, loc, nth_coord=None): - """ - nth_coord = along which coordinate value varies - in 2D, nth_coord = 0 -> x axis, nth_coord = 1 -> y axis - """ + def __init__(self, axes, loc): super().__init__(loc) self.axis = [axes.xaxis, axes.yaxis][self.nth_coord] @@ -285,10 +281,10 @@ def update_lim(self, axes): x1, x2 = axes.get_xlim() y1, y2 = axes.get_ylim() if self._old_limits != (x1, x2, y1, y2): - self._update_grid(x1, y1, x2, y2) + self._update_grid(Bbox.from_extents(x1, y1, x2, y2)) self._old_limits = (x1, x2, y1, y2) - def _update_grid(self, x1, y1, x2, y2): + def _update_grid(self, bbox): """Cache relevant computations when the axes limits have changed.""" def get_gridlines(self, which, axis): @@ -309,10 +305,9 @@ def __init__(self, axes): super().__init__() self.axes = axes - @_api.delete_parameter( - "3.9", "nth_coord", addendum="'nth_coord' is now inferred from 'loc'.") def new_fixed_axis( - self, loc, nth_coord=None, axis_direction=None, offset=None, axes=None): + self, loc, *, axis_direction=None, offset=None, axes=None + ): if axes is None: _api.warn_external( "'new_fixed_axis' explicitly requires the axes keyword.") @@ -370,10 +365,6 @@ def get_gridlines(self, which="major", axis="both"): class Axes(maxes.Axes): - @_api.deprecated("3.8", alternative="ax.axis") - def __call__(self, *args, **kwargs): - return maxes.Axes.axis(self.axes, *args, **kwargs) - def __init__(self, *args, grid_helper=None, **kwargs): self._axisline_on = True self._grid_helper = grid_helper if grid_helper else GridHelperRectlinear(self) diff --git a/lib/mpl_toolkits/axisartist/floating_axes.py b/lib/mpl_toolkits/axisartist/floating_axes.py index 24c9ce61afa7..aa8a2129aae7 100644 --- a/lib/mpl_toolkits/axisartist/floating_axes.py +++ b/lib/mpl_toolkits/axisartist/floating_axes.py @@ -13,9 +13,8 @@ from matplotlib import _api, cbook import matplotlib.patches as mpatches from matplotlib.path import Path - +from matplotlib.transforms import Bbox from mpl_toolkits.axes_grid1.parasite_axes import host_axes_class_factory - from . import axislines, grid_helper_curvelinear from .axis_artist import AxisArtist from .grid_finder import ExtremeFinderSimple @@ -34,7 +33,7 @@ def __init__(self, grid_helper, side, nth_coord_ticks=None): nth_coord = 0 -> x axis, nth_coord = 1 -> y axis """ lon1, lon2, lat1, lat2 = grid_helper.grid_finder.extreme_finder(*[None] * 5) - value, nth_coord = _api.check_getitem( + value, nth_coord = _api.getitem_checked( dict(left=(lon1, 0), right=(lon2, 0), bottom=(lat1, 1), top=(lat2, 1)), side=side) super().__init__(grid_helper, nth_coord, value, axis_direction=side) @@ -71,25 +70,19 @@ def trf_xy(x, y): if self.nth_coord == 0: mask = (ymin <= yy0) & (yy0 <= ymax) - (xx1, yy1), (dxx1, dyy1), (dxx2, dyy2) = \ - grid_helper_curvelinear._value_and_jacobian( + (xx1, yy1), angle_normal, angle_tangent = \ + grid_helper_curvelinear._value_and_jac_angle( trf_xy, self.value, yy0[mask], (xmin, xmax), (ymin, ymax)) labels = self._grid_info["lat_labels"] elif self.nth_coord == 1: mask = (xmin <= xx0) & (xx0 <= xmax) - (xx1, yy1), (dxx2, dyy2), (dxx1, dyy1) = \ - grid_helper_curvelinear._value_and_jacobian( + (xx1, yy1), angle_tangent, angle_normal = \ + grid_helper_curvelinear._value_and_jac_angle( trf_xy, xx0[mask], self.value, (xmin, xmax), (ymin, ymax)) labels = self._grid_info["lon_labels"] labels = [l for l, m in zip(labels, mask) if m] - - angle_normal = np.arctan2(dyy1, dxx1) - angle_tangent = np.arctan2(dyy2, dxx2) - mm = (dyy1 == 0) & (dxx1 == 0) # points with degenerate normal - angle_normal[mm] = angle_tangent[mm] + np.pi / 2 - tick_to_axes = self.get_tick_transform(axes) - axes.transAxes in_01 = functools.partial( mpl.transforms._interval_contains_close, (0, 1)) @@ -109,8 +102,7 @@ def get_line(self, axes): right=("lon_lines0", 1), bottom=("lat_lines0", 0), top=("lat_lines0", 1))[self._side] - xx, yy = self._grid_info[k][v] - return Path(np.column_stack([xx, yy])) + return Path(self._grid_info[k][v]) class ExtremeFinderFixed(ExtremeFinderSimple): @@ -125,11 +117,12 @@ def __init__(self, extremes): extremes : (float, float, float, float) The bounding box that this helper always returns. """ - self._extremes = extremes + x0, x1, y0, y1 = extremes + self._tbbox = Bbox.from_extents(x0, y0, x1, y1) - def __call__(self, transform_xy, x1, y1, x2, y2): + def _find_transformed_bbox(self, trans, bbox): # docstring inherited - return self._extremes + return self._tbbox class GridHelperCurveLinear(grid_helper_curvelinear.GridHelperCurveLinear): @@ -147,17 +140,6 @@ def __init__(self, aux_trans, extremes, tick_formatter1=tick_formatter1, tick_formatter2=tick_formatter2) - @_api.deprecated("3.8") - def get_data_boundary(self, side): - """ - Return v=0, nth=1. - """ - lon1, lon2, lat1, lat2 = self.grid_finder.extreme_finder(*[None] * 5) - return dict(left=(lon1, 0), - right=(lon2, 0), - bottom=(lat1, 1), - top=(lat2, 1))[side] - def new_fixed_axis( self, loc, nth_coord=None, axis_direction=None, offset=None, axes=None): if axes is None: @@ -188,25 +170,22 @@ def new_fixed_axis( # axis.get_helper().set_extremes(*self._extremes[2:]) # return axis - def _update_grid(self, x1, y1, x2, y2): + def _update_grid(self, bbox): if self._grid_info is None: self._grid_info = dict() grid_info = self._grid_info grid_finder = self.grid_finder - extremes = grid_finder.extreme_finder(grid_finder.inv_transform_xy, - x1, y1, x2, y2) + tbbox = grid_finder.extreme_finder._find_transformed_bbox( + grid_finder.get_transform().inverted(), bbox) - lon_min, lon_max = sorted(extremes[:2]) - lat_min, lat_max = sorted(extremes[2:]) - grid_info["extremes"] = lon_min, lon_max, lat_min, lat_max # extremes + lon_min, lat_min, lon_max, lat_max = tbbox.extents + grid_info["extremes"] = tbbox - lon_levs, lon_n, lon_factor = \ - grid_finder.grid_locator1(lon_min, lon_max) + lon_levs, lon_n, lon_factor = grid_finder.grid_locator1(lon_min, lon_max) lon_levs = np.asarray(lon_levs) - lat_levs, lat_n, lat_factor = \ - grid_finder.grid_locator2(lat_min, lat_max) + lat_levs, lat_n, lat_factor = grid_finder.grid_locator2(lat_min, lat_max) lat_levs = np.asarray(lat_levs) grid_info["lon_info"] = lon_levs, lon_n, lon_factor @@ -223,14 +202,13 @@ def _update_grid(self, x1, y1, x2, y2): lon_lines, lat_lines = grid_finder._get_raw_grid_lines( lon_values[(lon_min < lon_values) & (lon_values < lon_max)], lat_values[(lat_min < lat_values) & (lat_values < lat_max)], - lon_min, lon_max, lat_min, lat_max) + tbbox) grid_info["lon_lines"] = lon_lines grid_info["lat_lines"] = lat_lines lon_lines, lat_lines = grid_finder._get_raw_grid_lines( - # lon_min, lon_max, lat_min, lat_max) - extremes[:2], extremes[2:], *extremes) + tbbox.intervalx, tbbox.intervaly, tbbox) grid_info["lon_lines0"] = lon_lines grid_info["lat_lines0"] = lat_lines @@ -238,9 +216,9 @@ def _update_grid(self, x1, y1, x2, y2): def get_gridlines(self, which="major", axis="both"): grid_lines = [] if axis in ["both", "x"]: - grid_lines.extend(self._grid_info["lon_lines"]) + grid_lines.extend(map(np.transpose, self._grid_info["lon_lines"])) if axis in ["both", "y"]: - grid_lines.extend(self._grid_info["lat_lines"]) + grid_lines.extend(map(np.transpose, self._grid_info["lat_lines"])) return grid_lines @@ -266,7 +244,7 @@ def clear(self): # The original patch is not in the draw tree; it is only used for # clipping purposes. orig_patch = super()._gen_axes_patch() - orig_patch.set_figure(self.figure) + orig_patch.set_figure(self.get_figure(root=False)) orig_patch.set_transform(self.transAxes) self.patch.set_clip_path(orig_patch) self.gridlines.set_clip_path(orig_patch) diff --git a/lib/mpl_toolkits/axisartist/grid_finder.py b/lib/mpl_toolkits/axisartist/grid_finder.py index ff67aa6e8720..1f86757df7b8 100644 --- a/lib/mpl_toolkits/axisartist/grid_finder.py +++ b/lib/mpl_toolkits/axisartist/grid_finder.py @@ -36,14 +36,10 @@ def _find_line_box_crossings(xys, bbox): for u0, inside in [(umin, us > umin), (umax, us < umax)]: cross = [] idxs, = (inside[:-1] ^ inside[1:]).nonzero() - for idx in idxs: - v = vs[idx] + (u0 - us[idx]) * dvs[idx] / dus[idx] - if not vmin <= v <= vmax: - continue - crossing = (u0, v)[sl] - theta = np.degrees(np.arctan2(*dxys[idx][::-1])) - cross.append((crossing, theta)) - crossings.append(cross) + vv = vs[idxs] + (u0 - us[idxs]) * dvs[idxs] / dus[idxs] + crossings.append([ + ((u0, v)[sl], np.degrees(np.arctan2(*dxy[::-1]))) # ((x, y), theta) + for v, dxy in zip(vv, dxys[idxs]) if vmin <= v <= vmax]) return crossings @@ -77,20 +73,29 @@ def __call__(self, transform_xy, x1, y1, x2, y2): extremal coordinates; then adding some padding to take into account the finite sampling. - As each sampling step covers a relative range of *1/nx* or *1/ny*, + As each sampling step covers a relative range of ``1/nx`` or ``1/ny``, the padding is computed by expanding the span covered by the extremal coordinates by these fractions. """ - x, y = np.meshgrid( - np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny)) - xt, yt = transform_xy(np.ravel(x), np.ravel(y)) - return self._add_pad(xt.min(), xt.max(), yt.min(), yt.max()) + tbbox = self._find_transformed_bbox( + _User2DTransform(transform_xy, None), Bbox.from_extents(x1, y1, x2, y2)) + return tbbox.x0, tbbox.x1, tbbox.y0, tbbox.y1 - def _add_pad(self, x_min, x_max, y_min, y_max): - """Perform the padding mentioned in `__call__`.""" - dx = (x_max - x_min) / self.nx - dy = (y_max - y_min) / self.ny - return x_min - dx, x_max + dx, y_min - dy, y_max + dy + def _find_transformed_bbox(self, trans, bbox): + """ + Compute an approximation of the bounding box obtained by applying + *trans* to *bbox*. + + See ``__call__`` for details; this method performs similar + calculations, but using a different representation of the arguments and + return value. + """ + grid = np.reshape(np.meshgrid(np.linspace(bbox.x0, bbox.x1, self.nx), + np.linspace(bbox.y0, bbox.y1, self.ny)), + (2, -1)).T + tbbox = Bbox.null() + tbbox.update_from_data_xy(trans.transform(grid)) + return tbbox.expanded(1 + 2 / self.nx, 1 + 2 / self.ny) class _User2DTransform(Transform): @@ -159,53 +164,52 @@ def _format_ticks(self, idx, direction, factor, levels): method should be considered as a temporary workaround which will be removed in the future at the same time as axisartist-specific formatters. """ - fmt = _api.check_getitem( + fmt = _api.getitem_checked( {1: self.tick_formatter1, 2: self.tick_formatter2}, idx=idx) return (fmt.format_ticks(levels) if isinstance(fmt, mticker.Formatter) else fmt(direction, factor, levels)) - def get_grid_info(self, x1, y1, x2, y2): + def get_grid_info(self, *args, **kwargs): """ - lon_values, lat_values : list of grid values. if integer is given, - rough number of grids in each direction. + Compute positioning information for grid lines and ticks, given the + axes' data *bbox*. """ + params = _api.select_matching_signature( + [lambda x1, y1, x2, y2: locals(), lambda bbox: locals()], *args, **kwargs) + if "x1" in params: + _api.warn_deprecated("3.11", message=( + "Passing extents as separate arguments to get_grid_info is deprecated " + "since %(since)s and support will be removed %(removal)s; pass a " + "single bbox instead.")) + bbox = Bbox.from_extents( + params["x1"], params["y1"], params["x2"], params["y2"]) + else: + bbox = params["bbox"] - extremes = self.extreme_finder(self.inv_transform_xy, x1, y1, x2, y2) - - # min & max rage of lat (or lon) for each grid line will be drawn. - # i.e., gridline of lon=0 will be drawn from lat_min to lat_max. - - lon_min, lon_max, lat_min, lat_max = extremes - lon_levs, lon_n, lon_factor = self.grid_locator1(lon_min, lon_max) - lon_levs = np.asarray(lon_levs) - lat_levs, lat_n, lat_factor = self.grid_locator2(lat_min, lat_max) - lat_levs = np.asarray(lat_levs) + tbbox = self.extreme_finder._find_transformed_bbox( + self.get_transform().inverted(), bbox) - lon_values = lon_levs[:lon_n] / lon_factor - lat_values = lat_levs[:lat_n] / lat_factor + lon_levs, lon_n, lon_factor = self.grid_locator1(*tbbox.intervalx) + lat_levs, lat_n, lat_factor = self.grid_locator2(*tbbox.intervaly) - lon_lines, lat_lines = self._get_raw_grid_lines(lon_values, - lat_values, - lon_min, lon_max, - lat_min, lat_max) + lon_values = np.asarray(lon_levs[:lon_n]) / lon_factor + lat_values = np.asarray(lat_levs[:lat_n]) / lat_factor - bb = Bbox.from_extents(x1, y1, x2, y2).expanded(1 + 2e-10, 1 + 2e-10) + lon_lines, lat_lines = self._get_raw_grid_lines(lon_values, lat_values, tbbox) - grid_info = { - "extremes": extremes, - # "lon", "lat", filled below. - } + bbox_expanded = bbox.expanded(1 + 2e-10, 1 + 2e-10) + grid_info = {"extremes": tbbox} # "lon", "lat" keys filled below. for idx, lon_or_lat, levs, factor, values, lines in [ (1, "lon", lon_levs, lon_factor, lon_values, lon_lines), (2, "lat", lat_levs, lat_factor, lat_values, lat_lines), ]: grid_info[lon_or_lat] = gi = { - "lines": [[l] for l in lines], + "lines": lines, "ticks": {"left": [], "right": [], "bottom": [], "top": []}, } - for (lx, ly), v, level in zip(lines, values, levs): - all_crossings = _find_line_box_crossings(np.column_stack([lx, ly]), bb) + for xys, v, level in zip(lines, values, levs): + all_crossings = _find_line_box_crossings(xys, bbox_expanded) for side, crossings in zip( ["left", "right", "bottom", "top"], all_crossings): for crossing in crossings: @@ -218,18 +222,14 @@ def get_grid_info(self, x1, y1, x2, y2): return grid_info - def _get_raw_grid_lines(self, - lon_values, lat_values, - lon_min, lon_max, lat_min, lat_max): - - lons_i = np.linspace(lon_min, lon_max, 100) # for interpolation - lats_i = np.linspace(lat_min, lat_max, 100) - - lon_lines = [self.transform_xy(np.full_like(lats_i, lon), lats_i) + def _get_raw_grid_lines(self, lon_values, lat_values, bbox): + trans = self.get_transform() + lons = np.linspace(bbox.x0, bbox.x1, 100) # for interpolation + lats = np.linspace(bbox.y0, bbox.y1, 100) + lon_lines = [trans.transform(np.column_stack([np.full_like(lats, lon), lats])) for lon in lon_values] - lat_lines = [self.transform_xy(lons_i, np.full_like(lons_i, lat)) + lat_lines = [trans.transform(np.column_stack([lons, np.full_like(lons, lat)])) for lat in lat_values] - return lon_lines, lat_lines def set_transform(self, aux_trans): @@ -246,9 +246,11 @@ def get_transform(self): update_transform = set_transform # backcompat alias. + @_api.deprecated("3.11", alternative="grid_finder.get_transform()") def transform_xy(self, x, y): return self._aux_transform.transform(np.column_stack([x, y])).T + @_api.deprecated("3.11", alternative="grid_finder.get_transform().inverted()") def inv_transform_xy(self, x, y): return self._aux_transform.inverted().transform( np.column_stack([x, y])).T diff --git a/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py index a7eb9d5cfe21..aa37a3680fa5 100644 --- a/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py @@ -7,38 +7,83 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api from matplotlib.path import Path -from matplotlib.transforms import Affine2D, IdentityTransform +from matplotlib.transforms import Affine2D, Bbox, IdentityTransform from .axislines import ( _FixedAxisArtistHelperBase, _FloatingAxisArtistHelperBase, GridHelperBase) from .axis_artist import AxisArtist from .grid_finder import GridFinder -def _value_and_jacobian(func, xs, ys, xlims, ylims): +def _value_and_jac_angle(func, xs, ys, xlim, ylim): """ - Compute *func* and its derivatives along x and y at positions *xs*, *ys*, - while ensuring that finite difference calculations don't try to evaluate - values outside of *xlims*, *ylims*. + Parameters + ---------- + func : callable + A function that transforms the coordinates of a point (x, y) to a new coordinate + system (u, v), and which can also take x and y as arrays of shape *shape* and + returns (u, v) as a ``(2, shape)`` array. + xs, ys : array-likes + Points where *func* and its derivatives will be evaluated. + xlim, ylim : pairs of floats + (min, max) beyond which *func* should not be evaluated. + + Returns + ------- + val + Value of *func* at each point of ``(xs, ys)``. + thetas_dx + Angles (in radians) defined by the (u, v) components of the numerically + differentiated df/dx vector, at each point of ``(xs, ys)``. If needed, the + differentiation step size is increased until at least one component of df/dx + is nonzero, under the constraint of not going out of the *xlims*, *ylims* + bounds. If the gridline at a point is actually null (and the angle is thus not + well defined), the derivatives are evaluated after taking a small step along y; + this ensures e.g. that the tick at r=0 on a radial axis of a polar plot is + parallel with the ticks at r!=0. + thetas_dy + Like *thetas_dx*, but for df/dy. """ - eps = np.finfo(float).eps ** (1/2) # see e.g. scipy.optimize.approx_fprime + + shape = np.broadcast_shapes(np.shape(xs), np.shape(ys)) val = func(xs, ys) - # Take the finite difference step in the direction where the bound is the - # furthest; the step size is min of epsilon and distance to that bound. - xlo, xhi = sorted(xlims) - dxlo = xs - xlo - dxhi = xhi - xs - xeps = (np.take([-1, 1], dxhi >= dxlo) - * np.minimum(eps, np.maximum(dxlo, dxhi))) - val_dx = func(xs + xeps, ys) - ylo, yhi = sorted(ylims) - dylo = ys - ylo - dyhi = yhi - ys - yeps = (np.take([-1, 1], dyhi >= dylo) - * np.minimum(eps, np.maximum(dylo, dyhi))) - val_dy = func(xs, ys + yeps) - return (val, (val_dx - val) / xeps, (val_dy - val) / yeps) + + # Take finite difference steps towards the furthest bound; the step size will be the + # min of epsilon and the distance to that bound. + eps0 = np.finfo(float).eps ** (1/2) # cf. scipy.optimize.approx_fprime + + def calc_eps(vals, lim): + lo, hi = sorted(lim) + dlo = vals - lo + dhi = hi - vals + eps_max = np.maximum(dlo, dhi) + eps = np.where(dhi >= dlo, 1, -1) * np.minimum(eps0, eps_max) + return eps, eps_max + + xeps, xeps_max = calc_eps(xs, xlim) + yeps, yeps_max = calc_eps(ys, ylim) + + def calc_thetas(dfunc, ps, eps_p0, eps_max, eps_q): + thetas_dp = np.full(shape, np.nan) + missing = np.full(shape, True) + eps_p = eps_p0 + for it, eps_q in enumerate([0, eps_q]): + while missing.any() and (abs(eps_p) < eps_max).any(): + if it == 0 and (eps_p > 1).any(): + break # Degenerate derivative, move a bit along the other coord. + eps_p = np.minimum(eps_p, eps_max) + df_x, df_y = (dfunc(eps_p, eps_q) - dfunc(0, eps_q)) / eps_p + good = missing & ((df_x != 0) | (df_y != 0)) + thetas_dp[good] = np.arctan2(df_y, df_x)[good] + missing &= ~good + eps_p *= 2 + return thetas_dp + + thetas_dx = calc_thetas(lambda eps_p, eps_q: func(xs + eps_p, ys + eps_q), + xs, xeps, xeps_max, yeps) + thetas_dy = calc_thetas(lambda eps_p, eps_q: func(xs + eps_q, ys + eps_p), + ys, yeps, yeps_max, xeps) + return (val, thetas_dx, thetas_dy) class FixedAxisArtistHelper(_FixedAxisArtistHelperBase): @@ -115,10 +160,10 @@ def update_lim(self, axes): x1, x2 = axes.get_xlim() y1, y2 = axes.get_ylim() grid_finder = self.grid_helper.grid_finder - extremes = grid_finder.extreme_finder(grid_finder.inv_transform_xy, - x1, y1, x2, y2) + tbbox = grid_finder.extreme_finder._find_transformed_bbox( + grid_finder.get_transform().inverted(), Bbox.from_extents(x1, y1, x2, y2)) - lon_min, lon_max, lat_min, lat_max = extremes + lon_min, lat_min, lon_max, lat_max = tbbox.extents e_min, e_max = self._extremes # ranges of other coordinates if self.nth_coord == 0: lat_min = max(e_min, lat_min) @@ -127,29 +172,29 @@ def update_lim(self, axes): lon_min = max(e_min, lon_min) lon_max = min(e_max, lon_max) - lon_levs, lon_n, lon_factor = \ - grid_finder.grid_locator1(lon_min, lon_max) - lat_levs, lat_n, lat_factor = \ - grid_finder.grid_locator2(lat_min, lat_max) + lon_levs, lon_n, lon_factor = grid_finder.grid_locator1(lon_min, lon_max) + lat_levs, lat_n, lat_factor = grid_finder.grid_locator2(lat_min, lat_max) if self.nth_coord == 0: - xx0 = np.full(self._line_num_points, self.value) - yy0 = np.linspace(lat_min, lat_max, self._line_num_points) - xx, yy = grid_finder.transform_xy(xx0, yy0) + xys = grid_finder.get_transform().transform(np.column_stack([ + np.full(self._line_num_points, self.value), + np.linspace(lat_min, lat_max, self._line_num_points), + ])) elif self.nth_coord == 1: - xx0 = np.linspace(lon_min, lon_max, self._line_num_points) - yy0 = np.full(self._line_num_points, self.value) - xx, yy = grid_finder.transform_xy(xx0, yy0) + xys = grid_finder.get_transform().transform(np.column_stack([ + np.linspace(lon_min, lon_max, self._line_num_points), + np.full(self._line_num_points, self.value), + ])) self._grid_info = { - "extremes": (lon_min, lon_max, lat_min, lat_max), + "extremes": Bbox.from_extents(lon_min, lat_min, lon_max, lat_max), "lon_info": (lon_levs, lon_n, np.asarray(lon_factor)), "lat_info": (lat_levs, lat_n, np.asarray(lat_factor)), "lon_labels": grid_finder._format_ticks( 1, "bottom", lon_factor, lon_levs), "lat_labels": grid_finder._format_ticks( 2, "bottom", lat_factor, lat_levs), - "line_xy": (xx, yy), + "line_xy": xys, } def get_axislabel_transform(self, axes): @@ -160,19 +205,18 @@ def trf_xy(x, y): trf = self.grid_helper.grid_finder.get_transform() + axes.transData return trf.transform([x, y]).T - xmin, xmax, ymin, ymax = self._grid_info["extremes"] + xmin, ymin, xmax, ymax = self._grid_info["extremes"].extents if self.nth_coord == 0: xx0 = self.value yy0 = (ymin + ymax) / 2 elif self.nth_coord == 1: xx0 = (xmin + xmax) / 2 yy0 = self.value - xy1, dxy1_dx, dxy1_dy = _value_and_jacobian( + xy1, angle_dx, angle_dy = _value_and_jac_angle( trf_xy, xx0, yy0, (xmin, xmax), (ymin, ymax)) p = axes.transAxes.inverted().transform(xy1) if 0 <= p[0] <= 1 and 0 <= p[1] <= 1: - d = [dxy1_dy, dxy1_dx][self.nth_coord] - return xy1, np.rad2deg(np.arctan2(*d[::-1])) + return xy1, np.rad2deg([angle_dy, angle_dx][self.nth_coord]) else: return None, None @@ -197,23 +241,17 @@ def trf_xy(x, y): # find angles if self.nth_coord == 0: mask = (e0 <= yy0) & (yy0 <= e1) - (xx1, yy1), (dxx1, dyy1), (dxx2, dyy2) = _value_and_jacobian( + (xx1, yy1), angle_normal, angle_tangent = _value_and_jac_angle( trf_xy, self.value, yy0[mask], (-np.inf, np.inf), (e0, e1)) labels = self._grid_info["lat_labels"] elif self.nth_coord == 1: mask = (e0 <= xx0) & (xx0 <= e1) - (xx1, yy1), (dxx2, dyy2), (dxx1, dyy1) = _value_and_jacobian( + (xx1, yy1), angle_tangent, angle_normal = _value_and_jac_angle( trf_xy, xx0[mask], self.value, (-np.inf, np.inf), (e0, e1)) labels = self._grid_info["lon_labels"] labels = [l for l, m in zip(labels, mask) if m] - - angle_normal = np.arctan2(dyy1, dxx1) - angle_tangent = np.arctan2(dyy2, dxx2) - mm = (dyy1 == 0) & (dxx1 == 0) # points with degenerate normal - angle_normal[mm] = angle_tangent[mm] + np.pi / 2 - tick_to_axes = self.get_tick_transform(axes) - axes.transAxes in_01 = functools.partial( mpl.transforms._interval_contains_close, (0, 1)) @@ -232,8 +270,7 @@ def get_line_transform(self, axes): def get_line(self, axes): self.update_lim(axes) - x, y = self._grid_info["line_xy"] - return Path(np.column_stack([x, y])) + return Path(self._grid_info["line_xy"]) class GridHelperCurveLinear(GridHelperBase): @@ -278,9 +315,9 @@ def update_grid_finder(self, aux_trans=None, **kwargs): self.grid_finder.update(**kwargs) self._old_limits = None # Force revalidation. - @_api.make_keyword_only("3.9", "nth_coord") def new_fixed_axis( - self, loc, nth_coord=None, axis_direction=None, offset=None, axes=None): + self, loc, *, axis_direction=None, offset=None, axes=None, nth_coord=None + ): if axes is None: axes = self.axes if axis_direction is None: @@ -303,26 +340,13 @@ def new_floating_axis(self, nth_coord, value, axes=None, axis_direction="bottom" # axisline.minor_ticklabels.set_visible(False) return axisline - def _update_grid(self, x1, y1, x2, y2): - self._grid_info = self.grid_finder.get_grid_info(x1, y1, x2, y2) + def _update_grid(self, bbox): + self._grid_info = self.grid_finder.get_grid_info(bbox) def get_gridlines(self, which="major", axis="both"): grid_lines = [] if axis in ["both", "x"]: - for gl in self._grid_info["lon"]["lines"]: - grid_lines.extend(gl) + grid_lines.extend([gl.T for gl in self._grid_info["lon"]["lines"]]) if axis in ["both", "y"]: - for gl in self._grid_info["lat"]["lines"]: - grid_lines.extend(gl) + grid_lines.extend([gl.T for gl in self._grid_info["lat"]["lines"]]) return grid_lines - - @_api.deprecated("3.9") - def get_tick_iterator(self, nth_coord, axis_side, minor=False): - angle_tangent = dict(left=90, right=90, bottom=0, top=0)[axis_side] - lon_or_lat = ["lon", "lat"][nth_coord] - if not minor: # major ticks - for tick in self._grid_info[lon_or_lat]["ticks"][axis_side]: - yield *tick["loc"], angle_tangent, tick["label"] - else: - for tick in self._grid_info[lon_or_lat]["ticks"][axis_side]: - yield *tick["loc"], angle_tangent, "" diff --git a/lib/mpl_toolkits/axisartist/meson.build b/lib/mpl_toolkits/axisartist/meson.build index 8d9314e42576..6d95cf0dfdcd 100644 --- a/lib/mpl_toolkits/axisartist/meson.build +++ b/lib/mpl_toolkits/axisartist/meson.build @@ -2,8 +2,6 @@ python_sources = [ '__init__.py', 'angle_helper.py', 'axes_divider.py', - 'axes_grid.py', - 'axes_rgb.py', 'axis_artist.py', 'axislines.py', 'axisline_style.py', diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist.png index 29ddf31eb664..19f85334dfb2 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_labelbase.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_labelbase.png index 5d362e779069..a1337b7aca22 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_labelbase.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_labelbase.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_ticklabels.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_ticklabels.png index f79611c4e1ba..e7b6aa0fc9ca 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_ticklabels.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axis_artist/axis_artist_ticklabels.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/Subplot.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/Subplot.png index 83c6b147da99..917c2616a1f4 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/Subplot.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/Subplot.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/SubplotZero.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/SubplotZero.png index f6f9b9ddd578..6c21931e6b5f 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/SubplotZero.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/SubplotZero.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/axisline_style_tight.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/axisline_style_tight.png index 77314c1695a0..3b2b80f1f678 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/axisline_style_tight.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/axisline_style_tight.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/subplotzero_ylabel.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/subplotzero_ylabel.png index 9dc9e4a1540d..4319441f6115 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/subplotzero_ylabel.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_axislines/subplotzero_ylabel.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear3.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear3.png index 53fb5b9ba3ca..02e8d25c3121 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear3.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear3.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear4.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear4.png index fae8857d788a..86fee845d9f7 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear4.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_floating_axes/curvelinear4.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/axis_direction.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/axis_direction.png index 6ab41f94c78a..b0ec7bda6eba 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/axis_direction.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/axis_direction.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/custom_transform.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/custom_transform.png index be2b3e99c1c4..cd70ebfc30be 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/custom_transform.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/custom_transform.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/polar_box.png b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/polar_box.png index 8909355e9af8..48722e33522d 100644 Binary files a/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/polar_box.png and b/lib/mpl_toolkits/axisartist/tests/baseline_images/test_grid_helper_curvelinear/polar_box.png differ diff --git a/lib/mpl_toolkits/axisartist/tests/conftest.py b/lib/mpl_toolkits/axisartist/tests/conftest.py index 61c2de3e07ba..12eaac9ce2f4 100644 --- a/lib/mpl_toolkits/axisartist/tests/conftest.py +++ b/lib/mpl_toolkits/axisartist/tests/conftest.py @@ -1,2 +1,3 @@ -from matplotlib.testing.conftest import (mpl_test_settings, # noqa - pytest_configure, pytest_unconfigure) +from matplotlib.testing.conftest import ( # noqa + pytest_configure, pytest_unconfigure, + high_memory, mpl_test_settings) diff --git a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py index d44a61b6dd4a..8c67b18c0349 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py @@ -19,16 +19,13 @@ def test_ticks(): ticks_in.set_locs_angles(locs_angles) ax.add_artist(ticks_in) - ticks_out = Ticks(ticksize=10, tick_out=True, color='C3', axis=ax.xaxis) + ticks_out = Ticks(ticksize=10, tick_direction="out", color='C3', axis=ax.xaxis) ticks_out.set_locs_angles(locs_angles) ax.add_artist(ticks_out) @image_comparison(['axis_artist_labelbase.png'], style='default') def test_labelbase(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.plot([0.5], [0.5], "o") @@ -43,9 +40,6 @@ def test_labelbase(): @image_comparison(['axis_artist_ticklabels.png'], style='default') def test_ticklabels(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.xaxis.set_visible(False) @@ -78,9 +72,6 @@ def test_ticklabels(): @image_comparison(['axis_artist.png'], style='default') def test_axis_artist(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.xaxis.set_visible(False) @@ -89,11 +80,13 @@ def test_axis_artist(): for loc in ('left', 'right', 'bottom'): helper = AxisArtistHelperRectlinear.Fixed(ax, loc=loc) axisline = AxisArtist(ax, helper, offset=None, axis_direction=loc) + axisline.major_ticks.set_tick_direction({ + "left": "in", "right": "out", "bottom": "inout", + }[loc]) ax.add_artist(axisline) # Settings for bottom AxisArtist. axisline.set_label("TTT") - axisline.major_ticks.set_tick_out(False) axisline.label.set_pad(5) ax.set_ylabel("Test") diff --git a/lib/mpl_toolkits/axisartist/tests/test_axislines.py b/lib/mpl_toolkits/axisartist/tests/test_axislines.py index b722316a5c0c..a13432182c58 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axislines.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axislines.py @@ -7,11 +7,8 @@ from mpl_toolkits.axisartist import Axes, SubplotHost -@image_comparison(['SubplotZero.png'], style='default') +@image_comparison(['SubplotZero.png'], style='mpl20') def test_SubplotZero(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure() ax = SubplotZero(fig, 1, 1, 1) @@ -28,11 +25,8 @@ def test_SubplotZero(): ax.set_ylabel("Test") -@image_comparison(['Subplot.png'], style='default') +@image_comparison(['Subplot.png'], style='mpl20') def test_Subplot(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure() ax = Subplot(fig, 1, 1, 1) @@ -42,8 +36,8 @@ def test_Subplot(): ax.plot(xx, np.sin(xx)) ax.set_ylabel("Test") - ax.axis["top"].major_ticks.set_tick_out(True) - ax.axis["bottom"].major_ticks.set_tick_out(True) + ax.axis["left"].major_ticks.set_tick_out(False) + ax.axis["right"].major_ticks.set_tick_out(False) ax.axis["bottom"].set_label("Tk0") @@ -58,8 +52,9 @@ def test_Axes(): @image_comparison(['ParasiteAxesAuxTrans_meshplot.png'], - remove_text=True, style='default', tol=0.075) + remove_text=True, style='mpl20', tol=0.075) def test_ParasiteAxesAuxTrans(): + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) data = np.ones((6, 6)) data[2, 2] = 2 data[0, :] = 0 @@ -83,14 +78,16 @@ def test_ParasiteAxesAuxTrans(): getattr(ax2, name)(xx, yy, data[:-1, :-1]) else: getattr(ax2, name)(xx, yy, data) - ax1.set_xlim((0, 5)) - ax1.set_ylim((0, 5)) + ax1.set_xlim(0, 5) + ax1.set_ylim(0, 5) ax2.contour(xx, yy, data, colors='k') @image_comparison(['axisline_style.png'], remove_text=True, style='mpl20') def test_axisline_style(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(2, 2)) ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>") @@ -105,6 +102,8 @@ def test_axisline_style(): @image_comparison(['axisline_style_size_color.png'], remove_text=True, style='mpl20') def test_axisline_style_size_color(): + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) fig = plt.figure(figsize=(2, 2)) ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>", size=2.0, facecolor='r') @@ -119,7 +118,9 @@ def test_axisline_style_size_color(): @image_comparison(['axisline_style_tight.png'], remove_text=True, style='mpl20') def test_axisline_style_tight(): - fig = plt.figure(figsize=(2, 2)) + # Remove this line when this test image is regenerated. + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"}) + fig = plt.figure(figsize=(2, 2), layout='tight') ax = fig.add_subplot(axes_class=AxesZero) ax.axis["xzero"].set_axisline_style("-|>", size=5, facecolor='g') ax.axis["xzero"].set_visible(True) @@ -129,8 +130,6 @@ def test_axisline_style_tight(): for direction in ("left", "right", "bottom", "top"): ax.axis[direction].set_visible(False) - fig.tight_layout() - @image_comparison(['subplotzero_ylabel.png'], style='mpl20') def test_subplotzero_ylabel(): diff --git a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py index 7644fea16965..5575a48d499a 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py +++ b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py @@ -1,5 +1,7 @@ import numpy as np +import pytest + import matplotlib.pyplot as plt import matplotlib.projections as mprojections import matplotlib.transforms as mtransforms @@ -17,14 +19,12 @@ def test_subplot(): fig.add_subplot(ax) -# Rather high tolerance to allow ongoing work with floating axes internals; -# remove when image is regenerated. -@image_comparison(['curvelinear3.png'], style='default', tol=5) +@image_comparison(['curvelinear3.png'], style='mpl20') def test_curvelinear3(): fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + - mprojections.PolarAxes.PolarTransform(apply_theta_transforms=False)) + mprojections.PolarAxes.PolarTransform()) grid_helper = GridHelperCurveLinear( tr, extremes=(0, 360, 10, 3), @@ -63,17 +63,12 @@ def test_curvelinear3(): l.set_clip_path(ax1.patch) -# Rather high tolerance to allow ongoing work with floating axes internals; -# remove when image is regenerated. -@image_comparison(['curvelinear4.png'], style='default', tol=0.9) +@image_comparison(['curvelinear4.png'], style='mpl20', tol=0.04) def test_curvelinear4(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + - mprojections.PolarAxes.PolarTransform(apply_theta_transforms=False)) + mprojections.PolarAxes.PolarTransform()) grid_helper = GridHelperCurveLinear( tr, extremes=(120, 30, 10, 0), @@ -113,3 +108,29 @@ def test_axis_direction(): ax.axis['y'] = ax.new_floating_axis(nth_coord=1, value=0, axis_direction='left') assert ax.axis['y']._axis_direction == 'left' + + +def test_transform_with_zero_derivatives(): + # The transform is really a 45° rotation + # tr(x, y) = x-y, x+y; inv_tr(u, v) = (u+v)/2, (u-v)/2 + # with an additional x->exp(-x**-2) on each coordinate. + # Therefore all ticks should be at +/-45°, even the one at zero where the + # transform derivatives are zero. + + # at x=0, exp(-x**-2)=0; div-by-zero can be ignored. + @np.errstate(divide="ignore") + def tr(x, y): + return np.exp(-x**-2) - np.exp(-y**-2), np.exp(-x**-2) + np.exp(-y**-2) + + def inv_tr(u, v): + return (-np.log((u+v)/2))**(1/2), (-np.log((v-u)/2))**(1/2) + + fig = plt.figure() + ax = fig.add_subplot( + axes_class=FloatingAxes, grid_helper=GridHelperCurveLinear( + (tr, inv_tr), extremes=(0, 10, 0, 10))) + fig.canvas.draw() + + for k in ax.axis: + for l, a in ax.axis[k].major_ticks.locs_angles: + assert a % 90 == pytest.approx(45, abs=1e-3) diff --git a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py index 1b266044bdd0..ab1eedd9b797 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py @@ -1,3 +1,5 @@ +import platform + import numpy as np import matplotlib.pyplot as plt @@ -15,8 +17,11 @@ GridHelperCurveLinear -@image_comparison(['custom_transform.png'], style='default', tol=0.2) +@image_comparison(['custom_transform.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.04) def test_custom_transform(): + plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "inout"}) + class MyTransform(Transform): input_dims = output_dims = 2 @@ -76,14 +81,14 @@ def inverted(self): ax1.grid(True) -@image_comparison(['polar_box.png'], style='default', tol=0.04) +@image_comparison(['polar_box.png'], style='mpl20', tol=0.04) def test_polar_box(): + plt.rcParams.update({"xtick.direction": "inout", "ytick.direction": "out"}) fig = plt.figure(figsize=(5, 5)) # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = (Affine2D().scale(np.pi / 180., 1.) + - PolarAxes.PolarTransform(apply_theta_transforms=False)) + tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() # polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes @@ -136,17 +141,13 @@ def test_polar_box(): ax1.grid(True) -# Remove tol & kerning_factor when this test image is regenerated. -@image_comparison(['axis_direction.png'], style='default', tol=0.13) +@image_comparison(['axis_direction.png'], style='mpl20', tol=0.04) def test_axis_direction(): - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure(figsize=(5, 5)) # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = (Affine2D().scale(np.pi / 180., 1.) + - PolarAxes.PolarTransform(apply_theta_transforms=False)) + tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() # polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index ec4ab07e4874..6898a8aaf4cf 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -15,10 +15,9 @@ from matplotlib import ( _api, artist, cbook, colors as mcolors, lines, text as mtext, - path as mpath) + path as mpath, rcParams) from matplotlib.collections import ( Collection, LineCollection, PolyCollection, PatchCollection, PathCollection) -from matplotlib.colors import Normalize from matplotlib.patches import Patch from . import proj3d @@ -59,11 +58,11 @@ def get_dir_vector(zdir): x, y, z : array The direction vector. """ - if zdir == 'x': + if cbook._str_equal(zdir, 'x'): return np.array((1, 0, 0)) - elif zdir == 'y': + elif cbook._str_equal(zdir, 'y'): return np.array((0, 1, 0)) - elif zdir == 'z': + elif cbook._str_equal(zdir, 'z'): return np.array((0, 0, 1)) elif zdir is None: return np.array((0, 0, 0)) @@ -73,6 +72,31 @@ def get_dir_vector(zdir): raise ValueError("'x', 'y', 'z', None or vector of length 3 expected") +def _viewlim_mask(xs, ys, zs, axes): + """ + Return the mask of the points outside the axes view limits. + + Parameters + ---------- + xs, ys, zs : array-like + The points to mask. These should be in data coordinates. + axes : Axes3D + The axes to use for the view limits. + + Returns + ------- + mask : np.array + The mask of the points as a bool array. + """ + mask = np.logical_or.reduce((xs < axes.xy_viewLim.xmin, + xs > axes.xy_viewLim.xmax, + ys < axes.xy_viewLim.ymin, + ys > axes.xy_viewLim.ymax, + zs < axes.zz_viewLim.xmin, + zs > axes.zz_viewLim.xmax)) + return mask + + class Text3D(mtext.Text): """ Text object with 3D position and direction. @@ -86,6 +110,10 @@ class Text3D(mtext.Text): zdir : {'x', 'y', 'z', None, 3-tuple} The direction of the text. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide text outside the axes view limits. + + .. versionadded:: 3.10 Other Parameters ---------------- @@ -93,9 +121,20 @@ class Text3D(mtext.Text): All other parameters are passed on to `~matplotlib.text.Text`. """ - def __init__(self, x=0, y=0, z=0, text='', zdir='z', **kwargs): + def __init__(self, x=0, y=0, z=0, text='', zdir='z', axlim_clip=False, + **kwargs): + if 'rotation' in kwargs: + _api.warn_external( + "The `rotation` parameter has not yet been implemented " + "and is currently ignored." + ) + if 'rotation_mode' in kwargs: + _api.warn_external( + "The `rotation_mode` parameter has not yet been implemented " + "and is currently ignored." + ) mtext.Text.__init__(self, x, y, text, **kwargs) - self.set_3d_properties(z, zdir) + self.set_3d_properties(z, zdir, axlim_clip) def get_position_3d(self): """Return the (x, y, z) position of the text.""" @@ -129,7 +168,7 @@ def set_z(self, z): self._z = z self.stale = True - def set_3d_properties(self, z=0, zdir='z'): + def set_3d_properties(self, z=0, zdir='z', axlim_clip=False): """ Set the *z* position and direction of the text. @@ -140,16 +179,29 @@ def set_3d_properties(self, z=0, zdir='z'): zdir : {'x', 'y', 'z', 3-tuple} The direction of the text. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide text outside the axes view limits. + + .. versionadded:: 3.10 """ self._z = z self._dir_vec = get_dir_vector(zdir) + self._axlim_clip = axlim_clip self.stale = True @artist.allow_rasterization def draw(self, renderer): - position3d = np.array((self._x, self._y, self._z)) - proj = proj3d._proj_trans_points( - [position3d, position3d + self._dir_vec], self.axes.M) + if self._axlim_clip: + mask = _viewlim_mask(self._x, self._y, self._z, self.axes) + pos3d = np.ma.array([self._x, self._y, self._z], + mask=mask, dtype=float).filled(np.nan) + else: + pos3d = np.array([self._x, self._y, self._z], dtype=float) + + dir_end = pos3d + self._dir_vec + points = np.asarray([pos3d, dir_end]) + proj = proj3d._scale_proj_transform( + points[:, 0], points[:, 1], points[:, 2], self.axes) dx = proj[0][1] - proj[0][0] dy = proj[1][1] - proj[1][0] angle = math.degrees(math.atan2(dy, dx)) @@ -164,7 +216,7 @@ def get_tightbbox(self, renderer=None): return None -def text_2d_to_3d(obj, z=0, zdir='z'): +def text_2d_to_3d(obj, z=0, zdir='z', axlim_clip=False): """ Convert a `.Text` to a `.Text3D` object. @@ -175,9 +227,13 @@ def text_2d_to_3d(obj, z=0, zdir='z'): zdir : {'x', 'y', 'z', 3-tuple} The direction of the text. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide text outside the axes view limits. + + .. versionadded:: 3.10 """ obj.__class__ = Text3D - obj.set_3d_properties(z, zdir) + obj.set_3d_properties(z, zdir, axlim_clip) class Line3D(lines.Line2D): @@ -191,7 +247,7 @@ class Line3D(lines.Line2D): `~.Line2D.set_data`, `~.Line2D.set_xdata`, and `~.Line2D.set_ydata`. """ - def __init__(self, xs, ys, zs, *args, **kwargs): + def __init__(self, xs, ys, zs, *args, axlim_clip=False, **kwargs): """ Parameters @@ -207,8 +263,9 @@ def __init__(self, xs, ys, zs, *args, **kwargs): """ super().__init__([], [], *args, **kwargs) self.set_data_3d(xs, ys, zs) + self._axlim_clip = axlim_clip - def set_3d_properties(self, zs=0, zdir='z'): + def set_3d_properties(self, zs=0, zdir='z', axlim_clip=False): """ Set the *z* position and direction of the line. @@ -220,12 +277,17 @@ def set_3d_properties(self, zs=0, zdir='z'): zdir : {'x', 'y', 'z'} Plane to plot line orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide lines with an endpoint outside the axes view limits. + + .. versionadded:: 3.10 """ xs = self.get_xdata() ys = self.get_ydata() zs = cbook._to_unmasked_float_array(zs).ravel() zs = np.broadcast_to(zs, len(xs)) self._verts3d = juggle_axes(xs, ys, zs, zdir) + self._axlim_clip = axlim_clip self.stale = True def set_data_3d(self, *args): @@ -266,14 +328,22 @@ def get_data_3d(self): @artist.allow_rasterization def draw(self, renderer): - xs3d, ys3d, zs3d = self._verts3d - xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M) + if self._axlim_clip: + mask = np.broadcast_to( + _viewlim_mask(*self._verts3d, self.axes), + (len(self._verts3d), *self._verts3d[0].shape) + ) + xs3d, ys3d, zs3d = np.ma.array(self._verts3d, + dtype=float, mask=mask).filled(np.nan) + else: + xs3d, ys3d, zs3d = self._verts3d + xs, ys, zs, tis = proj3d._scale_proj_transform_clip(xs3d, ys3d, zs3d, self.axes) self.set_data(xs, ys) super().draw(renderer) self.stale = False -def line_2d_to_3d(line, zs=0, zdir='z'): +def line_2d_to_3d(line, zs=0, zdir='z', axlim_clip=False): """ Convert a `.Line2D` to a `.Line3D` object. @@ -284,10 +354,14 @@ def line_2d_to_3d(line, zs=0, zdir='z'): zdir : {'x', 'y', 'z'} Plane to plot line orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide lines with an endpoint outside the axes view limits. + + .. versionadded:: 3.10 """ line.__class__ = Line3D - line.set_3d_properties(zs, zdir) + line.set_3d_properties(zs, zdir, axlim_clip) def _path_to_3d_segment(path, zs=0, zdir='z'): @@ -349,15 +423,20 @@ class Collection3D(Collection): def do_3d_projection(self): """Project the points according to renderer matrix.""" - xyzs_list = [proj3d.proj_transform(*vs.T, self.axes.M) - for vs, _ in self._3dverts_codes] - self._paths = [mpath.Path(np.column_stack([xs, ys]), cs) + vs_list = [vs for vs, _ in self._3dverts_codes] + if self._axlim_clip: + vs_list = [np.ma.array(vs, mask=np.broadcast_to( + _viewlim_mask(*vs.T, self.axes), vs.shape)) + for vs in vs_list] + xyzs_list = [proj3d._scale_proj_transform( + vs[:, 0], vs[:, 1], vs[:, 2], self.axes) for vs in vs_list] + self._paths = [mpath.Path(np.ma.column_stack([xs, ys]), cs) for (xs, ys, _), (_, cs) in zip(xyzs_list, self._3dverts_codes)] zs = np.concatenate([zs for _, _, zs in xyzs_list]) return zs.min() if len(zs) else 1e9 -def collection_2d_to_3d(col, zs=0, zdir='z'): +def collection_2d_to_3d(col, zs=0, zdir='z', axlim_clip=False): """Convert a `.Collection` to a `.Collection3D` object.""" zs = np.broadcast_to(zs, len(col.get_paths())) col._3dverts_codes = [ @@ -367,12 +446,40 @@ def collection_2d_to_3d(col, zs=0, zdir='z'): p.codes) for p, z in zip(col.get_paths(), zs)] col.__class__ = cbook._make_class_factory(Collection3D, "{}3D")(type(col)) + col._axlim_clip = axlim_clip class Line3DCollection(LineCollection): """ A collection of 3D lines. """ + def __init__(self, lines, axlim_clip=False, **kwargs): + super().__init__(lines, **kwargs) + self._axlim_clip = axlim_clip + """ + Parameters + ---------- + lines : list of (N, 3) array-like + A sequence ``[line0, line1, ...]`` where each line is a (N, 3)-shape + array-like containing points:: line0 = [(x0, y0, z0), (x1, y1, z1), ...] + Each line can contain a different number of points. + linewidths : float or list of float, default: :rc:`lines.linewidth` + The width of each line in points. + colors : :mpltype:`color` or list of color, default: :rc:`lines.color` + A sequence of RGBA tuples (e.g., arbitrary color strings, etc, not + allowed). + antialiaseds : bool or list of bool, default: :rc:`lines.antialiased` + Whether to use antialiasing for each line. + facecolors : :mpltype:`color` or list of :mpltype:`color`, default: 'none' + When setting *facecolors*, each line is interpreted as a boundary + for an area, implicitly closing the path from the last point to the + first point. The enclosed area is filled with *facecolor*. + In order to manually specify what should count as the "interior" of + each line, please use `.PathCollection` instead, where the + "interior" can be specified by appropriate usage of + `~.path.Path.CLOSEPOLY`. + **kwargs : Forwarded to `.Collection`. + """ def set_sort_zpos(self, val): """Set the position to use for z-sorting.""" @@ -390,23 +497,47 @@ def do_3d_projection(self): """ Project the points according to renderer matrix. """ - xyslist = [proj3d._proj_trans_points(points, self.axes.M) - for points in self._segments3d] - segments_2d = [np.column_stack([xs, ys]) for xs, ys, zs in xyslist] + segments = np.asanyarray(self._segments3d) + + # Handle empty segments + if segments.size == 0: + LineCollection.set_segments(self, []) + return np.nan + + mask = False + if np.ma.isMA(segments): + mask = segments.mask + + if self._axlim_clip: + viewlim_mask = _viewlim_mask(segments[..., 0], + segments[..., 1], + segments[..., 2], + self.axes) + if np.any(viewlim_mask): + # broadcast mask to 3D + viewlim_mask = np.broadcast_to(viewlim_mask[..., np.newaxis], + (*viewlim_mask.shape, 3)) + mask = mask | viewlim_mask + + xyzs = np.ma.array( + proj3d._scale_proj_transform_vectors(segments, self.axes), mask=mask) + segments_2d = xyzs[..., 0:2] LineCollection.set_segments(self, segments_2d) # FIXME - minz = 1e9 - for xs, ys, zs in xyslist: - minz = min(minz, min(zs)) + if len(xyzs) > 0: + minz = min(xyzs[..., 2].min(), 1e9) + else: + minz = np.nan return minz -def line_collection_2d_to_3d(col, zs=0, zdir='z'): +def line_collection_2d_to_3d(col, zs=0, zdir='z', axlim_clip=False): """Convert a `.LineCollection` to a `.Line3DCollection` object.""" segments3d = _paths_to_3d_segments(col.get_paths(), zs, zdir) col.__class__ = Line3DCollection col.set_segments(segments3d) + col._axlim_clip = axlim_clip class Patch3D(Patch): @@ -414,7 +545,7 @@ class Patch3D(Patch): 3D patch object. """ - def __init__(self, *args, zs=(), zdir='z', **kwargs): + def __init__(self, *args, zs=(), zdir='z', axlim_clip=False, **kwargs): """ Parameters ---------- @@ -425,11 +556,15 @@ def __init__(self, *args, zs=(), zdir='z', **kwargs): zdir : {'x', 'y', 'z'} Plane to plot patch orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 """ super().__init__(*args, **kwargs) - self.set_3d_properties(zs, zdir) + self.set_3d_properties(zs, zdir, axlim_clip) - def set_3d_properties(self, verts, zs=0, zdir='z'): + def set_3d_properties(self, verts, zs=0, zdir='z', axlim_clip=False): """ Set the *z* position and direction of the patch. @@ -442,10 +577,15 @@ def set_3d_properties(self, verts, zs=0, zdir='z'): zdir : {'x', 'y', 'z'} Plane to plot patch orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 """ zs = np.broadcast_to(zs, len(verts)) self._segment3d = [juggle_axes(x, y, z, zdir) for ((x, y), z) in zip(verts, zs)] + self._axlim_clip = axlim_clip def get_path(self): # docstring inherited @@ -457,10 +597,14 @@ def get_path(self): def do_3d_projection(self): s = self._segment3d - xs, ys, zs = zip(*s) - vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, - self.axes.M) - self._path2d = mpath.Path(np.column_stack([vxs, vys])) + if self._axlim_clip: + mask = _viewlim_mask(*zip(*s), self.axes) + xs, ys, zs = np.ma.array(zip(*s), + dtype=float, mask=mask).filled(np.nan) + else: + xs, ys, zs = zip(*s) + vxs, vys, vzs, vis = proj3d._scale_proj_transform_clip(xs, ys, zs, self.axes) + self._path2d = mpath.Path(np.ma.column_stack([vxs, vys])) return min(vzs) @@ -469,7 +613,7 @@ class PathPatch3D(Patch3D): 3D PathPatch object. """ - def __init__(self, path, *, zs=(), zdir='z', **kwargs): + def __init__(self, path, *, zs=(), zdir='z', axlim_clip=False, **kwargs): """ Parameters ---------- @@ -480,12 +624,16 @@ def __init__(self, path, *, zs=(), zdir='z', **kwargs): zdir : {'x', 'y', 'z', 3-tuple} Plane to plot path patch orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide path patches with a point outside the axes view limits. + + .. versionadded:: 3.10 """ # Not super().__init__! Patch.__init__(self, **kwargs) - self.set_3d_properties(path, zs, zdir) + self.set_3d_properties(path, zs, zdir, axlim_clip) - def set_3d_properties(self, path, zs=0, zdir='z'): + def set_3d_properties(self, path, zs=0, zdir='z', axlim_clip=False): """ Set the *z* position and direction of the path patch. @@ -498,16 +646,25 @@ def set_3d_properties(self, path, zs=0, zdir='z'): zdir : {'x', 'y', 'z', 3-tuple} Plane to plot path patch orthogonal to. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide path patches with a point outside the axes view limits. + + .. versionadded:: 3.10 """ - Patch3D.set_3d_properties(self, path.vertices, zs=zs, zdir=zdir) + Patch3D.set_3d_properties(self, path.vertices, zs=zs, zdir=zdir, + axlim_clip=axlim_clip) self._code3d = path.codes def do_3d_projection(self): s = self._segment3d - xs, ys, zs = zip(*s) - vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, - self.axes.M) - self._path2d = mpath.Path(np.column_stack([vxs, vys]), self._code3d) + if self._axlim_clip: + mask = _viewlim_mask(*zip(*s), self.axes) + xs, ys, zs = np.ma.array(zip(*s), + dtype=float, mask=mask).filled(np.nan) + else: + xs, ys, zs = zip(*s) + vxs, vys, vzs, vis = proj3d._scale_proj_transform_clip(xs, ys, zs, self.axes) + self._path2d = mpath.Path(np.ma.column_stack([vxs, vys]), self._code3d) return min(vzs) @@ -519,11 +676,11 @@ def _get_patch_verts(patch): return polygons[0] if len(polygons) else np.array([]) -def patch_2d_to_3d(patch, z=0, zdir='z'): +def patch_2d_to_3d(patch, z=0, zdir='z', axlim_clip=False): """Convert a `.Patch` to a `.Patch3D` object.""" verts = _get_patch_verts(patch) patch.__class__ = Patch3D - patch.set_3d_properties(verts, z, zdir) + patch.set_3d_properties(verts, z, zdir, axlim_clip) def pathpatch_2d_to_3d(pathpatch, z=0, zdir='z'): @@ -541,7 +698,16 @@ class Patch3DCollection(PatchCollection): A collection of 3D patches. """ - def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs): + def __init__( + self, + *args, + zs=0, + zdir="z", + depthshade=None, + depthshade_minalpha=None, + axlim_clip=False, + **kwargs + ): """ Create a collection of flat 3D patches with its normal vector pointed in *zdir* direction, and located at *zs* on the *zdir* @@ -552,18 +718,31 @@ def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs): :class:`~matplotlib.collections.PatchCollection`. In addition, keywords *zs=0* and *zdir='z'* are available. - Also, the keyword argument *depthshade* is available to indicate - whether to shade the patches in order to give the appearance of depth - (default is *True*). This is typically desired in scatter plots. + The keyword argument *depthshade* is available to + indicate whether or not to shade the patches in order to + give the appearance of depth (default is *True*). + This is typically desired in scatter plots. + + *depthshade_minalpha* sets the minimum alpha value applied by + depth-shading. """ + if depthshade is None: + depthshade = rcParams['axes3d.depthshade'] + if depthshade_minalpha is None: + depthshade_minalpha = rcParams['axes3d.depthshade_minalpha'] self._depthshade = depthshade + self._depthshade_minalpha = depthshade_minalpha super().__init__(*args, **kwargs) - self.set_3d_properties(zs, zdir) + self.set_3d_properties(zs, zdir, axlim_clip) def get_depthshade(self): return self._depthshade - def set_depthshade(self, depthshade): + def set_depthshade( + self, + depthshade, + depthshade_minalpha=None, + ): """ Set whether depth shading is performed on collection members. @@ -572,8 +751,15 @@ def set_depthshade(self, depthshade): depthshade : bool Whether to shade the patches in order to give the appearance of depth. + depthshade_minalpha : float, default: :rc:`axes3d.depthshade_minalpha` + Sets the minimum alpha value used by depth-shading. + + .. versionadded:: 3.11 """ + if depthshade_minalpha is None: + depthshade_minalpha = rcParams['axes3d.depthshade_minalpha'] self._depthshade = depthshade + self._depthshade_minalpha = depthshade_minalpha self.stale = True def set_sort_zpos(self, val): @@ -581,7 +767,7 @@ def set_sort_zpos(self, val): self._sort_zpos = val self.stale = True - def set_3d_properties(self, zs, zdir): + def set_3d_properties(self, zs, zdir, axlim_clip=False): """ Set the *z* positions and direction of the patches. @@ -594,6 +780,10 @@ def set_3d_properties(self, zs, zdir): Plane to plot patches orthogonal to. All patches must have the same direction. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 """ # Force the collection to initialize the face and edgecolors # just in case it is a scalarmappable with a colormap. @@ -607,14 +797,21 @@ def set_3d_properties(self, zs, zdir): self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir) self._z_markers_idx = slice(-1) self._vzs = None + self._axlim_clip = axlim_clip self.stale = True def do_3d_projection(self): - xs, ys, zs = self._offsets3d - vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, - self.axes.M) + if self._axlim_clip: + mask = _viewlim_mask(*self._offsets3d, self.axes) + xs, ys, zs = np.ma.array(self._offsets3d, mask=mask) + else: + xs, ys, zs = self._offsets3d + vxs, vys, vzs, vis = proj3d._scale_proj_transform_clip(xs, ys, zs, self.axes) self._vzs = vzs - super().set_offsets(np.column_stack([vxs, vys])) + if np.ma.isMA(vxs): + super().set_offsets(np.ma.column_stack([vxs, vys])) + else: + super().set_offsets(np.column_stack([vxs, vys])) if vzs.size > 0: return min(vzs) @@ -623,7 +820,11 @@ def do_3d_projection(self): def _maybe_depth_shade_and_sort_colors(self, color_array): color_array = ( - _zalpha(color_array, self._vzs) + _zalpha( + color_array, + self._vzs, + min_alpha=self._depthshade_minalpha, + ) if self._vzs is not None and self._depthshade else color_array ) @@ -643,12 +844,44 @@ def get_edgecolor(self): return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor()) +def _get_data_scale(X, Y, Z): + """ + Estimate the scale of the 3D data for use in depth shading + + Parameters + ---------- + X, Y, Z : masked arrays + The data to estimate the scale of. + """ + # Account for empty datasets. Assume that X Y and Z have the same number + # of elements. + if not np.ma.count(X): + return 0 + + # Estimate the scale using the RSS of the ranges of the dimensions + # Note that we don't use np.ma.ptp() because we otherwise get a build + # warning about handing empty arrays. + ptp_x = X.max() - X.min() + ptp_y = Y.max() - Y.min() + ptp_z = Z.max() - Z.min() + return np.sqrt(ptp_x ** 2 + ptp_y ** 2 + ptp_z ** 2) + + class Path3DCollection(PathCollection): """ A collection of 3D paths. """ - def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs): + def __init__( + self, + *args, + zs=0, + zdir="z", + depthshade=None, + depthshade_minalpha=None, + axlim_clip=False, + **kwargs + ): """ Create a collection of flat 3D paths with its normal vector pointed in *zdir* direction, and located at *zs* on the *zdir* @@ -659,14 +892,23 @@ def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs): :class:`~matplotlib.collections.PathCollection`. In addition, keywords *zs=0* and *zdir='z'* are available. - Also, the keyword argument *depthshade* is available to indicate - whether to shade the patches in order to give the appearance of depth - (default is *True*). This is typically desired in scatter plots. + Also, the keyword argument *depthshade* is available to + indicate whether or not to shade the patches in order to + give the appearance of depth (default is *True*). + This is typically desired in scatter plots. + + *depthshade_minalpha* sets the minimum alpha value applied by + depth-shading. """ + if depthshade is None: + depthshade = rcParams['axes3d.depthshade'] + if depthshade_minalpha is None: + depthshade_minalpha = rcParams['axes3d.depthshade_minalpha'] self._depthshade = depthshade + self._depthshade_minalpha = depthshade_minalpha self._in_draw = False super().__init__(*args, **kwargs) - self.set_3d_properties(zs, zdir) + self.set_3d_properties(zs, zdir, axlim_clip) self._offset_zordered = None def draw(self, renderer): @@ -679,7 +921,7 @@ def set_sort_zpos(self, val): self._sort_zpos = val self.stale = True - def set_3d_properties(self, zs, zdir): + def set_3d_properties(self, zs, zdir, axlim_clip=False): """ Set the *z* positions and direction of the paths. @@ -692,6 +934,10 @@ def set_3d_properties(self, zs, zdir): Plane to plot paths orthogonal to. All paths must have the same direction. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide paths with a vertex outside the axes view limits. + + .. versionadded:: 3.10 """ # Force the collection to initialize the face and edgecolors # just in case it is a scalarmappable with a colormap. @@ -702,6 +948,7 @@ def set_3d_properties(self, zs, zdir): else: xs = [] ys = [] + self._zdir = zdir self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir) # In the base draw methods we access the attributes directly which # means we cannot resolve the shuffling in the getter methods like @@ -722,6 +969,8 @@ def set_3d_properties(self, zs, zdir): # points and point properties according to the index array self._z_markers_idx = slice(-1) self._vzs = None + + self._axlim_clip = axlim_clip self.stale = True def set_sizes(self, sizes, dpi=72.0): @@ -737,7 +986,11 @@ def set_linewidth(self, lw): def get_depthshade(self): return self._depthshade - def set_depthshade(self, depthshade): + def set_depthshade( + self, + depthshade, + depthshade_minalpha=None, + ): """ Set whether depth shading is performed on collection members. @@ -746,18 +999,35 @@ def set_depthshade(self, depthshade): depthshade : bool Whether to shade the patches in order to give the appearance of depth. + depthshade_minalpha : float + Sets the minimum alpha value used by depth-shading. + + .. versionadded:: 3.11 """ + if depthshade_minalpha is None: + depthshade_minalpha = rcParams['axes3d.depthshade_minalpha'] self._depthshade = depthshade + self._depthshade_minalpha = depthshade_minalpha self.stale = True def do_3d_projection(self): - xs, ys, zs = self._offsets3d - vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, - self.axes.M) + mask = False + for xyz in self._offsets3d: + if np.ma.isMA(xyz): + mask = mask | xyz.mask + if self._axlim_clip: + mask = mask | _viewlim_mask(*self._offsets3d, self.axes) + mask = np.broadcast_to(mask, + (len(self._offsets3d), *self._offsets3d[0].shape)) + xyzs = np.ma.array(self._offsets3d, mask=mask) + else: + xyzs = self._offsets3d + vxs, vys, vzs, vis = proj3d._scale_proj_transform_clip(*xyzs, self.axes) + self._data_scale = _get_data_scale(vxs, vys, vzs) # Sort the points based on z coordinates # Performance optimization: Create a sorted index array and reorder # points and point properties according to the index array - z_markers_idx = self._z_markers_idx = np.argsort(vzs)[::-1] + z_markers_idx = self._z_markers_idx = np.ma.argsort(vzs)[::-1] self._vzs = vzs # we have to special case the sizes because of code in collections.py @@ -771,7 +1041,7 @@ def do_3d_projection(self): if len(self._linewidths3d) > 1: self._linewidths = self._linewidths3d[z_markers_idx] - PathCollection.set_offsets(self, np.column_stack((vxs, vys))) + PathCollection.set_offsets(self, np.ma.column_stack((vxs, vys))) # Re-order items vzs = vzs[z_markers_idx] @@ -779,7 +1049,7 @@ def do_3d_projection(self): vys = vys[z_markers_idx] # Store ordered offset for drawing purpose - self._offset_zordered = np.column_stack((vxs, vys)) + self._offset_zordered = np.ma.column_stack((vxs, vys)) return np.min(vzs) if vzs.size else np.nan @@ -798,14 +1068,22 @@ def _use_zordered_offset(self): self._offsets = old_offset def _maybe_depth_shade_and_sort_colors(self, color_array): - color_array = ( - _zalpha(color_array, self._vzs) - if self._vzs is not None and self._depthshade - else color_array - ) + # Adjust the color_array alpha values if point depths are defined + # and depth shading is active + if self._vzs is not None and self._depthshade: + color_array = _zalpha( + color_array, + self._vzs, + min_alpha=self._depthshade_minalpha, + _data_scale=self._data_scale, + ) + + # Adjust the order of the color_array using the _z_markers_idx, + # which has been sorted by z-depth if len(color_array) > 1: color_array = color_array[self._z_markers_idx] - return mcolors.to_rgba_array(color_array, self._alpha) + + return mcolors.to_rgba_array(color_array) def get_facecolor(self): return self._maybe_depth_shade_and_sort_colors(super().get_facecolor()) @@ -819,7 +1097,15 @@ def get_edgecolor(self): return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor()) -def patch_collection_2d_to_3d(col, zs=0, zdir='z', depthshade=True): +def patch_collection_2d_to_3d( + col, + zs=0, + zdir="z", + depthshade=None, + axlim_clip=False, + *args, + depthshade_minalpha=None, +): """ Convert a `.PatchCollection` into a `.Patch3DCollection` object (or a `.PathCollection` into a `.Path3DCollection` object). @@ -835,18 +1121,31 @@ def patch_collection_2d_to_3d(col, zs=0, zdir='z', depthshade=True): zdir : {'x', 'y', 'z'} The axis in which to place the patches. Default: "z". See `.get_dir_vector` for a description of the values. - depthshade : bool, default: True + depthshade : bool, default: :rc:`axes3d.depthshade` Whether to shade the patches to give a sense of depth. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 + + depthshade_minalpha : float, default: :rc:`axes3d.depthshade_minalpha` + Sets the minimum alpha value used by depth-shading. + .. versionadded:: 3.11 """ if isinstance(col, PathCollection): col.__class__ = Path3DCollection col._offset_zordered = None elif isinstance(col, PatchCollection): col.__class__ = Patch3DCollection + if depthshade is None: + depthshade = rcParams['axes3d.depthshade'] + if depthshade_minalpha is None: + depthshade_minalpha = rcParams['axes3d.depthshade_minalpha'] col._depthshade = depthshade + col._depthshade_minalpha = depthshade_minalpha col._in_draw = False - col.set_3d_properties(zs, zdir) + col.set_3d_properties(zs, zdir, axlim_clip) class Poly3DCollection(PolyCollection): @@ -871,7 +1170,7 @@ class Poly3DCollection(PolyCollection): """ def __init__(self, verts, *args, zsort='average', shade=False, - lightsource=None, **kwargs): + lightsource=None, axlim_clip=False, **kwargs): """ Parameters ---------- @@ -893,6 +1192,11 @@ def __init__(self, verts, *args, zsort='average', shade=False, .. versionadded:: 3.7 + axlim_clip : bool, default: False + Whether to hide polygons with a vertex outside the view limits. + + .. versionadded:: 3.10 + *args, **kwargs All other parameters are forwarded to `.PolyCollection`. @@ -927,6 +1231,7 @@ def __init__(self, verts, *args, zsort='average', shade=False, raise ValueError('verts must be a list of (N, 3) array-like') self.set_zsort(zsort) self._codes3d = None + self._axlim_clip = axlim_clip _zsort_functions = { 'average': np.average, @@ -953,16 +1258,35 @@ def get_vector(self, segments3d): return self._get_vector(segments3d) def _get_vector(self, segments3d): - """Optimize points for projection.""" - if len(segments3d): - xs, ys, zs = np.vstack(segments3d).T - else: # vstack can't stack zero arrays. - xs, ys, zs = [], [], [] - ones = np.ones(len(xs)) - self._vec = np.array([xs, ys, zs, ones]) + """ + Optimize points for projection. - indices = [0, *np.cumsum([len(segment) for segment in segments3d])] - self._segslices = [*map(slice, indices[:-1], indices[1:])] + Parameters + ---------- + segments3d : NumPy array or list of NumPy arrays + List of vertices of the boundary of every segment. If all paths are + of equal length and this argument is a NumPy array, then it should + be of shape (num_faces, num_vertices, 3). + """ + if isinstance(segments3d, np.ndarray): + _api.check_shape((None, None, 3), segments3d=segments3d) + if isinstance(segments3d, np.ma.MaskedArray): + self._faces = segments3d.data + self._invalid_vertices = segments3d.mask.any(axis=-1) + else: + self._faces = segments3d + self._invalid_vertices = False + else: + # Turn the potentially ragged list into a numpy array for later speedups + # If it is ragged, set the unused vertices per face as invalid + num_faces = len(segments3d) + num_verts = np.fromiter(map(len, segments3d), dtype=np.intp) + max_verts = num_verts.max(initial=0) + segments = np.empty((num_faces, max_verts, 3)) + for i, face in enumerate(segments3d): + segments[i, :len(face)] = face + self._faces = segments + self._invalid_vertices = np.arange(max_verts) >= num_verts[:, None] def set_verts(self, verts, closed=True): """ @@ -991,7 +1315,7 @@ def set_verts_and_codes(self, verts, codes): # and set our own codes instead. self._codes3d = codes - def set_3d_properties(self): + def set_3d_properties(self, axlim_clip=False): # Force the collection to initialize the face and edgecolors # just in case it is a scalarmappable with a colormap. self.update_scalarmappable() @@ -1024,43 +1348,73 @@ def do_3d_projection(self): self._facecolor3d = self._facecolors if self._edge_is_mapped: self._edgecolor3d = self._edgecolors - txs, tys, tzs = proj3d._proj_transform_vec(self._vec, self.axes.M) - xyzlist = [(txs[sl], tys[sl], tzs[sl]) for sl in self._segslices] + + needs_masking = np.any(self._invalid_vertices) + num_faces = len(self._faces) + mask = self._invalid_vertices + + # Some faces might contain masked vertices, so we want to ignore any + # errors that those might cause + with np.errstate(invalid='ignore', divide='ignore'): + pfaces = proj3d._scale_proj_transform_vectors(self._faces, self.axes) + + if self._axlim_clip: + viewlim_mask = _viewlim_mask(self._faces[..., 0], self._faces[..., 1], + self._faces[..., 2], self.axes) + if np.any(viewlim_mask): + needs_masking = True + mask = mask | viewlim_mask + + pzs = pfaces[..., 2] + if needs_masking: + pzs = np.ma.MaskedArray(pzs, mask=mask) # This extra fuss is to re-order face / edge colors cface = self._facecolor3d cedge = self._edgecolor3d - if len(cface) != len(xyzlist): - cface = cface.repeat(len(xyzlist), axis=0) - if len(cedge) != len(xyzlist): + if len(cface) != num_faces: + cface = cface.repeat(num_faces, axis=0) + if len(cedge) != num_faces: if len(cedge) == 0: cedge = cface else: - cedge = cedge.repeat(len(xyzlist), axis=0) - - if xyzlist: - # sort by depth (furthest drawn first) - z_segments_2d = sorted( - ((self._zsortfunc(zs), np.column_stack([xs, ys]), fc, ec, idx) - for idx, ((xs, ys, zs), fc, ec) - in enumerate(zip(xyzlist, cface, cedge))), - key=lambda x: x[0], reverse=True) - - _, segments_2d, self._facecolors2d, self._edgecolors2d, idxs = \ - zip(*z_segments_2d) - else: - segments_2d = [] - self._facecolors2d = np.empty((0, 4)) - self._edgecolors2d = np.empty((0, 4)) - idxs = [] - - if self._codes3d is not None: - codes = [self._codes3d[idx] for idx in idxs] - PolyCollection.set_verts_and_codes(self, segments_2d, codes) + cedge = cedge.repeat(num_faces, axis=0) + + if len(pzs) > 0: + face_z = self._zsortfunc(pzs, axis=-1) else: - PolyCollection.set_verts(self, segments_2d, self._closed) + face_z = pzs + if needs_masking: + face_z = face_z.data + face_order = np.argsort(face_z, axis=-1)[::-1] - if len(self._edgecolor3d) != len(cface): + if len(pfaces) > 0: + faces_2d = pfaces[face_order, :, :2] + else: + faces_2d = pfaces + if self._codes3d is not None and len(self._codes3d) > 0: + if needs_masking: + segment_mask = ~mask[face_order, :] + faces_2d = [face[mask, :] for face, mask + in zip(faces_2d, segment_mask)] + codes = [self._codes3d[idx] for idx in face_order] + PolyCollection.set_verts_and_codes(self, faces_2d, codes) + else: + if needs_masking and len(faces_2d) > 0: + invalid_vertices_2d = np.broadcast_to( + mask[face_order, :, None], + faces_2d.shape) + faces_2d = np.ma.MaskedArray( + faces_2d, mask=invalid_vertices_2d) + PolyCollection.set_verts(self, faces_2d, self._closed) + + if len(cface) > 0: + self._facecolors2d = cface[face_order] + else: + self._facecolors2d = cface + if len(self._edgecolor3d) == len(cface) and len(cedge) > 0: + self._edgecolors2d = cedge[face_order] + else: self._edgecolors2d = self._edgecolor3d # Return zorder value @@ -1068,11 +1422,11 @@ def do_3d_projection(self): zvec = np.array([[0], [0], [self._sort_zpos], [1]]) ztrans = proj3d._proj_transform_vec(zvec, self.axes.M) return ztrans[2][0] - elif tzs.size > 0: + elif pzs.size > 0: # FIXME: Some results still don't look quite right. # In particular, examine contourf3d_demo2.py # with az = -54 and elev = -45. - return np.min(tzs) + return np.min(pzs) else: return np.nan @@ -1118,7 +1472,7 @@ def get_edgecolor(self): return np.asarray(self._edgecolors2d) -def poly_collection_2d_to_3d(col, zs=0, zdir='z'): +def poly_collection_2d_to_3d(col, zs=0, zdir='z', axlim_clip=False): """ Convert a `.PolyCollection` into a `.Poly3DCollection` object. @@ -1138,6 +1492,7 @@ def poly_collection_2d_to_3d(col, zs=0, zdir='z'): col.__class__ = Poly3DCollection col.set_verts_and_codes(segments_3d, codes) col.set_3d_properties() + col._axlim_clip = axlim_clip def juggle_axes(xs, ys, zs, zdir): @@ -1171,20 +1526,75 @@ def rotate_axes(xs, ys, zs, zdir): return xs, ys, zs -def _zalpha(colors, zs): - """Modify the alphas of the color list according to depth.""" - # FIXME: This only works well if the points for *zs* are well-spaced - # in all three dimensions. Otherwise, at certain orientations, - # the min and max zs are very close together. - # Should really normalize against the viewing depth. +def _zalpha( + colors, + zs, + min_alpha=0.3, + _data_scale=None, +): + """Modify the alpha values of the color list according to z-depth.""" + if len(colors) == 0 or len(zs) == 0: return np.zeros((0, 4)) - norm = Normalize(min(zs), max(zs)) - sats = 1 - norm(zs) * 0.7 + + # Alpha values beyond the range 0-1 inclusive make no sense, so clip them + min_alpha = np.clip(min_alpha, 0, 1) + + if _data_scale is None or _data_scale == 0: + # Don't scale the alpha values since we have no valid data scale for reference + sats = np.ones_like(zs) + + else: + # Deeper points have an increasingly transparent appearance + sats = np.clip(1 - (zs - np.min(zs)) / _data_scale, min_alpha, 1) + rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4)) + + # Change the alpha values of the colors using the generated alpha multipliers return np.column_stack([rgba[:, :3], rgba[:, 3] * sats]) +def _all_points_on_plane(xs, ys, zs, atol=1e-8): + """ + Check if all points are on the same plane. Note that NaN values are + ignored. + + Parameters + ---------- + xs, ys, zs : array-like + The x, y, and z coordinates of the points. + atol : float, default: 1e-8 + The tolerance for the equality check. + """ + xs, ys, zs = np.asarray(xs), np.asarray(ys), np.asarray(zs) + points = np.column_stack([xs, ys, zs]) + points = points[~np.isnan(points).any(axis=1)] + # Check for the case where we have less than 3 unique points + points = np.unique(points, axis=0) + if len(points) <= 3: + return True + # Calculate the vectors from the first point to all other points + vs = (points - points[0])[1:] + vs = vs / np.linalg.norm(vs, axis=1)[:, np.newaxis] + # Filter out parallel vectors + vs = np.unique(vs, axis=0) + if len(vs) <= 2: + return True + # Filter out parallel and antiparallel vectors to the first vector + cross_norms = np.linalg.norm(np.cross(vs[0], vs[1:]), axis=1) + zero_cross_norms = np.where(np.isclose(cross_norms, 0, atol=atol))[0] + 1 + vs = np.delete(vs, zero_cross_norms, axis=0) + if len(vs) <= 2: + return True + # Calculate the normal vector from the first three points + n = np.cross(vs[0], vs[1]) + n = n / np.linalg.norm(n) + # If the dot product of the normal vector and all other vectors is zero, + # all points are on the same plane + dots = np.dot(n, vs.transpose()) + return np.allclose(dots, 0, atol=atol) + + def _generate_normals(polygons): """ Compute the normals of a list of polygons, one normal per polygon. @@ -1222,6 +1632,7 @@ def _generate_normals(polygons): v2 = np.empty((len(polygons), 3)) for poly_i, ps in enumerate(polygons): n = len(ps) + ps = np.asarray(ps) i1, i2, i3 = 0, n//3, 2*n//3 v1[poly_i, :] = ps[i1, :] - ps[i2, :] v2[poly_i, :] = ps[i2, :] - ps[i3, :] diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 2315995e96d0..7d0a5ae009c4 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -8,9 +8,30 @@ Module containing Axes3D, an object which can plot 3D objects on a 2D matplotlib figure. + +Coordinate Systems +------------------ +3D plotting involves several coordinate transformations: + +1. **Data coordinates**: The user's raw x, y, z values. + +2. **Transformed coordinates**: Data coordinates after applying axis scale + transforms (log, symlog, etc.). For linear scales, these equal data + coordinates. Zoom/pan operations work in this space to ensure uniform + behavior with non-linear scales. + +3. **Normalized coordinates**: Transformed coordinates mapped to a [0, 1] + unit cube based on the current axis limits. + +4. **Projected coordinates**: 2D coordinates after applying the 3D to 2D + projection matrix, ready for display. + +Artists receive data in data coordinates, apply scale transforms internally +via ``do_3d_projection()``, then project to 2D for rendering. """ from collections import defaultdict +from functools import partialmethod import itertools import math import textwrap @@ -58,17 +79,19 @@ class Axes3D(Axes): Axes._shared_axes["view"] = cbook.Grouper() def __init__( - self, fig, rect=None, *args, - elev=30, azim=-60, roll=0, sharez=None, proj_type='persp', - box_aspect=None, computed_zorder=True, focal_length=None, - shareview=None, - **kwargs): + self, fig, rect=None, *args, + elev=30, azim=-60, roll=0, shareview=None, sharez=None, + proj_type='persp', focal_length=None, + box_aspect=None, + computed_zorder=True, + **kwargs, + ): """ Parameters ---------- fig : Figure The parent figure. - rect : tuple (left, bottom, width, height), default: None. + rect : tuple (left, bottom, width, height), default: (0, 0, 1, 1) The ``(left, bottom, width, height)`` Axes position. elev : float, default: 30 The elevation angle in degrees rotates the camera above and below @@ -83,32 +106,35 @@ def __init__( The roll angle in degrees rotates the camera about the viewing axis. A positive angle spins the camera clockwise, causing the scene to rotate counter-clockwise. + shareview : Axes3D, optional + Other Axes to share view angles with. Note that it is not possible + to unshare axes. sharez : Axes3D, optional - Other Axes to share z-limits with. + Other Axes to share z-limits with. Note that it is not possible to + unshare axes. proj_type : {'persp', 'ortho'} The projection type, default 'persp'. + focal_length : float, default: None + For a projection type of 'persp', the focal length of the virtual + camera. Must be > 0. If None, defaults to 1. + For a projection type of 'ortho', must be set to either None + or infinity (numpy.inf). If None, defaults to infinity. + The focal length can be computed from a desired Field Of View via + the equation: focal_length = 1/tan(FOV/2) box_aspect : 3-tuple of floats, default: None Changes the physical dimensions of the Axes3D, such that the ratio of the axis lengths in display units is x:y:z. If None, defaults to 4:4:3 computed_zorder : bool, default: True If True, the draw order is computed based on the average position - of the `.Artist`\\s along the view direction. + along the view direction for supported artist types (currently + Collections and Patches only). Set to False if you want to manually control the order in which Artists are drawn on top of each other using their *zorder* attribute. This can be used for fine-tuning if the automatic order does not produce the desired result. Note however, that a manual - zorder will only be correct for a limited view angle. If the figure + order will only be correct for a limited view angle. If the figure is rotated by the user, it will look wrong from certain angles. - focal_length : float, default: None - For a projection type of 'persp', the focal length of the virtual - camera. Must be > 0. If None, defaults to 1. - For a projection type of 'ortho', must be set to either None - or infinity (numpy.inf). If None, defaults to infinity. - The focal length can be computed from a desired Field Of View via - the equation: focal_length = 1/tan(FOV/2) - shareview : Axes3D, optional - Other Axes to share view angles with. **kwargs Other optional keyword arguments: @@ -169,11 +195,12 @@ def __init__( self.fmt_zdata = None self.mouse_init() - self.figure.canvas.callbacks._connect_picklable( + fig = self.get_figure(root=True) + fig.canvas.callbacks._connect_picklable( 'motion_notify_event', self._on_move) - self.figure.canvas.callbacks._connect_picklable( + fig.canvas.callbacks._connect_picklable( 'button_press_event', self._button_press) - self.figure.canvas.callbacks._connect_picklable( + fig.canvas.callbacks._connect_picklable( 'button_release_event', self._button_release) self.set_top_view() @@ -227,8 +254,16 @@ def get_zaxis(self): get_zticklines = _axis_method_wrapper("zaxis", "get_ticklines") def _transformed_cube(self, vals): - """Return cube with limits from *vals* transformed by self.M.""" + """Return cube with limits from *vals* transformed by self.M. + + The vals are in data space and are first transformed through the + axis scale transforms before being projected. + """ minx, maxx, miny, maxy, minz, maxz = vals + # Transform from data space to transformed coordinates + minx, maxx = self.xaxis.get_transform().transform([minx, maxx]) + miny, maxy = self.yaxis.get_transform().transform([miny, maxy]) + minz, maxz = self.zaxis.get_transform().transform([minz, maxz]) xyzs = [(minx, miny, minz), (maxx, miny, minz), (maxx, maxy, minz), @@ -237,8 +272,25 @@ def _transformed_cube(self, vals): (maxx, miny, maxz), (maxx, maxy, maxz), (minx, maxy, maxz)] - return proj3d._proj_points(xyzs, self.M) + return np.column_stack(proj3d._proj_trans_points(xyzs, self.M)) + def _update_transScale(self): + """ + Override transScale to always use identity transforms. + + In 2D axes, transScale applies scale transforms (log, symlog, etc.) to + convert data coordinates to display coordinates. In 3D axes, scale + transforms are applied to data coordinates before 3D projection via + each axis's transform. The projected 2D coordinates are already in + display space, so transScale must be identity to avoid double-scaling. + """ + self.transScale.set( + mtransforms.blended_transform_factory( + mtransforms.IdentityTransform(), + mtransforms.IdentityTransform())) + + @_api.delete_parameter("3.11", "share") + @_api.delete_parameter("3.11", "anchor") def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): """ Set the aspect ratios. @@ -258,39 +310,31 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): 'equalyz' adapt the y and z axes to have equal aspect ratios. ========= ================================================== - adjustable : None or {'box', 'datalim'}, optional - If not *None*, this defines which parameter will be adjusted to - meet the required aspect. See `.set_adjustable` for further - details. + adjustable : {'box', 'datalim'}, default: 'box' + Defines which parameter to adjust to meet the aspect ratio. + + - 'box': Change the physical dimensions of the axes bounding box. + - 'datalim': Change the x, y, or z data limits. anchor : None or str or 2-tuple of float, optional - If not *None*, this defines where the Axes will be drawn if there - is extra space due to aspect constraints. The most common way to - specify the anchor are abbreviations of cardinal directions: - - ===== ===================== - value description - ===== ===================== - 'C' centered - 'SW' lower left corner - 'S' middle of bottom edge - 'SE' lower right corner - etc. - ===== ===================== - - See `~.Axes.set_anchor` for further details. + .. deprecated:: 3.11 + This parameter has no effect. share : bool, default: False - If ``True``, apply the settings to all shared Axes. + .. deprecated:: 3.11 + This parameter has no effect. See Also -------- mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect """ + if adjustable is None: + adjustable = 'box' + _api.check_in_list(['box', 'datalim'], adjustable=adjustable) _api.check_in_list(('auto', 'equal', 'equalxy', 'equalyz', 'equalxz'), aspect=aspect) - super().set_aspect( - aspect='auto', adjustable=adjustable, anchor=anchor, share=share) + + self.set_adjustable(adjustable) self._aspect = aspect if aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'): @@ -603,6 +647,42 @@ def auto_scale_xyz(self, X, Y, Z=None, had_data=None): # Let autoscale_view figure out how to use this data. self.autoscale_view() + def _autoscale_axis(self, axis, v0, v1, minpos, margin, set_bound, _tight): + """ + Autoscale a single axis. + + Parameters + ---------- + axis : Axis + The axis to autoscale. + v0, v1 : float + Data interval limits. + minpos : float + Minimum positive value for log-scale handling. + margin : float + Margin to apply (e.g., self._xmargin). + set_bound : callable + Function to set the axis bound (e.g., self.set_xbound). + _tight : bool + Whether to use tight bounds. + """ + locator = axis.get_major_locator() + v0, v1 = locator.nonsingular(v0, v1) + # Validate limits for the scale (e.g., positive for log scale) + v0, v1 = axis._scale.limit_range_for_scale(v0, v1, minpos) + if margin > 0: + # Apply margin in transformed space to handle non-linear scales + transform = axis.get_transform() + inverse_trans = transform.inverted() + v0t, v1t = transform.transform([v0, v1]) + delta = (v1t - v0t) * margin + if not np.isfinite(delta): + delta = 0 + v0, v1 = inverse_trans.transform([v0t - delta, v1t + delta]) + if not _tight: + v0, v1 = locator.view_limits(v0, v1) + set_bound(v0, v1, self._view_margin) + def autoscale_view(self, tight=None, scalex=True, scaley=True, scalez=True): """ @@ -627,40 +707,22 @@ def autoscale_view(self, tight=None, _tight = self._tight = bool(tight) if scalex and self.get_autoscalex_on(): - x0, x1 = self.xy_dataLim.intervalx - xlocator = self.xaxis.get_major_locator() - x0, x1 = xlocator.nonsingular(x0, x1) - if self._xmargin > 0: - delta = (x1 - x0) * self._xmargin - x0 -= delta - x1 += delta - if not _tight: - x0, x1 = xlocator.view_limits(x0, x1) - self.set_xbound(x0, x1, self._view_margin) + self._autoscale_axis( + self.xaxis, *self.xy_dataLim.intervalx, + self.xy_dataLim.minposx, self._xmargin, + self.set_xbound, _tight) if scaley and self.get_autoscaley_on(): - y0, y1 = self.xy_dataLim.intervaly - ylocator = self.yaxis.get_major_locator() - y0, y1 = ylocator.nonsingular(y0, y1) - if self._ymargin > 0: - delta = (y1 - y0) * self._ymargin - y0 -= delta - y1 += delta - if not _tight: - y0, y1 = ylocator.view_limits(y0, y1) - self.set_ybound(y0, y1, self._view_margin) + self._autoscale_axis( + self.yaxis, *self.xy_dataLim.intervaly, + self.xy_dataLim.minposy, self._ymargin, + self.set_ybound, _tight) if scalez and self.get_autoscalez_on(): - z0, z1 = self.zz_dataLim.intervalx - zlocator = self.zaxis.get_major_locator() - z0, z1 = zlocator.nonsingular(z0, z1) - if self._zmargin > 0: - delta = (z1 - z0) * self._zmargin - z0 -= delta - z1 += delta - if not _tight: - z0, z1 = zlocator.view_limits(z0, z1) - self.set_zbound(z0, z1, self._view_margin) + self._autoscale_axis( + self.zaxis, *self.zz_dataLim.intervalx, + self.zz_dataLim.minposx, self._zmargin, + self.set_zbound, _tight) def get_w_lims(self): """Get 3D world limits.""" @@ -761,7 +823,8 @@ def set_zbound(self, lower=None, upper=None, view_margin=None): lower, upper, view_margin) def _set_lim3d(self, axis, lower=None, upper=None, *, emit=True, - auto=False, view_margin=None, axmin=None, axmax=None): + auto=False, view_margin=None, axmin=None, axmax=None, + minpos=np.inf): """ Set 3D axis limits. """ @@ -787,9 +850,20 @@ def _set_lim3d(self, axis, lower=None, upper=None, *, emit=True, view_margin = self._view_margin else: view_margin = 0 - delta = (upper - lower) * view_margin - lower -= delta - upper += delta + # Apply margin in transformed space to handle non-linear scales properly + if view_margin > 0 and hasattr(axis, '_scale') and axis._scale is not None: + transform = axis.get_transform() + inverse_trans = transform.inverted() + lower, upper = axis._scale.limit_range_for_scale(lower, upper, minpos) + lower_t, upper_t = transform.transform([lower, upper]) + delta = (upper_t - lower_t) * view_margin + if np.isfinite(delta): + new_range = [lower_t - delta, upper_t + delta] + lower, upper = inverse_trans.transform(new_range) + else: + delta = (upper - lower) * view_margin + lower -= delta + upper += delta return axis._set_lim(lower, upper, emit=emit, auto=auto) def set_xlim(self, left=None, right=None, *, emit=True, auto=False, @@ -862,7 +936,8 @@ def set_xlim(self, left=None, right=None, *, emit=True, auto=False, >>> set_xlim(5000, 0) """ return self._set_lim3d(self.xaxis, left, right, emit=emit, auto=auto, - view_margin=view_margin, axmin=xmin, axmax=xmax) + view_margin=view_margin, axmin=xmin, axmax=xmax, + minpos=self.xy_dataLim.minposx) def set_ylim(self, bottom=None, top=None, *, emit=True, auto=False, view_margin=None, ymin=None, ymax=None): @@ -934,7 +1009,8 @@ def set_ylim(self, bottom=None, top=None, *, emit=True, auto=False, >>> set_ylim(5000, 0) """ return self._set_lim3d(self.yaxis, bottom, top, emit=emit, auto=auto, - view_margin=view_margin, axmin=ymin, axmax=ymax) + view_margin=view_margin, axmin=ymin, axmax=ymax, + minpos=self.xy_dataLim.minposy) def set_zlim(self, bottom=None, top=None, *, emit=True, auto=False, view_margin=None, zmin=None, zmax=None): @@ -1006,7 +1082,8 @@ def set_zlim(self, bottom=None, top=None, *, emit=True, auto=False, >>> set_zlim(5000, 0) """ return self._set_lim3d(self.zaxis, bottom, top, emit=emit, auto=auto, - view_margin=view_margin, axmin=zmin, axmax=zmax) + view_margin=view_margin, axmin=zmin, axmax=zmax, + minpos=self.zz_dataLim.minposx) set_xlim3d = set_xlim set_ylim3d = set_ylim @@ -1044,25 +1121,92 @@ def get_zlim(self): get_zscale = _axis_method_wrapper("zaxis", "get_scale") - # Redefine all three methods to overwrite their docstrings. - set_xscale = _axis_method_wrapper("xaxis", "_set_axes_scale") - set_yscale = _axis_method_wrapper("yaxis", "_set_axes_scale") - set_zscale = _axis_method_wrapper("zaxis", "_set_axes_scale") - set_xscale.__doc__, set_yscale.__doc__, set_zscale.__doc__ = map( + # Custom scale setters that handle limit validation for non-linear scales + def _set_axis_scale(self, axis, value, **kwargs): + """ + Set scale for an axis and constrain limits to valid range. + + Parameters + ---------- + axis : Axis + The axis to set the scale on. + value : str + The scale name. + **kwargs + Forwarded to scale constructor. + """ + # For non-linear scales on the z-axis, switch from the [0, 1] + + # margin=0 representation to the same xymargin + margin=0.05 + # representation that x/y use. Both produce identical linear limits, + # but only the xymargin form has valid positive lower bounds for log + # etc. This must happen before _set_axes_scale because that triggers + # autoscale_view internally. + if (axis is self.zaxis and value != 'linear' + and np.array_equal(self.zz_dataLim.get_points(), [[0, 0], [1, 1]])): + xymargin = 0.05 * 10/11 + self.zz_dataLim = Bbox([[xymargin, xymargin], + [1 - xymargin, 1 - xymargin]]) + self._zmargin = self._xmargin + axis._set_axes_scale(value, **kwargs) + + def set_xscale(self, value, **kwargs): + """ + Set the x-axis scale. + + Parameters + ---------- + value : {"linear", "log", "symlog", "logit", ...} + The axis scale type to apply. See `~.scale.ScaleBase` for + the list of available scales. + + **kwargs + Keyword arguments are forwarded to the scale class. + For example, ``base=2`` can be passed when using a log scale. + """ + self._set_axis_scale(self.xaxis, value, **kwargs) + + def set_yscale(self, value, **kwargs): + """ + Set the y-axis scale. + + Parameters + ---------- + value : {"linear", "log", "symlog", "logit", ...} + The axis scale type to apply. See `~.scale.ScaleBase` for + the list of available scales. + + **kwargs + Keyword arguments are forwarded to the scale class. + For example, ``base=2`` can be passed when using a log scale. + """ + self._set_axis_scale(self.yaxis, value, **kwargs) + + def set_zscale(self, value, **kwargs): """ - Set the {}-axis scale. + Set the z-axis scale. Parameters ---------- - value : {{"linear"}} - The axis scale type to apply. 3D Axes currently only support - linear scales; other scales yield nonsensical results. + value : {"linear", "log", "symlog", "logit", ...} + The axis scale type to apply. See `~.scale.ScaleBase` for + the list of available scales. **kwargs - Keyword arguments are nominally forwarded to the scale class, but - none of them is applicable for linear scales. - """.format, - ["x", "y", "z"]) + Keyword arguments are forwarded to the scale class. + For example, ``base=2`` can be passed when using a log scale. + """ + self._set_axis_scale(self.zaxis, value, **kwargs) + + def _raise_semilog_not_implemented(self, name, *args, **kwargs): + raise NotImplementedError( + f"Axes3D does not support {name}. Use ax.set_xscale/set_yscale/set_zscale " + "and ax.plot(...) instead." + ) + + semilogx = partialmethod(_raise_semilog_not_implemented, "semilogx") + semilogy = partialmethod(_raise_semilog_not_implemented, "semilogy") + semilogz = partialmethod(_raise_semilog_not_implemented, "semilogz") + loglog = partialmethod(_raise_semilog_not_implemented, "loglog") get_zticks = _axis_method_wrapper("zaxis", "get_ticklocs") set_zticks = _axis_method_wrapper("zaxis", "set_ticks") @@ -1147,7 +1291,7 @@ def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z", azim = self.initial_azim if roll is None: roll = self.initial_roll - vertical_axis = _api.check_getitem( + vertical_axis = _api.getitem_checked( {name: idx for idx, name in enumerate(self._axis_names)}, vertical_axis=vertical_axis, ) @@ -1210,17 +1354,81 @@ def _roll_to_vertical( else: return np.roll(arr, (self._vertical_axis - 2)) + def _get_scaled_limits(self): + """ + Get axis limits transformed through their respective scale transforms. + + Returns + ------- + tuple + (xmin_scaled, xmax_scaled, ymin_scaled, ymax_scaled, + zmin_scaled, zmax_scaled) + """ + xmin, xmax = self.xaxis.get_transform().transform(self.get_xlim3d()) + ymin, ymax = self.yaxis.get_transform().transform(self.get_ylim3d()) + zmin, zmax = self.zaxis.get_transform().transform(self.get_zlim3d()) + return xmin, xmax, ymin, ymax, zmin, zmax + + def _untransform_point(self, x, y, z): + """ + Convert a point from transformed coordinates to data coordinates. + + Parameters + ---------- + x, y, z : float + A single point in transformed coordinates. + + Returns + ------- + x_data, y_data, z_data : float + The point in data coordinates. + """ + x_data = self.xaxis.get_transform().inverted().transform([x])[0] + y_data = self.yaxis.get_transform().inverted().transform([y])[0] + z_data = self.zaxis.get_transform().inverted().transform([z])[0] + return x_data, y_data, z_data + + def _set_lims_from_transformed(self, xmin_t, xmax_t, ymin_t, ymax_t, + zmin_t, zmax_t): + """ + Set axis limits from transformed coordinates. + + Converts limits from transformed coordinates back to data coordinates, + applies limit_range_for_scale validation, and sets the axis limits. + + Parameters + ---------- + xmin_t, xmax_t, ymin_t, ymax_t, zmin_t, zmax_t : float + Axis limits in transformed coordinates. + """ + # Transform back to data space + xmin, xmax = self.xaxis.get_transform().inverted().transform([xmin_t, xmax_t]) + ymin, ymax = self.yaxis.get_transform().inverted().transform([ymin_t, ymax_t]) + zmin, zmax = self.zaxis.get_transform().inverted().transform([zmin_t, zmax_t]) + + # Validate limits for scale constraints (e.g., positive for log scale) + xmin, xmax = self.xaxis._scale.limit_range_for_scale( + xmin, xmax, self.xy_dataLim.minposx) + ymin, ymax = self.yaxis._scale.limit_range_for_scale( + ymin, ymax, self.xy_dataLim.minposy) + zmin, zmax = self.zaxis._scale.limit_range_for_scale( + zmin, zmax, self.zz_dataLim.minposx) + + # Set the new axis limits + self.set_xlim3d(xmin, xmax, auto=None) + self.set_ylim3d(ymin, ymax, auto=None) + self.set_zlim3d(zmin, zmax, auto=None) + def get_proj(self): """Create the projection matrix from the current viewing position.""" # Transform to uniform world coordinates 0-1, 0-1, 0-1 box_aspect = self._roll_to_vertical(self._box_aspect) - worldM = proj3d.world_transformation( - *self.get_xlim3d(), - *self.get_ylim3d(), - *self.get_zlim3d(), - pb_aspect=box_aspect, - ) + # For non-linear scales, we use the scaled limits so the world + # transformation maps transformed coordinates (not data coordinates) + # to the unit cube + scaled_limits = self._get_scaled_limits() + worldM = proj3d.world_transformation(*scaled_limits, pb_aspect=box_aspect) # Look into the middle of the world coordinates: R = 0.5 * box_aspect @@ -1308,7 +1516,7 @@ def sharez(self, other): This is equivalent to passing ``sharez=other`` when constructing the Axes, and cannot be used if the z-axis is already being shared with - another Axes. + another Axes. Note that it is not possible to unshare axes. """ _api.check_isinstance(Axes3D, other=other) if self._sharez is not None and other is not self._sharez: @@ -1325,9 +1533,9 @@ def shareview(self, other): """ Share the view angles with *other*. - This is equivalent to passing ``shareview=other`` when - constructing the Axes, and cannot be used if the view angles are - already being shared with another Axes. + This is equivalent to passing ``shareview=other`` when constructing the + Axes, and cannot be used if the view angles are already being shared + with another Axes. Note that it is not possible to unshare axes. """ _api.check_isinstance(Axes3D, other=other) if self._shareview is not None and other is not self._shareview: @@ -1360,7 +1568,7 @@ def _button_press(self, event): if event.inaxes == self: self.button_pressed = event.button self._sx, self._sy = event.xdata, event.ydata - toolbar = self.figure.canvas.toolbar + toolbar = self.get_figure(root=True).canvas.toolbar if toolbar and toolbar._nav_stack() is None: toolbar.push_current() if toolbar: @@ -1368,7 +1576,7 @@ def _button_press(self, event): def _button_release(self, event): self.button_pressed = None - toolbar = self.figure.canvas.toolbar + toolbar = self.get_figure(root=True).canvas.toolbar # backend_bases.release_zoom and backend_bases.release_pan call # push_current, so check the navigation mode so we don't call it twice if toolbar and self.get_navigate_mode() is None: @@ -1395,7 +1603,7 @@ def _set_view(self, view): def format_zdata(self, z): """ Return *z* string formatted. This function will use the - :attr:`fmt_zdata` attribute if it is callable, else will fall + :attr:`!fmt_zdata` attribute if it is callable, else will fall back on the zaxis major formatter """ try: @@ -1453,7 +1661,7 @@ def _location_coords(self, xv, yv, renderer): def _get_camera_loc(self): """ - Returns the current camera location in data coordinates. + Returns the current camera location in transformed coordinates. """ cx, cy, cz, dx, dy, dz = self._get_w_centers_ranges() c = np.array([cx, cy, cz]) @@ -1477,17 +1685,13 @@ def _calc_coord(self, xv, yv, renderer=None): else: # perspective projection zv = -1 / self._focal_length - # Convert point on view plane to data coordinates p1 = np.array(proj3d.inv_transform(xv, yv, zv, self.invM)).ravel() # Get the vector from the camera to the point on the view plane vec = self._get_camera_loc() - p1 # Get the pane locations for each of the axes - pane_locs = [] - for axis in self._axis_map.values(): - xys, loc = axis.active_pane() - pane_locs.append(loc) + pane_locs_data = [axis.active_pane()[1] for axis in self._axis_map.values()] # Find the distance to the nearest pane by projecting the view vector scales = np.zeros(3) @@ -1495,30 +1699,48 @@ def _calc_coord(self, xv, yv, renderer=None): if vec[i] == 0: scales[i] = np.inf else: - scales[i] = (p1[i] - pane_locs[i]) / vec[i] + scales[i] = (pane_locs_data[i] - p1[i]) / vec[i] pane_idx = np.argmin(abs(scales)) scale = scales[pane_idx] # Calculate the point on the closest pane - p2 = p1 - scale*vec + p2 = p1 + scale * vec + + # Convert from transformed to data coordinates + p2 = np.array(self._untransform_point(p2[0], p2[1], p2[2])) return p2, pane_idx def _arcball(self, x: float, y: float) -> np.ndarray: """ - Convert a point (x, y) to a point on a virtual trackball - This is Ken Shoemake's arcball + Convert a point (x, y) to a point on a virtual trackball. + + This is Ken Shoemake's arcball (a sphere), modified + to soften the abrupt edge (optionally). See: Ken Shoemake, "ARCBALL: A user interface for specifying three-dimensional rotation using a mouse." in Proceedings of Graphics Interface '92, 1992, pp. 151-156, https://doi.org/10.20380/GI1992.18 - """ - x *= 2 - y *= 2 + The smoothing of the edge is inspired by Gavin Bell's arcball + (a sphere combined with a hyperbola), but here, the sphere + is combined with a section of a cylinder, so it has finite support. + """ + s = mpl.rcParams['axes3d.trackballsize'] / 2 + b = mpl.rcParams['axes3d.trackballborder'] / s + x /= s + y /= s r2 = x*x + y*y - if r2 > 1: - p = np.array([0, x/math.sqrt(r2), y/math.sqrt(r2)]) + r = np.sqrt(r2) + ra = 1 + b + a = b * (1 + b/2) + ri = 2/(ra + 1/ra) + if r < ri: + p = np.array([np.sqrt(1 - r2), x, y]) + elif r < ra: + dr = ra - r + p = np.array([a - np.sqrt((a + dr) * (a - dr)), x, y]) + p /= np.linalg.norm(p) else: - p = np.array([math.sqrt(1-r2), x, y]) + p = np.array([0, x/r, y/r]) return p def _on_move(self, event): @@ -1556,23 +1778,40 @@ def _on_move(self, event): if dx == 0 and dy == 0: return - # Convert to quaternion - elev = np.deg2rad(self.elev) - azim = np.deg2rad(self.azim) - roll = np.deg2rad(self.roll) - q = _Quaternion.from_cardan_angles(elev, azim, roll) - - # Update quaternion - a variation on Ken Shoemake's ARCBALL - current_vec = self._arcball(self._sx/w, self._sy/h) - new_vec = self._arcball(x/w, y/h) - dq = _Quaternion.rotate_from_to(current_vec, new_vec) - q = dq * q - - # Convert to elev, azim, roll - elev, azim, roll = q.as_cardan_angles() - azim = np.rad2deg(azim) - elev = np.rad2deg(elev) - roll = np.rad2deg(roll) + style = mpl.rcParams['axes3d.mouserotationstyle'] + if style == 'azel': + roll = np.deg2rad(self.roll) + delev = -(dy/h)*180*np.cos(roll) + (dx/w)*180*np.sin(roll) + dazim = -(dy/h)*180*np.sin(roll) - (dx/w)*180*np.cos(roll) + elev = self.elev + delev + azim = self.azim + dazim + roll = self.roll + else: + q = _Quaternion.from_cardan_angles( + *np.deg2rad((self.elev, self.azim, self.roll))) + + if style == 'trackball': + k = np.array([0, -dy/h, dx/w]) + nk = np.linalg.norm(k) + th = nk / mpl.rcParams['axes3d.trackballsize'] + dq = _Quaternion(np.cos(th), k*np.sin(th)/nk) + else: # 'sphere', 'arcball' + current_vec = self._arcball(self._sx/w, self._sy/h) + new_vec = self._arcball(x/w, y/h) + if style == 'sphere': + dq = _Quaternion.rotate_from_to(current_vec, new_vec) + else: # 'arcball' + dq = _Quaternion(0, new_vec) * _Quaternion(0, -current_vec) + + q = dq * q + elev, azim, roll = np.rad2deg(q.as_cardan_angles()) + step = mpl.rcParams["axes3d.snap_rotation"] + if step > 0 and getattr(event, "key", None) == "control": + elev = step * round(elev / step) + azim = step * round(azim / step) + roll = step * round(roll / step) + + # update view vertical_axis = self._axis_names[self._vertical_axis] self.view_init( elev=elev, @@ -1601,7 +1840,7 @@ def _on_move(self, event): # Store the event coordinates for the next time through. self._sx, self._sy = x, y # Always request a draw update at the end of interaction - self.figure.canvas.draw_idle() + self.get_figure(root=True).canvas.draw_idle() def drag_pan(self, button, key, x, y): # docstring inherited @@ -1628,16 +1867,17 @@ def drag_pan(self, button, key, x, y): R = -R / self._box_aspect * self._dist duvw_projected = R.T @ np.array([du, dv, dw]) - # Calculate pan distance - minx, maxx, miny, maxy, minz, maxz = self.get_w_lims() + # Calculate pan distance in transformed coordinates for non-linear scales + minx, maxx, miny, maxy, minz, maxz = self._get_scaled_limits() dx = (maxx - minx) * duvw_projected[0] dy = (maxy - miny) * duvw_projected[1] dz = (maxz - minz) * duvw_projected[2] - # Set the new axis limits - self.set_xlim3d(minx + dx, maxx + dx, auto=None) - self.set_ylim3d(miny + dy, maxy + dy, auto=None) - self.set_zlim3d(minz + dz, maxz + dz, auto=None) + # Compute new limits in transformed coordinates + self._set_lims_from_transformed( + minx + dx, maxx + dx, + miny + dy, maxy + dy, + minz + dz, maxz + dz) def _calc_view_axes(self, eye): """ @@ -1753,6 +1993,9 @@ def _scale_axis_limits(self, scale_x, scale_y, scale_z): limits by scale factors. A scale factor > 1 zooms out and a scale factor < 1 zooms in. + For non-linear scales, the scaling happens in transformed coordinates to ensure + uniform zoom behavior. + Parameters ---------- scale_x : float @@ -1762,23 +2005,29 @@ def _scale_axis_limits(self, scale_x, scale_y, scale_z): scale_z : float Scale factor for the z data axis. """ - # Get the axis centers and ranges + # Get the axis centers and ranges in transformed coordinates cx, cy, cz, dx, dy, dz = self._get_w_centers_ranges() - # Set the scaled axis limits - self.set_xlim3d(cx - dx*scale_x/2, cx + dx*scale_x/2, auto=None) - self.set_ylim3d(cy - dy*scale_y/2, cy + dy*scale_y/2, auto=None) - self.set_zlim3d(cz - dz*scale_z/2, cz + dz*scale_z/2, auto=None) + # Compute new limits in transformed coordinates and set + self._set_lims_from_transformed( + cx - dx*scale_x/2, cx + dx*scale_x/2, + cy - dy*scale_y/2, cy + dy*scale_y/2, + cz - dz*scale_z/2, cz + dz*scale_z/2) def _get_w_centers_ranges(self): - """Get 3D world centers and axis ranges.""" - # Calculate center of axis limits - minx, maxx, miny, maxy, minz, maxz = self.get_w_lims() + """ + Get 3D world centers and axis ranges in transformed coordinates. + + For non-linear scales (log, symlog, etc.), centers and ranges are + computed in transformed coordinates to ensure uniform zoom/pan behavior. + """ + # Get limits in transformed coordinates for non-linear scale zoom/pan + minx, maxx, miny, maxy, minz, maxz = self._get_scaled_limits() cx = (maxx + minx)/2 cy = (maxy + miny)/2 cz = (maxz + minz)/2 - # Calculate range of axis limits + # Calculate range of axis limits in transformed coordinates dx = (maxx - minx) dy = (maxy - miny) dz = (maxz - minz) @@ -1796,7 +2045,7 @@ def get_zlabel(self): """ Get the z-label text string. """ - label = self.zaxis.get_label() + label = self.zaxis.label return label.get_text() # Axes rectangle characteristics @@ -1854,18 +2103,34 @@ def tick_params(self, axis='both', **kwargs): def invert_zaxis(self): """ - Invert the z-axis. + [*Discouraged*] Invert the z-axis. + + .. admonition:: Discouraged + + The use of this method is discouraged. + Use `.Axes3D.set_zinverted` instead. See Also -------- - zaxis_inverted + get_zinverted get_zlim, set_zlim get_zbound, set_zbound """ bottom, top = self.get_zlim() self.set_zlim(top, bottom, auto=None) + set_zinverted = _axis_method_wrapper("zaxis", "set_inverted") + get_zinverted = _axis_method_wrapper("zaxis", "get_inverted") zaxis_inverted = _axis_method_wrapper("zaxis", "get_inverted") + if zaxis_inverted.__doc__: + zaxis_inverted.__doc__ = ("[*Discouraged*] " + zaxis_inverted.__doc__ + + textwrap.dedent(""" + + .. admonition:: Discouraged + + The use of this method is discouraged. + Use `.Axes3D.get_zinverted` instead. + """)) def get_zbound(self): """ @@ -1883,7 +2148,7 @@ def get_zbound(self): else: return upper, lower - def text(self, x, y, z, s, zdir=None, **kwargs): + def text(self, x, y, z, s, zdir=None, *, axlim_clip=False, **kwargs): """ Add the text *s* to the 3D Axes at location *x*, *y*, *z* in data coordinates. @@ -1896,6 +2161,10 @@ def text(self, x, y, z, s, zdir=None, **kwargs): zdir : {'x', 'y', 'z', 3-tuple}, optional The direction to be used as the z-direction. Default: 'z'. See `.get_dir_vector` for a description of the values. + axlim_clip : bool, default: False + Whether to hide text that is outside the axes view limits. + + .. versionadded:: 3.10 **kwargs Other arguments are forwarded to `matplotlib.axes.Axes.text`. @@ -1904,14 +2173,24 @@ def text(self, x, y, z, s, zdir=None, **kwargs): `.Text3D` The created `.Text3D` instance. """ + if 'rotation' in kwargs: + _api.warn_external( + "The `rotation` parameter has not yet been implemented " + "and is currently ignored." + ) + if 'rotation_mode' in kwargs: + _api.warn_external( + "The `rotation_mode` parameter has not yet been implemented " + "and is currently ignored." + ) text = super().text(x, y, s, **kwargs) - art3d.text_2d_to_3d(text, z, zdir) + art3d.text_2d_to_3d(text, z, zdir, axlim_clip) return text text3D = text text2D = Axes.text - def plot(self, xs, ys, *args, zdir='z', **kwargs): + def plot(self, xs, ys, *args, zdir='z', axlim_clip=False, **kwargs): """ Plot 2D or 3D data. @@ -1926,6 +2205,10 @@ def plot(self, xs, ys, *args, zdir='z', **kwargs): each point. zdir : {'x', 'y', 'z'}, default: 'z' When plotting 2D data, the direction to use as z. + axlim_clip : bool, default: False + Whether to hide data that is outside the axes view limits. + + .. versionadded:: 3.10 **kwargs Other arguments are forwarded to `matplotlib.axes.Axes.plot`. """ @@ -1945,7 +2228,7 @@ def plot(self, xs, ys, *args, zdir='z', **kwargs): lines = super().plot(xs, ys, *args, **kwargs) for line in lines: - art3d.line_2d_to_3d(line, zs=zs, zdir=zdir) + art3d.line_2d_to_3d(line, zs=zs, zdir=zdir, axlim_clip=axlim_clip) xs, ys, zs = art3d.juggle_axes(xs, ys, zs, zdir) self.auto_scale_xyz(xs, ys, zs, had_data) @@ -1953,8 +2236,138 @@ def plot(self, xs, ys, *args, zdir='z', **kwargs): plot3D = plot + def fill_between(self, x1, y1, z1, x2, y2, z2, *, + where=None, mode='auto', facecolors=None, shade=None, + axlim_clip=False, **kwargs): + """ + Fill the area between two 3D curves. + + The curves are defined by the points (*x1*, *y1*, *z1*) and + (*x2*, *y2*, *z2*). This creates one or multiple quadrangle + polygons that are filled. All points must be the same length N, or a + single value to be used for all points. + + Parameters + ---------- + x1, y1, z1 : float or 1D array-like + x, y, and z coordinates of vertices for 1st line. + + x2, y2, z2 : float or 1D array-like + x, y, and z coordinates of vertices for 2nd line. + + where : array of bool (length N), optional + Define *where* to exclude some regions from being filled. The + filled regions are defined by the coordinates ``pts[where]``, + for all x, y, and z pts. More precisely, fill between ``pts[i]`` + and ``pts[i+1]`` if ``where[i] and where[i+1]``. Note that this + definition implies that an isolated *True* value between two + *False* values in *where* will not result in filling. Both sides of + the *True* position remain unfilled due to the adjacent *False* + values. + + mode : {'quad', 'polygon', 'auto'}, default: 'auto' + The fill mode. One of: + + - 'quad': A separate quadrilateral polygon is created for each + pair of subsequent points in the two lines. + - 'polygon': The two lines are connected to form a single polygon. + This is faster and can render more cleanly for simple shapes + (e.g. for filling between two lines that lie within a plane). + - 'auto': If the points all lie on the same 3D plane, 'polygon' is + used. Otherwise, 'quad' is used. + + facecolors : :mpltype:`color` or list of :mpltype:`color`, optional + Colors of each individual patch, or a single color to be used for + all patches. If not given, the next color from the patch color + cycle is used. + + shade : bool, default: None + Whether to shade the facecolors. If *None*, then defaults to *True* + for 'quad' mode and *False* for 'polygon' mode. + + axlim_clip : bool, default: False + Whether to hide data that is outside the axes view limits. + + .. versionadded:: 3.10 + + **kwargs + All other keyword arguments are passed on to `.Poly3DCollection`. + + Returns + ------- + `.Poly3DCollection` + A `.Poly3DCollection` containing the plotted polygons. + + """ + _api.check_in_list(['auto', 'quad', 'polygon'], mode=mode) + + had_data = self.has_data() + x1, y1, z1, x2, y2, z2 = cbook._broadcast_with_masks(x1, y1, z1, x2, y2, z2) + + if facecolors is None: + facecolors = [self._get_patches_for_fill.get_next_color()] + facecolors = list(mcolors.to_rgba_array(facecolors)) + + if where is None: + where = True + else: + where = np.asarray(where, dtype=bool) + if where.size != x1.size: + raise ValueError(f"where size ({where.size}) does not match " + f"size ({x1.size})") + where = where & ~np.isnan(x1) # NaNs were broadcast in _broadcast_with_masks + + if mode == 'auto': + if art3d._all_points_on_plane(np.concatenate((x1[where], x2[where])), + np.concatenate((y1[where], y2[where])), + np.concatenate((z1[where], z2[where])), + atol=1e-12): + mode = 'polygon' + else: + mode = 'quad' + + if shade is None: + if mode == 'quad': + shade = True + else: + shade = False + + polys = [] + for idx0, idx1 in cbook.contiguous_regions(where): + x1i = x1[idx0:idx1] + y1i = y1[idx0:idx1] + z1i = z1[idx0:idx1] + x2i = x2[idx0:idx1] + y2i = y2[idx0:idx1] + z2i = z2[idx0:idx1] + + if not len(x1i): + continue + + if mode == 'quad': + # Preallocate the array for the region's vertices, and fill it in + n_polys_i = len(x1i) - 1 + polys_i = np.empty((n_polys_i, 4, 3)) + polys_i[:, 0, :] = np.column_stack((x1i[:-1], y1i[:-1], z1i[:-1])) + polys_i[:, 1, :] = np.column_stack((x1i[1:], y1i[1:], z1i[1:])) + polys_i[:, 2, :] = np.column_stack((x2i[1:], y2i[1:], z2i[1:])) + polys_i[:, 3, :] = np.column_stack((x2i[:-1], y2i[:-1], z2i[:-1])) + polys = polys + [*polys_i] + elif mode == 'polygon': + line1 = np.column_stack((x1i, y1i, z1i)) + line2 = np.column_stack((x2i[::-1], y2i[::-1], z2i[::-1])) + poly = np.concatenate((line1, line2), axis=0) + polys.append(poly) + + polyc = art3d.Poly3DCollection(polys, facecolors=facecolors, shade=shade, + axlim_clip=axlim_clip, **kwargs) + self.add_collection(polyc, autolim="_datalim_only") + + self.auto_scale_xyz([x1, x2], [y1, y2], [z1, z2], had_data) + return polyc + def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, - vmax=None, lightsource=None, **kwargs): + vmax=None, lightsource=None, axlim_clip=False, **kwargs): """ Create a surface plot. @@ -2019,6 +2432,11 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, lightsource : `~matplotlib.colors.LightSource`, optional The lightsource to use when *shade* is True. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 + **kwargs Other keyword arguments are forwarded to `.Poly3DCollection`. """ @@ -2078,8 +2496,8 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, col_inds = list(range(0, cols-1, cstride)) + [cols-1] polys = [] - for rs, rs_next in zip(row_inds[:-1], row_inds[1:]): - for cs, cs_next in zip(col_inds[:-1], col_inds[1:]): + for rs, rs_next in itertools.pairwise(row_inds): + for cs, cs_next in itertools.pairwise(col_inds): ps = [ # +1 ensures we share edges between polygons cbook._array_perimeter(a[rs:rs_next+1, cs:cs_next+1]) @@ -2119,9 +2537,9 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, if fcolors is not None: polyc = art3d.Poly3DCollection( polys, edgecolors=colset, facecolors=colset, shade=shade, - lightsource=lightsource, **kwargs) + lightsource=lightsource, axlim_clip=axlim_clip, **kwargs) elif cmap: - polyc = art3d.Poly3DCollection(polys, **kwargs) + polyc = art3d.Poly3DCollection(polys, axlim_clip=axlim_clip, **kwargs) # can't always vectorize, because polys might be jagged if isinstance(polys, np.ndarray): avg_z = polys[..., 2].mean(axis=-1) @@ -2139,15 +2557,15 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, color = np.array(mcolors.to_rgba(color)) polyc = art3d.Poly3DCollection( - polys, facecolors=color, shade=shade, - lightsource=lightsource, **kwargs) + polys, facecolors=color, shade=shade, lightsource=lightsource, + axlim_clip=axlim_clip, **kwargs) - self.add_collection(polyc) + self.add_collection(polyc, autolim="_datalim_only") self.auto_scale_xyz(X, Y, Z, had_data) return polyc - def plot_wireframe(self, X, Y, Z, **kwargs): + def plot_wireframe(self, X, Y, Z, *, axlim_clip=False, **kwargs): """ Plot a 3D wireframe. @@ -2163,6 +2581,12 @@ def plot_wireframe(self, X, Y, Z, **kwargs): X, Y, Z : 2D arrays Data values. + axlim_clip : bool, default: False + Whether to hide lines and patches with vertices outside the axes + view limits. + + .. versionadded:: 3.10 + rcount, ccount : int Maximum number of samples used in each direction. If the input data is larger, it will be downsampled (by slicing) to these @@ -2217,56 +2641,57 @@ def plot_wireframe(self, X, Y, Z, **kwargs): rstride = int(max(np.ceil(rows / rcount), 1)) if rcount else 0 cstride = int(max(np.ceil(cols / ccount), 1)) if ccount else 0 + if rstride == 0 and cstride == 0: + raise ValueError("Either rstride or cstride must be non zero") + # We want two sets of lines, one running along the "rows" of # Z and another set of lines running along the "columns" of Z. # This transpose will make it easy to obtain the columns. tX, tY, tZ = np.transpose(X), np.transpose(Y), np.transpose(Z) - if rstride: - rii = list(range(0, rows, rstride)) - # Add the last index only if needed - if rows > 0 and rii[-1] != (rows - 1): - rii += [rows-1] + # Compute the indices of the row and column lines to be drawn + # For Z.size == 0, we don't want to draw any lines since the data is empty + if rstride == 0 or Z.size == 0: + rii = np.array([], dtype=int) + elif (rows - 1) % rstride == 0: + # last index is hit: rii[-1] == rows - 1 + rii = np.arange(0, rows, rstride) else: - rii = [] - if cstride: - cii = list(range(0, cols, cstride)) - # Add the last index only if needed - if cols > 0 and cii[-1] != (cols - 1): - cii += [cols-1] + # add the last index + rii = np.arange(0, rows + rstride, rstride) + rii[-1] = rows - 1 + + if cstride == 0 or Z.size == 0: + cii = np.array([], dtype=int) + elif (cols - 1) % cstride == 0: + # last index is hit: cii[-1] == cols - 1 + cii = np.arange(0, cols, cstride) else: - cii = [] - - if rstride == 0 and cstride == 0: - raise ValueError("Either rstride or cstride must be non zero") - - # If the inputs were empty, then just - # reset everything. - if Z.size == 0: - rii = [] - cii = [] - - xlines = [X[i] for i in rii] - ylines = [Y[i] for i in rii] - zlines = [Z[i] for i in rii] - - txlines = [tX[i] for i in cii] - tylines = [tY[i] for i in cii] - tzlines = [tZ[i] for i in cii] - - lines = ([list(zip(xl, yl, zl)) - for xl, yl, zl in zip(xlines, ylines, zlines)] - + [list(zip(xl, yl, zl)) - for xl, yl, zl in zip(txlines, tylines, tzlines)]) - - linec = art3d.Line3DCollection(lines, **kwargs) - self.add_collection(linec) - self.auto_scale_xyz(X, Y, Z, had_data) + # add the last index + cii = np.arange(0, cols + cstride, cstride) + cii[-1] = cols - 1 + + row_lines = np.stack([X[rii], Y[rii], Z[rii]], axis=-1) + col_lines = np.stack([tX[cii], tY[cii], tZ[cii]], axis=-1) + + # We autoscale twice because autoscaling is much faster with vectorized numpy + # arrays, but row_lines and col_lines might not be the same shape, so we can't + # stack them to check them in a single pass. + # Note that while the column and row grid points are the same, the lines + # between them may expand the view limits, so we have to check both. + self.auto_scale_xyz(row_lines[..., 0], row_lines[..., 1], row_lines[..., 2], + had_data) + self.auto_scale_xyz(col_lines[..., 0], col_lines[..., 1], col_lines[..., 2], + had_data=True) + + lines = list(row_lines) + list(col_lines) + linec = art3d.Line3DCollection(lines, axlim_clip=axlim_clip, **kwargs) + self.add_collection(linec, autolim="_datalim_only") return linec def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None, - lightsource=None, **kwargs): + lightsource=None, axlim_clip=False, **kwargs): """ Plot a triangulated surface. @@ -2308,6 +2733,10 @@ def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None, *cmap* is specified. lightsource : `~matplotlib.colors.LightSource`, optional The lightsource to use when *shade* is True. + axlim_clip : bool, default: False + Whether to hide patches with a vertex outside the axes view limits. + + .. versionadded:: 3.10 **kwargs All other keyword arguments are passed on to :class:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection` @@ -2344,7 +2773,8 @@ def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None, verts = np.stack((xt, yt, zt), axis=-1) if cmap: - polyc = art3d.Poly3DCollection(verts, *args, **kwargs) + polyc = art3d.Poly3DCollection(verts, *args, + axlim_clip=axlim_clip, **kwargs) # average over the three points of each triangle avg_z = verts[:, :, 2].mean(axis=1) polyc.set_array(avg_z) @@ -2355,9 +2785,9 @@ def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None, else: polyc = art3d.Poly3DCollection( verts, *args, shade=shade, lightsource=lightsource, - facecolors=color, **kwargs) + facecolors=color, axlim_clip=axlim_clip, **kwargs) - self.add_collection(polyc) + self.add_collection(polyc, autolim="_datalim_only") self.auto_scale_xyz(tri.x, tri.y, z, had_data) return polyc @@ -2391,18 +2821,21 @@ def _3d_extend_contour(self, cset, stride=5): cset.remove() def add_contour_set( - self, cset, extend3d=False, stride=5, zdir='z', offset=None): + self, cset, extend3d=False, stride=5, zdir='z', offset=None, + axlim_clip=False): zdir = '-' + zdir if extend3d: self._3d_extend_contour(cset, stride) else: art3d.collection_2d_to_3d( - cset, zs=offset if offset is not None else cset.levels, zdir=zdir) + cset, zs=offset if offset is not None else cset.levels, zdir=zdir, + axlim_clip=axlim_clip) - def add_contourf_set(self, cset, zdir='z', offset=None): - self._add_contourf_set(cset, zdir=zdir, offset=offset) + def add_contourf_set(self, cset, zdir='z', offset=None, *, axlim_clip=False): + self._add_contourf_set(cset, zdir=zdir, offset=offset, + axlim_clip=axlim_clip) - def _add_contourf_set(self, cset, zdir='z', offset=None): + def _add_contourf_set(self, cset, zdir='z', offset=None, axlim_clip=False): """ Returns ------- @@ -2421,12 +2854,14 @@ def _add_contourf_set(self, cset, zdir='z', offset=None): midpoints = np.append(midpoints, max_level) art3d.collection_2d_to_3d( - cset, zs=offset if offset is not None else midpoints, zdir=zdir) + cset, zs=offset if offset is not None else midpoints, zdir=zdir, + axlim_clip=axlim_clip) return midpoints @_preprocess_data() def contour(self, X, Y, Z, *args, - extend3d=False, stride=5, zdir='z', offset=None, **kwargs): + extend3d=False, stride=5, zdir='z', offset=None, axlim_clip=False, + **kwargs): """ Create a 3D contour plot. @@ -2443,6 +2878,10 @@ def contour(self, X, Y, Z, *args, offset : float, optional If specified, plot a projection of the contour lines at this position in a plane normal to *zdir*. + axlim_clip : bool, default: False + Whether to hide lines with a vertex outside the axes view limits. + + .. versionadded:: 3.10 data : indexable object, optional DATA_PARAMETER_PLACEHOLDER @@ -2457,7 +2896,7 @@ def contour(self, X, Y, Z, *args, jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) cset = super().contour(jX, jY, jZ, *args, **kwargs) - self.add_contour_set(cset, extend3d, stride, zdir, offset) + self.add_contour_set(cset, extend3d, stride, zdir, offset, axlim_clip) self.auto_scale_xyz(X, Y, Z, had_data) return cset @@ -2466,7 +2905,8 @@ def contour(self, X, Y, Z, *args, @_preprocess_data() def tricontour(self, *args, - extend3d=False, stride=5, zdir='z', offset=None, **kwargs): + extend3d=False, stride=5, zdir='z', offset=None, axlim_clip=False, + **kwargs): """ Create a 3D contour plot. @@ -2487,6 +2927,10 @@ def tricontour(self, *args, offset : float, optional If specified, plot a projection of the contour lines at this position in a plane normal to *zdir*. + axlim_clip : bool, default: False + Whether to hide lines with a vertex outside the axes view limits. + + .. versionadded:: 3.10 data : indexable object, optional DATA_PARAMETER_PLACEHOLDER *args, **kwargs @@ -2512,7 +2956,7 @@ def tricontour(self, *args, tri = Triangulation(jX, jY, tri.triangles, tri.mask) cset = super().tricontour(tri, jZ, *args, **kwargs) - self.add_contour_set(cset, extend3d, stride, zdir, offset) + self.add_contour_set(cset, extend3d, stride, zdir, offset, axlim_clip) self.auto_scale_xyz(X, Y, Z, had_data) return cset @@ -2528,7 +2972,8 @@ def _auto_scale_contourf(self, X, Y, Z, zdir, levels, had_data): self.auto_scale_xyz(*limits, had_data) @_preprocess_data() - def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs): + def contourf(self, X, Y, Z, *args, + zdir='z', offset=None, axlim_clip=False, **kwargs): """ Create a 3D filled contour plot. @@ -2541,6 +2986,10 @@ def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs): offset : float, optional If specified, plot a projection of the contour lines at this position in a plane normal to *zdir*. + axlim_clip : bool, default: False + Whether to hide lines with a vertex outside the axes view limits. + + .. versionadded:: 3.10 data : indexable object, optional DATA_PARAMETER_PLACEHOLDER *args, **kwargs @@ -2554,7 +3003,7 @@ def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs): jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) cset = super().contourf(jX, jY, jZ, *args, **kwargs) - levels = self._add_contourf_set(cset, zdir, offset) + levels = self._add_contourf_set(cset, zdir, offset, axlim_clip) self._auto_scale_contourf(X, Y, Z, zdir, levels, had_data) return cset @@ -2562,7 +3011,7 @@ def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs): contourf3D = contourf @_preprocess_data() - def tricontourf(self, *args, zdir='z', offset=None, **kwargs): + def tricontourf(self, *args, zdir='z', offset=None, axlim_clip=False, **kwargs): """ Create a 3D filled contour plot. @@ -2579,6 +3028,10 @@ def tricontourf(self, *args, zdir='z', offset=None, **kwargs): offset : float, optional If specified, plot a projection of the contour lines at this position in a plane normal to zdir. + axlim_clip : bool, default: False + Whether to hide lines with a vertex outside the axes view limits. + + .. versionadded:: 3.10 data : indexable object, optional DATA_PARAMETER_PLACEHOLDER *args, **kwargs @@ -2605,12 +3058,13 @@ def tricontourf(self, *args, zdir='z', offset=None, **kwargs): tri = Triangulation(jX, jY, tri.triangles, tri.mask) cset = super().tricontourf(tri, jZ, *args, **kwargs) - levels = self._add_contourf_set(cset, zdir, offset) + levels = self._add_contourf_set(cset, zdir, offset, axlim_clip) self._auto_scale_contourf(X, Y, Z, zdir, levels, had_data) return cset - def add_collection3d(self, col, zs=0, zdir='z'): + def add_collection3d(self, col, zs=0, zdir='z', autolim=True, *, + axlim_clip=False): """ Add a 3D collection object to the plot. @@ -2622,8 +3076,25 @@ def add_collection3d(self, col, zs=0, zdir='z'): - `.PolyCollection` - `.LineCollection` - - `.PatchCollection` + - `.PatchCollection` (currently not supporting *autolim*) + + Parameters + ---------- + col : `.Collection` + A 2D collection object. + zs : float or array-like, default: 0 + The z-positions to be used for the 2D objects. + zdir : {'x', 'y', 'z'}, default: 'z' + The direction to use for the z-positions. + autolim : bool, default: True + Whether to update the data limits. + axlim_clip : bool, default: False + Whether to hide the scatter points outside the axes view limits. + + .. versionadded:: 3.10 """ + had_data = self.has_data() + zvals = np.atleast_1d(zs) zsortval = (np.min(zvals) if zvals.size else 0) # FIXME: arbitrary default @@ -2632,23 +3103,45 @@ def add_collection3d(self, col, zs=0, zdir='z'): # object would also pass.) Maybe have a collection3d # abstract class to test for and exclude? if type(col) is mcoll.PolyCollection: - art3d.poly_collection_2d_to_3d(col, zs=zs, zdir=zdir) + art3d.poly_collection_2d_to_3d(col, zs=zs, zdir=zdir, + axlim_clip=axlim_clip) col.set_sort_zpos(zsortval) elif type(col) is mcoll.LineCollection: - art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir) + art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir, + axlim_clip=axlim_clip) col.set_sort_zpos(zsortval) elif type(col) is mcoll.PatchCollection: - art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir) + art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir, + axlim_clip=axlim_clip) col.set_sort_zpos(zsortval) - collection = super().add_collection(col) + if autolim: + if isinstance(col, art3d.Line3DCollection): + # Handle ragged arrays by extracting coordinates separately + all_points = np.concatenate(col._segments3d) + self.auto_scale_xyz(all_points[:, 0], all_points[:, 1], + all_points[:, 2], had_data=had_data) + elif isinstance(col, art3d.Poly3DCollection): + self.auto_scale_xyz(col._faces[..., 0], + col._faces[..., 1], + col._faces[..., 2], had_data=had_data) + elif isinstance(col, art3d.Patch3DCollection): + pass + # FIXME: Implement auto-scaling function for Patch3DCollection + # Currently unable to do so due to issues with Patch3DCollection + # See https://github.com/matplotlib/matplotlib/issues/14298 for details + + collection = super().add_collection(col, autolim="_datalim_only") return collection @_preprocess_data(replace_names=["xs", "ys", "zs", "s", "edgecolors", "c", "facecolor", "facecolors", "color"]) - def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, - *args, **kwargs): + def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=None, + *args, + depthshade_minalpha=None, + axlim_clip=False, + **kwargs): """ Create a scatter plot. @@ -2680,12 +3173,24 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, - A 2D array in which the rows are RGB or RGBA. For more details see the *c* argument of `~.axes.Axes.scatter`. - depthshade : bool, default: True + depthshade : bool, default: :rc:`axes3d.depthshade` Whether to shade the scatter markers to give the appearance of depth. Each call to ``scatter()`` will perform its depthshading independently. + + depthshade_minalpha : float, default: :rc:`axes3d.depthshade_minalpha` + The lowest alpha value applied by depth-shading. + + .. versionadded:: 3.11 + + axlim_clip : bool, default: False + Whether to hide the scatter points outside the axes view limits. + + .. versionadded:: 3.10 + data : indexable object, optional DATA_PARAMETER_PLACEHOLDER + **kwargs All other keyword arguments are passed on to `~.axes.Axes.scatter`. @@ -2705,15 +3210,24 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, ) if kwargs.get("color") is not None: kwargs['color'] = color + if depthshade is None: + depthshade = mpl.rcParams['axes3d.depthshade'] + if depthshade_minalpha is None: + depthshade_minalpha = mpl.rcParams['axes3d.depthshade_minalpha'] # For xs and ys, 2D scatter() will do the copying. if np.may_share_memory(zs_orig, zs): # Avoid unnecessary copies. zs = zs.copy() patches = super().scatter(xs, ys, s=s, c=c, *args, **kwargs) - art3d.patch_collection_2d_to_3d(patches, zs=zs, zdir=zdir, - depthshade=depthshade) - + art3d.patch_collection_2d_to_3d( + patches, + zs=zs, + zdir=zdir, + depthshade=depthshade, + depthshade_minalpha=depthshade_minalpha, + axlim_clip=axlim_clip, + ) if self._zmargin < 0.05 and xs.size > 0: self.set_zmargin(0.05) @@ -2724,7 +3238,8 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, scatter3D = scatter @_preprocess_data() - def bar(self, left, height, zs=0, zdir='z', *args, **kwargs): + def bar(self, left, height, zs=0, zdir='z', *args, + axlim_clip=False, **kwargs): """ Add 2D bar(s). @@ -2739,6 +3254,10 @@ def bar(self, left, height, zs=0, zdir='z', *args, **kwargs): used for all bars. zdir : {'x', 'y', 'z'}, default: 'z' When plotting 2D data, the direction to use as z ('x', 'y' or 'z'). + axlim_clip : bool, default: False + Whether to hide bars with points outside the axes view limits. + + .. versionadded:: 3.10 data : indexable object, optional DATA_PARAMETER_PLACEHOLDER **kwargs @@ -2761,7 +3280,7 @@ def bar(self, left, height, zs=0, zdir='z', *args, **kwargs): vs = art3d._get_patch_verts(p) verts += vs.tolist() verts_zs += [z] * len(vs) - art3d.patch_2d_to_3d(p, z, zdir) + art3d.patch_2d_to_3d(p, z, zdir, axlim_clip) if 'alpha' in kwargs: p.set_alpha(kwargs['alpha']) @@ -2780,7 +3299,8 @@ def bar(self, left, height, zs=0, zdir='z', *args, **kwargs): @_preprocess_data() def bar3d(self, x, y, z, dx, dy, dz, color=None, - zsort='average', shade=True, lightsource=None, *args, **kwargs): + zsort='average', shade=True, lightsource=None, *args, + axlim_clip=False, **kwargs): """ Generate a 3D barplot. @@ -2827,6 +3347,11 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None, lightsource : `~matplotlib.colors.LightSource`, optional The lightsource to use when *shade* is True. + axlim_clip : bool, default: False + Whether to hide the bars with points outside the axes view limits. + + .. versionadded:: 3.10 + data : indexable object, optional DATA_PARAMETER_PLACEHOLDER @@ -2932,8 +3457,9 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None, facecolors=facecolors, shade=shade, lightsource=lightsource, + axlim_clip=axlim_clip, *args, **kwargs) - self.add_collection(col) + self.add_collection(col, autolim="_datalim_only") self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data) @@ -2949,7 +3475,7 @@ def set_title(self, label, fontdict=None, loc='center', **kwargs): @_preprocess_data() def quiver(self, X, Y, Z, U, V, W, *, length=1, arrow_length_ratio=.3, pivot='tail', normalize=False, - **kwargs): + axlim_clip=False, **kwargs): """ Plot a 3D field of arrows. @@ -2981,6 +3507,11 @@ def quiver(self, X, Y, Z, U, V, W, *, Whether all arrows are normalized to have the same length, or keep the lengths defined by *u*, *v*, and *w*. + axlim_clip : bool, default: False + Whether to hide arrows with points outside the axes view limits. + + .. versionadded:: 3.10 + data : indexable object, optional DATA_PARAMETER_PLACEHOLDER @@ -3025,7 +3556,7 @@ def calc_arrows(UVW): if any(len(v) == 0 for v in input_args): # No quivers, so just make an empty collection and return early linec = art3d.Line3DCollection([], **kwargs) - self.add_collection(linec) + self.add_collection(linec, autolim="_datalim_only") return linec shaft_dt = np.array([0., length], dtype=float) @@ -3062,8 +3593,8 @@ def calc_arrows(UVW): else: lines = [] - linec = art3d.Line3DCollection(lines, **kwargs) - self.add_collection(linec) + linec = art3d.Line3DCollection(lines, axlim_clip=axlim_clip, **kwargs) + self.add_collection(linec, autolim="_datalim_only") self.auto_scale_xyz(XYZ[:, 0], XYZ[:, 1], XYZ[:, 2], had_data) @@ -3072,7 +3603,7 @@ def calc_arrows(UVW): quiver3D = quiver def voxels(self, *args, facecolors=None, edgecolors=None, shade=True, - lightsource=None, **kwargs): + lightsource=None, axlim_clip=False, **kwargs): """ ax.voxels([x, y, z,] /, filled, facecolors=None, edgecolors=None, \ **kwargs) @@ -3119,6 +3650,11 @@ def voxels(self, *args, facecolors=None, edgecolors=None, shade=True, lightsource : `~matplotlib.colors.LightSource`, optional The lightsource to use when *shade* is True. + axlim_clip : bool, default: False + Whether to hide voxels with points outside the axes view limits. + + .. versionadded:: 3.10 + **kwargs Additional keyword arguments to pass onto `~mpl_toolkits.mplot3d.art3d.Poly3DCollection`. @@ -3232,7 +3768,7 @@ def permutation_matrices(n): voxel_faces[i0].append(p0 + square_rot_neg) # draw middle faces - for r1, r2 in zip(rinds[:-1], rinds[1:]): + for r1, r2 in itertools.pairwise(rinds): p1 = permute.dot([p, q, r1]) p2 = permute.dot([p, q, r2]) @@ -3274,7 +3810,8 @@ def permutation_matrices(n): poly = art3d.Poly3DCollection( faces, facecolors=facecolor, edgecolors=edgecolor, - shade=shade, lightsource=lightsource, **kwargs) + shade=shade, lightsource=lightsource, axlim_clip=axlim_clip, + **kwargs) self.add_collection3d(poly) polygons[coord] = poly @@ -3285,6 +3822,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='', barsabove=False, errorevery=1, ecolor=None, elinewidth=None, capsize=None, capthick=None, xlolims=False, xuplims=False, ylolims=False, yuplims=False, zlolims=False, zuplims=False, + axlim_clip=False, **kwargs): """ Plot lines and/or markers with errorbars around them. @@ -3317,12 +3855,12 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='', Use 'none' (case-insensitive) to plot errorbars without any data markers. - ecolor : :mpltype:`color`, default: None - The color of the errorbar lines. If None, use the color of the + ecolor : :mpltype:`color`, optional + The color of the errorbar lines. If not given, use the color of the line connecting the markers. - elinewidth : float, default: None - The linewidth of the errorbar lines. If None, the linewidth of + elinewidth : float, optional + The linewidth of the errorbar lines. If not given, the linewidth of the current style is used. capsize : float, default: :rc:`errorbar.capsize` @@ -3362,6 +3900,11 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='', Used to avoid overlapping error bars when two series share x-axis values. + axlim_clip : bool, default: False + Whether to hide error bars that are outside the axes limits. + + .. versionadded:: 3.10 + Returns ------- errlines : list @@ -3417,7 +3960,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='', # data processing. (data_line, base_style), = self._get_lines._plot_args( self, (x, y) if fmt == '' else (x, y, fmt), kwargs, return_kwargs=True) - art3d.line_2d_to_3d(data_line, zs=z) + art3d.line_2d_to_3d(data_line, zs=z, axlim_clip=axlim_clip) # Do this after creating `data_line` to avoid modifying `base_style`. if barsabove: @@ -3503,7 +4046,7 @@ def _extract_errs(err, data, lomask, himask): # them directly in planar form. quiversize = eb_cap_style.get('markersize', mpl.rcParams['lines.markersize']) ** 2 - quiversize *= self.figure.dpi / 72 + quiversize *= self.get_figure(root=True).dpi / 72 quiversize = self.transAxes.inverted().transform([ (0, 0), (quiversize, quiversize)]) quiversize = np.mean(np.diff(quiversize, axis=0)) @@ -3561,9 +4104,11 @@ def _extract_errs(err, data, lomask, himask): # these markers will rotate as the viewing angle changes cap_lo = art3d.Line3D(*lo_caps_xyz, ls='', marker=capmarker[i_zdir], + axlim_clip=axlim_clip, **eb_cap_style) cap_hi = art3d.Line3D(*hi_caps_xyz, ls='', marker=capmarker[i_zdir], + axlim_clip=axlim_clip, **eb_cap_style) self.add_line(cap_lo) self.add_line(cap_hi) @@ -3578,8 +4123,9 @@ def _extract_errs(err, data, lomask, himask): self.quiver(xl0, yl0, zl0, *-dir_vector, **eb_quiver_style) errline = art3d.Line3DCollection(np.array(coorderr).T, + axlim_clip=axlim_clip, **eb_lines_style) - self.add_collection(errline) + self.add_collection(errline, autolim="_datalim_only") errlines.append(errline) coorderrs.append(coorderr) @@ -3604,9 +4150,8 @@ def _digout_minmax(err_arr, coord_label): return errlines, caplines, limmarks - @_api.make_keyword_only("3.8", "call_axes_locator") - def get_tightbbox(self, renderer=None, call_axes_locator=True, - bbox_extra_artists=None, *, for_layout_only=False): + def get_tightbbox(self, renderer=None, *, call_axes_locator=True, + bbox_extra_artists=None, for_layout_only=False): ret = super().get_tightbbox(renderer, call_axes_locator=call_axes_locator, bbox_extra_artists=bbox_extra_artists, @@ -3623,7 +4168,7 @@ def get_tightbbox(self, renderer=None, call_axes_locator=True, @_preprocess_data() def stem(self, x, y, z, *, linefmt='C0-', markerfmt='C0o', basefmt='C3-', - bottom=0, label=None, orientation='z'): + bottom=0, label=None, orientation='z', axlim_clip=False): """ Create a 3D stem plot. @@ -3673,6 +4218,11 @@ def stem(self, x, y, z, *, linefmt='C0-', markerfmt='C0o', basefmt='C3-', orientation : {'x', 'y', 'z'}, default: 'z' The direction along which stems are drawn. + axlim_clip : bool, default: False + Whether to hide stems that are outside the axes limits. + + .. versionadded:: 3.10 + data : indexable object, optional DATA_PARAMETER_PLACEHOLDER @@ -3717,15 +4267,15 @@ def stem(self, x, y, z, *, linefmt='C0-', markerfmt='C0o', basefmt='C3-', # Determine style for stem lines. linestyle, linemarker, linecolor = _process_plot_format(linefmt) - if linestyle is None: - linestyle = mpl.rcParams['lines.linestyle'] + linestyle = mpl._val_or_rc(linestyle, 'lines.linestyle') # Plot everything in required order. baseline, = self.plot(basex, basey, basefmt, zs=bottom, zdir=orientation, label='_nolegend_') stemlines = art3d.Line3DCollection( - lines, linestyles=linestyle, colors=linecolor, label='_nolegend_') - self.add_collection(stemlines) + lines, linestyles=linestyle, colors=linecolor, label='_nolegend_', + axlim_clip=axlim_clip) + self.add_collection(stemlines, autolim="_datalim_only") markerline, = self.plot(x, y, z, markerfmt, label='_nolegend_') stem_container = StemContainer((markerline, stemlines, baseline), @@ -3831,7 +4381,7 @@ def rotate_from_to(cls, r1, r2): k = np.cross(r1, r2) nk = np.linalg.norm(k) th = np.arctan2(nk, np.dot(r1, r2)) - th = th/2 + th /= 2 if nk == 0: # r1 and r2 are parallel or anti-parallel if np.dot(r1, r2) < 0: warnings.warn("Rotation defined by anti-parallel vectors is ambiguous") @@ -3843,7 +4393,7 @@ def rotate_from_to(cls, r1, r2): else: q = cls(1, [0, 0, 0]) # = 1, no rotation else: - q = cls(math.cos(th), k*math.sin(th)/nk) + q = cls(np.cos(th), k*np.sin(th)/nk) return q @classmethod @@ -3868,10 +4418,11 @@ def as_cardan_angles(self): """ The inverse of `from_cardan_angles()`. Note that the angles returned are in radians, not degrees. + The angles are not sensitive to the quaternion's norm(). """ qw = self.scalar qx, qy, qz = self.vector[..., :] azim = np.arctan2(2*(-qw*qz+qx*qy), qw*qw+qx*qx-qy*qy-qz*qz) - elev = np.arcsin( 2*( qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz)) # noqa E201 - roll = np.arctan2(2*( qw*qx-qy*qz), qw*qw-qx*qx-qy*qy+qz*qz) # noqa E201 + elev = np.arcsin(np.clip(2*(qw*qy+qz*qx)/(qw*qw+qx*qx+qy*qy+qz*qz), -1, 1)) + roll = np.arctan2(2*(qw*qx-qy*qz), qw*qw-qx*qx-qy*qy+qz*qz) return elev, azim, roll diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index 79b78657bdb9..0ac2e50b1a1a 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -195,11 +195,6 @@ def set_ticks_position(self, position): position : {'lower', 'upper', 'both', 'default', 'none'} The position of the bolded axis lines, ticks, and tick labels. """ - if position in ['top', 'bottom']: - _api.warn_deprecated('3.8', name=f'{position=}', - obj_type='argument value', - alternative="'upper' or 'lower'") - return _api.check_in_list(['lower', 'upper', 'both', 'default', 'none'], position=position) self._tick_position = position @@ -224,11 +219,6 @@ def set_label_position(self, position): position : {'lower', 'upper', 'both', 'default', 'none'} The position of the axis label. """ - if position in ['top', 'bottom']: - _api.warn_deprecated('3.8', name=f'{position=}', - obj_type='argument value', - alternative="'upper' or 'lower'") - return _api.check_in_list(['lower', 'upper', 'both', 'default', 'none'], position=position) self._label_position = position @@ -277,14 +267,15 @@ def get_rotate_label(self, text): return len(text) > 4 def _get_coord_info(self): - mins, maxs = np.array([ - self.axes.get_xbound(), - self.axes.get_ybound(), - self.axes.get_zbound(), - ]).T - - # Project the bounds along the current position of the cube: - bounds = mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2] + # Get scaled limits directly from the axes helper + xmin, xmax, ymin, ymax, zmin, zmax = self.axes._get_scaled_limits() + mins = np.array([xmin, ymin, zmin]) + maxs = np.array([xmax, ymax, zmax]) + + # Get data-space bounds for _transformed_cube + bounds = (*self.axes.get_xbound(), + *self.axes.get_ybound(), + *self.axes.get_zbound()) bounds_proj = self.axes._transformed_cube(bounds) # Determine which one of the parallel planes are higher up: @@ -453,6 +444,10 @@ def _draw_ticks(self, renderer, edgep1, centers, deltas, highs, mins, maxs, tc, highs = self._get_coord_info() centers, deltas = self._calc_centers_deltas(maxs, mins) + # Get the scale transform for this axis to transform tick locations + axis = [self.axes.xaxis, self.axes.yaxis, self.axes.zaxis][index] + axis_trans = axis.get_transform() + # Draw ticks: tickdir = self._get_tickdir(pos) tickdelta = deltas[tickdir] if highs[tickdir] else -deltas[tickdir] @@ -467,10 +462,11 @@ def _draw_ticks(self, renderer, edgep1, centers, deltas, highs, default_label_offset = 8. # A rough estimate points = deltas_per_point * deltas + # All coordinates below are in transformed coordinates for proper projection for tick in ticks: # Get tick line positions pos = edgep1.copy() - pos[index] = tick.get_loc() + pos[index] = axis_trans.transform([tick.get_loc()])[0] pos[tickdir] = out_tickdir x1, y1, z1 = proj3d.proj_transform(*pos, self.axes.M) pos[tickdir] = in_tickdir @@ -478,7 +474,6 @@ def _draw_ticks(self, renderer, edgep1, centers, deltas, highs, # Get position of label labeldeltas = (tick.get_pad() + default_label_offset) * points - pos[tickdir] = edgep1_tickdir pos = _move_from_center(pos, centers, labeldeltas, self._axmask()) lx, ly, lz = proj3d.proj_transform(*pos, self.axes.M) @@ -586,7 +581,7 @@ def draw(self, renderer): # Calculate offset distances # A rough estimate; points are ambiguous since 3D plots rotate - reltoinches = self.figure.dpi_scale_trans.inverted() + reltoinches = self.get_figure(root=False).dpi_scale_trans.inverted() ax_inches = reltoinches.transform(self.axes.bbox.size) ax_points_estimate = sum(72. * ax_inches) deltas_per_point = 48 / ax_points_estimate @@ -652,10 +647,15 @@ def draw_grid(self, renderer): info = self._axinfo index = info["i"] + # Grid lines use data-space bounds (Line3DCollection applies transforms) mins, maxs, tc, highs = self._get_coord_info() - - minmax = np.where(highs, maxs, mins) - maxmin = np.where(~highs, maxs, mins) + xlim, ylim, zlim = (self.axes.get_xbound(), + self.axes.get_ybound(), + self.axes.get_zbound()) + data_mins = np.array([xlim[0], ylim[0], zlim[0]]) + data_maxs = np.array([xlim[1], ylim[1], zlim[1]]) + minmax = np.where(highs, data_maxs, data_mins) + maxmin = np.where(~highs, data_maxs, data_mins) # Grid points where the planes meet xyz0 = np.tile(minmax, (len(ticks), 1)) @@ -718,6 +718,8 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False): bb_1, bb_2 = self._get_ticklabel_bboxes(ticks, renderer) other = [] + if self.offsetText.get_visible() and self.offsetText.get_text(): + other.append(self.offsetText.get_window_extent(renderer)) if self.line.get_visible(): other.append(self.line.get_window_extent(renderer)) if (self.label.get_visible() and not for_layout_only and diff --git a/lib/mpl_toolkits/mplot3d/proj3d.py b/lib/mpl_toolkits/mplot3d/proj3d.py index 098a7b6f6667..81a5aacbdded 100644 --- a/lib/mpl_toolkits/mplot3d/proj3d.py +++ b/lib/mpl_toolkits/mplot3d/proj3d.py @@ -23,18 +23,10 @@ def world_transformation(xmin, xmax, dy /= ay dz /= az - return np.array([[1/dx, 0, 0, -xmin/dx], - [0, 1/dy, 0, -ymin/dy], - [0, 0, 1/dz, -zmin/dz], - [0, 0, 0, 1]]) - - -@_api.deprecated("3.8") -def rotation_about_vector(v, angle): - """ - Produce a rotation matrix for an angle in radians about a vector. - """ - return _rotation_about_vector(v, angle) + return np.array([[1/dx, 0, 0, -xmin/dx], + [ 0, 1/dy, 0, -ymin/dy], + [ 0, 0, 1/dz, -zmin/dz], + [ 0, 0, 0, 1]]) def _rotation_about_vector(v, angle): @@ -116,32 +108,6 @@ def _view_transformation_uvw(u, v, w, E): return M -@_api.deprecated("3.8") -def view_transformation(E, R, V, roll): - """ - Return the view transformation matrix. - - Parameters - ---------- - E : 3-element numpy array - The coordinates of the eye/camera. - R : 3-element numpy array - The coordinates of the center of the view box. - V : 3-element numpy array - Unit vector in the direction of the vertical axis. - roll : float - The roll angle in radians. - """ - u, v, w = _view_axes(E, R, V, roll) - M = _view_transformation_uvw(u, v, w, E) - return M - - -@_api.deprecated("3.8") -def persp_transformation(zfront, zback, focal_length): - return _persp_transformation(zfront, zback, focal_length) - - def _persp_transformation(zfront, zback, focal_length): e = focal_length a = 1 # aspect ratio @@ -154,11 +120,6 @@ def _persp_transformation(zfront, zback, focal_length): return proj_matrix -@_api.deprecated("3.8") -def ortho_transformation(zfront, zback): - return _ortho_transformation(zfront, zback) - - def _ortho_transformation(zfront, zback): # note: w component in the resulting vector will be (zback-zfront), not 1 a = -(zfront + zback) @@ -170,22 +131,78 @@ def _ortho_transformation(zfront, zback): return proj_matrix +def _apply_scale_transforms(xs, ys, zs, axes): + """ + Apply axis scale transforms to 3D coordinates. + + Transforms data coordinates to transformed coordinates (applying log, + symlog, etc.) for 3D projection. Preserves masked arrays. + """ + def transform_coord(coord, axis): + coord = np.asanyarray(coord) + data = np.ma.getdata(coord).ravel() + return axis.get_transform().transform(data).reshape(coord.shape) + + xs_scaled = transform_coord(xs, axes.xaxis) + ys_scaled = transform_coord(ys, axes.yaxis) + zs_scaled = transform_coord(zs, axes.zaxis) + + # Preserve combined mask from any masked input + masks = [np.ma.getmask(a) for a in [xs, ys, zs]] + if any(m is not np.ma.nomask for m in masks): + combined = np.ma.mask_or(np.ma.mask_or(masks[0], masks[1]), masks[2]) + xs_scaled = np.ma.array(xs_scaled, mask=combined) + ys_scaled = np.ma.array(ys_scaled, mask=combined) + zs_scaled = np.ma.array(zs_scaled, mask=combined) + + return xs_scaled, ys_scaled, zs_scaled + + def _proj_transform_vec(vec, M): - vecw = np.dot(M, vec) - w = vecw[3] - # clip here.. - txs, tys, tzs = vecw[0]/w, vecw[1]/w, vecw[2]/w - return txs, tys, tzs - - -def _proj_transform_vec_clip(vec, M): - vecw = np.dot(M, vec) - w = vecw[3] - # clip here. - txs, tys, tzs = vecw[0] / w, vecw[1] / w, vecw[2] / w - tis = (0 <= vecw[0]) & (vecw[0] <= 1) & (0 <= vecw[1]) & (vecw[1] <= 1) - if np.any(tis): - tis = vecw[1] < 1 + vecw = np.dot(M, vec.data) + ts = vecw[0:3]/vecw[3] + if np.ma.isMA(vec): + ts = np.ma.array(ts, mask=vec.mask) + return ts[0], ts[1], ts[2] + + +def _scale_proj_transform_vectors(vecs, axes): + """ + Apply scale transforms and project vectors. + + Parameters + ---------- + vecs : ... x 3 np.ndarray + Input vectors. + axes : Axes3D + The 3D axes (used for scale transforms and projection matrix). + """ + result_shape = vecs.shape + xs, ys, zs = _apply_scale_transforms( + vecs[..., 0], vecs[..., 1], vecs[..., 2], axes) + vec = _vec_pad_ones(xs.ravel(), ys.ravel(), zs.ravel()) + product = np.dot(axes.M, vec) + tvecs = product[:3] / product[3] + return tvecs.T.reshape(result_shape) + + +def _proj_transform_vec_clip(vec, M, focal_length): + vecw = np.dot(M, vec.data) + txs, tys, tzs = vecw[0:3] / vecw[3] + if np.isinf(focal_length): # don't clip orthographic projection + tis = np.ones(txs.shape, dtype=bool) + else: + tis = (-1 <= txs) & (txs <= 1) & (-1 <= tys) & (tys <= 1) & (tzs <= 0) + if np.ma.isMA(vec[0]): + tis = tis & ~vec[0].mask + if np.ma.isMA(vec[1]): + tis = tis & ~vec[1].mask + if np.ma.isMA(vec[2]): + tis = tis & ~vec[2].mask + + txs = np.ma.masked_array(txs, ~tis) + tys = np.ma.masked_array(tys, ~tis) + tzs = np.ma.masked_array(tzs, ~tis) return txs, tys, tzs, tis @@ -204,7 +221,10 @@ def inv_transform(xs, ys, zs, invM): def _vec_pad_ones(xs, ys, zs): - return np.array([xs, ys, zs, np.ones_like(xs)]) + if np.ma.isMA(xs) or np.ma.isMA(ys) or np.ma.isMA(zs): + return np.ma.array([xs, ys, zs, np.ones_like(xs)]) + else: + return np.array([xs, ys, zs, np.ones_like(xs)]) def proj_transform(xs, ys, zs, M): @@ -215,45 +235,35 @@ def proj_transform(xs, ys, zs, M): return _proj_transform_vec(vec, M) -transform = _api.deprecated( - "3.8", obj_type="function", name="transform", - alternative="proj_transform")(proj_transform) - - +@_api.deprecated("3.10") def proj_transform_clip(xs, ys, zs, M): - """ - Transform the points by the projection matrix - and return the clipping result - returns txs, tys, tzs, tis - """ vec = _vec_pad_ones(xs, ys, zs) - return _proj_transform_vec_clip(vec, M) - - -@_api.deprecated("3.8") -def proj_points(points, M): - return _proj_points(points, M) - + return _proj_transform_vec_clip(vec, M, focal_length=np.inf) -def _proj_points(points, M): - return np.column_stack(_proj_trans_points(points, M)) +def _scale_proj_transform_clip(xs, ys, zs, axes): + """ + Apply scale transforms, project, and return clipping result. -@_api.deprecated("3.8") -def proj_trans_points(points, M): - return _proj_trans_points(points, M) + Returns txs, tys, tzs, tis. + """ + xs, ys, zs = _apply_scale_transforms(xs, ys, zs, axes) + vec = _vec_pad_ones(xs, ys, zs) + return _proj_transform_vec_clip(vec, axes.M, axes._focal_length) def _proj_trans_points(points, M): - xs, ys, zs = zip(*points) + points = np.asanyarray(points) + xs, ys, zs = points[:, 0], points[:, 1], points[:, 2] return proj_transform(xs, ys, zs, M) -@_api.deprecated("3.8") -def rot_x(V, alpha): - cosa, sina = np.cos(alpha), np.sin(alpha) - M1 = np.array([[1, 0, 0, 0], - [0, cosa, -sina, 0], - [0, sina, cosa, 0], - [0, 0, 0, 1]]) - return np.dot(M1, V) +def _scale_proj_transform(xs, ys, zs, axes): + """ + Apply scale transforms and project. + + Combines `_apply_scale_transforms` and `proj_transform` into a single + call. Returns txs, tys, tzs. + """ + xs, ys, zs = _apply_scale_transforms(xs, ys, zs, axes) + return proj_transform(xs, ys, zs, axes.M) diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects.png index a969d3d82b4c..764e3c0b8837 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects_adjust_box.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects_adjust_box.png index 4c24873de4a3..3d43e4e9c5d5 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects_adjust_box.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/aspects_adjust_box.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_cla.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_cla.png index 7709e7ac06cb..d062f946d9ca 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_cla.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_cla.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_focal_length.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_focal_length.png index c5595a812663..76770d4fedf4 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_focal_length.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_focal_length.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_labelpad.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_labelpad.png index 0d7eed251e1c..4a149c1ed214 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_labelpad.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_labelpad.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_ortho.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_ortho.png index 654951ee73aa..f67d415c3eb8 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_ortho.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_ortho.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_primary_views.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_primary_views.png index 42e67ce4db9b..4af8ea9040a0 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_primary_views.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_primary_views.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_rotated.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_rotated.png index b7129c184f8a..fe1acf336028 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_rotated.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axes3d_rotated.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axis_positions.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axis_positions.png index d4155479d213..aebf2d618853 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axis_positions.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/axis_positions.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/errorbar3d.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/errorbar3d.png index 02644360e1a4..d53ede165cd2 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/errorbar3d.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/errorbar3d.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_polygon.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_polygon.png new file mode 100644 index 000000000000..f1f160fe5579 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_polygon.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_quad.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_quad.png new file mode 100644 index 000000000000..e405bcffb965 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/fill_between_quad.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/minor_ticks.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/minor_ticks.png index e079c96d78ed..c8b2fbb585ae 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/minor_ticks.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/minor_ticks.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube.png index a7cc7e23bcc7..383e6b232e59 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube_ortho.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube_ortho.png index 205ac97158aa..e072ca24c6ed 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube_ortho.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/proj3d_axes_cube_ortho.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d.png index 5d58cea8bccf..af8cc16b14cc 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_all_scales.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_all_scales.png new file mode 100644 index 000000000000..f17a08f8abb8 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_all_scales.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_artists_log.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_artists_log.png new file mode 100644 index 000000000000..b2538ffb85c0 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_artists_log.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_log_bases.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_log_bases.png new file mode 100644 index 000000000000..5228547aa555 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_log_bases.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_symlog_params.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_symlog_params.png new file mode 100644 index 000000000000..61f7a6cbc993 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scale3d_symlog_params.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d.png index ed8b3831726e..aa15bb95168c 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_color.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_color.png index 2d35d95e68bd..f295ec7132ba 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_color.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_color.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_linewidth.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_linewidth.png index 15cc2d77a2ac..676ee10370f6 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_linewidth.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter3d_linewidth.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter_spiral.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter_spiral.png index 8e8df221d640..ee562e27242b 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter_spiral.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/scatter_spiral.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/stem3d.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/stem3d.png index 59facceb5d41..468c684081dd 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/stem3d.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/stem3d.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_label_offset_tick_position.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_label_offset_tick_position.png index a8b0d4cd665a..6f72d0483daa 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_label_offset_tick_position.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_label_offset_tick_position.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_masked.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_masked.png index df893f9c843f..9e5af36ffbfc 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_masked.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/surface3d_masked.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/text3d.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/text3d.png index 15096f05d189..7e9bb2f8c29f 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/text3d.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/text3d.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png index b71ad19c1608..33dfc2f2313a 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-named-colors.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-xyz.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-xyz.png index 9c20f04fe709..628eddacd78f 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-xyz.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/voxels-xyz.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dasymmetric.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dasymmetric.png new file mode 100644 index 000000000000..73507bf2f6c1 Binary files /dev/null and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dasymmetric.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dzerocstride.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dzerocstride.png index 0623cad002e8..7e4cf6a0c014 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dzerocstride.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/wireframe3dzerocstride.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/fancy.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/fancy.png index b9b0fb6ef094..9b20cdc5dbe9 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/fancy.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/fancy.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_bar.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_bar.png index 72b9da0faffe..8203aebae0ec 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_bar.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_bar.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_plot.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_plot.png index 0169979e5846..724ebe25c736 100644 Binary files a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_plot.png and b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_legend3d/legend_plot.png differ diff --git a/lib/mpl_toolkits/mplot3d/tests/conftest.py b/lib/mpl_toolkits/mplot3d/tests/conftest.py index 61c2de3e07ba..12eaac9ce2f4 100644 --- a/lib/mpl_toolkits/mplot3d/tests/conftest.py +++ b/lib/mpl_toolkits/mplot3d/tests/conftest.py @@ -1,2 +1,3 @@ -from matplotlib.testing.conftest import (mpl_test_settings, # noqa - pytest_configure, pytest_unconfigure) +from matplotlib.testing.conftest import ( # noqa + pytest_configure, pytest_unconfigure, + high_memory, mpl_test_settings) diff --git a/lib/mpl_toolkits/mplot3d/tests/test_art3d.py b/lib/mpl_toolkits/mplot3d/tests/test_art3d.py index 4ed48aae4685..aca943f9e0c0 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_art3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_art3d.py @@ -1,9 +1,30 @@ import numpy as np +import numpy.testing as nptest +import pytest import matplotlib.pyplot as plt from matplotlib.backend_bases import MouseEvent -from mpl_toolkits.mplot3d.art3d import Line3DCollection +from mpl_toolkits.mplot3d.art3d import ( + get_dir_vector, + Line3DCollection, + Poly3DCollection, + _all_points_on_plane, +) + + +@pytest.mark.parametrize("zdir, expected", [ + ("x", (1, 0, 0)), + ("y", (0, 1, 0)), + ("z", (0, 0, 1)), + (None, (0, 0, 0)), + ((1, 2, 3), (1, 2, 3)), + (np.array([4, 5, 6]), (4, 5, 6)), +]) +def test_get_dir_vector(zdir, expected): + res = get_dir_vector(zdir) + assert isinstance(res, np.ndarray) + nptest.assert_array_equal(res, expected) def test_scatter_3d_projection_conservation(): @@ -51,6 +72,48 @@ def test_zordered_error(): fig = plt.figure() ax = fig.add_subplot(projection="3d") - ax.add_collection(Line3DCollection(lc)) + ax.add_collection(Line3DCollection(lc), autolim="_datalim_only") ax.scatter(*pc, visible=False) plt.draw() + + +def test_all_points_on_plane(): + # Non-coplanar points + points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]) + assert not _all_points_on_plane(*points.T) + + # Duplicate points + points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0]]) + assert _all_points_on_plane(*points.T) + + # NaN values + points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, np.nan]]) + assert _all_points_on_plane(*points.T) + + # Less than 3 unique points + points = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) + assert _all_points_on_plane(*points.T) + + # All points lie on a line + points = np.array([[0, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0]]) + assert _all_points_on_plane(*points.T) + + # All points lie on two lines, with antiparallel vectors + points = np.array([[-2, 2, 0], [-1, 1, 0], [1, -1, 0], + [0, 0, 0], [2, 0, 0], [1, 0, 0]]) + assert _all_points_on_plane(*points.T) + + # All points lie on a plane + points = np.array([[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [1, 2, 0]]) + assert _all_points_on_plane(*points.T) + + +def test_generate_normals(): + # Smoke test for https://github.com/matplotlib/matplotlib/issues/29156 + vertices = ((0, 0, 0), (0, 5, 0), (5, 5, 0), (5, 0, 0)) + shape = Poly3DCollection([vertices], edgecolors='r', shade=True) + + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.add_collection3d(shape) + plt.draw() diff --git a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py index fdd90ccf4c90..ac0168ce775e 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py @@ -1,7 +1,9 @@ import functools import itertools import platform +import sys +from packaging.version import parse as parse_version import pytest from mpl_toolkits.mplot3d import Axes3D, axes3d, proj3d, art3d @@ -12,11 +14,11 @@ from matplotlib import cm from matplotlib import colors as mcolors, patches as mpatch from matplotlib.testing.decorators import image_comparison, check_figures_equal -from matplotlib.testing.widgets import mock_event from matplotlib.collections import LineCollection, PolyCollection from matplotlib.patches import Circle, PathPatch from matplotlib.path import Path from matplotlib.text import Text +from matplotlib import _api import matplotlib.pyplot as plt import numpy as np @@ -35,7 +37,7 @@ def plot_cuboid(ax, scale): ax.plot3D(*zip(start*np.array(scale), end*np.array(scale))) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_invisible_axes(fig_test, fig_ref): ax = fig_test.subplots(subplot_kw=dict(projection='3d')) ax.set_visible(False) @@ -115,7 +117,7 @@ def test_axes3d_repr(): @mpl3d_image_comparison(['axes3d_primary_views.png'], style='mpl20', - tol=0.05 if platform.machine() == "arm64" else 0) + tol=0.045 if sys.platform == 'darwin' else 0) def test_axes3d_primary_views(): # (elev, azim, roll) views = [(90, -90, 0), # XY @@ -180,7 +182,8 @@ def test_bar3d_shaded(): fig.canvas.draw() -@mpl3d_image_comparison(['bar3d_notshaded.png'], style='mpl20') +@mpl3d_image_comparison(['bar3d_notshaded.png'], style='mpl20', + tol=0.01 if parse_version(np.version.version).major < 2 else 0) def test_bar3d_notshaded(): fig = plt.figure() ax = fig.add_subplot(projection='3d') @@ -220,17 +223,16 @@ def test_bar3d_lightsource(): np.testing.assert_array_max_ulp(color, collection._facecolor3d[1::6], 4) -@mpl3d_image_comparison( - ['contour3d.png'], style='mpl20', - tol=0.002 if platform.machine() in ('aarch64', 'ppc64le', 's390x') else 0) +@mpl3d_image_comparison(['contour3d.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.002) def test_contour3d(): plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig = plt.figure() ax = fig.add_subplot(projection='3d') X, Y, Z = axes3d.get_test_data(0.05) - ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm) - ax.contour(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm) - ax.contour(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm) + ax.contour(X, Y, Z, zdir='z', offset=-100, cmap="coolwarm") + ax.contour(X, Y, Z, zdir='x', offset=-40, cmap="coolwarm") + ax.contour(X, Y, Z, zdir='y', offset=40, cmap="coolwarm") ax.axis(xmin=-40, xmax=40, ymin=-40, ymax=40, zmin=-100, zmax=100) @@ -240,7 +242,7 @@ def test_contour3d_extend3d(): fig = plt.figure() ax = fig.add_subplot(projection='3d') X, Y, Z = axes3d.get_test_data(0.05) - ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm, extend3d=True) + ax.contour(X, Y, Z, zdir='z', offset=-100, cmap="coolwarm", extend3d=True) ax.set_xlim(-30, 30) ax.set_ylim(-20, 40) ax.set_zlim(-80, 80) @@ -252,9 +254,9 @@ def test_contourf3d(): fig = plt.figure() ax = fig.add_subplot(projection='3d') X, Y, Z = axes3d.get_test_data(0.05) - ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm) - ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm) - ax.contourf(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm) + ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap="coolwarm") + ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap="coolwarm") + ax.contourf(X, Y, Z, zdir='y', offset=40, cmap="coolwarm") ax.set_xlim(-40, 40) ax.set_ylim(-40, 40) ax.set_zlim(-100, 100) @@ -270,7 +272,7 @@ def test_contourf3d_fill(): # This produces holes in the z=0 surface that causes rendering errors if # the Poly3DCollection is not aware of path code information (issue #4784) Z[::5, ::5] = 0.1 - ax.contourf(X, Y, Z, offset=0, levels=[-0.1, 0], cmap=cm.coolwarm) + ax.contourf(X, Y, Z, offset=0, levels=[-0.1, 0], cmap="coolwarm") ax.set_xlim(-2, 2) ax.set_ylim(-2, 2) ax.set_zlim(-1, 1) @@ -279,24 +281,17 @@ def test_contourf3d_fill(): @pytest.mark.parametrize('extend, levels', [['both', [2, 4, 6]], ['min', [2, 4, 6, 8]], ['max', [0, 2, 4, 6]]]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_contourf3d_extend(fig_test, fig_ref, extend, levels): X, Y = np.meshgrid(np.arange(-2, 2, 0.25), np.arange(-2, 2, 0.25)) # Z is in the range [0, 8] Z = X**2 + Y**2 - # Manually set the over/under colors to be the end of the colormap - cmap = mpl.colormaps['viridis'].copy() - cmap.set_under(cmap(0)) - cmap.set_over(cmap(255)) - # Set vmin/max to be the min/max values plotted on the reference image - kwargs = {'vmin': 1, 'vmax': 7, 'cmap': cmap} - ax_ref = fig_ref.add_subplot(projection='3d') - ax_ref.contourf(X, Y, Z, levels=[0, 2, 4, 6, 8], **kwargs) + ax_ref.contourf(X, Y, Z, levels=[0, 2, 4, 6, 8], vmin=1, vmax=7) ax_test = fig_test.add_subplot(projection='3d') - ax_test.contourf(X, Y, Z, levels, extend=extend, **kwargs) + ax_test.contourf(X, Y, Z, levels, extend=extend, vmin=1, vmax=7) for ax in [ax_ref, ax_test]: ax.set_xlim(-2, 2) @@ -343,7 +338,7 @@ def test_lines3d(): ax.plot(x, y, z) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_plot_scalar(fig_test, fig_ref): ax1 = fig_test.add_subplot(projection='3d') ax1.plot([1], [1], "o") @@ -393,7 +388,7 @@ def f(t): ax.set_zlim3d(-1, 1) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_tight_layout_text(fig_test, fig_ref): # text is currently ignored in tight layout. So the order of text() and # tight_layout() calls should not influence the result. @@ -408,7 +403,6 @@ def test_tight_layout_text(fig_test, fig_ref): @mpl3d_image_comparison(['scatter3d.png'], style='mpl20') def test_scatter3d(): - plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig = plt.figure() ax = fig.add_subplot(projection='3d') ax.scatter(np.arange(10), np.arange(10), np.arange(10), @@ -422,7 +416,6 @@ def test_scatter3d(): @mpl3d_image_comparison(['scatter3d_color.png'], style='mpl20') def test_scatter3d_color(): - plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig = plt.figure() ax = fig.add_subplot(projection='3d') @@ -447,7 +440,7 @@ def test_scatter3d_linewidth(): marker='o', linewidth=np.arange(10)) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_scatter3d_linewidth_modification(fig_ref, fig_test): # Changing Path3DCollection linewidths with array-like post-creation # should work correctly. @@ -461,12 +454,12 @@ def test_scatter3d_linewidth_modification(fig_ref, fig_test): linewidths=np.arange(10)) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_scatter3d_modification(fig_ref, fig_test): # Changing Path3DCollection properties post-creation should work correctly. ax_test = fig_test.add_subplot(projection='3d') c = ax_test.scatter(np.arange(10), np.arange(10), np.arange(10), - marker='o') + marker='o', depthshade=True) c.set_facecolor('C1') c.set_edgecolor('C2') c.set_alpha([0.3, 0.7] * 5) @@ -482,13 +475,13 @@ def test_scatter3d_modification(fig_ref, fig_test): depthshade=False, s=75, linewidths=3) -@pytest.mark.parametrize('depthshade', [True, False]) -@check_figures_equal(extensions=['png']) -def test_scatter3d_sorting(fig_ref, fig_test, depthshade): +@check_figures_equal() +def test_scatter3d_sorting(fig_ref, fig_test): """Test that marker properties are correctly sorted.""" y, x = np.mgrid[:10, :10] z = np.arange(x.size).reshape(x.shape) + depthshade = False sizes = np.full(z.shape, 25) sizes[0::2, 0::2] = 100 @@ -508,10 +501,10 @@ def test_scatter3d_sorting(fig_ref, fig_test, depthshade): linewidths[0::2, 0::2] = 5 linewidths[1::2, 1::2] = 5 - x, y, z, sizes, facecolors, edgecolors, linewidths = [ + x, y, z, sizes, facecolors, edgecolors, linewidths = ( a.flatten() for a in [x, y, z, sizes, facecolors, edgecolors, linewidths] - ] + ) ax_ref = fig_ref.add_subplot(projection='3d') sets = (np.unique(a) for a in [sizes, facecolors, edgecolors, linewidths]) @@ -539,7 +532,7 @@ def test_scatter3d_sorting(fig_ref, fig_test, depthshade): @pytest.mark.parametrize('azim', [-50, 130]) # yellow first, blue first -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_marker_draw_order_data_reversed(fig_test, fig_ref, azim): """ Test that the draw order does not depend on the data point order. @@ -559,7 +552,7 @@ def test_marker_draw_order_data_reversed(fig_test, fig_ref, azim): ax.view_init(elev=0, azim=azim, roll=0) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_marker_draw_order_view_rotated(fig_test, fig_ref): """ Test that the draw order changes with the direction. @@ -593,6 +586,48 @@ def test_plot_3d_from_2d(): ax.plot(xs, ys, zs=0, zdir='y') +@mpl3d_image_comparison(['fill_between_quad.png'], style='mpl20') +def test_fill_between_quad(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + theta = np.linspace(0, 2*np.pi, 50) + + x1 = np.cos(theta) + y1 = np.sin(theta) + z1 = 0.1 * np.sin(6 * theta) + + x2 = 0.6 * np.cos(theta) + y2 = 0.6 * np.sin(theta) + z2 = 2 + + where = (theta < np.pi/2) | (theta > 3*np.pi/2) + + # Since none of x1 == x2, y1 == y2, or z1 == z2 is True, the fill_between + # mode will map to 'quad' + ax.fill_between(x1, y1, z1, x2, y2, z2, + where=where, mode='auto', alpha=0.5, edgecolor='k') + + +@mpl3d_image_comparison(['fill_between_polygon.png'], style='mpl20') +def test_fill_between_polygon(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + theta = np.linspace(0, 2*np.pi, 50) + + x1 = x2 = theta + y1 = y2 = 0 + z1 = np.cos(theta) + z2 = z1 + 1 + + where = (theta < np.pi/2) | (theta > 3*np.pi/2) + + # Since x1 == x2 and y1 == y2, the fill_between mode will be 'polygon' + ax.fill_between(x1, y1, z1, x2, y2, z2, + where=where, mode='auto', edgecolor='k') + + @mpl3d_image_comparison(['surface3d.png'], style='mpl20') def test_surface3d(): # Remove this line when this test image is regenerated. @@ -605,7 +640,7 @@ def test_surface3d(): X, Y = np.meshgrid(X, Y) R = np.hypot(X, Y) Z = np.sin(R) - surf = ax.plot_surface(X, Y, Z, rcount=40, ccount=40, cmap=cm.coolwarm, + surf = ax.plot_surface(X, Y, Z, rcount=40, ccount=40, cmap="coolwarm", lw=0, antialiased=False) plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated ax.set_zlim(-1.01, 1.01) @@ -614,7 +649,6 @@ def test_surface3d(): @image_comparison(['surface3d_label_offset_tick_position.png'], style='mpl20') def test_surface3d_label_offset_tick_position(): - plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated ax = plt.figure().add_subplot(projection="3d") x, y = np.mgrid[0:6 * np.pi:0.25, 0:4 * np.pi:0.25] @@ -625,8 +659,6 @@ def test_surface3d_label_offset_tick_position(): ax.set_ylabel("Y label") ax.set_zlabel("Z label") - ax.figure.canvas.draw() - @mpl3d_image_comparison(['surface3d_shaded.png'], style='mpl20') def test_surface3d_shaded(): @@ -670,7 +702,7 @@ def test_surface3d_masked(): ax.view_init(30, -80, 0) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_plot_scatter_masks(fig_test, fig_ref): x = np.linspace(0, 10, 100) y = np.linspace(0, 10, 100) @@ -688,7 +720,7 @@ def test_plot_scatter_masks(fig_test, fig_ref): ax_ref.plot(x, y, z) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_plot_surface_None_arg(fig_test, fig_ref): x, y = np.meshgrid(np.arange(5), np.arange(5)) z = x + y @@ -735,7 +767,7 @@ def test_text3d(): ax.set_zlabel('Z axis') -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_text3d_modification(fig_ref, fig_test): # Modifying the Text position after the fact should work the same as # setting it directly. @@ -775,7 +807,7 @@ def test_trisurf3d(): fig = plt.figure() ax = fig.add_subplot(projection='3d') - ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.2) + ax.plot_trisurf(x, y, z, cmap="jet", linewidth=0.2) @mpl3d_image_comparison(['trisurf3d_shaded.png'], tol=0.03, style='mpl20') @@ -804,6 +836,14 @@ def test_wireframe3d(): ax.plot_wireframe(X, Y, Z, rcount=13, ccount=13) +@mpl3d_image_comparison(['wireframe3dasymmetric.png'], style='mpl20') +def test_wireframe3dasymmetric(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + X, Y, Z = axes3d.get_test_data(0.05) + ax.plot_wireframe(X, Y, Z, rcount=3, ccount=13) + + @mpl3d_image_comparison(['wireframe3dzerocstride.png'], style='mpl20') def test_wireframe3dzerocstride(): fig = plt.figure() @@ -841,7 +881,6 @@ def test_mixedsamplesraises(): # remove tolerance when regenerating the test image @mpl3d_image_comparison(['quiver3d.png'], style='mpl20', tol=0.003) def test_quiver3d(): - plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig = plt.figure() ax = fig.add_subplot(projection='3d') pivots = ['tip', 'middle', 'tail'] @@ -861,7 +900,7 @@ def test_quiver3d(): ax.set_zlim(-1, 5) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_quiver3d_empty(fig_test, fig_ref): fig_ref.add_subplot(projection='3d') x = y = z = u = v = w = [] @@ -895,7 +934,7 @@ def test_quiver3d_colorcoded(): x = y = dx = dz = np.zeros(10) z = dy = np.arange(10.) - color = plt.cm.Reds(dy/dy.max()) + color = plt.colormaps["Reds"](dy/dy.max()) ax.quiver(x, y, z, dx, dy, dz, colors=color) ax.set_ylim(0, 10) @@ -913,13 +952,13 @@ def test_patch_modification(): assert mcolors.same_color(circle.get_facecolor(), (1, 0, 0, 1)) -@check_figures_equal(extensions=['png']) +@check_figures_equal() def test_patch_collection_modification(fig_test, fig_ref): # Test that modifying Patch3DCollection properties after creation works. patch1 = Circle((0, 0), 0.05) patch2 = Circle((0.1, 0.1), 0.03) facecolors = np.array([[0., 0.5, 0., 1.], [0.5, 0., 0., 0.5]]) - c = art3d.Patch3DCollection([patch1, patch2], linewidths=3) + c = art3d.Patch3DCollection([patch1, patch2], linewidths=3, depthshade=True) ax_test = fig_test.add_subplot(projection='3d') ax_test.add_collection3d(c) @@ -947,7 +986,7 @@ def test_poly3dcollection_verts_validation(): art3d.Poly3DCollection(poly) # should be Poly3DCollection([poly]) poly = np.array(poly, dtype=float) - with pytest.raises(ValueError, match=r'list of \(N, 3\) array-like'): + with pytest.raises(ValueError, match=r'shape \(M, N, 3\)'): art3d.Poly3DCollection(poly) # should be Poly3DCollection([poly]) @@ -962,8 +1001,8 @@ def test_poly3dcollection_closed(): facecolor=(0.5, 0.5, 1, 0.5), closed=True) c2 = art3d.Poly3DCollection([poly2], linewidths=3, edgecolor='k', facecolor=(1, 0.5, 0.5, 0.5), closed=False) - ax.add_collection3d(c1) - ax.add_collection3d(c2) + ax.add_collection3d(c1, autolim=False) + ax.add_collection3d(c2, autolim=False) def test_poly_collection_2d_to_3d_empty(): @@ -996,8 +1035,8 @@ def test_poly3dcollection_alpha(): c2.set_facecolor((1, 0.5, 0.5)) c2.set_edgecolor('k') c2.set_alpha(0.5) - ax.add_collection3d(c1) - ax.add_collection3d(c2) + ax.add_collection3d(c1, autolim=False) + ax.add_collection3d(c2, autolim=False) @mpl3d_image_comparison(['add_collection3d_zs_array.png'], style='mpl20') @@ -1056,8 +1095,33 @@ def test_add_collection3d_zs_scalar(): ax.set_zlim(0, 2) -@mpl3d_image_comparison(['axes3d_labelpad.png'], - remove_text=False, style='mpl20') +def test_line3dCollection_autoscaling(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + lines = [[(0, 0, 0), (1, 4, 2)], + [(1, 1, 3), (2, 0, 2)], + [(1, 0, 4), (1, 4, 5)]] + + lc = art3d.Line3DCollection(lines) + ax.add_collection3d(lc) + assert np.allclose(ax.get_xlim3d(), (-0.041666666666666664, 2.0416666666666665)) + assert np.allclose(ax.get_ylim3d(), (-0.08333333333333333, 4.083333333333333)) + assert np.allclose(ax.get_zlim3d(), (-0.10416666666666666, 5.104166666666667)) + + +def test_poly3dCollection_autoscaling(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + poly = np.array([[0, 0, 0], [1, 1, 3], [1, 0, 4]]) + col = art3d.Poly3DCollection([poly]) + ax.add_collection3d(col) + assert np.allclose(ax.get_xlim3d(), (-0.020833333333333332, 1.0208333333333333)) + assert np.allclose(ax.get_ylim3d(), (-0.020833333333333332, 1.0208333333333333)) + assert np.allclose(ax.get_zlim3d(), (-0.0833333333333333, 4.083333333333333)) + + +@mpl3d_image_comparison(['axes3d_labelpad.png'], remove_text=False, style='mpl20') def test_axes3d_labelpad(): fig = plt.figure() ax = fig.add_axes(Axes3D(fig)) @@ -1147,7 +1211,7 @@ def _test_proj_draw_axes(M, s=1, *args, **kwargs): fig, ax = plt.subplots(*args, **kwargs) linec = LineCollection(lines) - ax.add_collection(linec) + ax.add_collection(linec, autolim="_datalim_only") for x, y, t in zip(txs, tys, ['o', 'x', 'y', 'z']): ax.text(x, y, t) @@ -1257,6 +1321,21 @@ def test_unautoscale(axis, auto): np.testing.assert_array_equal(get_lim(), (-0.5, 0.5)) +@check_figures_equal() +def test_culling(fig_test, fig_ref): + xmins = (-100, -50) + for fig, xmin in zip((fig_test, fig_ref), xmins): + ax = fig.add_subplot(projection='3d') + n = abs(xmin) + 1 + xs = np.linspace(0, xmin, n) + ys = np.ones(n) + zs = np.zeros(n) + ax.plot(xs, ys, zs, 'k') + + ax.set(xlim=(-5, 5), ylim=(-5, 5), zlim=(-5, 5)) + ax.view_init(5, 180, 0) + + def test_axes3d_focal_length_checks(): fig = plt.figure() ax = fig.add_subplot(projection='3d') @@ -1297,6 +1376,45 @@ def test_axes3d_isometric(): ax.grid(True) +@check_figures_equal() +def test_axlim_clip(fig_test, fig_ref): + # With axlim clipping + ax = fig_test.add_subplot(projection="3d") + x = np.linspace(0, 1, 11) + y = np.linspace(0, 1, 11) + X, Y = np.meshgrid(x, y) + Z = X + Y + ax.plot_surface(X, Y, Z, facecolor='C1', edgecolors=None, + rcount=50, ccount=50, axlim_clip=True) + # This ax.plot is to cover the extra surface edge which is not clipped out + ax.plot([0.5, 0.5], [0, 1], [0.5, 1.5], + color='k', linewidth=3, zorder=5, axlim_clip=True) + ax.scatter(X.ravel(), Y.ravel(), Z.ravel() + 1, axlim_clip=True) + ax.quiver(X.ravel(), Y.ravel(), Z.ravel() + 2, + 0*X.ravel(), 0*Y.ravel(), 0*Z.ravel() + 1, + arrow_length_ratio=0, axlim_clip=True) + ax.plot(X[0], Y[0], Z[0] + 3, color='C2', axlim_clip=True) + ax.text(1.1, 0.5, 4, 'test', axlim_clip=True) # won't be visible + ax.set(xlim=(0, 0.5), ylim=(0, 1), zlim=(0, 5)) + + # With manual clipping + ax = fig_ref.add_subplot(projection="3d") + idx = (X <= 0.5) + X = X[idx].reshape(11, 6) + Y = Y[idx].reshape(11, 6) + Z = Z[idx].reshape(11, 6) + ax.plot_surface(X, Y, Z, facecolor='C1', edgecolors=None, + rcount=50, ccount=50, axlim_clip=False) + ax.plot([0.5, 0.5], [0, 1], [0.5, 1.5], + color='k', linewidth=3, zorder=5, axlim_clip=False) + ax.scatter(X.ravel(), Y.ravel(), Z.ravel() + 1, axlim_clip=False) + ax.quiver(X.ravel(), Y.ravel(), Z.ravel() + 2, + 0*X.ravel(), 0*Y.ravel(), 0*Z.ravel() + 1, + arrow_length_ratio=0, axlim_clip=False) + ax.plot(X[0], Y[0], Z[0] + 3, color='C2', axlim_clip=False) + ax.set(xlim=(0, 0.5), ylim=(0, 1), zlim=(0, 5)) + + @pytest.mark.parametrize('value', [np.inf, np.nan]) @pytest.mark.parametrize(('setter', 'side'), [ ('set_xlim3d', 'left'), @@ -1378,8 +1496,8 @@ def test_alpha(self): assert voxels[coord], "faces returned for absent voxel" assert isinstance(poly, art3d.Poly3DCollection) - @mpl3d_image_comparison(['voxels-xyz.png'], - tol=0.01, remove_text=False, style='mpl20') + @mpl3d_image_comparison(['voxels-xyz.png'], remove_text=False, style='mpl20', + tol=0.002 if sys.platform == 'win32' else 0) def test_xyz(self): fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) @@ -1433,8 +1551,9 @@ def test_calling_conventions(self): ax.voxels(x, y) # x, y, z are positional only - this passes them on as attributes of # Poly3DCollection - with pytest.raises(AttributeError): + with pytest.raises(AttributeError, match="keyword argument 'x'") as exec_info: ax.voxels(filled=filled, x=x, y=y, z=z) + assert exec_info.value.name == 'x' def test_line3d_set_get_data_3d(): @@ -1455,7 +1574,7 @@ def test_line3d_set_get_data_3d(): np.testing.assert_array_equal((x, y, np.zeros_like(z)), line.get_data_3d()) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_inverted(fig_test, fig_ref): # Plot then invert. ax = fig_test.add_subplot(projection="3d") @@ -1504,7 +1623,7 @@ def test_ax3d_tickcolour(): assert tick.tick1line._color == 'red' -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_ticklabel_format(fig_test, fig_ref): axs = fig_test.subplots(4, 5, subplot_kw={"projection": "3d"}) for ax in axs.flat: @@ -1544,7 +1663,7 @@ def get_formatters(ax, names): not mpl.rcParams["axes.formatter.use_mathtext"]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_quiver3D_smoke(fig_test, fig_ref): pivot = "middle" # Make the grid @@ -1591,7 +1710,7 @@ def test_errorbar3d_errorevery(): @mpl3d_image_comparison(['errorbar3d.png'], style='mpl20', - tol=0.02 if platform.machine() == 'arm64' else 0) + tol=0.015 if sys.platform == 'darwin' else 0) def test_errorbar3d(): """Tests limits, color styling, and legend for 3D errorbars.""" fig = plt.figure() @@ -1607,9 +1726,9 @@ def test_errorbar3d(): ax.legend() -@image_comparison(['stem3d.png'], style='mpl20', tol=0.008) +@image_comparison(['stem3d.png'], style='mpl20', + tol=0 if platform.machine() == 'x86_64' else 0.008) def test_stem3d(): - plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig, axs = plt.subplots(2, 3, figsize=(8, 6), constrained_layout=True, subplot_kw={'projection': '3d'}) @@ -1741,7 +1860,7 @@ def test_set_zlim(): ax.set_zlim(top=0, zmax=1) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_shared_view(fig_test, fig_ref): elev, azim, roll = 5, 20, 30 ax1 = fig_test.add_subplot(131, projection="3d") @@ -1857,37 +1976,78 @@ def test_quaternion(): np.deg2rad(elev), np.deg2rad(azim), np.deg2rad(roll)) assert np.isclose(q.norm, 1) q = Quaternion(mag * q.scalar, mag * q.vector) - e, a, r = np.rad2deg(Quaternion.as_cardan_angles(q)) - assert np.isclose(e, elev) - assert np.isclose(a, azim) - assert np.isclose(r, roll) + np.testing.assert_allclose(np.rad2deg(Quaternion.as_cardan_angles(q)), + (elev, azim, roll), atol=1e-6) -def test_rotate(): +@pytest.mark.parametrize('style', + ('azel', 'trackball', 'sphere', 'arcball')) +def test_rotate(style): """Test rotating using the left mouse button.""" - for roll, dx, dy, new_elev, new_azim, new_roll in [ - [0, 0.5, 0, 0, -90, 0], - [30, 0.5, 0, 30, -90, 0], - [0, 0, 0.5, -90, 0, 0], - [30, 0, 0.5, -60, -90, 90], - [0, 0.5, 0.5, -45, -90, 45], - [30, 0.5, 0.5, -15, -90, 45]]: - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1, projection='3d') - ax.view_init(0, 0, roll) - ax.figure.canvas.draw() - - # drag mouse to change orientation - ax._button_press( - mock_event(ax, button=MouseButton.LEFT, xdata=0, ydata=0)) - ax._on_move( - mock_event(ax, button=MouseButton.LEFT, - xdata=dx*ax._pseudo_w, ydata=dy*ax._pseudo_h)) - ax.figure.canvas.draw() - - assert np.isclose(ax.elev, new_elev) - assert np.isclose(ax.azim, new_azim) - assert np.isclose(ax.roll, new_roll) + if style == 'azel': + s = 0.5 + else: + s = mpl.rcParams['axes3d.trackballsize'] / 2 + s *= 0.5 + mpl.rcParams['axes3d.trackballborder'] = 0 + with mpl.rc_context({'axes3d.mouserotationstyle': style}): + for roll, dx, dy in [ + [0, 1, 0], + [30, 1, 0], + [0, 0, 1], + [30, 0, 1], + [0, 0.5, np.sqrt(3)/2], + [30, 0.5, np.sqrt(3)/2], + [0, 2, 0]]: + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1, projection='3d') + ax.view_init(0, 0, roll) + ax.figure.canvas.draw() + + # drag mouse to change orientation + MouseEvent._from_ax_coords( + "button_press_event", ax, (0, 0), MouseButton.LEFT)._process() + MouseEvent._from_ax_coords( + "motion_notify_event", ax, (s*dx*ax._pseudo_w, s*dy*ax._pseudo_h), + MouseButton.LEFT)._process() + ax.figure.canvas.draw() + + c = np.sqrt(3)/2 + expectations = { + ('azel', 0, 1, 0): (0, -45, 0), + ('azel', 0, 0, 1): (-45, 0, 0), + ('azel', 0, 0.5, c): (-38.971143, -22.5, 0), + ('azel', 0, 2, 0): (0, -90, 0), + ('azel', 30, 1, 0): (22.5, -38.971143, 30), + ('azel', 30, 0, 1): (-38.971143, -22.5, 30), + ('azel', 30, 0.5, c): (-22.5, -38.971143, 30), + + ('trackball', 0, 1, 0): (0, -28.64789, 0), + ('trackball', 0, 0, 1): (-28.64789, 0, 0), + ('trackball', 0, 0.5, c): (-24.531578, -15.277726, 3.340403), + ('trackball', 0, 2, 0): (0, -180/np.pi, 0), + ('trackball', 30, 1, 0): (13.869588, -25.319385, 26.87008), + ('trackball', 30, 0, 1): (-24.531578, -15.277726, 33.340403), + ('trackball', 30, 0.5, c): (-13.869588, -25.319385, 33.129920), + + ('sphere', 0, 1, 0): (0, -30, 0), + ('sphere', 0, 0, 1): (-30, 0, 0), + ('sphere', 0, 0.5, c): (-25.658906, -16.102114, 3.690068), + ('sphere', 0, 2, 0): (0, -90, 0), + ('sphere', 30, 1, 0): (14.477512, -26.565051, 26.565051), + ('sphere', 30, 0, 1): (-25.658906, -16.102114, 33.690068), + ('sphere', 30, 0.5, c): (-14.477512, -26.565051, 33.434949), + + ('arcball', 0, 1, 0): (0, -60, 0), + ('arcball', 0, 0, 1): (-60, 0, 0), + ('arcball', 0, 0.5, c): (-48.590378, -40.893395, 19.106605), + ('arcball', 0, 2, 0): (0, 180, 0), + ('arcball', 30, 1, 0): (25.658906, -56.309932, 16.102114), + ('arcball', 30, 0, 1): (-48.590378, -40.893395, 49.106605), + ('arcball', 30, 0.5, c): (-25.658906, -56.309932, 43.897886)} + new_elev, new_azim, new_roll = expectations[(style, roll, dx, dy)] + np.testing.assert_allclose((ax.elev, ax.azim, ax.roll), + (new_elev, new_azim, new_roll), atol=1e-6) def test_pan(): @@ -1899,19 +2059,20 @@ def convert_lim(dmin, dmax): range_ = dmax - dmin return center, range_ - ax = plt.figure().add_subplot(projection='3d') + fig = plt.figure() + ax = fig.add_subplot(projection='3d') ax.scatter(0, 0, 0) - ax.figure.canvas.draw() + fig.canvas.draw() x_center0, x_range0 = convert_lim(*ax.get_xlim3d()) y_center0, y_range0 = convert_lim(*ax.get_ylim3d()) z_center0, z_range0 = convert_lim(*ax.get_zlim3d()) # move mouse diagonally to pan along all axis. - ax._button_press( - mock_event(ax, button=MouseButton.MIDDLE, xdata=0, ydata=0)) - ax._on_move( - mock_event(ax, button=MouseButton.MIDDLE, xdata=1, ydata=1)) + MouseEvent._from_ax_coords( + "button_press_event", ax, (0, 0), MouseButton.MIDDLE)._process() + MouseEvent._from_ax_coords( + "motion_notify_event", ax, (1, 1), MouseButton.MIDDLE)._process() x_center, x_range = convert_lim(*ax.get_xlim3d()) y_center, y_range = convert_lim(*ax.get_ylim3d()) @@ -1966,20 +2127,20 @@ def test_toolbar_zoom_pan(tool, button, key, expected): # Set up the mouse movements start_event = MouseEvent( "button_press_event", fig.canvas, *s0, button, key=key) + drag_event = MouseEvent( + "motion_notify_event", fig.canvas, *s1, button, key=key, buttons={button}) stop_event = MouseEvent( "button_release_event", fig.canvas, *s1, button, key=key) tb = NavigationToolbar2(fig.canvas) if tool == "zoom": tb.zoom() - tb.press_zoom(start_event) - tb.drag_zoom(stop_event) - tb.release_zoom(stop_event) else: tb.pan() - tb.press_pan(start_event) - tb.drag_pan(stop_event) - tb.release_pan(stop_event) + + start_event._process() + drag_event._process() + stop_event._process() # Should be close, but won't be exact due to screen integer resolution xlim, ylim, zlim = expected @@ -2005,7 +2166,7 @@ def test_toolbar_zoom_pan(tool, button, key, expected): @mpl.style.context('default') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_scalarmap_update(fig_test, fig_ref): x, y, z = np.array(list(itertools.product(*[np.arange(0, 5, 1), @@ -2034,9 +2195,7 @@ def test_subfigure_simple(): ax = sf[1].add_subplot(1, 1, 1, projection='3d', label='other') -# Update style when regenerating the test image -@image_comparison(baseline_images=['computed_zorder'], remove_text=True, - extensions=['png'], style=('mpl20')) +@image_comparison(['computed_zorder.png'], remove_text=True, style='mpl20') def test_computed_zorder(): plt.rcParams['axes3d.automargin'] = True # Remove when image is regenerated fig = plt.figure() @@ -2059,9 +2218,9 @@ def test_computed_zorder(): # plot some points ax.scatter((3, 3), (1, 3), (1, 3), c='red', zorder=10) - ax.set_xlim((0, 5.0)) - ax.set_ylim((0, 5.0)) - ax.set_zlim((0, 2.5)) + ax.set_xlim(0, 5.0) + ax.set_ylim(0, 5.0) + ax.set_zlim(0, 2.5) ax3 = fig.add_subplot(223, projection='3d') ax4 = fig.add_subplot(224, projection='3d') @@ -2205,7 +2364,7 @@ def test_margins_errors(err, args, kwargs, match): ax.margins(*args, **kwargs) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_text_3d(fig_test, fig_ref): ax = fig_ref.add_subplot(projection="3d") txt = Text(0.5, 0.5, r'Foo bar $\int$') @@ -2226,7 +2385,7 @@ def test_draw_single_lines_from_Nx1(): ax.plot([[0], [1]], [[0], [1]], [[0], [1]]) -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_pathpatch_3d(fig_test, fig_ref): ax = fig_ref.add_subplot(projection="3d") path = Path.unit_rectangle() @@ -2356,7 +2515,7 @@ def test_view_init_vertical_axis( rtol = 2e-06 ax = plt.subplot(1, 1, 1, projection="3d") ax.view_init(elev=0, azim=0, roll=0, vertical_axis=vertical_axis) - ax.figure.canvas.draw() + ax.get_figure().canvas.draw() # Assert the projection matrix: proj_actual = ax.get_proj() @@ -2382,14 +2541,13 @@ def test_on_move_vertical_axis(vertical_axis: str) -> None: """ ax = plt.subplot(1, 1, 1, projection="3d") ax.view_init(elev=0, azim=0, roll=0, vertical_axis=vertical_axis) - ax.figure.canvas.draw() + ax.get_figure().canvas.draw() proj_before = ax.get_proj() - event_click = mock_event(ax, button=MouseButton.LEFT, xdata=0, ydata=1) - ax._button_press(event_click) - - event_move = mock_event(ax, button=MouseButton.LEFT, xdata=0.5, ydata=0.8) - ax._on_move(event_move) + MouseEvent._from_ax_coords( + "button_press_event", ax, (0, 1), MouseButton.LEFT)._process() + MouseEvent._from_ax_coords( + "motion_notify_event", ax, (.5, .8), MouseButton.LEFT)._process() assert ax._axis_names.index(vertical_axis) == ax._vertical_axis @@ -2411,7 +2569,7 @@ def test_on_move_vertical_axis(vertical_axis: str) -> None: def test_set_box_aspect_vertical_axis(vertical_axis, aspect_expected): ax = plt.subplot(1, 1, 1, projection="3d") ax.view_init(elev=0, azim=0, roll=0, vertical_axis=vertical_axis) - ax.figure.canvas.draw() + ax.get_figure().canvas.draw() ax.set_box_aspect(None) @@ -2440,7 +2598,7 @@ def test_panecolor_rcparams(): fig.add_subplot(projection='3d') -@check_figures_equal(extensions=["png"]) +@check_figures_equal() def test_mutating_input_arrays_y_and_z(fig_test, fig_ref): """ Test to see if the `z` axis does not get mutated @@ -2523,3 +2681,507 @@ def test_ndarray_color_kwargs_value_error(): ax = fig.add_subplot(111, projection='3d') ax.scatter(1, 0, 0, color=np.array([0, 0, 0, 1])) fig.canvas.draw() + + +def test_line3dcollection_autolim_ragged(): + """Test Line3DCollection with autolim=True and lines of different lengths.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + # Create lines with different numbers of points (ragged arrays) + edges = [ + [(0, 0, 0), (1, 1, 1), (2, 2, 2)], # 3 points + [(0, 1, 0), (1, 2, 1)], # 2 points + [(1, 0, 1), (2, 1, 2), (3, 2, 3), (4, 3, 4)] # 4 points + ] + + # This should not raise an exception. + collections = ax.add_collection3d(art3d.Line3DCollection(edges), autolim=True) + + # Check that limits were computed correctly with margins + # The limits should include all points with default margins + assert np.allclose(ax.get_xlim3d(), (-0.08333333333333333, 4.083333333333333)) + assert np.allclose(ax.get_ylim3d(), (-0.0625, 3.0625)) + assert np.allclose(ax.get_zlim3d(), (-0.08333333333333333, 4.083333333333333)) + + +def test_axes3d_set_aspect_deperecated_params(): + """ + Test that using the deprecated 'anchor' and 'share' kwargs in + set_aspect raises the correct warning. + """ + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + # Test that providing the `anchor` parameter raises a deprecation warning. + with pytest.warns(_api.MatplotlibDeprecationWarning, match="'anchor' parameter"): + ax.set_aspect('equal', anchor='C') + + # Test that using the 'share' parameter is now deprecated. + with pytest.warns(_api.MatplotlibDeprecationWarning, match="'share' parameter"): + ax.set_aspect('equal', share=True) + + # Test that the `adjustable` parameter is correctly processed to satisfy + # code coverage. + ax.set_aspect('equal', adjustable='box') + assert ax.get_adjustable() == 'box' + + ax.set_aspect('equal', adjustable='datalim') + assert ax.get_adjustable() == 'datalim' + + with pytest.raises(ValueError, match="adjustable"): + ax.set_aspect('equal', adjustable='invalid_value') + + +def test_axis_get_tightbbox_includes_offset_text(): + # Test that axis.get_tightbbox includes the offset_text + # Regression test for issue #30744 + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Create data with high precision values that trigger offset text + Z = np.array([[0.1, 0.100000001], [0.100000000001, 0.100000000]]) + ny, nx = Z.shape + x = np.arange(nx) + y = np.arange(ny) + X, Y = np.meshgrid(x, y) + + ax.plot_surface(X, Y, Z) + + # Force a draw to ensure offset text is created and positioned + fig.canvas.draw() + renderer = fig.canvas.get_renderer() + + # Get the z-axis (which should have the offset text) + zaxis = ax.zaxis + + # Check that offset text is visible and has content + # The offset text may not be visible on all backends/configurations, + # so we only test the inclusion when it's actually present + if (zaxis.offsetText.get_visible() and + zaxis.offsetText.get_text()): + offset_bbox = zaxis.offsetText.get_window_extent(renderer) + + # Get the tight bbox - this should include the offset text + bbox = zaxis.get_tightbbox(renderer) + assert bbox is not None + assert offset_bbox is not None + + # The tight bbox should fully contain the offset text bbox + # Check that offset_bbox is within bbox bounds (with small tolerance for + # floating point errors) + assert bbox.x0 <= offset_bbox.x0 + 1e-6, \ + f"bbox.x0 ({bbox.x0}) should be <= offset_bbox.x0 ({offset_bbox.x0})" + assert bbox.y0 <= offset_bbox.y0 + 1e-6, \ + f"bbox.y0 ({bbox.y0}) should be <= offset_bbox.y0 ({offset_bbox.y0})" + assert bbox.x1 >= offset_bbox.x1 - 1e-6, \ + f"bbox.x1 ({bbox.x1}) should be >= offset_bbox.x1 ({offset_bbox.x1})" + assert bbox.y1 >= offset_bbox.y1 - 1e-6, \ + f"bbox.y1 ({bbox.y1}) should be >= offset_bbox.y1 ({offset_bbox.y1})" + + +def test_ctrl_rotation_snaps_to_5deg(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + initial = (12.3, 33.7, 2.2) + ax.view_init(*initial) + fig.canvas.draw() + + s = 0.25 + step = plt.rcParams["axes3d.snap_rotation"] + + # First rotation without Ctrl + with mpl.rc_context({'axes3d.mouserotationstyle': 'azel'}): + MouseEvent._from_ax_coords( + "button_press_event", ax, (0, 0), MouseButton.LEFT + )._process() + + MouseEvent._from_ax_coords( + "motion_notify_event", + ax, + (s * ax._pseudo_w, s * ax._pseudo_h), + MouseButton.LEFT, + )._process() + + fig.canvas.draw() + + rotated_elev = ax.elev + rotated_azim = ax.azim + rotated_roll = ax.roll + + # Reset before ctrl rotation + ax.view_init(*initial) + fig.canvas.draw() + + # Now rotate with Ctrl + with mpl.rc_context({'axes3d.mouserotationstyle': 'azel'}): + MouseEvent._from_ax_coords( + "button_press_event", ax, (0, 0), MouseButton.LEFT + )._process() + + MouseEvent._from_ax_coords( + "motion_notify_event", + ax, + (s * ax._pseudo_w, s * ax._pseudo_h), + MouseButton.LEFT, + key="control" + )._process() + + fig.canvas.draw() + + expected_elev = step * round(rotated_elev / step) + expected_azim = step * round(rotated_azim / step) + expected_roll = step * round(rotated_roll / step) + + assert ax.elev == pytest.approx(expected_elev) + assert ax.azim == pytest.approx(expected_azim) + assert ax.roll == pytest.approx(expected_roll) + + plt.close(fig) + + +# ============================================================================= +# Tests for 3D scale transforms (log, symlog, logit, etc.) +# ============================================================================= + +def _make_log_data(): + """Data spanning 1 to ~1000 for log scale.""" + t = np.linspace(0, 2 * np.pi, 50) + x = 10 ** (t / 2) + y = 10 ** (1 + np.sin(t)) + z = 10 ** (2 * (1 + np.cos(t) / 2)) + return x, y, z + + +def _make_surface_log_data(): + """Grid data for surface with positive Z.""" + x = np.linspace(1, 10, 20) + y = np.linspace(1, 10, 20) + X, Y = np.meshgrid(x, y) + Z = X * Y + return X, Y, Z + + +def _make_triangulation_data(): + """Data for trisurf with positive values.""" + np.random.seed(42) + x = np.random.uniform(1, 100, 100) + y = np.random.uniform(1, 100, 100) + z = x * y / 10 + return x, y, z + + +@mpl3d_image_comparison(['scale3d_artists_log.png'], style='mpl20', + remove_text=False, tol=0.016) +def test_scale3d_artists_log(): + """Test all 3D artist types with log scale.""" + fig = plt.figure(figsize=(16, 12)) + log_kw = dict(xscale='log', yscale='log', zscale='log') + line_data = _make_log_data() + surf_X, surf_Y, surf_Z = _make_surface_log_data() + + # Row 1: plot, wireframe, scatter, bar3d + ax = fig.add_subplot(3, 4, 1, projection='3d') + ax.plot(*line_data) + ax.set(**log_kw, title='plot') + + ax = fig.add_subplot(3, 4, 2, projection='3d') + ax.plot_wireframe(surf_X, surf_Y, surf_Z, rstride=5, cstride=5) + ax.set(**log_kw, title='wireframe') + + ax = fig.add_subplot(3, 4, 3, projection='3d') + ax.scatter(*line_data, c=line_data[2], cmap='viridis') + ax.set(**log_kw, title='scatter') + + ax = fig.add_subplot(3, 4, 4, projection='3d') + bx, by = np.meshgrid([1, 10, 100], [1, 10, 100]) + bx, by = bx.flatten(), by.flatten() + ax.bar3d(bx, by, np.ones_like(bx, dtype=float), + bx * 0.3, by * 0.3, bx * by / 10, alpha=0.8) + ax.set(**log_kw, title='bar3d') + + # Row 2: surface, trisurf, contour, contourf + ax = fig.add_subplot(3, 4, 5, projection='3d') + ax.plot_surface(surf_X, surf_Y, surf_Z, cmap='viridis', alpha=0.8) + ax.set(**log_kw, title='surface') + + ax = fig.add_subplot(3, 4, 6, projection='3d') + tri_data = _make_triangulation_data() + ax.plot_trisurf(*tri_data, cmap='viridis', alpha=0.8) + ax.set(**log_kw, title='trisurf') + + ax = fig.add_subplot(3, 4, 7, projection='3d') + ax.contour(surf_X, surf_Y, surf_Z, levels=10) + ax.set(**log_kw, title='contour') + + ax = fig.add_subplot(3, 4, 8, projection='3d') + ax.contourf(surf_X, surf_Y, surf_Z, levels=10, alpha=0.8) + ax.set(**log_kw, title='contourf') + + # Row 3: stem, quiver, text + ax = fig.add_subplot(3, 4, 9, projection='3d') + ax.stem([1, 10, 100], [1, 10, 100], [10, 100, 1000], bottom=1) + ax.set(**log_kw, title='stem') + + ax = fig.add_subplot(3, 4, 10, projection='3d') + qxyz = np.array([1, 10, 100]) + ax.quiver(qxyz, qxyz, qxyz, qxyz * 0.5, qxyz * 0.5, qxyz * 0.5) + ax.set(**log_kw, title='quiver') + + ax = fig.add_subplot(3, 4, 11, projection='3d') + ax.text(1, 1, 1, "Point A") + ax.text(10, 10, 10, "Point B") + ax.text(100, 100, 100, "Point C") + ax.set(**log_kw, title='text', + xlim=(0.5, 200), ylim=(0.5, 200), zlim=(0.5, 200)) + + +@mpl3d_image_comparison(['scale3d_all_scales.png'], style='mpl20', remove_text=False) +def test_scale3d_all_scales(): + """Test all scale types with mixed scales on each axis.""" + fig, axs = plt.subplots(1, 2, subplot_kw={'projection': '3d'}, figsize=(10, 6)) + + # Data that works across all scale types + t = np.linspace(0.1, 0.9, 30) + # x: positive for log/asinh, y: spans neg/pos for symlog, z: (0,1) for logit + x = t * 100 # 10 to 90 + y = (t - 0.5) * 20 # -10 to 10 + z = t # 0.1 to 0.9 + + # Subplot 1: x=log, y=symlog, z=logit + axs[0].scatter(x, y, z) + axs[0].set(xscale='log', yscale='symlog', zscale='logit', + xlabel='log', ylabel='symlog', zlabel='logit') + + # Subplot 2: x=asinh, y=linear, z=function (square root) + axs[1].scatter(x, y, z) + axs[1].set_xscale('asinh') + axs[1].set_zscale('function', functions=(lambda v: v**0.5, lambda v: v**2)) + axs[1].set(xlabel='asinh', ylabel='linear', zlabel='function') + + +@pytest.mark.parametrize("scale, expected_lims", [ + ("linear", (-0.020833333333333332, 1.0208333333333333)), + ("log", (0.03640537388223389, 1.1918138759519783)), + ("symlog", (-0.020833333333333332, 1.0208333333333333)), + ("logit", (0.029640777806688817, 0.9703592221933112)), + ("asinh", (-0.020833333333333332, 1.0208333333333333)), +]) +@mpl.style.context("default") +def test_scale3d_default_limits(scale, expected_lims): + """Default axis limits on an empty plot should be correct for each scale.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set_xscale(scale) + ax.set_yscale(scale) + ax.set_zscale(scale) + fig.canvas.draw() + + for get_lim in (ax.get_xlim, ax.get_ylim, ax.get_zlim): + np.testing.assert_allclose(get_lim(), expected_lims) + + +@check_figures_equal() +@pytest.mark.filterwarnings("ignore:Data has no positive values") +def test_scale3d_all_clipped(fig_test, fig_ref): + """Fully clipped data (e.g. negative values on log) should look like an empty plot. + """ + lims = (0.1, 10) + for ax in [fig_test.add_subplot(projection='3d'), + fig_ref.add_subplot(projection='3d')]: + ax.set_xscale('log') + ax.set_yscale('log') + ax.set_zscale('log') + ax.set(xlim=lims, ylim=lims, zlim=lims) + + # All negative data — everything is invalid for log scale + fig_test.axes[0].plot([-1, -2, -3], [-4, -5, -6], [-7, -8, -9]) + + +@mpl3d_image_comparison(['scale3d_log_bases.png'], style='mpl20', remove_text=False) +def test_scale3d_log_bases(): + """Test log scale with different bases and subs.""" + fig, axs = plt.subplots(2, 2, subplot_kw={'projection': '3d'}, figsize=(10, 8)) + x, y, z = _make_log_data() + + for ax, base, title in [(axs[0, 0], 10, 'base=10'), + (axs[0, 1], 2, 'base=2'), + (axs[1, 0], np.e, 'base=e')]: + ax.scatter(x, y, z, s=10) + ax.set_xscale('log', base=base) + ax.set_yscale('log', base=base) + ax.set_zscale('log', base=base) + ax.set_title(title) + if base == np.e: + # Format tick labels as e^n instead of 2.718...^n + def fmt_e(x, pos=None): + if x <= 0: + return '' + exp = np.log(x) + if np.isclose(exp, round(exp)): + return r'$e^{%d}$' % round(exp) + return '' + ax.xaxis.set_major_formatter(fmt_e) + ax.yaxis.set_major_formatter(fmt_e) + ax.zaxis.set_major_formatter(fmt_e) + + # subs + axs[1, 1].scatter(x, y, z, s=10) + axs[1, 1].set_xscale('log', subs=[2, 5]) + axs[1, 1].set_yscale('log', subs=[2, 5]) + axs[1, 1].set_zscale('log', subs=[2, 5]) + axs[1, 1].set_title('subs=[2,5]') + + +@mpl3d_image_comparison(['scale3d_symlog_params.png'], style='mpl20', + remove_text=False) +def test_scale3d_symlog_params(): + """Test symlog scale with different linthresh values.""" + fig, axs = plt.subplots(1, 2, subplot_kw={'projection': '3d'}) + + # Data spanning negative, zero, and positive + t = np.linspace(-3, 3, 50) + x = np.sinh(t) * 10 + y = t ** 3 + z = np.sign(t) * np.abs(t) ** 2 + + for ax, linthresh in [(axs[0], 0.1), (axs[1], 10)]: + ax.scatter(x, y, z, c=np.abs(z), cmap='viridis', s=10) + ax.set_xscale('symlog', linthresh=linthresh) + ax.set_yscale('symlog', linthresh=linthresh) + ax.set_zscale('symlog', linthresh=linthresh) + ax.set_title(f'linthresh={linthresh}') + + +@pytest.mark.parametrize('scale_type,kwargs', [ + ('log', {'base': 10}), + ('log', {'base': 2}), + ('log', {'subs': [2, 5]}), + ('log', {'nonpositive': 'mask'}), + ('symlog', {'base': 2}), + ('symlog', {'linthresh': 1}), + ('symlog', {'linscale': 0.5}), + ('symlog', {'subs': [2, 5]}), + ('asinh', {'linear_width': 0.5}), + ('asinh', {'base': 2}), + ('logit', {'nonpositive': 'clip'}), +]) +def test_scale3d_keywords_accepted(scale_type, kwargs): + """Verify that scale keywords are accepted on all 3 axes.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + for setter in [ax.set_xscale, ax.set_yscale, ax.set_zscale]: + setter(scale_type, **kwargs) + assert (ax.get_xscale(), ax.get_yscale(), ax.get_zscale()) == (scale_type,) * 3 + + +@pytest.mark.parametrize('axis', ['x', 'y', 'z']) +def test_scale3d_limit_range_log(axis): + """Log scale should warn when setting non-positive limits.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + getattr(ax, f'set_{axis}scale')('log') + + # Setting non-positive limits should warn + with pytest.warns(UserWarning, match="non-positive"): + getattr(ax, f'set_{axis}lim')(-10, 100) + + +def test_scale3d_limit_range_logit(): + """Logit scale should constrain axis to (0, 1).""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set(xscale='logit', yscale='logit', zscale='logit', + xlim=(-0.5, 1.5), ylim=(-0.5, 1.5), zlim=(-0.5, 1.5)) + + # Limits should be constrained to (0, 1) + for name, lim in [('x', ax.get_xlim()), ('y', ax.get_ylim()), + ('z', ax.get_zlim())]: + assert lim[0] > 0, f"{name} lower limit should be > 0 for logit" + assert lim[1] < 1, f"{name} upper limit should be < 1 for logit" + + +@pytest.mark.parametrize('scale_type', ['log', 'symlog', 'logit', 'asinh']) +def test_scale3d_transform_roundtrip(scale_type): + """Forward/inverse transform should preserve values.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set(xscale=scale_type, yscale=scale_type, zscale=scale_type) + + # Use appropriate test values for each scale type + test_values = { + 'log': [1, 10, 100, 1000], + 'symlog': [-100, -1, 0, 1, 100], + 'asinh': [-100, -1, 0, 1, 100], + 'logit': [0.01, 0.1, 0.5, 0.9, 0.99], + }[scale_type] + test_values = np.array(test_values) + + # Test round-trip for each axis + for axis in [ax.xaxis, ax.yaxis, ax.zaxis]: + trans = axis.get_transform() + forward = trans.transform(test_values.reshape(-1, 1)) + inverse = trans.inverted().transform(forward) + np.testing.assert_allclose(inverse.flatten(), test_values, rtol=1e-10) + + +def test_scale3d_invalid_keywords_raise(): + """Invalid kwargs should raise TypeError.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + with pytest.raises(TypeError): + ax.set_xscale('log', invalid_kwarg=True) + + with pytest.raises(TypeError): + ax.set_yscale('symlog', invalid_kwarg=True) + + with pytest.raises(TypeError): + ax.set_zscale('logit', invalid_kwarg=True) + + +def test_scale3d_persists_after_plot(): + """Scale should persist after adding plot data.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set(xscale='log', yscale='log', zscale='log') + ax.plot(*_make_log_data()) + assert (ax.get_xscale(), ax.get_yscale(), ax.get_zscale()) == ('log',) * 3 + + +def test_scale3d_autoscale_with_log(): + """Autoscale should work correctly with log scale.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set(xscale='log', yscale='log', zscale='log') + ax.scatter([1, 10, 100], [1, 10, 100], [1, 10, 100]) + + # All limits should be positive + for name, lim in [('x', ax.get_xlim()), ('y', ax.get_ylim()), + ('z', ax.get_zlim())]: + assert lim[0] > 0, f"{name} lower limit should be positive" + assert lim[1] > 0, f"{name} upper limit should be positive" + + +@pytest.mark.parametrize("method", ["semilogx", "semilogy", "semilogz", "loglog"]) +def test_semilog_loglog_not_implemented(method): + """semilogx/y/z and loglog should raise NotImplementedError on Axes3D.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + with pytest.raises(NotImplementedError, match="Axes3D does not support"): + getattr(ax, method)([1, 10, 100], [1, 2, 3]) + + +def test_scale3d_calc_coord(): + """_calc_coord should return data coordinates with correct pane values.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.scatter([1, 10, 100], [1, 10, 100], [1, 10, 100]) + ax.set(xscale='log', yscale='log', zscale='log') + fig.canvas.draw() + + point, pane_idx = ax._calc_coord(0.5, 0.5) + # Pane coordinate should match axis limit (y-pane at max) + assert pane_idx == 1 + assert point[pane_idx] == pytest.approx(ax.get_ylim()[1]) diff --git a/lib/mpl_toolkits/mplot3d/tests/test_legend3d.py b/lib/mpl_toolkits/mplot3d/tests/test_legend3d.py index 0935bbe7f6b0..a46c958222d8 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_legend3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_legend3d.py @@ -1,4 +1,4 @@ -import platform +import sys import numpy as np @@ -28,7 +28,7 @@ def test_legend_bar(): @image_comparison(['fancy.png'], remove_text=True, style='mpl20', - tol=0.011 if platform.machine() == 'arm64' else 0) + tol=0.01 if sys.platform == 'darwin' else 0) def test_fancy(): fig, ax = plt.subplots(subplot_kw=dict(projection='3d')) ax.plot(np.arange(10), np.full(10, 5), np.full(10, 5), 'o--', label='line') @@ -47,9 +47,9 @@ def test_linecollection_scaled_dashes(): lc3 = art3d.Line3DCollection(lines3, linestyles=":", lw=.5) fig, ax = plt.subplots(subplot_kw=dict(projection='3d')) - ax.add_collection(lc1) - ax.add_collection(lc2) - ax.add_collection(lc3) + ax.add_collection(lc1, autolim="_datalim_only") + ax.add_collection(lc2, autolim="_datalim_only") + ax.add_collection(lc3, autolim="_datalim_only") leg = ax.legend([lc1, lc2, lc3], ['line1', 'line2', 'line 3']) h1, h2, h3 = leg.legend_handles @@ -90,8 +90,7 @@ def test_contourf_legend_elements(): cs = ax.contourf(x, y, h, levels=[10, 30, 50], colors=['#FFFF00', '#FF00FF', '#00FFFF'], extend='both') - cs.cmap.set_over('red') - cs.cmap.set_under('blue') + cs.cmap = cs.cmap.with_extremes(over='red', under='blue') cs.changed() artists, labels = cs.legend_elements() assert labels == ['$x \\leq -1e+250s$', diff --git a/meson.build b/meson.build index c022becfd9d9..820335e2c9d8 100644 --- a/meson.build +++ b/meson.build @@ -1,21 +1,30 @@ project( 'matplotlib', 'c', 'cpp', - version: run_command(find_program('python3'), '-m', 'setuptools_scm', check: true).stdout().strip(), + version: run_command( + # Also keep version in sync with pyproject.toml. + find_program('python3', 'python', version: '>= 3.11'), + '-m', 'setuptools_scm', check: true).stdout().strip(), # qt_editor backend is MIT # ResizeObserver at end of lib/matplotlib/backends/web_backend/js/mpl.js is CC0 - # Carlogo, STIX and Computer Modern is OFL + # Carlogo, STIX, Computer Modern, and Last Resort are OFL # DejaVu is Bitstream Vera and Public Domain license: 'PSF-2.0 AND MIT AND CC0-1.0 AND OFL-1.1 AND Bitstream-Vera AND Public-Domain', license_files: [ 'LICENSE/LICENSE', + 'extern/agg24-svn/src/copying', 'LICENSE/LICENSE_AMSFONTS', 'LICENSE/LICENSE_BAKOMA', 'LICENSE/LICENSE_CARLOGO', 'LICENSE/LICENSE_COLORBREWER', 'LICENSE/LICENSE_COURIERTEN', + 'LICENSE/LICENSE_FREETYPE', + 'LICENSE/LICENSE_HARFBUZZ', 'LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER', + 'LICENSE/LICENSE_LAST_RESORT_FONT', + 'LICENSE/LICENSE_LIBRAQM', 'LICENSE/LICENSE_QT4_EDITOR', + 'LICENSE/LICENSE_SHEENBIDI', 'LICENSE/LICENSE_SOLARIZED', 'LICENSE/LICENSE_STIX', 'LICENSE/LICENSE_YORICK', @@ -28,6 +37,10 @@ project( ], ) +# Enable bug fixes in Agg +add_project_arguments('-DMPL_FIX_AGG_IMAGE_FILTER_LUT_BUGS', language : 'cpp') +add_project_arguments('-DMPL_FIX_AGG_INTERPOLATION_ENDPOINT_BUG', language : 'cpp') + cc = meson.get_compiler('c') cpp = meson.get_compiler('cpp') @@ -36,7 +49,7 @@ py_mod = import('python') py3 = py_mod.find_installation(pure: false) py3_dep = py3.dependency() -pybind11_dep = dependency('pybind11', version: '>=2.6') +pybind11_dep = dependency('pybind11', version: '>=2.13.2') subdir('extern') subdir('src') diff --git a/meson.options b/meson.options index d21cbedb9bb9..7e03ff405f85 100644 --- a/meson.options +++ b/meson.options @@ -7,6 +7,8 @@ # FreeType on AIX. option('system-freetype', type: 'boolean', value: false, description: 'Build against system version of FreeType') +option('system-libraqm', type: 'boolean', value: false, + description: 'Build against system version of libraqm') option('system-qhull', type: 'boolean', value: false, description: 'Build against system version of Qhull') diff --git a/pyproject.toml b/pyproject.toml index a9fb7df68450..2d1308b63347 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,48 +16,33 @@ classifiers=[ "License :: OSI Approved :: Python Software Foundation License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering :: Visualization", ] # When updating the list of dependencies, add an api_changes/development # entry and also update the following places: +# - the `build` dependency group below # - lib/matplotlib/__init__.py (matplotlib._check_versions()) -# - requirements/testing/minver.txt +# - ci/minver-requirements.txt # - doc/devel/dependencies.rst -# - .github/workflows/tests.yml # - environment.yml dependencies = [ "contourpy >= 1.0.1", "cycler >= 0.10", "fonttools >= 4.22.0", "kiwisolver >= 1.3.1", - "numpy >= 1.23", + "numpy >= 1.25", "packaging >= 20.0", - "pillow >= 8", - "pyparsing >= 2.3.1", + "pillow >= 9", + "pyparsing >= 3", "python-dateutil >= 2.7", - "importlib-resources >= 3.2.0; python_version < '3.10'", -] -requires-python = ">=3.9" - -[project.optional-dependencies] -# Should be a copy of the build dependencies below. -dev = [ - "meson-python>=0.13.1", - "numpy>=1.25", - "pybind11>=2.6", - "setuptools_scm>=7", - # Not required by us but setuptools_scm without a version, cso _if_ - # installed, then setuptools_scm 8 requires at least this version. - # Unfortunately, we can't do a sort of minimum-if-instaled dependency, so - # we need to keep this for now until setuptools_scm _fully_ drops - # setuptools. - "setuptools>=64", ] +# Also keep in sync with find_program of meson.build. +requires-python = ">=3.11" [project.urls] "Homepage" = "https://matplotlib.org" @@ -70,23 +55,110 @@ dev = [ [build-system] build-backend = "mesonpy" -# Also keep in sync with optional dependencies above. +# Also keep in sync with dependency groups below. requires = [ - "meson-python>=0.13.1", - "pybind11>=2.6", - "setuptools_scm>=7", + # meson-python 0.17.x breaks symlinks in sdists. You can remove this pin if + # you really need it and aren't using an sdist. + "meson-python>=0.13.2,!=0.17.*", + "pybind11>=2.13.2,!=2.13.3", + "setuptools_scm>=7,<10", +] + +[dependency-groups] +build = [ + # Should be the same as `[project] dependencies` above. + "contourpy >= 1.0.1", + "cycler >= 0.10", + "fonttools >= 4.22.0", + "kiwisolver >= 1.3.1", + "numpy >= 1.25", + "packaging >= 20.0", + "pillow >= 9", + "pyparsing >= 3", + "python-dateutil >= 2.7", + + # Should be the same as `[build-system] requires` above. + "meson-python>=0.13.1,!=0.17.*", + "pybind11>=2.13.2,!=2.13.3", + "setuptools_scm>=7,<10", + # Not required by us but setuptools_scm without a version, so _if_ + # installed, then setuptools_scm 8 requires at least this version. + # Unfortunately, we can't do a sort of minimum-if-installed dependency, so + # we need to keep this for now until setuptools_scm _fully_ drops + # setuptools. + "setuptools>=64", +] +dev = [ + {include-group = "build"}, + {include-group = "doc"}, + {include-group = "test"}, + {include-group = "test-extra"}, + "pre-commit", +] +# Requirements for building docs +# +# You will first need a matching Matplotlib installation +# e.g (from the Matplotlib root directory) +# pip install --group build --no-build-isolation --editable . +# +# Install the documentation requirements with: +# pip install --group doc +# +doc = [ + "sphinx>=5.1.0,!=6.1.2", + "colorspacious", + "ipython", + "ipywidgets", + "ipykernel", + "numpydoc>=1.0", + "mpl-sphinx-theme~=3.10.0", + "pyyaml", + "PyStemmer", + "sphinxcontrib-svg2pdfconverter>=1.1.0", + "sphinxcontrib-video>=0.2.1", + "sphinx-copybutton", + "sphinx-design", + "sphinx-gallery[parallel]>=0.17.0", + "sphinx-tags>=0.4.0", +] - # Comments on numpy build requirement range: - # - # 1. >=2.0.x is the numpy requirement for wheel builds for distribution - # on PyPI - building against 2.x yields wheels that are also - # ABI-compatible with numpy 1.x at runtime. - # 2. Note that building against numpy 1.x works fine too - users and - # redistributors can do this by installing the numpy version they like - # and disabling build isolation. - # 3. The <2.3 upper bound is for matching the numpy deprecation policy, - # it should not be loosened. - "numpy>=2.0.0rc1,<2.3", +# pip requirements for all the CI builds +test = [ + "black<27", + "certifi", + "coverage!=6.3", + "psutil; sys_platform != 'cygwin'", + "pytest!=4.6.0,!=5.4.0,!=8.1.0", + "pytest-cov", + "pytest-rerunfailures!=16.0", + "pytest-timeout", + "pytest-xdist", + "pytest-xvfb", + "tornado", +] + +# Extra pip requirements +test-extra = [ + "ipykernel", + # jupyter/nbconvert#1970 for the 7.3 series exclusions + "nbconvert[execute]!=6.0.0,!=6.0.1,!=7.3.0,!=7.3.1", + "nbformat!=5.0.0,!=5.0.1", + "pandas!=0.25.0", + "pikepdf", + "pytz", + "xarray", +] + +# Extra pip requirements for the GitHub Actions mypy build +typing = [ + "mypy>=1.9", + "typing-extensions>=4.6", + # Extra stubs distributed separately from the main pypi package + "pandas-stubs", + "types-pillow", + "types-python-dateutil", + "types-psutil", + "sphinx", ] [tool.meson-python.args] @@ -103,17 +175,18 @@ known_pydata = "numpy, matplotlib.pyplot" known_firstparty = "matplotlib,mpl_toolkits" sections = "FUTURE,STDLIB,THIRDPARTY,PYDATA,FIRSTPARTY,LOCALFOLDER" force_sort_within_sections = true +line_length = 88 [tool.ruff] -exclude = [ - ".git", +extend-exclude = [ "build", "doc/gallery", "doc/tutorials", "tools/gh_api.py", - ".tox", - ".eggs", ] +line-length = 88 + +[tool.ruff.lint] ignore = [ "D100", "D101", @@ -122,55 +195,70 @@ ignore = [ "D104", "D105", "D106", + "D107", "D200", "D202", + "D203", "D204", "D205", + "D212", "D301", "D400", "D401", + "D402", "D403", "D404", + "D413", + "D415", + "D417", + "E266", + "E305", + "E306", + "E721", "E741", "F841", ] -line-length = 88 +preview = true +explicit-preview-rules = true select = [ "D", "E", "F", "W", + "UP035", + # The following error codes require the preview mode to be enabled. + "E201", + "E202", + "E203", + "E221", + "E251", + "E261", + "E272", + "E302", + "E703", ] -# The following error codes are not supported by ruff v0.0.240 +# The following error codes are not supported by ruff v0.2.0 # They are planned and should be selected once implemented # even if they are deselected by default. # These are primarily whitespace/corrected by autoformatters (which we don't use). # See https://github.com/charliermarsh/ruff/issues/2402 for status on implementation external = [ "E122", - "E201", - "E202", - "E203", - "E221", - "E251", - "E261", - "E272", - "E302", - "E703", ] -target-version = "py39" - -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "numpy" -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] +"*.pyi" = ["E501"] +"*.ipynb" = ["E402"] "doc/conf.py" = ["E402"] -"galleries/examples/animation/frame_grabbing_sgskip.py" = ["E402"] +"galleries/examples/images_contours_and_fields/tricontour_demo.py" = ["E201"] +"galleries/examples/images_contours_and_fields/tripcolor_demo.py" = ["E201"] +"galleries/examples/images_contours_and_fields/triplot_demo.py" = ["E201"] "galleries/examples/lines_bars_and_markers/marker_reference.py" = ["E402"] -"galleries/examples/misc/print_stdout_sgskip.py" = ["E402"] -"galleries/examples/style_sheets/bmh.py" = ["E501"] +"galleries/examples/misc/table_demo.py" = ["E201"] "galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py" = ["E402"] "galleries/examples/text_labels_and_annotations/custom_legends.py" = ["E402"] "galleries/examples/ticks/date_concise_formatter.py" = ["E402"] @@ -184,35 +272,32 @@ convention = "numpy" "galleries/examples/user_interfaces/mpl_with_glade3_sgskip.py" = ["E402"] "galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py" = ["E402"] "galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py" = ["E402"] -"galleries/examples/userdemo/pgf_preamble_sgskip.py" = ["E402"] -"lib/matplotlib/__init__.py" = ["E402", "F401"] -"lib/matplotlib/_animation_data.py" = ["E501"] -"lib/matplotlib/_api/__init__.py" = ["F401"] -"lib/matplotlib/axes/__init__.py" = ["F401", "F403"] +"lib/matplotlib/__init__.py" = ["F822"] +"lib/matplotlib/_cm.py" = ["E202", "E203", "E302"] +"lib/matplotlib/_mathtext.py" = ["E221"] +"lib/matplotlib/_mathtext_data.py" = ["E203"] "lib/matplotlib/backends/backend_template.py" = ["F401"] -"lib/matplotlib/font_manager.py" = ["E501"] -"lib/matplotlib/image.py" = ["F401", "F403"] "lib/matplotlib/pylab.py" = ["F401", "F403"] -"lib/matplotlib/pyplot.py" = ["F401", "F811"] +"lib/matplotlib/pyplot.py" = ["F811"] "lib/matplotlib/tests/test_mathtext.py" = ["E501"] -"lib/mpl_toolkits/axisartist/__init__.py" = ["F401"] -"lib/pylab.py" = ["F401", "F403"] +"lib/matplotlib/transforms.py" = ["E201"] +"lib/matplotlib/tri/_triinterpolate.py" = ["E201", "E221"] +"lib/mpl_toolkits/axes_grid1/axes_size.py" = ["E272"] +"lib/mpl_toolkits/axisartist/angle_helper.py" = ["E221"] +"lib/mpl_toolkits/mplot3d/proj3d.py" = ["E201"] -"galleries/users_explain/artists/paths.py" = ["E402"] +"galleries/users_explain/quick_start.py" = ["E402"] "galleries/users_explain/artists/patheffects_guide.py" = ["E402"] -"galleries/users_explain/artists/transforms_tutorial.py" = ["E402", "E501"] -"galleries/users_explain/colors/colormaps.py" = ["E501"] +"galleries/users_explain/artists/transforms_tutorial.py" = ["E402"] "galleries/users_explain/colors/colors.py" = ["E402"] "galleries/tutorials/artists.py" = ["E402"] "galleries/users_explain/axes/constrainedlayout_guide.py" = ["E402"] "galleries/users_explain/axes/legend_guide.py" = ["E402"] "galleries/users_explain/axes/tight_layout_guide.py" = ["E402"] "galleries/users_explain/animations/animations.py" = ["E501"] -"galleries/tutorials/images.py" = ["E501"] "galleries/tutorials/pyplot.py" = ["E402", "E501"] "galleries/users_explain/text/annotations.py" = ["E402", "E501"] -"galleries/users_explain/text/mathtext.py" = ["E501"] "galleries/users_explain/text/text_intro.py" = ["E402"] "galleries/users_explain/text/text_props.py" = ["E501"] @@ -223,24 +308,20 @@ enable_error_code = [ "redundant-expr", "truthy-bool", ] -enable_incomplete_feature = [ - "Unpack", -] exclude = [ #stubtest - ".*/matplotlib/(sphinxext|backends|testing/jpl_units)", + ".*/matplotlib/(sphinxext|backends|pylab|testing/jpl_units)", #mypy precommit "galleries/", "doc/", - "lib/matplotlib/backends/", - "lib/matplotlib/sphinxext", - "lib/matplotlib/testing/jpl_units", "lib/mpl_toolkits/", #removing tests causes errors in backends "lib/matplotlib/tests/", # tinypages is used for testing the sphinx ext, # stubtest will import and run, opening a figure if not excluded - ".*/tinypages" + ".*/tinypages", + # pylab's numpy wildcard imports cause re-def failures since numpy 2.2 + "lib/matplotlib/pylab.py", ] files = [ "lib/matplotlib", @@ -259,6 +340,7 @@ ignore_directives = [ # sphinxext.redirect_from "redirect-from", # sphinx-design + "card", "dropdown", "grid", "tab-set", @@ -279,15 +361,17 @@ ignore_directives = [ "ifconfig", # sphinx.ext.inheritance_diagram "inheritance-diagram", + # sphinx-tags + "tags", # include directive is causing attribute errors "include" ] ignore_roles = [ - # sphinxext.custom_roles - "rc", # matplotlib.sphinxext.mathmpl "mathmpl", "math-stix", + # matplotlib.sphinxext.roles + "rc", # sphinxext.github "ghissue", "ghpull", @@ -321,3 +405,26 @@ testpaths = ["lib"] addopts = [ "--import-mode=importlib", ] + +[tool.cibuildwheel.pyodide] +test-requires = "pytest" +test-command = [ + # Wheels are built without test images, so copy them into the testing directory. + "basedir=$(python -c 'import pathlib, matplotlib; print(pathlib.Path(matplotlib.__file__).parent.parent)')", + "cp -a {package}/lib/matplotlib/tests/data $basedir/matplotlib/tests/", + """ + for subdir in matplotlib mpl_toolkits/axes_grid1 mpl_toolkits/axisartist mpl_toolkits/mplot3d; do + cp -a {package}/lib/${subdir}/tests/baseline_images $basedir/${subdir}/tests/ + done""", + # Test installed, not repository, copy as we aren't using an editable install. + """\ + pytest -p no:cacheprovider --pyargs \ + matplotlib mpl_toolkits.axes_grid1 mpl_toolkits.axisartist mpl_toolkits.mplot3d \ + -k 'not test_complex_shaping'""", +] +[tool.cibuildwheel.pyodide.environment] +# Exceptions are needed for pybind11: +# https://github.com/pybind/pybind11/pull/5298 +CFLAGS = "-fexceptions" +CXXFLAGS = "-fexceptions" +LDFLAGS = "-fexceptions" diff --git a/requirements/dev/build-requirements.txt b/requirements/dev/build-requirements.txt deleted file mode 100644 index 1b22d228e217..000000000000 --- a/requirements/dev/build-requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pybind11 -meson-python -numpy -setuptools-scm diff --git a/requirements/dev/dev-requirements.txt b/requirements/dev/dev-requirements.txt deleted file mode 100644 index e5cbc1091bb2..000000000000 --- a/requirements/dev/dev-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ --r build-requirements.txt --r ../doc/doc-requirements.txt --r ../testing/all.txt --r ../testing/extra.txt --r ../testing/flake8.txt diff --git a/requirements/doc/doc-requirements.txt b/requirements/doc/doc-requirements.txt deleted file mode 100644 index 87bc483b15c0..000000000000 --- a/requirements/doc/doc-requirements.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Requirements for building docs -# -# You will first need a matching Matplotlib installation -# e.g (from the Matplotlib root directory) -# pip install --no-build-isolation --editable .[dev] -# -# Install the documentation requirements with: -# pip install -r requirements/doc/doc-requirements.txt -# -sphinx>=3.0.0,!=6.1.2 -colorspacious -ipython -ipywidgets -ipykernel -numpydoc>=1.0 -packaging>=20 -pydata-sphinx-theme~=0.15.0 -mpl-sphinx-theme~=3.9.0 -pyyaml -sphinxcontrib-svg2pdfconverter>=1.1.0 -sphinx-copybutton -sphinx-design -sphinx-gallery>=0.12.0 -sphinx-tags>=0.3.0 diff --git a/requirements/testing/all.txt b/requirements/testing/all.txt deleted file mode 100644 index e386924a9b67..000000000000 --- a/requirements/testing/all.txt +++ /dev/null @@ -1,13 +0,0 @@ -# pip requirements for all the CI builds - -black<24 -certifi -coverage!=6.3 -psutil -pytest!=4.6.0,!=5.4.0,!=8.1.0 -pytest-cov -pytest-rerunfailures -pytest-timeout -pytest-xdist -pytest-xvfb -tornado diff --git a/requirements/testing/extra.txt b/requirements/testing/extra.txt deleted file mode 100644 index b3e9009b561c..000000000000 --- a/requirements/testing/extra.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Extra pip requirements for the Python 3.9+ builds - ---prefer-binary -ipykernel -# jupyter/nbconvert#1970 for the 7.3 series exclusions -nbconvert[execute]!=6.0.0,!=6.0.1,!=7.3.0,!=7.3.1 -nbformat!=5.0.0,!=5.0.1 -pandas!=0.25.0 -pikepdf -pytz -pywin32; sys.platform == 'win32' -xarray diff --git a/requirements/testing/flake8.txt b/requirements/testing/flake8.txt deleted file mode 100644 index a4d006b8551e..000000000000 --- a/requirements/testing/flake8.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Extra pip requirements for the GitHub Actions flake8 build - -flake8>=3.8 -# versions less than 5.1.0 raise on some interp'd docstrings -pydocstyle>=5.1.0 -# 1.4.0 adds docstring-convention=all -flake8-docstrings>=1.4.0 -# fix bug where flake8 aborts checking on syntax error -flake8-force diff --git a/requirements/testing/minver.txt b/requirements/testing/minver.txt deleted file mode 100644 index 1a95367eff14..000000000000 --- a/requirements/testing/minver.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Extra pip requirements for the minimum-version CI run - -contourpy==1.0.1 -cycler==0.10 -fonttools==4.22.0 -importlib-resources==3.2.0 -kiwisolver==1.3.1 -meson-python==0.13.1 -meson==1.1.0 -numpy==1.23.0 -packaging==20.0 -pillow==8.0.0 -pyparsing==2.3.1 -pytest==7.0.0 -python-dateutil==2.7 diff --git a/requirements/testing/mypy.txt b/requirements/testing/mypy.txt deleted file mode 100644 index a5ca15cfbdad..000000000000 --- a/requirements/testing/mypy.txt +++ /dev/null @@ -1,29 +0,0 @@ -# Extra pip requirements for the GitHub Actions mypy build - -mypy==1.1.1 -typing-extensions>=4.1,<5 - -# Extra stubs distributed separately from the main pypi package -pandas-stubs -types-pillow -types-python-dateutil -types-psutil - -sphinx - -# Default requirements, included here because mpl itself does not -# need to be installed for mypy to run, but deps are needed -# and pip has no --deps-only install command -contourpy>=1.0.1 -cycler>=0.10 -fonttools>=4.22.0 -kiwisolver>=1.3.1 -numpy>=1.19 -packaging>=20.0 -pillow>=8 -pyparsing>=2.3.1 -python-dateutil>=2.7 -setuptools_scm>=7 -setuptools>=64 - -importlib-resources>=3.2.0 ; python_version < "3.10" diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index ce88f504dc1e..aba284719df0 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -10,9 +10,9 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi) height(height), dpi(dpi), NUMBYTES((size_t)width * (size_t)height * 4), - pixBuffer(NULL), + pixBuffer(nullptr), renderingBuffer(), - alphaBuffer(NULL), + alphaBuffer(nullptr), alphaMaskRenderingBuffer(), alphaMask(alphaMaskRenderingBuffer), pixfmtAlphaMask(alphaMaskRenderingBuffer), @@ -26,9 +26,19 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi) rendererAA(), rendererBin(), theRasterizer(32768), - lastclippath(NULL), + lastclippath(nullptr), _fill_color(agg::rgba(1, 1, 1, 0)) { + if (dpi <= 0.0) { + throw std::range_error("dpi must be positive"); + } + + if (width >= 1 << 23 || height >= 1 << 23) { + throw std::range_error( + "Image size of " + std::to_string(width) + "x" + std::to_string(height) + + " pixels is too large. It must be less than 2^23 in each direction."); + } + unsigned stride(width * 4); pixBuffer = new agg::int8u[NUMBYTES]; @@ -65,7 +75,7 @@ BufferRegion *RendererAgg::copy_from_bbox(agg::rect_d in_rect) agg::rect_i rect( (int)in_rect.x1, height - (int)in_rect.y2, (int)in_rect.x2, height - (int)in_rect.y1); - BufferRegion *reg = NULL; + BufferRegion *reg = nullptr; reg = new BufferRegion(rect); agg::rendering_buffer rbuf; @@ -80,21 +90,21 @@ BufferRegion *RendererAgg::copy_from_bbox(agg::rect_d in_rect) void RendererAgg::restore_region(BufferRegion ®ion) { - if (region.get_data() == NULL) { + if (region.get_data() == nullptr) { throw std::runtime_error("Cannot restore_region from NULL data"); } agg::rendering_buffer rbuf; rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride()); - rendererBase.copy_from(rbuf, 0, region.get_rect().x1, region.get_rect().y1); + rendererBase.copy_from(rbuf, nullptr, region.get_rect().x1, region.get_rect().y1); } // Restore the part of the saved region with offsets void RendererAgg::restore_region(BufferRegion ®ion, int xx1, int yy1, int xx2, int yy2, int x, int y ) { - if (region.get_data() == NULL) { + if (region.get_data() == nullptr) { throw std::runtime_error("Cannot restore_region from NULL data"); } @@ -112,15 +122,6 @@ bool RendererAgg::render_clippath(mpl::PathIterator &clippath, const agg::trans_affine &clippath_trans, e_snap_mode snap_mode) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removed_t; - /* Unlike normal Paths, the clip path cannot be clipped to the Figure bbox, - * because it needs to remain a complete closed path, so there is no - * PathClipper step. */ - typedef PathSnapper snapped_t; - typedef PathSimplifier simplify_t; - typedef agg::conv_curve curve_t; - bool has_clippath = (clippath.total_vertices() != 0); if (has_clippath && @@ -131,13 +132,19 @@ bool RendererAgg::render_clippath(mpl::PathIterator &clippath, trans *= agg::trans_affine_translation(0.0, (double)height); rendererBaseAlphaMask.clear(agg::gray8(0, 0)); - transformed_path_t transformed_clippath(clippath, trans); - nan_removed_t nan_removed_clippath(transformed_clippath, true, clippath.has_codes()); - snapped_t snapped_clippath(nan_removed_clippath, snap_mode, clippath.total_vertices(), 0.0); - simplify_t simplified_clippath(snapped_clippath, - clippath.should_simplify() && !clippath.has_codes(), - clippath.simplify_threshold()); - curve_t curved_clippath(simplified_clippath); + auto transformed_clippath = agg::conv_transform{clippath, trans}; + auto nan_removed_clippath = PathNanRemover{ + transformed_clippath, true, clippath.has_codes()}; + // Unlike normal Paths, the clip path cannot be clipped to the Figure + // bbox, because it needs to remain a complete closed path, so there is + // no PathClipper step after nan-removal. + auto snapped_clippath = PathSnapper{ + nan_removed_clippath, snap_mode, clippath.total_vertices(), 0.0}; + auto simplified_clippath = PathSimplifier{ + snapped_clippath, + clippath.should_simplify() && !clippath.has_codes(), + clippath.simplify_threshold()}; + auto curved_clippath = agg::conv_curve{simplified_clippath}; theRasterizer.add_path(curved_clippath); rendererAlphaMask.color(agg::gray8(255, 255)); agg::render_scanlines(theRasterizer, scanlineAlphaMask, rendererAlphaMask); diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 470d459de341..a4e2aa10040d 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -6,8 +6,13 @@ #ifndef MPL_BACKEND_AGG_H #define MPL_BACKEND_AGG_H +#include + #include #include +#include +#include +#include #include "agg_alpha_mask_u8.h" #include "agg_conv_curve.h" @@ -40,6 +45,8 @@ #include "array.h" #include "agg_workaround.h" +namespace py = pybind11; + /**********************************************************************/ // a helper class to pass agg::buffer objects around. @@ -60,6 +67,10 @@ class BufferRegion delete[] data; }; + // prevent copying + BufferRegion(const BufferRegion &) = delete; + BufferRegion &operator=(const BufferRegion &) = delete; + agg::int8u *get_data() { return data; @@ -91,15 +102,8 @@ class BufferRegion int width; int height; int stride; - - private: - // prevent copying - BufferRegion(const BufferRegion &); - BufferRegion &operator=(const BufferRegion &); }; -#define MARKER_CACHE_SIZE 512 - // the renderer class RendererAgg { @@ -112,17 +116,14 @@ class RendererAgg typedef agg::renderer_scanline_bin_solid renderer_bin; typedef agg::rasterizer_scanline_aa rasterizer; - typedef agg::scanline_p8 scanline_p8; - typedef agg::scanline_bin scanline_bin; + typedef agg::scanline32_p8 scanline_p8; + typedef agg::scanline32_bin scanline_bin; typedef agg::amask_no_clip_gray8 alpha_mask_type; - typedef agg::scanline_u8_am scanline_am; + typedef agg::scanline32_u8_am scanline_am; typedef agg::renderer_base renderer_base_alpha_mask_type; typedef agg::renderer_scanline_aa_solid renderer_alpha_mask_type; - /* TODO: Remove facepair_t */ - typedef std::pair facepair_t; - RendererAgg(unsigned int width, unsigned int height, double dpi); virtual ~RendererAgg(); @@ -173,7 +174,8 @@ class RendererAgg ColorArray &edgecolors, LineWidthArray &linewidths, DashesVector &linestyles, - AntialiasedArray &antialiaseds); + AntialiasedArray &antialiaseds, + ColorArray &hatchcolors); template void draw_quad_mesh(GCAgg &gc, @@ -244,7 +246,7 @@ class RendererAgg bool render_clippath(mpl::PathIterator &clippath, const agg::trans_affine &clippath_trans, e_snap_mode snap_mode); template - void _draw_path(PathIteratorType &path, bool has_clippath, const facepair_t &face, GCAgg &gc); + void _draw_path(PathIteratorType &path, bool has_clippath, const std::optional &face, GCAgg &gc); template void _draw_gouraud_triangle(PointArray &points, @@ -290,40 +293,32 @@ class RendererAgg template inline void -RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, GCAgg &gc) +RendererAgg::_draw_path(path_t &path, bool has_clippath, const std::optional &face, GCAgg &gc) { - typedef agg::conv_stroke stroke_t; - typedef agg::conv_dash dash_t; - typedef agg::conv_stroke stroke_dash_t; - typedef agg::pixfmt_amask_adaptor pixfmt_amask_type; - typedef agg::renderer_base amask_ren_type; - typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type; - typedef agg::renderer_scanline_bin_solid amask_bin_renderer_type; - // Render face - if (face.first) { + if (face) { theRasterizer.add_path(path); if (gc.isaa) { if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); - ren.color(face.second); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_aa_solid{r}; + ren.color(*face); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { - rendererAA.color(face.second); + rendererAA.color(*face); agg::render_scanlines(theRasterizer, slineP8, rendererAA); } } else { if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_bin_renderer_type ren(r); - ren.color(face.second); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_bin_solid{r}; + ren.color(*face); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { - rendererBin.color(face.second); + rendererBin.color(*face); agg::render_scanlines(theRasterizer, slineP8, rendererBin); } } @@ -337,18 +332,15 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, rendererBase.reset_clipping(true); // Create and transform the path - typedef agg::conv_transform hatch_path_trans_t; - typedef agg::conv_curve hatch_path_curve_t; - typedef agg::conv_stroke hatch_path_stroke_t; - mpl::PathIterator hatch_path(gc.hatchpath); agg::trans_affine hatch_trans; hatch_trans *= agg::trans_affine_scaling(1.0, -1.0); hatch_trans *= agg::trans_affine_translation(0.0, 1.0); - hatch_trans *= agg::trans_affine_scaling(hatch_size, hatch_size); - hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans); - hatch_path_curve_t hatch_path_curve(hatch_path_trans); - hatch_path_stroke_t hatch_path_stroke(hatch_path_curve); + hatch_trans *= agg::trans_affine_scaling(static_cast(hatch_size), + static_cast(hatch_size)); + auto hatch_path_trans = agg::conv_transform{hatch_path, hatch_trans}; + auto hatch_path_curve = agg::conv_curve{hatch_path_trans}; + auto hatch_path_stroke = agg::conv_stroke{hatch_path_curve}; hatch_path_stroke.width(points_to_pixels(gc.hatch_linewidth)); hatch_path_stroke.line_cap(agg::square_cap); @@ -372,18 +364,16 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, } // Transfer the hatch to the main image buffer - typedef agg::image_accessor_wrap img_source_type; - typedef agg::span_pattern_rgba span_gen_type; agg::span_allocator sa; - img_source_type img_src(hatch_img_pixf); - span_gen_type sg(img_src, 0, 0); + auto img_src = agg::image_accessor_wrap< + pixfmt, agg::wrap_mode_repeat_auto_pow2, agg::wrap_mode_repeat_auto_pow2>{ + hatch_img_pixf}; + auto sg = agg::span_pattern_rgba{img_src, 0, 0}; theRasterizer.add_path(path); if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type ren(pfa); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto ren = agg::renderer_base{pfa}; agg::render_scanlines_aa(theRasterizer, slineP8, ren, sa, sg); } else { agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg); @@ -397,16 +387,16 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth); } if (gc.dashes.size() == 0) { - stroke_t stroke(path); + auto stroke = agg::conv_stroke{path}; stroke.width(points_to_pixels(gc.linewidth)); stroke.line_cap(gc.cap); stroke.line_join(gc.join); stroke.miter_limit(points_to_pixels(gc.linewidth)); theRasterizer.add_path(stroke); } else { - dash_t dash(path); + auto dash = agg::conv_dash{path}; gc.dashes.dash_to_stroke(dash, dpi, gc.isaa); - stroke_dash_t stroke(dash); + auto stroke = agg::conv_stroke{dash}; stroke.line_cap(gc.cap); stroke.line_join(gc.join); stroke.width(linewidth); @@ -416,9 +406,9 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, if (gc.isaa) { if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_aa_solid{r}; ren.color(gc.color); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { @@ -427,9 +417,9 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, } } else { if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_bin_renderer_type ren(r); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_bin_solid{r}; ren.color(gc.color); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { @@ -444,15 +434,10 @@ template inline void RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, agg::rgba &color) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removed_t; - typedef PathClipper clipped_t; - typedef PathSnapper snapped_t; - typedef PathSimplifier simplify_t; - typedef agg::conv_curve curve_t; - typedef Sketch sketch_t; - - facepair_t face(color.a != 0.0, color); + std::optional face; + if (color.a != 0.0) { + face = color; + } theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); @@ -461,20 +446,22 @@ RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - bool clip = !face.first && !gc.has_hatchpath(); + bool clip = !face && !gc.has_hatchpath(); bool simplify = path.should_simplify() && clip; double snapping_linewidth = points_to_pixels(gc.linewidth); if (gc.color.a == 0.0) { snapping_linewidth = 0.0; } - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, path.has_codes()); - clipped_t clipped(nan_removed, clip, width, height); - snapped_t snapped(clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth); - simplify_t simplified(snapped, simplify, path.simplify_threshold()); - curve_t curve(simplified); - sketch_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, true, path.has_codes()}; + auto clipped = PathClipper(nan_removed, clip, width, height); + auto snapped = PathSnapper{ + clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth}; + auto simplified = PathSimplifier{snapped, simplify, path.simplify_threshold()}; + auto curve = agg::conv_curve{simplified}; + auto sketch = Sketch{ + curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness}; _draw_path(sketch, has_clippath, face, gc); } @@ -487,28 +474,19 @@ inline void RendererAgg::draw_markers(GCAgg &gc, agg::trans_affine &trans, agg::rgba color) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removed_t; - typedef PathSnapper snap_t; - typedef agg::conv_curve curve_t; - typedef agg::conv_stroke stroke_t; - typedef agg::pixfmt_amask_adaptor pixfmt_amask_type; - typedef agg::renderer_base amask_ren_type; - typedef agg::renderer_scanline_aa_solid amask_aa_renderer_type; - // Deal with the difference in y-axis direction marker_trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.5, (double)height + 0.5); - transformed_path_t marker_path_transformed(marker_path, marker_trans); - nan_removed_t marker_path_nan_removed(marker_path_transformed, true, marker_path.has_codes()); - snap_t marker_path_snapped(marker_path_nan_removed, - gc.snap_mode, - marker_path.total_vertices(), - points_to_pixels(gc.linewidth)); - curve_t marker_path_curve(marker_path_snapped); + auto marker_path_transformed = agg::conv_transform{marker_path, marker_trans}; + auto marker_path_nan_removed = PathNanRemover{ + marker_path_transformed, true, marker_path.has_codes()}; + auto marker_path_snapped = PathSnapper{ + marker_path_nan_removed, + gc.snap_mode, marker_path.total_vertices(), points_to_pixels(gc.linewidth)}; + auto marker_path_curve = agg::conv_curve{marker_path_snapped}; if (!marker_path_snapped.is_snapping()) { // If the path snapper isn't in effect, at least make sure the marker @@ -517,13 +495,17 @@ inline void RendererAgg::draw_markers(GCAgg &gc, marker_trans *= agg::trans_affine_translation(0.5, 0.5); } - transformed_path_t path_transformed(path, trans); - nan_removed_t path_nan_removed(path_transformed, false, false); - snap_t path_snapped(path_nan_removed, SNAP_FALSE, path.total_vertices(), 0.0); - curve_t path_curve(path_snapped); + auto path_transformed = agg::conv_transform{path, trans}; + auto path_nan_removed = PathNanRemover{path_transformed, false, false}; + auto path_snapped = PathSnapper{ + path_nan_removed, SNAP_FALSE, path.total_vertices(), 0.0}; + auto path_curve = agg::conv_curve{path_snapped}; path_curve.rewind(0); - facepair_t face(color.a != 0.0, color); + std::optional face; + if (color.a != 0.0) { + face = color; + } // maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; @@ -532,29 +514,21 @@ inline void RendererAgg::draw_markers(GCAgg &gc, rendererBase.reset_clipping(true); agg::rect_i marker_size(0x7FFFFFFF, 0x7FFFFFFF, -0x7FFFFFFF, -0x7FFFFFFF); - agg::int8u staticFillCache[MARKER_CACHE_SIZE]; - agg::int8u staticStrokeCache[MARKER_CACHE_SIZE]; - agg::int8u *fillCache = staticFillCache; - agg::int8u *strokeCache = staticStrokeCache; - try { - unsigned fillSize = 0; - if (face.first) { + std::vector fillBuffer; + if (face) { theRasterizer.add_path(marker_path_curve); agg::render_scanlines(theRasterizer, slineP8, scanlines); - fillSize = scanlines.byte_size(); - if (fillSize >= MARKER_CACHE_SIZE) { - fillCache = new agg::int8u[fillSize]; - } - scanlines.serialize(fillCache); + fillBuffer.resize(scanlines.byte_size()); + scanlines.serialize(fillBuffer.data()); marker_size = agg::rect_i(scanlines.min_x(), scanlines.min_y(), scanlines.max_x(), scanlines.max_y()); } - stroke_t stroke(marker_path_curve); + auto stroke = agg::conv_stroke{marker_path_curve}; stroke.width(points_to_pixels(gc.linewidth)); stroke.line_cap(gc.cap); stroke.line_join(gc.join); @@ -562,11 +536,8 @@ inline void RendererAgg::draw_markers(GCAgg &gc, theRasterizer.reset(); theRasterizer.add_path(stroke); agg::render_scanlines(theRasterizer, slineP8, scanlines); - unsigned strokeSize = scanlines.byte_size(); - if (strokeSize >= MARKER_CACHE_SIZE) { - strokeCache = new agg::int8u[strokeSize]; - } - scanlines.serialize(strokeCache); + std::vector strokeBuffer(scanlines.byte_size()); + scanlines.serialize(strokeBuffer.data()); marker_size = agg::rect_i(std::min(marker_size.x1, scanlines.min_x()), std::min(marker_size.y1, scanlines.min_y()), std::max(marker_size.x2, scanlines.max_x()), @@ -606,17 +577,17 @@ inline void RendererAgg::draw_markers(GCAgg &gc, continue; } - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_aa_solid{r}; - if (face.first) { - ren.color(face.second); - sa.init(fillCache, fillSize, x, y); + if (face) { + ren.color(*face); + sa.init(fillBuffer.data(), fillBuffer.size(), x, y); agg::render_scanlines(sa, sl, ren); } ren.color(gc.color); - sa.init(strokeCache, strokeSize, x, y); + sa.init(strokeBuffer.data(), strokeBuffer.size(), x, y); agg::render_scanlines(sa, sl, ren); } } else { @@ -638,34 +609,25 @@ inline void RendererAgg::draw_markers(GCAgg &gc, continue; } - if (face.first) { - rendererAA.color(face.second); - sa.init(fillCache, fillSize, x, y); + if (face) { + rendererAA.color(*face); + sa.init(fillBuffer.data(), fillBuffer.size(), x, y); agg::render_scanlines(sa, sl, rendererAA); } rendererAA.color(gc.color); - sa.init(strokeCache, strokeSize, x, y); + sa.init(strokeBuffer.data(), strokeBuffer.size(), x, y); agg::render_scanlines(sa, sl, rendererAA); } } } catch (...) { - if (fillCache != staticFillCache) - delete[] fillCache; - if (strokeCache != staticStrokeCache) - delete[] strokeCache; theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); throw; } - if (fillCache != staticFillCache) - delete[] fillCache; - if (strokeCache != staticStrokeCache) - delete[] strokeCache; - theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); } @@ -716,34 +678,29 @@ class font_to_rgba template inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, int y, double angle) { - typedef agg::span_allocator color_span_alloc_type; - typedef agg::span_interpolator_linear<> interpolator_type; - typedef agg::image_accessor_clip image_accessor_type; - typedef agg::span_image_filter_gray image_span_gen_type; - typedef font_to_rgba span_gen_type; - typedef agg::renderer_scanline_aa - renderer_type; - theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); if (angle != 0.0) { agg::rendering_buffer srcbuf( - image.data(), (unsigned)image.shape(1), + image.mutable_data(0, 0), (unsigned)image.shape(1), (unsigned)image.shape(0), (unsigned)image.shape(1)); agg::pixfmt_gray8 pixf_img(srcbuf); set_clipbox(gc.cliprect, theRasterizer); + auto image_height = static_cast(image.shape(0)), + image_width = static_cast(image.shape(1)); + agg::trans_affine mtx; - mtx *= agg::trans_affine_translation(0, -image.shape(0)); + mtx *= agg::trans_affine_translation(0, -image_height); mtx *= agg::trans_affine_rotation(-angle * (agg::pi / 180.0)); mtx *= agg::trans_affine_translation(x, y); agg::path_storage rect; rect.move_to(0, 0); - rect.line_to(image.shape(1), 0); - rect.line_to(image.shape(1), image.shape(0)); - rect.line_to(0, image.shape(0)); + rect.line_to(image_width, 0); + rect.line_to(image_width, image_height); + rect.line_to(0, image_height); rect.line_to(0, 0); agg::conv_transform rect2(rect, mtx); @@ -752,12 +709,12 @@ inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, in agg::image_filter_lut filter; filter.calculate(agg::image_filter_spline36()); - interpolator_type interpolator(inv_mtx); - color_span_alloc_type sa; - image_accessor_type ia(pixf_img, agg::gray8(0)); - image_span_gen_type image_span_generator(ia, interpolator, filter); - span_gen_type output_span_generator(&image_span_generator, gc.color); - renderer_type ri(rendererBase, sa, output_span_generator); + auto interpolator = agg::span_interpolator_linear{inv_mtx}; + auto sa = agg::span_allocator{}; + auto ia = agg::image_accessor_clip{pixf_img, agg::gray8(0)}; + auto image_span_generator = agg::span_image_filter_gray{ia, interpolator, filter}; + auto output_span_generator = font_to_rgba{&image_span_generator, gc.color}; + auto ri = agg::renderer_scanline_aa{rendererBase, sa, output_span_generator}; theRasterizer.add_path(rect2); agg::render_scanlines(theRasterizer, slineP8, ri); @@ -828,20 +785,24 @@ inline void RendererAgg::draw_image(GCAgg &gc, bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); agg::rendering_buffer buffer; - buffer.attach( - image.data(), (unsigned)image.shape(1), (unsigned)image.shape(0), -(int)image.shape(1) * 4); + buffer.attach(image.mutable_data(0, 0, 0), + (unsigned)image.shape(1), (unsigned)image.shape(0), + -(int)image.shape(1) * 4); pixfmt pixf(buffer); if (has_clippath) { agg::trans_affine mtx; agg::path_storage rect; - mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.shape(0)))); + auto image_height = static_cast(image.shape(0)), + image_width = static_cast(image.shape(1)); + + mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image_height))); rect.move_to(0, 0); - rect.line_to(image.shape(1), 0); - rect.line_to(image.shape(1), image.shape(0)); - rect.line_to(0, image.shape(0)); + rect.line_to(image_width, 0); + rect.line_to(image_width, image_height); + rect.line_to(0, image_height); rect.line_to(0, 0); agg::conv_transform rect2(rect, mtx); @@ -849,35 +810,23 @@ inline void RendererAgg::draw_image(GCAgg &gc, agg::trans_affine inv_mtx(mtx); inv_mtx.invert(); - typedef agg::span_allocator color_span_alloc_type; - typedef agg::image_accessor_clip image_accessor_type; - typedef agg::span_interpolator_linear<> interpolator_type; - typedef agg::span_image_filter_rgba_nn - image_span_gen_type; - typedef agg::span_converter span_conv; - - color_span_alloc_type sa; - image_accessor_type ia(pixf, agg::rgba8(0, 0, 0, 0)); - interpolator_type interpolator(inv_mtx); - image_span_gen_type image_span_generator(ia, interpolator); - span_conv_alpha conv_alpha(alpha); - span_conv spans(image_span_generator, conv_alpha); - - typedef agg::pixfmt_amask_adaptor pixfmt_amask_type; - typedef agg::renderer_base amask_ren_type; - typedef agg::renderer_scanline_aa - renderer_type_alpha; - - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - renderer_type_alpha ri(r, sa, spans); + auto sa = agg::span_allocator{}; + auto ia = agg::image_accessor_clip{pixf, agg::rgba8(0, 0, 0, 0)}; + auto interpolator = agg::span_interpolator_linear{inv_mtx}; + auto image_span_generator = agg::span_image_filter_rgba_nn{ia, interpolator}; + auto conv_alpha = span_conv_alpha{alpha}; + auto spans = agg::span_converter{image_span_generator, conv_alpha}; + + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ri = agg::renderer_scanline_aa{r, sa, spans}; theRasterizer.add_path(rect2); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ri); } else { set_clipbox(gc.cliprect, rendererBase); rendererBase.blend_from( - pixf, 0, (int)x, (int)(height - (y + image.shape(0))), (agg::int8u)(alpha * 255)); + pixf, nullptr, (int)x, (int)(height - (y + image.shape(0))), (agg::int8u)(alpha * 255)); } rendererBase.reset_clipping(true); @@ -905,15 +854,9 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, DashesVector &linestyles, AntialiasedArray &antialiaseds, bool check_snap, - bool has_codes) + bool has_codes, + ColorArray &hatchcolors) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removed_t; - typedef PathClipper clipped_t; - typedef PathSnapper snapped_t; - typedef agg::conv_curve snapped_curve_t; - typedef agg::conv_curve curve_t; - size_t Npaths = path_generator.num_paths(); size_t Noffsets = safe_first_shape(offsets); size_t N = std::max(Npaths, Noffsets); @@ -921,11 +864,12 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, size_t Ntransforms = safe_first_shape(transforms); size_t Nfacecolors = safe_first_shape(facecolors); size_t Nedgecolors = safe_first_shape(edgecolors); + size_t Nhatchcolors = safe_first_shape(hatchcolors); size_t Nlinewidths = safe_first_shape(linewidths); size_t Nlinestyles = std::min(linestyles.size(), N); size_t Naa = safe_first_shape(antialiaseds); - if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) { + if ((Nfacecolors == 0 && Nedgecolors == 0 && Nhatchcolors == 0) || Npaths == 0) { return; } @@ -937,10 +881,9 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, // Set some defaults, assuming no face or edge gc.linewidth = 0.0; - facepair_t face; - face.first = Nfacecolors != 0; + std::optional face; agg::trans_affine trans; - bool do_clip = !face.first && !gc.has_hatchpath(); + bool do_clip = Nfacecolors == 0 && !gc.has_hatchpath(); for (int i = 0; i < (int)N; ++i) { typename PathGenerator::path_iterator path = path_generator(i); @@ -971,7 +914,7 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, if (Nfacecolors) { int ic = i % Nfacecolors; - face.second = agg::rgba(facecolors(ic, 0), facecolors(ic, 1), facecolors(ic, 2), facecolors(ic, 3)); + face.emplace(facecolors(ic, 0), facecolors(ic, 1), facecolors(ic, 2), facecolors(ic, 3)); } if (Nedgecolors) { @@ -988,31 +931,38 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, } } - if (check_snap) { - gc.isaa = antialiaseds(i % Naa); + if(Nhatchcolors) { + int ic = i % Nhatchcolors; + gc.hatch_color = agg::rgba(hatchcolors(ic, 0), hatchcolors(ic, 1), hatchcolors(ic, 2), hatchcolors(ic, 3)); + } - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, has_codes); - clipped_t clipped(nan_removed, do_clip, width, height); - snapped_t snapped( - clipped, gc.snap_mode, path.total_vertices(), points_to_pixels(gc.linewidth)); + gc.isaa = antialiaseds(i % Naa); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, true, has_codes}; + auto clipped = PathClipper(nan_removed, do_clip, width, height); + if (check_snap) { + auto snapped = PathSnapper{ + clipped, gc.snap_mode, path.total_vertices(), points_to_pixels(gc.linewidth)}; if (has_codes) { - snapped_curve_t curve(snapped); - _draw_path(curve, has_clippath, face, gc); + auto curve = agg::conv_curve{snapped}; + auto sketch = Sketch{ + curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness}; + _draw_path(sketch, has_clippath, face, gc); } else { - _draw_path(snapped, has_clippath, face, gc); + auto sketch = Sketch{ + snapped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness}; + _draw_path(sketch, has_clippath, face, gc); } } else { - gc.isaa = antialiaseds(i % Naa); - - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, has_codes); - clipped_t clipped(nan_removed, do_clip, width, height); if (has_codes) { - curve_t curve(clipped); - _draw_path(curve, has_clippath, face, gc); + auto curve = agg::conv_curve{clipped}; + auto sketch = Sketch{ + curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness}; + _draw_path(sketch, has_clippath, face, gc); } else { - _draw_path(clipped, has_clippath, face, gc); + auto sketch = Sketch{ + clipped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness}; + _draw_path(sketch, has_clippath, face, gc); } } } @@ -1034,7 +984,8 @@ inline void RendererAgg::draw_path_collection(GCAgg &gc, ColorArray &edgecolors, LineWidthArray &linewidths, DashesVector &linestyles, - AntialiasedArray &antialiaseds) + AntialiasedArray &antialiaseds, + ColorArray &hatchcolors) { _draw_path_collection_generic(gc, master_transform, @@ -1051,7 +1002,8 @@ inline void RendererAgg::draw_path_collection(GCAgg &gc, linestyles, antialiaseds, true, - true); + true, + hatchcolors); } template @@ -1145,6 +1097,7 @@ inline void RendererAgg::draw_quad_mesh(GCAgg &gc, array::scalar linewidths(gc.linewidth); array::scalar antialiaseds(antialiased); DashesVector linestyles; + ColorArray hatchcolors = py::array_t().reshape({0, 4}).unchecked(); _draw_path_collection_generic(gc, master_transform, @@ -1161,7 +1114,8 @@ inline void RendererAgg::draw_quad_mesh(GCAgg &gc, linestyles, antialiaseds, true, // check_snap - false); + false, + hatchcolors); } template @@ -1206,14 +1160,9 @@ inline void RendererAgg::_draw_gouraud_triangle(PointArray &points, theRasterizer.add_path(span_gen); if (has_clippath) { - typedef agg::pixfmt_amask_adaptor pixfmt_amask_type; - typedef agg::renderer_base amask_ren_type; - typedef agg::renderer_scanline_aa - amask_aa_renderer_type; - - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r, span_alloc, span_gen); + auto pfa = agg::pixfmt_amask_adaptor{pixFmt, alphaMask}; + auto r = agg::renderer_base{pfa}; + auto ren = agg::renderer_scanline_aa{r, span_alloc, span_gen}; agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, span_alloc, span_gen); @@ -1226,14 +1175,27 @@ inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, ColorArray &colors, agg::trans_affine &trans) { + if (points.shape(0)) { + check_trailing_shape(points, "points", 3, 2); + } + if (colors.shape(0)) { + check_trailing_shape(colors, "colors", 3, 4); + } + if (points.shape(0) != colors.shape(0)) { + throw py::value_error( + "points and colors arrays must be the same length, got " + + std::to_string(points.shape(0)) + " points and " + + std::to_string(colors.shape(0)) + "colors"); + } + theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); for (int i = 0; i < points.shape(0); ++i) { - typename PointArray::sub_t point = points.subarray(i); - typename ColorArray::sub_t color = colors.subarray(i); + auto point = std::bind(points, i, std::placeholders::_1, std::placeholders::_2); + auto color = std::bind(colors, i, std::placeholders::_1, std::placeholders::_2); _draw_gouraud_triangle(point, color, trans, has_clippath); } diff --git a/src/_backend_agg_basic_types.h b/src/_backend_agg_basic_types.h index 4fbf846d8cb4..b424419ec99e 100644 --- a/src/_backend_agg_basic_types.h +++ b/src/_backend_agg_basic_types.h @@ -4,6 +4,9 @@ /* Contains some simple types from the Agg backend that are also used by other modules */ +#include + +#include #include #include "agg_color_rgba.h" @@ -13,6 +16,8 @@ #include "py_adaptors.h" +namespace py = pybind11; + struct ClipPath { mpl::PathIterator path; @@ -43,7 +48,7 @@ class Dashes } void add_dash_pair(double length, double skip) { - dashes.push_back(std::make_pair(length, skip)); + dashes.emplace_back(length, skip); } size_t size() const { @@ -54,9 +59,7 @@ class Dashes void dash_to_stroke(T &stroke, double dpi, bool isaa) { double scaleddpi = dpi / 72.0; - for (dash_t::const_iterator i = dashes.begin(); i != dashes.end(); ++i) { - double val0 = i->first; - double val1 = i->second; + for (auto [val0, val1] : dashes) { val0 = val0 * scaleddpi; val1 = val1 * scaleddpi; if (!isaa) { @@ -121,4 +124,132 @@ class GCAgg GCAgg &operator=(const GCAgg &); }; +namespace PYBIND11_NAMESPACE { namespace detail { + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(agg::line_cap_e, const_name("line_cap_e")); + + bool load(handle src, bool) { + const std::unordered_map enum_values = { + {"butt", agg::butt_cap}, + {"round", agg::round_cap}, + {"projecting", agg::square_cap}, + }; + value = enum_values.at(src.cast()); + return true; + } + }; + + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(agg::line_join_e, const_name("line_join_e")); + + bool load(handle src, bool) { + const std::unordered_map enum_values = { + {"miter", agg::miter_join_revert}, + {"round", agg::round_join}, + {"bevel", agg::bevel_join}, + }; + value = enum_values.at(src.cast()); + return true; + } + }; + + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(ClipPath, const_name("ClipPath")); + + bool load(handle src, bool) { + if (src.is_none()) { + return true; + } + + auto [path, trans] = + src.cast, agg::trans_affine>>(); + if (path) { + value.path = *path; + } + value.trans = trans; + + return true; + } + }; + + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(Dashes, const_name("Dashes")); + + bool load(handle src, bool) { + auto [dash_offset, dashes_seq_or_none] = + src.cast>>(); + + if (!dashes_seq_or_none) { + return true; + } + + auto dashes_seq = *dashes_seq_or_none; + + auto nentries = dashes_seq.size(); + // If the dashpattern has odd length, iterate through it twice (in + // accordance with the pdf/ps/svg specs). + auto dash_pattern_length = (nentries % 2) ? 2 * nentries : nentries; + + for (py::size_t i = 0; i < dash_pattern_length; i += 2) { + auto length = dashes_seq[i % nentries].cast(); + auto skip = dashes_seq[(i + 1) % nentries].cast(); + + value.add_dash_pair(length, skip); + } + + value.set_dash_offset(dash_offset); + + return true; + } + }; + + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(SketchParams, const_name("SketchParams")); + + bool load(handle src, bool) { + if (src.is_none()) { + value.scale = 0.0; + value.length = 0.0; + value.randomness = 0.0; + return true; + } + + auto params = src.cast>(); + std::tie(value.scale, value.length, value.randomness) = params; + + return true; + } + }; + + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(GCAgg, const_name("GCAgg")); + + bool load(handle src, bool) { + value.linewidth = src.attr("_linewidth").cast(); + value.alpha = src.attr("_alpha").cast(); + value.forced_alpha = src.attr("_forced_alpha").cast(); + value.color = src.attr("_rgb").cast(); + value.isaa = src.attr("_antialiased").cast(); + value.cap = src.attr("_capstyle").cast(); + value.join = src.attr("_joinstyle").cast(); + value.dashes = src.attr("get_dashes")().cast(); + value.cliprect = src.attr("_cliprect").cast(); + value.clippath = src.attr("get_clip_path")().cast(); + value.snap_mode = src.attr("get_snap")().cast(); + value.hatchpath = src.attr("get_hatch_path")().cast(); + value.hatch_color = src.attr("get_hatch_color")().cast(); + value.hatch_linewidth = src.attr("get_hatch_linewidth")().cast(); + value.sketch = src.attr("get_sketch_params")().cast(); + + return true; + } + }; +}} // namespace PYBIND11_NAMESPACE::detail + #endif diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index eaf4bf6f5f9d..11d45773d186 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -1,566 +1,287 @@ +#include +#include +#include #include "mplutils.h" -#include "numpy_cpp.h" #include "py_converters.h" #include "_backend_agg.h" -typedef struct -{ - PyObject_HEAD - RendererAgg *x; - Py_ssize_t shape[3]; - Py_ssize_t strides[3]; - Py_ssize_t suboffsets[3]; -} PyRendererAgg; - -static PyTypeObject PyRendererAggType; - -typedef struct -{ - PyObject_HEAD - BufferRegion *x; - Py_ssize_t shape[3]; - Py_ssize_t strides[3]; - Py_ssize_t suboffsets[3]; -} PyBufferRegion; - -static PyTypeObject PyBufferRegionType; - +namespace py = pybind11; +using namespace pybind11::literals; /********************************************************************** * BufferRegion * */ -static PyObject *PyBufferRegion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyBufferRegion *self; - self = (PyBufferRegion *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} - -static void PyBufferRegion_dealloc(PyBufferRegion *self) -{ - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} - /* TODO: This doesn't seem to be used internally. Remove? */ -static PyObject *PyBufferRegion_set_x(PyBufferRegion *self, PyObject *args) -{ - int x; - if (!PyArg_ParseTuple(args, "i:set_x", &x)) { - return NULL; - } - self->x->get_rect().x1 = x; - - Py_RETURN_NONE; -} - -static PyObject *PyBufferRegion_set_y(PyBufferRegion *self, PyObject *args) +static void +PyBufferRegion_set_x(BufferRegion *self, int x) { - int y; - if (!PyArg_ParseTuple(args, "i:set_y", &y)) { - return NULL; - } - self->x->get_rect().y1 = y; - - Py_RETURN_NONE; + self->get_rect().x1 = x; } -static PyObject *PyBufferRegion_get_extents(PyBufferRegion *self, PyObject *args) +static void +PyBufferRegion_set_y(BufferRegion *self, int y) { - agg::rect_i rect = self->x->get_rect(); - - return Py_BuildValue("IIII", rect.x1, rect.y1, rect.x2, rect.y2); + self->get_rect().y1 = y; } -int PyBufferRegion_get_buffer(PyBufferRegion *self, Py_buffer *buf, int flags) +static py::object +PyBufferRegion_get_extents(BufferRegion *self) { - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = self->x->get_data(); - buf->len = (Py_ssize_t)self->x->get_width() * (Py_ssize_t)self->x->get_height() * 4; - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 3; - self->shape[0] = self->x->get_height(); - self->shape[1] = self->x->get_width(); - self->shape[2] = 4; - buf->shape = self->shape; - self->strides[0] = self->x->get_width() * 4; - self->strides[1] = 4; - self->strides[2] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} + agg::rect_i rect = self->get_rect(); -static PyTypeObject *PyBufferRegion_init_type() -{ - static PyMethodDef methods[] = { - { "set_x", (PyCFunction)PyBufferRegion_set_x, METH_VARARGS, NULL }, - { "set_y", (PyCFunction)PyBufferRegion_set_y, METH_VARARGS, NULL }, - { "get_extents", (PyCFunction)PyBufferRegion_get_extents, METH_NOARGS, NULL }, - { NULL } - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyBufferRegion_get_buffer; - - PyBufferRegionType.tp_name = "matplotlib.backends._backend_agg.BufferRegion"; - PyBufferRegionType.tp_basicsize = sizeof(PyBufferRegion); - PyBufferRegionType.tp_dealloc = (destructor)PyBufferRegion_dealloc; - PyBufferRegionType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBufferRegionType.tp_methods = methods; - PyBufferRegionType.tp_new = PyBufferRegion_new; - PyBufferRegionType.tp_as_buffer = &buffer_procs; - - return &PyBufferRegionType; + return py::make_tuple(rect.x1, rect.y1, rect.x2, rect.y2); } /********************************************************************** * RendererAgg * */ -static PyObject *PyRendererAgg_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyRendererAgg *self; - self = (PyRendererAgg *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} - -static int PyRendererAgg_init(PyRendererAgg *self, PyObject *args, PyObject *kwds) +static void +PyRendererAgg_draw_path(RendererAgg *self, + GCAgg &gc, + mpl::PathIterator path, + agg::trans_affine trans, + py::object rgbFace) { - unsigned int width; - unsigned int height; - double dpi; - int debug = 0; - - if (!PyArg_ParseTuple(args, "IId|i:RendererAgg", &width, &height, &dpi, &debug)) { - return -1; + agg::rgba face = rgbFace.cast(); + if (!rgbFace.is_none()) { + if (gc.forced_alpha || rgbFace.cast().size() == 3) { + face.a = gc.alpha; + } } - if (dpi <= 0.0) { - PyErr_SetString(PyExc_ValueError, "dpi must be positive"); - return -1; - } - - if (width >= 1 << 16 || height >= 1 << 16) { - PyErr_Format( - PyExc_ValueError, - "Image size of %dx%d pixels is too large. " - "It must be less than 2^16 in each direction.", - width, height); - return -1; - } - - CALL_CPP_INIT("RendererAgg", self->x = new RendererAgg(width, height, dpi)) - - return 0; + self->draw_path(gc, path, trans, face); } -static void PyRendererAgg_dealloc(PyRendererAgg *self) +static void +PyRendererAgg_draw_text_image(RendererAgg *self, + py::array_t image_obj, + std::variant vx, + std::variant vy, + double angle, + GCAgg &gc) { - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *PyRendererAgg_draw_path(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - mpl::PathIterator path; - agg::trans_affine trans; - PyObject *faceobj = NULL; - agg::rgba face; - - if (!PyArg_ParseTuple(args, - "O&O&O&|O:draw_path", - &convert_gcagg, - &gc, - &convert_path, - &path, - &convert_trans_affine, - &trans, - &faceobj)) { - return NULL; - } - - if (!convert_face(faceobj, gc, &face)) { - return NULL; + int x, y; + + if (auto value = std::get_if(&vx)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="x", "obj_type"_a="parameter as float", + "alternative"_a="int(x)"); + x = static_cast(*value); + } else if (auto value = std::get_if(&vx)) { + x = *value; + } else { + throw std::runtime_error("Should not happen"); } - CALL_CPP("draw_path", (self->x->draw_path(gc, path, trans, face))); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_draw_text_image(PyRendererAgg *self, PyObject *args) -{ - numpy::array_view image; - double x; - double y; - double angle; - GCAgg gc; - - if (!PyArg_ParseTuple(args, - "O&dddO&:draw_text_image", - &image.converter_contiguous, - &image, - &x, - &y, - &angle, - &convert_gcagg, - &gc)) { - return NULL; + if (auto value = std::get_if(&vy)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="y", "obj_type"_a="parameter as float", + "alternative"_a="int(y)"); + y = static_cast(*value); + } else if (auto value = std::get_if(&vy)) { + y = *value; + } else { + throw std::runtime_error("Should not happen"); } - CALL_CPP("draw_text_image", (self->x->draw_text_image(gc, image, x, y, angle))); + // TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const. + auto image = image_obj.mutable_unchecked<2>(); - Py_RETURN_NONE; + self->draw_text_image(gc, image, x, y, angle); } -PyObject *PyRendererAgg_draw_markers(PyRendererAgg *self, PyObject *args) +static void +PyRendererAgg_draw_markers(RendererAgg *self, + GCAgg &gc, + mpl::PathIterator marker_path, + agg::trans_affine marker_path_trans, + mpl::PathIterator path, + agg::trans_affine trans, + py::object rgbFace) { - GCAgg gc; - mpl::PathIterator marker_path; - agg::trans_affine marker_path_trans; - mpl::PathIterator path; - agg::trans_affine trans; - PyObject *faceobj = NULL; - agg::rgba face; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&|O:draw_markers", - &convert_gcagg, - &gc, - &convert_path, - &marker_path, - &convert_trans_affine, - &marker_path_trans, - &convert_path, - &path, - &convert_trans_affine, - &trans, - &faceobj)) { - return NULL; + agg::rgba face = rgbFace.cast(); + if (!rgbFace.is_none()) { + if (gc.forced_alpha || rgbFace.cast().size() == 3) { + face.a = gc.alpha; + } } - if (!convert_face(faceobj, gc, &face)) { - return NULL; - } - - CALL_CPP("draw_markers", - (self->x->draw_markers(gc, marker_path, marker_path_trans, path, trans, face))); - - Py_RETURN_NONE; + self->draw_markers(gc, marker_path, marker_path_trans, path, trans, face); } -static PyObject *PyRendererAgg_draw_image(PyRendererAgg *self, PyObject *args) +static void +PyRendererAgg_draw_image(RendererAgg *self, + GCAgg &gc, + double x, + double y, + py::array_t image_obj) { - GCAgg gc; - double x; - double y; - numpy::array_view image; - - if (!PyArg_ParseTuple(args, - "O&ddO&:draw_image", - &convert_gcagg, - &gc, - &x, - &y, - &image.converter_contiguous, - &image)) { - return NULL; - } + // TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const. + auto image = image_obj.mutable_unchecked<3>(); x = mpl_round(x); y = mpl_round(y); gc.alpha = 1.0; - CALL_CPP("draw_image", (self->x->draw_image(gc, x, y, image))); - - Py_RETURN_NONE; -} - -static PyObject * -PyRendererAgg_draw_path_collection(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - agg::trans_affine master_transform; - mpl::PathGenerator paths; - numpy::array_view transforms; - numpy::array_view offsets; - agg::trans_affine offset_trans; - numpy::array_view facecolors; - numpy::array_view edgecolors; - numpy::array_view linewidths; - DashesVector dashes; - numpy::array_view antialiaseds; - PyObject *ignored; - PyObject *offset_position; // offset position is no longer used - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&O&O&O&O&O&O&OO:draw_path_collection", - &convert_gcagg, - &gc, - &convert_trans_affine, - &master_transform, - &convert_pathgen, - &paths, - &convert_transforms, - &transforms, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans, - &convert_colors, - &facecolors, - &convert_colors, - &edgecolors, - &linewidths.converter, - &linewidths, - &convert_dashes_vector, - &dashes, - &antialiaseds.converter, - &antialiaseds, - &ignored, - &offset_position)) { - return NULL; - } - - CALL_CPP("draw_path_collection", - (self->x->draw_path_collection(gc, - master_transform, - paths, - transforms, - offsets, - offset_trans, - facecolors, - edgecolors, - linewidths, - dashes, - antialiaseds))); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_draw_quad_mesh(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - agg::trans_affine master_transform; - unsigned int mesh_width; - unsigned int mesh_height; - numpy::array_view coordinates; - numpy::array_view offsets; - agg::trans_affine offset_trans; - numpy::array_view facecolors; - bool antialiased; - numpy::array_view edgecolors; - - if (!PyArg_ParseTuple(args, - "O&O&IIO&O&O&O&O&O&:draw_quad_mesh", - &convert_gcagg, - &gc, - &convert_trans_affine, - &master_transform, - &mesh_width, - &mesh_height, - &coordinates.converter, - &coordinates, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans, - &convert_colors, - &facecolors, - &convert_bool, - &antialiased, - &convert_colors, - &edgecolors)) { - return NULL; - } - - CALL_CPP("draw_quad_mesh", - (self->x->draw_quad_mesh(gc, - master_transform, - mesh_width, - mesh_height, - coordinates, - offsets, - offset_trans, - facecolors, - antialiased, - edgecolors))); - - Py_RETURN_NONE; -} - -static PyObject * -PyRendererAgg_draw_gouraud_triangles(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - numpy::array_view points; - numpy::array_view colors; - agg::trans_affine trans; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&|O:draw_gouraud_triangles", - &convert_gcagg, - &gc, - &points.converter, - &points, - &colors.converter, - &colors, - &convert_trans_affine, - &trans)) { - return NULL; - } - if (points.shape(0) && !check_trailing_shape(points, "points", 3, 2)) { - return NULL; - } - if (colors.shape(0) && !check_trailing_shape(colors, "colors", 3, 4)) { - return NULL; - } - if (points.shape(0) != colors.shape(0)) { - PyErr_Format(PyExc_ValueError, - "points and colors arrays must be the same length, got " - "%" NPY_INTP_FMT " points and %" NPY_INTP_FMT "colors", - points.shape(0), colors.shape(0)); - return NULL; - } - - CALL_CPP("draw_gouraud_triangles", self->x->draw_gouraud_triangles(gc, points, colors, trans)); - - Py_RETURN_NONE; + self->draw_image(gc, x, y, image); } -int PyRendererAgg_get_buffer(PyRendererAgg *self, Py_buffer *buf, int flags) +static void +PyRendererAgg_draw_path_collection(RendererAgg *self, + GCAgg &gc, + agg::trans_affine master_transform, + mpl::PathGenerator paths, + py::array_t transforms_obj, + py::array_t offsets_obj, + agg::trans_affine offset_trans, + py::array_t facecolors_obj, + py::array_t edgecolors_obj, + py::array_t linewidths_obj, + DashesVector dashes, + py::array_t antialiaseds_obj, + py::object Py_UNUSED(ignored_obj), + // offset position is no longer used + py::object Py_UNUSED(offset_position_obj), + py::array_t hatchcolors_obj) { - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = self->x->pixBuffer; - buf->len = (Py_ssize_t)self->x->get_width() * (Py_ssize_t)self->x->get_height() * 4; - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 3; - self->shape[0] = self->x->get_height(); - self->shape[1] = self->x->get_width(); - self->shape[2] = 4; - buf->shape = self->shape; - self->strides[0] = self->x->get_width() * 4; - self->strides[1] = 4; - self->strides[2] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; + auto transforms = convert_transforms(transforms_obj); + auto offsets = convert_points(offsets_obj); + auto facecolors = convert_colors(facecolors_obj); + auto edgecolors = convert_colors(edgecolors_obj); + auto hatchcolors = convert_colors(hatchcolors_obj); + auto linewidths = linewidths_obj.unchecked<1>(); + auto antialiaseds = antialiaseds_obj.unchecked<1>(); + + self->draw_path_collection(gc, + master_transform, + paths, + transforms, + offsets, + offset_trans, + facecolors, + edgecolors, + linewidths, + dashes, + antialiaseds, + hatchcolors); } -static PyObject *PyRendererAgg_clear(PyRendererAgg *self, PyObject *args) +static void +PyRendererAgg_draw_quad_mesh(RendererAgg *self, + GCAgg &gc, + agg::trans_affine master_transform, + unsigned int mesh_width, + unsigned int mesh_height, + py::array_t coordinates_obj, + py::array_t offsets_obj, + agg::trans_affine offset_trans, + py::array_t facecolors_obj, + bool antialiased, + py::array_t edgecolors_obj) { - CALL_CPP("clear", self->x->clear()); - - Py_RETURN_NONE; + auto coordinates = coordinates_obj.mutable_unchecked<3>(); + auto offsets = convert_points(offsets_obj); + auto facecolors = convert_colors(facecolors_obj); + auto edgecolors = convert_colors(edgecolors_obj); + + self->draw_quad_mesh(gc, + master_transform, + mesh_width, + mesh_height, + coordinates, + offsets, + offset_trans, + facecolors, + antialiased, + edgecolors); } -static PyObject *PyRendererAgg_copy_from_bbox(PyRendererAgg *self, PyObject *args) +static void +PyRendererAgg_draw_gouraud_triangles(RendererAgg *self, + GCAgg &gc, + py::array_t points_obj, + py::array_t colors_obj, + agg::trans_affine trans) { - agg::rect_d bbox; - BufferRegion *reg; - PyObject *regobj; - - if (!PyArg_ParseTuple(args, "O&:copy_from_bbox", &convert_rect, &bbox)) { - return 0; - } + auto points = points_obj.unchecked<3>(); + auto colors = colors_obj.unchecked<3>(); - CALL_CPP("copy_from_bbox", (reg = self->x->copy_from_bbox(bbox))); - - regobj = PyBufferRegion_new(&PyBufferRegionType, NULL, NULL); - ((PyBufferRegion *)regobj)->x = reg; - - return regobj; + self->draw_gouraud_triangles(gc, points, colors, trans); } -static PyObject *PyRendererAgg_restore_region(PyRendererAgg *self, PyObject *args) +PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used()) { - PyBufferRegion *regobj; - int xx1 = 0, yy1 = 0, xx2 = 0, yy2 = 0, x = 0, y = 0; - - if (!PyArg_ParseTuple(args, - "O!|iiiiii:restore_region", - &PyBufferRegionType, - ®obj, - &xx1, - &yy1, - &xx2, - &yy2, - &x, - &y)) { - return 0; - } - - if (PySequence_Size(args) == 1) { - CALL_CPP("restore_region", self->x->restore_region(*(regobj->x))); - } else { - CALL_CPP("restore_region", self->x->restore_region(*(regobj->x), xx1, yy1, xx2, yy2, x, y)); - } - - Py_RETURN_NONE; -} - -static PyTypeObject *PyRendererAgg_init_type() -{ - static PyMethodDef methods[] = { - {"draw_path", (PyCFunction)PyRendererAgg_draw_path, METH_VARARGS, NULL}, - {"draw_markers", (PyCFunction)PyRendererAgg_draw_markers, METH_VARARGS, NULL}, - {"draw_text_image", (PyCFunction)PyRendererAgg_draw_text_image, METH_VARARGS, NULL}, - {"draw_image", (PyCFunction)PyRendererAgg_draw_image, METH_VARARGS, NULL}, - {"draw_path_collection", (PyCFunction)PyRendererAgg_draw_path_collection, METH_VARARGS, NULL}, - {"draw_quad_mesh", (PyCFunction)PyRendererAgg_draw_quad_mesh, METH_VARARGS, NULL}, - {"draw_gouraud_triangles", (PyCFunction)PyRendererAgg_draw_gouraud_triangles, METH_VARARGS, NULL}, - - {"clear", (PyCFunction)PyRendererAgg_clear, METH_NOARGS, NULL}, - - {"copy_from_bbox", (PyCFunction)PyRendererAgg_copy_from_bbox, METH_VARARGS, NULL}, - {"restore_region", (PyCFunction)PyRendererAgg_restore_region, METH_VARARGS, NULL}, - {NULL} - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyRendererAgg_get_buffer; - - PyRendererAggType.tp_name = "matplotlib.backends._backend_agg.RendererAgg"; - PyRendererAggType.tp_basicsize = sizeof(PyRendererAgg); - PyRendererAggType.tp_dealloc = (destructor)PyRendererAgg_dealloc; - PyRendererAggType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyRendererAggType.tp_methods = methods; - PyRendererAggType.tp_init = (initproc)PyRendererAgg_init; - PyRendererAggType.tp_new = PyRendererAgg_new; - PyRendererAggType.tp_as_buffer = &buffer_procs; - - return &PyRendererAggType; -} - -static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_backend_agg" }; - -PyMODINIT_FUNC PyInit__backend_agg(void) -{ - import_array(); - PyObject *m; - if (!(m = PyModule_Create(&moduledef)) - || prepare_and_add_type(PyRendererAgg_init_type(), m) - // BufferRegion is not constructible from Python, thus not added to the module. - || PyType_Ready(PyBufferRegion_init_type()) - ) { - Py_XDECREF(m); - return NULL; - } - return m; + py::class_(m, "RendererAgg", py::buffer_protocol()) + .def(py::init(), + "width"_a, "height"_a, "dpi"_a) + + .def("draw_path", &PyRendererAgg_draw_path, + "gc"_a, "path"_a, "trans"_a, "face"_a = nullptr) + .def("draw_markers", &PyRendererAgg_draw_markers, + "gc"_a, "marker_path"_a, "marker_path_trans"_a, "path"_a, "trans"_a, + "face"_a = nullptr) + .def("draw_text_image", &PyRendererAgg_draw_text_image, + "image"_a, "x"_a, "y"_a, "angle"_a, "gc"_a) + .def("draw_image", &PyRendererAgg_draw_image, + "gc"_a, "x"_a, "y"_a, "image"_a) + .def("draw_path_collection", &PyRendererAgg_draw_path_collection, + "gc"_a, "master_transform"_a, "paths"_a, "transforms"_a, "offsets"_a, + "offset_trans"_a, "facecolors"_a, "edgecolors"_a, "linewidths"_a, + "dashes"_a, "antialiaseds"_a, "ignored"_a, "offset_position"_a, + py::kw_only(), "hatchcolors"_a = py::array_t().reshape({0, 4})) + .def("draw_quad_mesh", &PyRendererAgg_draw_quad_mesh, + "gc"_a, "master_transform"_a, "mesh_width"_a, "mesh_height"_a, + "coordinates"_a, "offsets"_a, "offset_trans"_a, "facecolors"_a, + "antialiased"_a, "edgecolors"_a) + .def("draw_gouraud_triangles", &PyRendererAgg_draw_gouraud_triangles, + "gc"_a, "points"_a, "colors"_a, "trans"_a = nullptr) + + .def("clear", &RendererAgg::clear) + + .def("copy_from_bbox", &RendererAgg::copy_from_bbox, + "bbox"_a) + .def("restore_region", + py::overload_cast(&RendererAgg::restore_region), + "region"_a) + .def("restore_region", + py::overload_cast(&RendererAgg::restore_region), + "region"_a, "xx1"_a, "yy1"_a, "xx2"_a, "yy2"_a, "x"_a, "y"_a) + + .def_buffer([](RendererAgg *renderer) -> py::buffer_info { + std::vector shape { + static_cast(renderer->get_height()), + static_cast(renderer->get_width()), + 4 + }; + std::vector strides { + static_cast(renderer->get_width() * 4), + 4, + 1 + }; + return py::buffer_info(renderer->pixBuffer, shape, strides); + }); + + py::class_(m, "BufferRegion", py::buffer_protocol()) + // BufferRegion is not constructible from Python, thus no py::init is added. + .def("set_x", &PyBufferRegion_set_x) + .def("set_y", &PyBufferRegion_set_y) + .def("get_extents", &PyBufferRegion_get_extents) + .def_buffer([](BufferRegion *buffer) -> py::buffer_info { + std::vector shape { + buffer->get_height(), + buffer->get_width(), + 4 + }; + std::vector strides { + buffer->get_width() * 4, + 4, + 1 + }; + return py::buffer_info(buffer->get_data(), shape, strides); + }); } diff --git a/src/_c_internal_utils.cpp b/src/_c_internal_utils.cpp index e118183ecc8b..31eb92444862 100644 --- a/src/_c_internal_utils.cpp +++ b/src/_c_internal_utils.cpp @@ -7,7 +7,14 @@ #define WIN32_LEAN_AND_MEAN // Windows 10, for latest HiDPI API support. #define WINVER 0x0A00 -#define _WIN32_WINNT 0x0A00 +#if defined(_WIN32_WINNT) +#if _WIN32_WINNT < WINVER +#undef _WIN32_WINNT +#define _WIN32_WINNT WINVER +#endif +#else +#define _WIN32_WINNT WINVER +#endif #endif #include #ifdef __linux__ @@ -26,7 +33,7 @@ namespace py = pybind11; using namespace pybind11::literals; static bool -mpl_display_is_valid(void) +mpl_xdisplay_is_valid(void) { #ifdef __linux__ void* libX11; @@ -34,13 +41,13 @@ mpl_display_is_valid(void) // than dlopen(). if (getenv("DISPLAY") && (libX11 = dlopen("libX11.so.6", RTLD_LAZY))) { - typedef struct Display* (*XOpenDisplay_t)(char const*); - typedef int (*XCloseDisplay_t)(struct Display*); - struct Display* display = NULL; - XOpenDisplay_t XOpenDisplay = (XOpenDisplay_t)dlsym(libX11, "XOpenDisplay"); - XCloseDisplay_t XCloseDisplay = (XCloseDisplay_t)dlsym(libX11, "XCloseDisplay"); + struct Display* display = nullptr; + auto XOpenDisplay = (struct Display* (*)(char const*)) + dlsym(libX11, "XOpenDisplay"); + auto XCloseDisplay = (int (*)(struct Display*)) + dlsym(libX11, "XCloseDisplay"); if (XOpenDisplay && XCloseDisplay - && (display = XOpenDisplay(NULL))) { + && (display = XOpenDisplay(nullptr))) { XCloseDisplay(display); } if (dlclose(libX11)) { @@ -50,18 +57,29 @@ mpl_display_is_valid(void) return true; } } + return false; +#else + return true; +#endif +} + +static bool +mpl_display_is_valid(void) +{ +#ifdef __linux__ + if (mpl_xdisplay_is_valid()) { + return true; + } void* libwayland_client; if (getenv("WAYLAND_DISPLAY") && (libwayland_client = dlopen("libwayland-client.so.0", RTLD_LAZY))) { - typedef struct wl_display* (*wl_display_connect_t)(char const*); - typedef void (*wl_display_disconnect_t)(struct wl_display*); - struct wl_display* display = NULL; - wl_display_connect_t wl_display_connect = - (wl_display_connect_t)dlsym(libwayland_client, "wl_display_connect"); - wl_display_disconnect_t wl_display_disconnect = - (wl_display_disconnect_t)dlsym(libwayland_client, "wl_display_disconnect"); + struct wl_display* display = nullptr; + auto wl_display_connect = (struct wl_display* (*)(char const*)) + dlsym(libwayland_client, "wl_display_connect"); + auto wl_display_disconnect = (void (*)(struct wl_display*)) + dlsym(libwayland_client, "wl_display_disconnect"); if (wl_display_connect && wl_display_disconnect - && (display = wl_display_connect(NULL))) { + && (display = wl_display_connect(nullptr))) { wl_display_disconnect(display); } if (dlclose(libwayland_client)) { @@ -125,7 +143,7 @@ static void mpl_SetForegroundWindow(py::capsule UNUSED_ON_NON_WINDOWS(handle_p)) { #ifdef _WIN32 - if (handle_p.name() != "HWND") { + if (strcmp(handle_p.name(), "HWND") != 0) { throw std::runtime_error("Handle must be a value returned from Win32_GetForegroundWindow"); } HWND handle = static_cast(handle_p.get_pointer()); @@ -142,25 +160,19 @@ mpl_SetProcessDpiAwareness_max(void) #ifdef _DPI_AWARENESS_CONTEXTS_ // These functions and options were added in later Windows 10 updates, so // must be loaded dynamically. - typedef BOOL (WINAPI *IsValidDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT); - typedef BOOL (WINAPI *SetProcessDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT); - HMODULE user32 = LoadLibrary("user32.dll"); - IsValidDpiAwarenessContext_t IsValidDpiAwarenessContextPtr = - (IsValidDpiAwarenessContext_t)GetProcAddress( - user32, "IsValidDpiAwarenessContext"); - SetProcessDpiAwarenessContext_t SetProcessDpiAwarenessContextPtr = - (SetProcessDpiAwarenessContext_t)GetProcAddress( - user32, "SetProcessDpiAwarenessContext"); + auto IsValidDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT)) + GetProcAddress(user32, "IsValidDpiAwarenessContext"); + auto SetProcessDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT)) + GetProcAddress(user32, "SetProcessDpiAwarenessContext"); DPI_AWARENESS_CONTEXT ctxs[3] = { DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, // Win10 Creators Update DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, // Win10 DPI_AWARENESS_CONTEXT_SYSTEM_AWARE}; // Win10 - if (IsValidDpiAwarenessContextPtr != NULL - && SetProcessDpiAwarenessContextPtr != NULL) { - for (int i = 0; i < sizeof(ctxs) / sizeof(DPI_AWARENESS_CONTEXT); ++i) { - if (IsValidDpiAwarenessContextPtr(ctxs[i])) { - SetProcessDpiAwarenessContextPtr(ctxs[i]); + if (IsValidDpiAwarenessContext && SetProcessDpiAwarenessContext) { + for (size_t i = 0; i < sizeof(ctxs) / sizeof(DPI_AWARENESS_CONTEXT); ++i) { + if (IsValidDpiAwarenessContext(ctxs[i])) { + SetProcessDpiAwarenessContext(ctxs[i]); break; } } @@ -176,7 +188,7 @@ mpl_SetProcessDpiAwareness_max(void) #endif } -PYBIND11_MODULE(_c_internal_utils, m) +PYBIND11_MODULE(_c_internal_utils, m, py::mod_gil_not_used()) { m.def( "display_is_valid", &mpl_display_is_valid, @@ -187,6 +199,16 @@ PYBIND11_MODULE(_c_internal_utils, m) succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL) succeeds. + On other platforms, always returns True.)"""); + m.def( + "xdisplay_is_valid", &mpl_xdisplay_is_valid, + R"""( -- + Check whether the current X11 display is valid. + + On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL) + succeeds. Use this function if you need to specifically check for X11 + only (e.g., for Tkinter). + On other platforms, always returns True.)"""); m.def( "Win32_GetCurrentProcessExplicitAppUserModelID", diff --git a/src/_enums.h b/src/_enums.h new file mode 100644 index 000000000000..18f3d9aac9fa --- /dev/null +++ b/src/_enums.h @@ -0,0 +1,95 @@ +#ifndef MPL_ENUMS_H +#define MPL_ENUMS_H + +#include + +// Extension for pybind11: Pythonic enums. +// This allows creating classes based on ``enum.*`` types. +// This code was copied from mplcairo, with some slight tweaks. +// The API is: +// +// - P11X_DECLARE_ENUM(py_name: str, py_base_cls: str, ...: {str, enum value}): +// py_name: The name to expose in the module. +// py_base_cls: The name of the enum base class to use. +// ...: The enum name/value pairs to expose. +// +// Use this macro to declare an enum and its values. +// +// - py11x::bind_enums(m: pybind11::module): +// m: The module to use to register the enum classes. +// +// Place this in PYBIND11_MODULE to register the enums declared by P11X_DECLARE_ENUM. + +// a1 includes the opening brace and a2 the closing brace. +// This definition is compatible with older compiler versions compared to +// #define P11X_ENUM_TYPE(...) decltype(std::map{std::pair __VA_ARGS__})::mapped_type +#define P11X_ENUM_TYPE(a1, a2, ...) decltype(std::pair a1, a2)::second_type + +#define P11X_CAT2(a, b) a##b +#define P11X_CAT(a, b) P11X_CAT2(a, b) + +namespace p11x { + namespace { + namespace py = pybind11; + + // Holder is (py_base_cls, [(name, value), ...]) before module init; + // converted to the Python class object after init. + auto enums = std::unordered_map{}; + + auto bind_enums(py::module mod) -> void + { + for (auto& [py_name, spec]: enums) { + auto const& [py_base_cls, pairs] = + spec.cast>(); + mod.attr(py::cast(py_name)) = spec = + py::module::import("enum").attr(py_base_cls.c_str())( + py_name, pairs, py::arg("module") = mod.attr("__name__")); + } + } + } +} + +// Immediately converting the args to a vector outside of the lambda avoids +// name collisions. +#define P11X_DECLARE_ENUM(py_name, py_base_cls, ...) \ + namespace p11x { \ + namespace { \ + [[maybe_unused]] auto const P11X_CAT(enum_placeholder_, __COUNTER__) = \ + [](auto args) { \ + py::gil_scoped_acquire gil; \ + using int_t = std::underlying_type_t; \ + auto pairs = std::vector>{}; \ + for (auto& [k, v]: args) { \ + pairs.emplace_back(k, int_t(v)); \ + } \ + p11x::enums[py_name] = pybind11::cast(std::pair{py_base_cls, pairs}); \ + return 0; \ + } (std::vector{std::pair __VA_ARGS__}); \ + } \ + } \ + namespace pybind11::detail { \ + template<> struct type_caster { \ + using type = P11X_ENUM_TYPE(__VA_ARGS__); \ + static_assert(std::is_enum_v, "Not an enum"); \ + PYBIND11_TYPE_CASTER(type, _(py_name)); \ + bool load(handle src, bool) { \ + auto cls = p11x::enums.at(py_name); \ + PyObject* tmp = nullptr; \ + if (pybind11::isinstance(src, cls) \ + && (tmp = PyNumber_Index(src.attr("value").ptr()))) { \ + auto ival = PyLong_AsLong(tmp); \ + value = decltype(value)(ival); \ + Py_DECREF(tmp); \ + return !(ival == -1 && !PyErr_Occurred()); \ + } else { \ + return false; \ + } \ + } \ + static handle cast(decltype(value) obj, return_value_policy, handle) { \ + auto cls = p11x::enums.at(py_name); \ + return cls(std::underlying_type_t(obj)).inc_ref(); \ + } \ + }; \ + } + +#endif /* MPL_ENUMS_H */ diff --git a/src/_image_resample.h b/src/_image_resample.h index 745fe9f10cd7..eaaf2306ae9f 100644 --- a/src/_image_resample.h +++ b/src/_image_resample.h @@ -3,6 +3,8 @@ #ifndef MPL_RESAMPLE_H #define MPL_RESAMPLE_H +#define MPL_DISABLE_AGG_GRAY_CLIPPING + #include "agg_image_accessors.h" #include "agg_path_storage.h" #include "agg_pixfmt_gray.h" @@ -58,20 +60,16 @@ namespace agg value_type a; //-------------------------------------------------------------------- - gray64() {} + gray64() = default; //-------------------------------------------------------------------- - explicit gray64(value_type v_, value_type a_ = 1) : - v(v_), a(a_) {} + explicit gray64(value_type v_, value_type a_ = 1) : v(v_), a(a_) {} //-------------------------------------------------------------------- - gray64(const self_type& c, value_type a_) : - v(c.v), a(a_) {} + gray64(const self_type& c, value_type a_) : v(c.v), a(a_) {} //-------------------------------------------------------------------- - gray64(const gray64& c) : - v(c.v), - a(c.a) {} + gray64(const gray64& c) = default; //-------------------------------------------------------------------- static AGG_INLINE double to_double(value_type a) @@ -244,7 +242,7 @@ namespace agg value_type a; //-------------------------------------------------------------------- - rgba64() {} + rgba64() = default; //-------------------------------------------------------------------- rgba64(value_type r_, value_type g_, value_type b_, value_type a_= 1) : @@ -498,54 +496,44 @@ typedef enum { } interpolation_e; -// T is rgba if and only if it has an T::r field. +// T is rgba if and only if it has a T::r field. template struct is_grayscale : std::true_type {}; -template struct is_grayscale : std::false_type {}; +template struct is_grayscale> : std::false_type {}; +template constexpr bool is_grayscale_v = is_grayscale::value; template struct type_mapping { - using blender_type = typename std::conditional< - is_grayscale::value, + using blender_type = std::conditional_t< + is_grayscale_v, agg::blender_gray, - typename std::conditional< - std::is_same::value, + std::conditional_t< + std::is_same_v, fixed_blender_rgba_plain, - agg::blender_rgba_plain - >::type - >::type; - using pixfmt_type = typename std::conditional< - is_grayscale::value, + agg::blender_rgba_pre + > + >; + using pixfmt_type = std::conditional_t< + is_grayscale_v, agg::pixfmt_alpha_blend_gray, agg::pixfmt_alpha_blend_rgba - >::type; - using pixfmt_pre_type = typename std::conditional< - is_grayscale::value, - pixfmt_type, - agg::pixfmt_alpha_blend_rgba< - typename std::conditional< - std::is_same::value, - fixed_blender_rgba_pre, - agg::blender_rgba_pre - >::type, - agg::rendering_buffer> - >::type; - template using span_gen_affine_type = typename std::conditional< - is_grayscale::value, + >; + template using span_gen_affine_type = std::conditional_t< + is_grayscale_v, agg::span_image_resample_gray_affine, agg::span_image_resample_rgba_affine - >::type; - template using span_gen_filter_type = typename std::conditional< - is_grayscale::value, + >; + template using span_gen_filter_type = std::conditional_t< + is_grayscale_v, agg::span_image_filter_gray, agg::span_image_filter_rgba - >::type; - template using span_gen_nn_type = typename std::conditional< - is_grayscale::value, + >; + template using span_gen_nn_type = std::conditional_t< + is_grayscale_v, agg::span_image_filter_gray_nn, agg::span_image_filter_rgba_nn - >::type; + >; }; @@ -564,7 +552,8 @@ class span_conv_alpha { if (m_alpha != 1.0) { do { - span->a *= m_alpha; + span->a = static_cast( + static_cast(span->a) * m_alpha); ++span; } while (--len); } @@ -580,23 +569,29 @@ class lookup_distortion { public: lookup_distortion(const double *mesh, int in_width, int in_height, - int out_width, int out_height) : + int out_width, int out_height, bool edge_aligned_subpixels) : m_mesh(mesh), m_in_width(in_width), m_in_height(in_height), m_out_width(out_width), - m_out_height(out_height) + m_out_height(out_height), + m_edge_aligned_subpixels(edge_aligned_subpixels) {} void calculate(int* x, int* y) { if (m_mesh) { + // Nearest-neighbor interpolation needs edge-aligned subpixels + // All other interpolation approaches need center-aligned subpixels + double offset = m_edge_aligned_subpixels ? 0 : 0.5; + double dx = double(*x) / agg::image_subpixel_scale; double dy = double(*y) / agg::image_subpixel_scale; if (dx >= 0 && dx < m_out_width && dy >= 0 && dy < m_out_height) { const double *coord = m_mesh + (int(dy) * m_out_width + int(dx)) * 2; - *x = int(coord[0] * agg::image_subpixel_scale); - *y = int(coord[1] * agg::image_subpixel_scale); + // Add a tiny fudge amount to account for numerical precision loss + *x = int(coord[0] * agg::image_subpixel_scale + offset + 1e-8); + *y = int(coord[1] * agg::image_subpixel_scale + offset + 1e-8); } } } @@ -607,6 +602,7 @@ class lookup_distortion int m_in_height; int m_out_width; int m_out_height; + bool m_edge_aligned_subpixels; }; @@ -710,13 +706,16 @@ void resample( using renderer_t = agg::renderer_base; using rasterizer_t = agg::rasterizer_scanline_aa; + using scanline_t = agg::scanline32_u8; using reflect_t = agg::wrap_mode_reflect; - using image_accessor_t = agg::image_accessor_wrap; + using image_accessor_wrap_t = agg::image_accessor_wrap; + using image_accessor_clip_t = agg::image_accessor_clip; using span_alloc_t = agg::span_allocator; using span_conv_alpha_t = span_conv_alpha; + using nn_affine_interpolator_t = accurate_interpolator_affine_nn<>; using affine_interpolator_t = agg::span_interpolator_linear<>; using arbitrary_interpolator_t = agg::span_interpolator_adaptor, lookup_distortion>; @@ -737,7 +736,7 @@ void resample( span_alloc_t span_alloc; rasterizer_t rasterizer; - agg::scanline_u8 scanline; + scanline_t scanline; span_conv_alpha_t conv_alpha(params.alpha); @@ -745,7 +744,8 @@ void resample( input_buffer.attach( (unsigned char *)input, in_width, in_height, in_width * itemsize); input_pixfmt_t input_pixfmt(input_buffer); - image_accessor_t input_accessor(input_pixfmt); + image_accessor_wrap_t input_accessor_wrap(input_pixfmt); + image_accessor_clip_t input_accessor_clip(input_pixfmt, color_type::no_color()); agg::rendering_buffer output_buffer; output_buffer.attach( @@ -759,14 +759,44 @@ void resample( rasterizer.clip_box(0, 0, out_width, out_height); agg::path_storage path; - if (params.is_affine) { - path.move_to(0, 0); - path.line_to(in_width, 0); - path.line_to(in_width, in_height); - path.line_to(0, in_height); - path.close_polygon(); - agg::conv_transform rectangle(path, params.affine); - rasterizer.add_path(rectangle); + if (params.is_affine && params.interpolation != NEAREST) { + if (params.affine.shx != 0 || params.affine.shy != 0) { + path.move_to(0, 0); + path.line_to(in_width, 0); + path.line_to(in_width, in_height); + path.line_to(0, in_height); + path.close_polygon(); + agg::conv_transform rectangle(path, params.affine); + rasterizer.add_path(rectangle); + } else { + // If there is no shear/rotation, bump out the rendering edges that are + // within a half pixel of a full pixel so that axes are visually filled. + // This bumping out is equivalent to treating any edge pixel that is at + // least half-covered by the source as fully covered by the source. + double left = 0; + double right = in_width; + double bottom = 0; + double top = in_height; + params.affine.transform(&left, &bottom); + params.affine.transform(&right, &top); + if (left > right) { std::swap(left, right); } + if (bottom > top) { std::swap(top, bottom); } + // Add a tiny fudge amount to account for numerical precision loss + int rleft = agg::iround(left - 1e-8); + int rright = agg::iround(right + 1e-8); + int rbottom = agg::iround(bottom - 1e-8); + int rtop = agg::iround(top + 1e-8); + if (rleft < left) { left = rleft; } + if (rright > right) { right = rright; } + if (rbottom < bottom) { bottom = rbottom; } + if (rtop > top) { top = rtop; } + path.move_to(left, bottom); + path.line_to(right, bottom); + path.line_to(right, top); + path.line_to(left, top); + path.close_polygon(); + rasterizer.add_path(path); + } } else { path.move_to(0, 0); path.line_to(out_width, 0); @@ -778,22 +808,22 @@ void resample( if (params.interpolation == NEAREST) { if (params.is_affine) { - using span_gen_t = typename type_mapping_t::template span_gen_nn_type; + using span_gen_t = typename type_mapping_t::template span_gen_nn_type; using span_conv_t = agg::span_converter; using nn_renderer_t = agg::renderer_scanline_aa; - affine_interpolator_t interpolator(inverted); - span_gen_t span_gen(input_accessor, interpolator); + nn_affine_interpolator_t interpolator(inverted); + span_gen_t span_gen(input_accessor_clip, interpolator); span_conv_t span_conv(span_gen, conv_alpha); nn_renderer_t nn_renderer(renderer, span_alloc, span_conv); agg::render_scanlines(rasterizer, scanline, nn_renderer); } else { - using span_gen_t = typename type_mapping_t::template span_gen_nn_type; + using span_gen_t = typename type_mapping_t::template span_gen_nn_type; using span_conv_t = agg::span_converter; using nn_renderer_t = agg::renderer_scanline_aa; lookup_distortion dist( - params.transform_mesh, in_width, in_height, out_width, out_height); + params.transform_mesh, in_width, in_height, out_width, out_height, true); arbitrary_interpolator_t interpolator(inverted, dist); - span_gen_t span_gen(input_accessor, interpolator); + span_gen_t span_gen(input_accessor_clip, interpolator); span_conv_t span_conv(span_gen, conv_alpha); nn_renderer_t nn_renderer(renderer, span_alloc, span_conv); agg::render_scanlines(rasterizer, scanline, nn_renderer); @@ -803,22 +833,22 @@ void resample( get_filter(params, filter); if (params.is_affine && params.resample) { - using span_gen_t = typename type_mapping_t::template span_gen_affine_type; + using span_gen_t = typename type_mapping_t::template span_gen_affine_type; using span_conv_t = agg::span_converter; using int_renderer_t = agg::renderer_scanline_aa; affine_interpolator_t interpolator(inverted); - span_gen_t span_gen(input_accessor, interpolator, filter); + span_gen_t span_gen(input_accessor_wrap, interpolator, filter); span_conv_t span_conv(span_gen, conv_alpha); int_renderer_t int_renderer(renderer, span_alloc, span_conv); agg::render_scanlines(rasterizer, scanline, int_renderer); } else { - using span_gen_t = typename type_mapping_t::template span_gen_filter_type; + using span_gen_t = typename type_mapping_t::template span_gen_filter_type; using span_conv_t = agg::span_converter; using int_renderer_t = agg::renderer_scanline_aa; lookup_distortion dist( - params.transform_mesh, in_width, in_height, out_width, out_height); + params.transform_mesh, in_width, in_height, out_width, out_height, false); arbitrary_interpolator_t interpolator(inverted, dist); - span_gen_t span_gen(input_accessor, interpolator, filter); + span_gen_t span_gen(input_accessor_wrap, interpolator, filter); span_conv_t span_conv(span_gen, conv_alpha); int_renderer_t int_renderer(renderer, span_alloc, span_conv); agg::render_scanlines(rasterizer, scanline, int_renderer); diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp index 65c8c8324ebc..8944a2d44041 100644 --- a/src/_image_wrapper.cpp +++ b/src/_image_wrapper.cpp @@ -1,8 +1,10 @@ #include #include +#include + #include "_image_resample.h" -#include "py_converters_11.h" +#include "py_converters.h" namespace py = pybind11; using namespace pybind11::literals; @@ -54,7 +56,7 @@ _get_transform_mesh(const py::object& transform, const py::ssize_t *dims) /* TODO: Could we get away with float, rather than double, arrays here? */ /* Given a non-affine transform object, create a mesh that maps - every pixel in the output image to the input image. This is used + every pixel center in the output image to the input image. This is used as a lookup table during the actual resampling. */ // If attribute doesn't exist, raises Python AttributeError @@ -66,8 +68,10 @@ _get_transform_mesh(const py::object& transform, const py::ssize_t *dims) for (auto y = 0; y < dims[0]; ++y) { for (auto x = 0; x < dims[1]; ++x) { - *p++ = (double)x; - *p++ = (double)y; + // The convention for the supplied transform is that pixel centers + // are at 0.5, 1.5, 2.5, etc. + *p++ = (double)x + 0.5; + *p++ = (double)y + 0.5; } } @@ -163,7 +167,7 @@ image_resample(py::array input_array, if (is_affine) { convert_trans_affine(transform, params.affine); - params.is_affine = true; + params.is_affine = is_affine; } else { transform_mesh = _get_transform_mesh(transform, output_array.shape()); params.transform_mesh = transform_mesh.data(); @@ -173,20 +177,20 @@ image_resample(py::array input_array, if (auto resampler = (ndim == 2) ? ( - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : nullptr) : ( // ndim == 3 - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : - (dtype.is(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : + (dtype.equal(py::dtype::of())) ? resample : nullptr)) { Py_BEGIN_ALLOW_THREADS resampler( @@ -200,7 +204,82 @@ image_resample(py::array input_array, } -PYBIND11_MODULE(_image, m) { +// This is used by matplotlib.testing.compare to calculate RMS and a difference image. +static py::tuple +calculate_rms_and_diff(py::array_t expected_image, + py::array_t actual_image) +{ + for (const auto & [image, name] : {std::pair{expected_image, "Expected"}, + std::pair{actual_image, "Actual"}}) + { + if (image.ndim() != 3) { + auto exceptions = py::module_::import("matplotlib.testing.exceptions"); + auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure"); + py::set_error( + ImageComparisonFailure, + "{name} image must be 3-dimensional, but is {ndim}-dimensional"_s.format( + "name"_a=name, "ndim"_a=image.ndim())); + throw py::error_already_set(); + } + } + + auto height = expected_image.shape(0); + auto width = expected_image.shape(1); + auto depth = expected_image.shape(2); + + if (depth != 3 && depth != 4) { + auto exceptions = py::module_::import("matplotlib.testing.exceptions"); + auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure"); + py::set_error( + ImageComparisonFailure, + "Image must be RGB or RGBA but has depth {depth}"_s.format( + "depth"_a=depth)); + throw py::error_already_set(); + } + + if (height != actual_image.shape(0) || width != actual_image.shape(1) || + depth != actual_image.shape(2)) { + auto exceptions = py::module_::import("matplotlib.testing.exceptions"); + auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure"); + py::set_error( + ImageComparisonFailure, + "Image sizes do not match expected size: {expected_image.shape} "_s + "actual size {actual_image.shape}"_s.format( + "expected_image"_a=expected_image, "actual_image"_a=actual_image)); + throw py::error_already_set(); + } + auto expected = expected_image.unchecked<3>(); + auto actual = actual_image.unchecked<3>(); + + py::ssize_t diff_dims[3] = {height, width, 3}; + py::array_t diff_image(diff_dims); + auto diff = diff_image.mutable_unchecked<3>(); + + double total = 0.0; + for (auto i = 0; i < height; i++) { + for (auto j = 0; j < width; j++) { + for (auto k = 0; k < depth; k++) { + auto pixel_diff = static_cast(expected(i, j, k)) - + static_cast(actual(i, j, k)); + + total += pixel_diff*pixel_diff; + + if (k != 3) { // Hard-code a fully solid alpha channel by omitting it. + diff(i, j, k) = static_cast(std::clamp( + abs(pixel_diff) * 10, // Expand differences in luminance domain. + 0.0, 255.0)); + } + } + } + } + total = total / (width * height * depth); + + return py::make_tuple(sqrt(total), diff_image); +} + + +PYBIND11_MODULE(_image, m, py::mod_gil_not_used()) +{ py::enum_(m, "_InterpolationType") .value("NEAREST", NEAREST) .value("BILINEAR", BILINEAR) @@ -231,4 +310,7 @@ PYBIND11_MODULE(_image, m) { "norm"_a = false, "radius"_a = 1, image_resample__doc__); + + m.def("calculate_rms_and_diff", &calculate_rms_and_diff, + "expected_image"_a, "actual_image"_a); } diff --git a/src/_macosx.m b/src/_macosx.m index 656d502fa17c..9ca6c0749322 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -40,57 +40,84 @@ static bool keyChangeCapsLock = false; /* Keep track of the current mouse up/down state for open/closed cursor hand */ static bool leftMouseGrabbing = false; -/* Keep track of whether stdin has been received */ -static bool stdin_received = false; -static bool stdin_sigint = false; // Global variable to store the original SIGINT handler static PyOS_sighandler_t originalSigintAction = NULL; -// Signal handler for SIGINT, only sets a flag to exit the run loop +// Stop the current app's run loop, sending an event to ensure it actually stops +static void stopWithEvent() { + [NSApp stop: nil]; + // Post an event to trigger the actual stopping. + [NSApp postEvent: [NSEvent otherEventWithType: NSEventTypeApplicationDefined + location: NSZeroPoint + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: 0 + data1: 0 + data2: 0] + atStart: YES]; +} + +// Signal handler for SIGINT, only argument matching for stopWithEvent static void handleSigint(int signal) { - stdin_sigint = true; + stopWithEvent(); +} + +// Helper function to flush all events. +// This is needed in some instances to ensure e.g. that windows are properly closed. +// It is used in the input hook as well as wrapped in a version callable from Python. +static void flushEvents() { + while (true) { + NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny + untilDate: [NSDate distantPast] + inMode: NSDefaultRunLoopMode + dequeue: YES]; + if (!event) { + break; + } + [NSApp sendEvent:event]; + } } static int wait_for_stdin() { - @autoreleasepool { - stdin_received = false; - stdin_sigint = false; + // Short circuit if no windows are active + // Rely on Python's input handling to manage CPU usage + // This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still + // need to be processed to properly close the windows. + if (![[NSApp windows] count]) { + flushEvents(); + return 1; + } + @autoreleasepool { // Set up a SIGINT handler to interrupt the event loop if ctrl+c comes in too originalSigintAction = PyOS_setsig(SIGINT, handleSigint); // Create an NSFileHandle for standard input NSFileHandle *stdinHandle = [NSFileHandle fileHandleWithStandardInput]; + // Register for data available notifications on standard input - [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification - object: stdinHandle - queue: [NSOperationQueue mainQueue] // Use the main queue - usingBlock: ^(NSNotification *notification) { - // Mark that input has been received - stdin_received = true; - } + id notificationID = [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification + object: stdinHandle + queue: [NSOperationQueue mainQueue] // Use the main queue + usingBlock: ^(NSNotification *notification) {stopWithEvent();} ]; // Wait in the background for anything that happens to stdin [stdinHandle waitForDataInBackgroundAndNotify]; - // continuously run an event loop until the stdin_received flag is set to exit - while (!stdin_received && !stdin_sigint) { - while (true) { - NSEvent *event = [NSApp nextEventMatchingMask: NSEventMaskAny - untilDate: [NSDate distantPast] - inMode: NSDefaultRunLoopMode - dequeue: YES]; - if (!event) { break; } - [NSApp sendEvent: event]; - } - } + // Run the application's event loop, which will be interrupted on stdin or SIGINT + [NSApp run]; + // Remove the input handler as an observer - [[NSNotificationCenter defaultCenter] removeObserver: stdinHandle]; + [[NSNotificationCenter defaultCenter] removeObserver: notificationID]; + // Restore the original SIGINT handler upon exiting the function PyOS_setsig(SIGINT, originalSigintAction); + return 1; } } @@ -231,20 +258,9 @@ static void lazy_init(void) { } static PyObject* -stop(PyObject* self) +stop(PyObject* self, PyObject* _ /* ignored */) { - [NSApp stop: nil]; - // Post an event to trigger the actual stopping. - [NSApp postEvent: [NSEvent otherEventWithType: NSEventTypeApplicationDefined - location: NSZeroPoint - modifierFlags: 0 - timestamp: 0 - windowNumber: 0 - context: nil - subtype: 0 - data1: 0 - data2: 0] - atStart: YES]; + stopWithEvent(); Py_RETURN_NONE; } @@ -254,20 +270,46 @@ static CGFloat _get_device_scale(CGContextRef cr) return pixelSize.width; } -bool -mpl_check_modifier( - NSUInteger modifiers, NSEventModifierFlags flag, - PyObject* list, char const* name) +bool mpl_check_button(bool present, PyObject* set, char const* name) { + PyObject* module = NULL, * cls = NULL, * button = NULL; + bool failed = ( + present + && (!(module = PyImport_ImportModule("matplotlib.backend_bases")) + || !(cls = PyObject_GetAttrString(module, "MouseButton")) + || !(button = PyObject_GetAttrString(cls, name)) + || PySet_Add(set, button))); + Py_XDECREF(module); + Py_XDECREF(cls); + Py_XDECREF(button); + return failed; +} + +PyObject* mpl_buttons() { - bool failed = false; - if (modifiers & flag) { - PyObject* py_name = NULL; - if (!(py_name = PyUnicode_FromString(name)) - || PyList_Append(list, py_name)) { - failed = true; - } - Py_XDECREF(py_name); + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* set = NULL; + NSUInteger buttons = [NSEvent pressedMouseButtons]; + + if (!(set = PySet_New(NULL)) + || mpl_check_button(buttons & (1 << 0), set, "LEFT") + || mpl_check_button(buttons & (1 << 1), set, "RIGHT") + || mpl_check_button(buttons & (1 << 2), set, "MIDDLE") + || mpl_check_button(buttons & (1 << 3), set, "BACK") + || mpl_check_button(buttons & (1 << 4), set, "FORWARD")) { + Py_CLEAR(set); // On failure, return NULL with an exception set. } + PyGILState_Release(gstate); + return set; +} + +bool mpl_check_modifier(bool present, PyObject* list, char const* name) +{ + PyObject* py_name = NULL; + bool failed = ( + present + && (!(py_name = PyUnicode_FromString(name)) + || (PyList_Append(list, py_name)))); + Py_XDECREF(py_name); return failed; } @@ -275,17 +317,14 @@ static CGFloat _get_device_scale(CGContextRef cr) { PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* list = NULL; - if (!(list = PyList_New(0))) { - goto exit; - } NSUInteger modifiers = [event modifierFlags]; - if (mpl_check_modifier(modifiers, NSEventModifierFlagControl, list, "ctrl") - || mpl_check_modifier(modifiers, NSEventModifierFlagOption, list, "alt") - || mpl_check_modifier(modifiers, NSEventModifierFlagShift, list, "shift") - || mpl_check_modifier(modifiers, NSEventModifierFlagCommand, list, "cmd")) { + if (!(list = PyList_New(0)) + || mpl_check_modifier(modifiers & NSEventModifierFlagControl, list, "ctrl") + || mpl_check_modifier(modifiers & NSEventModifierFlagOption, list, "alt") + || mpl_check_modifier(modifiers & NSEventModifierFlagShift, list, "shift") + || mpl_check_modifier(modifiers & NSEventModifierFlagCommand, list, "cmd")) { Py_CLEAR(list); // On failure, return NULL with an exception set. } -exit: PyGILState_Release(gstate); return list; } @@ -379,17 +418,12 @@ static CGFloat _get_device_scale(CGContextRef cr) // We run the app, matching any events that are waiting in the queue // to process, breaking out of the loop when no events remain and // displaying the canvas if needed. - NSEvent *event; - while (true) { - event = [NSApp nextEventMatchingMask: NSEventMaskAny - untilDate: [NSDate distantPast] - inMode: NSDefaultRunLoopMode - dequeue: YES]; - if (!event) { - break; - } - [NSApp sendEvent:event]; - } + Py_BEGIN_ALLOW_THREADS + + flushEvents(); + + Py_END_ALLOW_THREADS + [self->view displayIfNeeded]; Py_RETURN_NONE; } @@ -538,6 +572,8 @@ static CGFloat _get_device_scale(CGContextRef cr) }, }; +static PyTypeObject FigureManagerType; // forward declaration, needed in destroy() + typedef struct { PyObject_HEAD Window* window; @@ -546,6 +582,16 @@ static CGFloat _get_device_scale(CGContextRef cr) static PyObject* FigureManager_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + if (![NSThread isMainThread]) { + PyErr_SetString( + PyExc_RuntimeError, + "Cannot create a GUI FigureManager outside the main thread " + "using the MacOS backend. Use a non-interactive " + "backend like 'agg' to make plots on worker threads." + ); + return NULL; + } + lazy_init(); Window* window = [Window alloc]; if (!window) { return NULL; } @@ -652,6 +698,25 @@ static CGFloat _get_device_scale(CGContextRef cr) { [self->window close]; self->window = NULL; + + // call super(self, FigureManager).destroy() - it seems we need the + // explicit arguments, and just super() doesn't work in the C API. + PyObject *super_obj = PyObject_CallFunctionObjArgs( + (PyObject *)&PySuper_Type, + (PyObject *)&FigureManagerType, + self, + NULL + ); + if (super_obj == NULL) { + return NULL; // error + } + PyObject *result = PyObject_CallMethod(super_obj, "destroy", NULL); + Py_DECREF(super_obj); + if (result == NULL) { + return NULL; // error + } + Py_DECREF(result); + Py_RETURN_NONE; } @@ -969,7 +1034,7 @@ -(void)save_figure:(id)sender { gil_call_method(toolbar, "save_figure"); } // Make it a zero-width box if we don't have enough room rect.size.width = fmax(bounds.size.width - rect.origin.x, 0); rect.origin.x = bounds.size.width - rect.size.width; - NSTextView* messagebox = [[[NSTextView alloc] initWithFrame: rect] autorelease]; + NSTextView* messagebox = [[NSTextView alloc] initWithFrame: rect]; messagebox.textContainer.maximumNumberOfLines = 2; messagebox.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; messagebox.alignment = NSTextAlignmentRight; @@ -979,7 +1044,6 @@ -(void)save_figure:(id)sender { gil_call_method(toolbar, "save_figure"); } /* if selectable, the messagebox can become first responder, * which is not supposed to happen */ [[window contentView] addSubview: messagebox]; - [messagebox release]; [[window contentView] display]; self->messagebox = messagebox; @@ -990,6 +1054,7 @@ -(void)save_figure:(id)sender { gil_call_method(toolbar, "save_figure"); } NavigationToolbar2_dealloc(NavigationToolbar2 *self) { [self->handler release]; + [self->messagebox release]; Py_TYPE(self)->tp_free((PyObject*)self); } @@ -1227,7 +1292,7 @@ -(void)drawRect:(NSRect)rect CGContextRef cr = [[NSGraphicsContext currentContext] CGContext]; if (!(renderer = PyObject_CallMethod(canvas, "get_renderer", "")) - || !(renderer_buffer = PyObject_GetAttrString(renderer, "_renderer"))) { + || !(renderer_buffer = PyObject_CallMethod(renderer, "buffer_rgba", ""))) { PyErr_Print(); goto exit; } @@ -1439,9 +1504,9 @@ - (void)mouseMoved:(NSEvent *)event x = location.x * device_scale; y = location.y * device_scale; process_event( - "MouseEvent", "{s:s, s:O, s:i, s:i, s:N}", + "MouseEvent", "{s:s, s:O, s:i, s:i, s:N, s:N}", "name", "motion_notify_event", "canvas", canvas, "x", x, "y", y, - "modifiers", mpl_modifiers(event)); + "buttons", mpl_buttons(), "modifiers", mpl_modifiers(event)); } - (void)mouseDragged:(NSEvent *)event @@ -1452,9 +1517,9 @@ - (void)mouseDragged:(NSEvent *)event x = location.x * device_scale; y = location.y * device_scale; process_event( - "MouseEvent", "{s:s, s:O, s:i, s:i, s:N}", + "MouseEvent", "{s:s, s:O, s:i, s:i, s:N, s:N}", "name", "motion_notify_event", "canvas", canvas, "x", x, "y", y, - "modifiers", mpl_modifiers(event)); + "buttons", mpl_buttons(), "modifiers", mpl_modifiers(event)); } - (void)rightMouseDown:(NSEvent *)event { [self mouseDown: event]; } @@ -1829,7 +1894,7 @@ - (void)flagsChanged:(NSEvent *)event "written on the file descriptor given as argument.")}, {"stop", (PyCFunction)stop, - METH_NOARGS, + METH_VARARGS, PyDoc_STR("Stop the NSApp.")}, {"show", (PyCFunction)show, @@ -1861,6 +1926,9 @@ - (void)flagsChanged:(NSEvent *)event Py_XDECREF(m); return NULL; } +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif return m; } diff --git a/src/_path.h b/src/_path.h index 7f17d0bc2933..d74d5c0d8ba2 100644 --- a/src/_path.h +++ b/src/_path.h @@ -3,12 +3,12 @@ #ifndef MPL_PATH_H #define MPL_PATH_H -#include -#include -#include -#include #include +#include +#include +#include #include +#include #include "agg_conv_contour.h" #include "agg_conv_curve.h" @@ -18,7 +18,6 @@ #include "path_converters.h" #include "_backend_agg_basic_types.h" -#include "numpy_cpp.h" const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3 }; @@ -27,6 +26,8 @@ struct XY double x; double y; + XY() : x(0), y(0) {} + XY(double x_, double y_) : x(x_), y(y_) { } @@ -44,7 +45,8 @@ struct XY typedef std::vector Polygon; -void _finalize_polygon(std::vector &result, int closed_only) +inline void +_finalize_polygon(std::vector &result, bool closed_only) { if (result.size() == 0) { return; @@ -240,25 +242,17 @@ inline void points_in_path(PointArray &points, agg::trans_affine &trans, ResultArray &result) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover no_nans_t; - typedef agg::conv_curve curve_t; - typedef agg::conv_contour contour_t; - - size_t i; - for (i = 0; i < safe_first_shape(points); ++i) { + for (auto i = 0; i < safe_first_shape(points); ++i) { result[i] = false; } - if (path.total_vertices() < 3) { return; } - - transformed_path_t trans_path(path, trans); - no_nans_t no_nans_path(trans_path, true, path.has_codes()); - curve_t curved_path(no_nans_path); + auto trans_path = agg::conv_transform{path, trans}; + auto no_nans_path = PathNanRemover{trans_path, true, path.has_codes()}; + auto curved_path = agg::conv_curve{no_nans_path}; if (r != 0.0) { - contour_t contoured_path(curved_path); + auto contoured_path = agg::conv_contour{curved_path}; contoured_path.width(r); point_in_path_impl(points, contoured_path, result); } else { @@ -270,10 +264,11 @@ template inline bool point_in_path( double x, double y, const double r, PathIterator &path, agg::trans_affine &trans) { - npy_intp shape[] = {1, 2}; - numpy::array_view points(shape); - points(0, 0) = x; - points(0, 1) = y; + py::ssize_t shape[] = {1, 2}; + py::array_t points_arr(shape); + *points_arr.mutable_data(0, 0) = x; + *points_arr.mutable_data(0, 1) = y; + auto points = points_arr.mutable_unchecked<2>(); int result[1]; result[0] = 0; @@ -287,23 +282,19 @@ template inline bool point_on_path( double x, double y, const double r, PathIterator &path, agg::trans_affine &trans) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover no_nans_t; - typedef agg::conv_curve curve_t; - typedef agg::conv_stroke stroke_t; - - npy_intp shape[] = {1, 2}; - numpy::array_view points(shape); - points(0, 0) = x; - points(0, 1) = y; + py::ssize_t shape[] = {1, 2}; + py::array_t points_arr(shape); + *points_arr.mutable_data(0, 0) = x; + *points_arr.mutable_data(0, 1) = y; + auto points = points_arr.mutable_unchecked<2>(); int result[1]; result[0] = 0; - transformed_path_t trans_path(path, trans); - no_nans_t nan_removed_path(trans_path, true, path.has_codes()); - curve_t curved_path(nan_removed_path); - stroke_t stroked_path(curved_path); + auto trans_path = agg::conv_transform{path, trans}; + auto nan_removed_path = PathNanRemover{trans_path, true, path.has_codes()}; + auto curved_path = agg::conv_curve{nan_removed_path}; + auto stroked_path = agg::conv_stroke{curved_path}; stroked_path.width(r * 2.0); point_in_path_impl(points, stroked_path, result); return result[0] != 0; @@ -311,54 +302,48 @@ inline bool point_on_path( struct extent_limits { - double x0; - double y0; - double x1; - double y1; - double xm; - double ym; -}; + XY start; + XY end; + /* minpos is the minimum positive values in the data; used by log scaling. */ + XY minpos; -void reset_limits(extent_limits &e) -{ - e.x0 = std::numeric_limits::infinity(); - e.y0 = std::numeric_limits::infinity(); - e.x1 = -std::numeric_limits::infinity(); - e.y1 = -std::numeric_limits::infinity(); - /* xm and ym are the minimum positive values in the data, used - by log scaling */ - e.xm = std::numeric_limits::infinity(); - e.ym = std::numeric_limits::infinity(); -} + extent_limits() : start{0,0}, end{0,0}, minpos{0,0} { + reset(); + } -inline void update_limits(double x, double y, extent_limits &e) -{ - if (x < e.x0) - e.x0 = x; - if (y < e.y0) - e.y0 = y; - if (x > e.x1) - e.x1 = x; - if (y > e.y1) - e.y1 = y; - /* xm and ym are the minimum positive values in the data, used - by log scaling */ - if (x > 0.0 && x < e.xm) - e.xm = x; - if (y > 0.0 && y < e.ym) - e.ym = y; -} + void reset() + { + start.x = std::numeric_limits::infinity(); + start.y = std::numeric_limits::infinity(); + end.x = -std::numeric_limits::infinity(); + end.y = -std::numeric_limits::infinity(); + minpos.x = std::numeric_limits::infinity(); + minpos.y = std::numeric_limits::infinity(); + } + + void update(double x, double y) + { + start.x = std::min(start.x, x); + start.y = std::min(start.y, y); + end.x = std::max(end.x, x); + end.y = std::max(end.y, y); + if (x > 0.0) { + minpos.x = std::min(minpos.x, x); + } + if (y > 0.0) { + minpos.y = std::min(minpos.y, y); + } + } +}; template void update_path_extents(PathIterator &path, agg::trans_affine &trans, extent_limits &extents) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removed_t; double x, y; unsigned code; - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, path.has_codes()); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, true, path.has_codes()}; nan_removed.rewind(0); @@ -366,7 +351,7 @@ void update_path_extents(PathIterator &path, agg::trans_affine &trans, extent_li if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { continue; } - update_limits(x, y, extents); + extents.update(x, y); } } @@ -382,20 +367,19 @@ void get_path_collection_extents(agg::trans_affine &master_transform, throw std::runtime_error("Offsets array must have shape (N, 2)"); } - size_t Npaths = paths.size(); - size_t Noffsets = safe_first_shape(offsets); - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(safe_first_shape(transforms), N); - size_t i; + auto Npaths = paths.size(); + auto Noffsets = safe_first_shape(offsets); + auto N = std::max(Npaths, Noffsets); + auto Ntransforms = std::min(safe_first_shape(transforms), N); agg::trans_affine trans; - reset_limits(extent); + extent.reset(); - for (i = 0; i < N; ++i) { + for (auto i = 0; i < N; ++i) { typename PathGenerator::path_iterator path(paths(i % Npaths)); if (Ntransforms) { - size_t ti = i % Ntransforms; + py::ssize_t ti = i % Ntransforms; trans = agg::trans_affine(transforms(ti, 0, 0), transforms(ti, 1, 0), transforms(ti, 0, 1), @@ -429,24 +413,23 @@ void point_in_path_collection(double x, bool filled, std::vector &result) { - size_t Npaths = paths.size(); + auto Npaths = paths.size(); if (Npaths == 0) { return; } - size_t Noffsets = safe_first_shape(offsets); - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(safe_first_shape(transforms), N); - size_t i; + auto Noffsets = safe_first_shape(offsets); + auto N = std::max(Npaths, Noffsets); + auto Ntransforms = std::min(safe_first_shape(transforms), N); agg::trans_affine trans; - for (i = 0; i < N; ++i) { + for (auto i = 0; i < N; ++i) { typename PathGenerator::path_iterator path = paths(i % Npaths); if (Ntransforms) { - size_t ti = i % Ntransforms; + auto ti = i % Ntransforms; trans = agg::trans_affine(transforms(ti, 0, 0), transforms(ti, 1, 0), transforms(ti, 0, 1), @@ -483,17 +466,13 @@ bool path_in_path(PathIterator1 &a, PathIterator2 &b, agg::trans_affine &btrans) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover no_nans_t; - typedef agg::conv_curve curve_t; - if (a.total_vertices() < 3) { return false; } - transformed_path_t b_path_trans(b, btrans); - no_nans_t b_no_nans(b_path_trans, true, b.has_codes()); - curve_t b_curved(b_no_nans); + auto b_path_trans = agg::conv_transform{b, btrans}; + auto b_no_nans = PathNanRemover{b_path_trans, true, b.has_codes()}; + auto b_curved = agg::conv_curve{b_no_nans}; double x, y; b_curved.rewind(0); @@ -526,12 +505,14 @@ struct bisectx { } - inline void bisect(double sx, double sy, double px, double py, double *bx, double *by) const + inline XY bisect(const XY s, const XY p) const { - *bx = m_x; - double dx = px - sx; - double dy = py - sy; - *by = sy + dy * ((m_x - sx) / dx); + double dx = p.x - s.x; + double dy = p.y - s.y; + return { + m_x, + s.y + dy * ((m_x - s.x) / dx), + }; } }; @@ -541,9 +522,9 @@ struct xlt : public bisectx { } - inline bool is_inside(double x, double y) const + inline bool is_inside(const XY point) const { - return x <= m_x; + return point.x <= m_x; } }; @@ -553,9 +534,9 @@ struct xgt : public bisectx { } - inline bool is_inside(double x, double y) const + inline bool is_inside(const XY point) const { - return x >= m_x; + return point.x >= m_x; } }; @@ -567,12 +548,14 @@ struct bisecty { } - inline void bisect(double sx, double sy, double px, double py, double *bx, double *by) const + inline XY bisect(const XY s, const XY p) const { - *by = m_y; - double dx = px - sx; - double dy = py - sy; - *bx = sx + dx * ((m_y - sy) / dy); + double dx = p.x - s.x; + double dy = p.y - s.y; + return { + s.x + dx * ((m_y - s.y) / dy), + m_y, + }; } }; @@ -582,9 +565,9 @@ struct ylt : public bisecty { } - inline bool is_inside(double x, double y) const + inline bool is_inside(const XY point) const { - return y <= m_y; + return point.y <= m_y; } }; @@ -594,9 +577,9 @@ struct ygt : public bisecty { } - inline bool is_inside(double x, double y) const + inline bool is_inside(const XY point) const { - return y >= m_y; + return point.y >= m_y; } }; } @@ -604,7 +587,6 @@ struct ygt : public bisecty template inline void clip_to_rect_one_step(const Polygon &polygon, Polygon &result, const Filter &filter) { - double sx, sy, px, py, bx, by; bool sinside, pinside; result.clear(); @@ -612,79 +594,60 @@ inline void clip_to_rect_one_step(const Polygon &polygon, Polygon &result, const return; } - sx = polygon.back().x; - sy = polygon.back().y; - for (Polygon::const_iterator i = polygon.begin(); i != polygon.end(); ++i) { - px = i->x; - py = i->y; - - sinside = filter.is_inside(sx, sy); - pinside = filter.is_inside(px, py); + auto s = polygon.back(); + for (auto p : polygon) { + sinside = filter.is_inside(s); + pinside = filter.is_inside(p); if (sinside ^ pinside) { - filter.bisect(sx, sy, px, py, &bx, &by); - result.push_back(XY(bx, by)); + result.emplace_back(filter.bisect(s, p)); } if (pinside) { - result.push_back(XY(px, py)); + result.emplace_back(p); } - sx = px; - sy = py; + s = p; } } template -void -clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside, std::vector &results) +auto +clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside) { - double xmin, ymin, xmax, ymax; - if (rect.x1 < rect.x2) { - xmin = rect.x1; - xmax = rect.x2; - } else { - xmin = rect.x2; - xmax = rect.x1; - } - - if (rect.y1 < rect.y2) { - ymin = rect.y1; - ymax = rect.y2; - } else { - ymin = rect.y2; - ymax = rect.y1; - } + rect.normalize(); + auto xmin = rect.x1, xmax = rect.x2; + auto ymin = rect.y1, ymax = rect.y2; if (!inside) { std::swap(xmin, xmax); std::swap(ymin, ymax); } - typedef agg::conv_curve curve_t; - curve_t curve(path); + auto curve = agg::conv_curve{path}; Polygon polygon1, polygon2; - double x = 0, y = 0; + XY point; unsigned code = 0; curve.rewind(0); + std::vector results; do { // Grab the next subpath and store it in polygon1 polygon1.clear(); do { if (code == agg::path_cmd_move_to) { - polygon1.push_back(XY(x, y)); + polygon1.emplace_back(point); } - code = curve.vertex(&x, &y); + code = curve.vertex(&point.x, &point.y); if (code == agg::path_cmd_stop) { break; } if (code != agg::path_cmd_move_to) { - polygon1.push_back(XY(x, y)); + polygon1.emplace_back(point); } } while ((code & agg::path_cmd_end_poly) != agg::path_cmd_end_poly); @@ -697,12 +660,14 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside, std::vecto // Empty polygons aren't very useful, so skip them if (polygon1.size()) { - _finalize_polygon(results, 1); + _finalize_polygon(results, true); results.push_back(polygon1); } } while (code != agg::path_cmd_stop); - _finalize_polygon(results, 1); + _finalize_polygon(results, true); + + return results; } template @@ -855,18 +820,15 @@ inline bool segments_intersect(const double &x1, template bool path_intersects_path(PathIterator1 &p1, PathIterator2 &p2) { - typedef PathNanRemover no_nans_t; - typedef agg::conv_curve curve_t; - if (p1.total_vertices() < 2 || p2.total_vertices() < 2) { return false; } - no_nans_t n1(p1, true, p1.has_codes()); - no_nans_t n2(p2, true, p2.has_codes()); + auto n1 = PathNanRemover{p1, true, p1.has_codes()}, + n2 = PathNanRemover{p2, true, p2.has_codes()}; - curve_t c1(n1); - curve_t c2(n2); + auto c1 = agg::conv_curve{n1}, + c2 = agg::conv_curve{n2}; double x11, y11, x12, y12; double x21, y21, x22, y22; @@ -919,15 +881,12 @@ bool path_intersects_rectangle(PathIterator &path, double rect_x2, double rect_y2, bool filled) { - typedef PathNanRemover no_nans_t; - typedef agg::conv_curve curve_t; - if (path.total_vertices() == 0) { return false; } - no_nans_t no_nans(path, true, path.has_codes()); - curve_t curve(no_nans); + auto no_nans = PathNanRemover{path, true, path.has_codes()}; + auto curve = agg::conv_curve{no_nans}; double cx = (rect_x1 + rect_x2) * 0.5, cy = (rect_y1 + rect_y2) * 0.5; double w = fabs(rect_x1 - rect_x2), h = fabs(rect_y1 - rect_y2); @@ -962,41 +921,32 @@ void convert_path_to_polygons(PathIterator &path, agg::trans_affine &trans, double width, double height, - int closed_only, + bool closed_only, std::vector &result) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removal_t; - typedef PathClipper clipped_t; - typedef PathSimplifier simplify_t; - typedef agg::conv_curve curve_t; - bool do_clip = width != 0.0 && height != 0.0; bool simplify = path.should_simplify(); - transformed_path_t tpath(path, trans); - nan_removal_t nan_removed(tpath, true, path.has_codes()); - clipped_t clipped(nan_removed, do_clip, width, height); - simplify_t simplified(clipped, simplify, path.simplify_threshold()); - curve_t curve(simplified); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, true, path.has_codes()}; + auto clipped = PathClipper(nan_removed, do_clip, width, height); + auto simplified = PathSimplifier{clipped, simplify, path.simplify_threshold()}; + auto curve = agg::conv_curve{simplified}; - result.push_back(Polygon()); - Polygon *polygon = &result.back(); + Polygon *polygon = &result.emplace_back(); double x, y; unsigned code; while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop) { if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { - _finalize_polygon(result, 1); - result.push_back(Polygon()); - polygon = &result.back(); + _finalize_polygon(result, true); + polygon = &result.emplace_back(); } else { if (code == agg::path_cmd_move_to) { _finalize_polygon(result, closed_only); - result.push_back(Polygon()); - polygon = &result.back(); + polygon = &result.emplace_back(); } - polygon->push_back(XY(x, y)); + polygon->emplace_back(x, y); } } @@ -1005,7 +955,7 @@ void convert_path_to_polygons(PathIterator &path, template void -__cleanup_path(VertexSource &source, std::vector &vertices, std::vector &codes) +__cleanup_path(VertexSource &source, std::vector &vertices, std::vector &codes) { unsigned code; double x, y; @@ -1013,7 +963,7 @@ __cleanup_path(VertexSource &source, std::vector &vertices, std::vector< code = source.vertex(&x, &y); vertices.push_back(x); vertices.push_back(y); - codes.push_back((npy_uint8)code); + codes.push_back(static_cast(code)); } while (code != agg::path_cmd_stop); } @@ -1031,19 +981,12 @@ void cleanup_path(PathIterator &path, std::vector &vertices, std::vector &codes) { - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removal_t; - typedef PathClipper clipped_t; - typedef PathSnapper snapped_t; - typedef PathSimplifier simplify_t; - typedef agg::conv_curve curve_t; - typedef Sketch sketch_t; - - transformed_path_t tpath(path, trans); - nan_removal_t nan_removed(tpath, remove_nans, path.has_codes()); - clipped_t clipped(nan_removed, do_clip, rect); - snapped_t snapped(clipped, snap_mode, path.total_vertices(), stroke_width); - simplify_t simplified(snapped, do_simplify, path.simplify_threshold()); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, remove_nans, path.has_codes()}; + auto clipped = PathClipper{nan_removed, do_clip, rect}; + auto snapped = PathSnapper{ + clipped, snap_mode, path.total_vertices(), stroke_width}; + auto simplified = PathSimplifier{snapped, do_simplify, path.simplify_threshold()}; vertices.reserve(path.total_vertices() * 2); codes.reserve(path.total_vertices()); @@ -1051,8 +994,9 @@ void cleanup_path(PathIterator &path, if (return_curves && sketch_params.scale == 0.0) { __cleanup_path(simplified, vertices, codes); } else { - curve_t curve(simplified); - sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness); + auto curve = agg::conv_curve{simplified}; + auto sketch = Sketch{ + curve, sketch_params.scale, sketch_params.length, sketch_params.randomness}; __cleanup_path(sketch, vertices, codes); } } @@ -1060,80 +1004,66 @@ void cleanup_path(PathIterator &path, void quad2cubic(double x0, double y0, double x1, double y1, double x2, double y2, - double *outx, double *outy) + std::array &outx, std::array &outy) { - - outx[0] = x0 + 2./3. * (x1 - x0); - outy[0] = y0 + 2./3. * (y1 - y0); - outx[1] = outx[0] + 1./3. * (x2 - x0); - outy[1] = outy[0] + 1./3. * (y2 - y0); - outx[2] = x2; - outy[2] = y2; + std::get<0>(outx) = x0 + 2./3. * (x1 - x0); + std::get<0>(outy) = y0 + 2./3. * (y1 - y0); + std::get<1>(outx) = std::get<0>(outx) + 1./3. * (x2 - x0); + std::get<1>(outy) = std::get<0>(outy) + 1./3. * (y2 - y0); + std::get<2>(outx) = x2; + std::get<2>(outy) = y2; } void __add_number(double val, char format_code, int precision, std::string& buffer) { - if (precision == -1) { - // Special-case for compat with old ttconv code, which *truncated* - // values with a cast to int instead of rounding them as printf - // would do. The only point where non-integer values arise is from - // quad2cubic conversion (as we already perform a first truncation - // on Python's side), which can introduce additional floating point - // error (by adding 2/3 delta-x and then 1/3 delta-x), so compensate by - // first rounding to the closest 1/3 and then truncating. - char str[255]; - PyOS_snprintf(str, 255, "%d", (int)(round(val * 3)) / 3); - buffer += str; - } else { - char *str = PyOS_double_to_string( - val, format_code, precision, Py_DTSF_ADD_DOT_0, NULL); - // Delete trailing zeros and decimal point - char *c = str + strlen(str) - 1; // Start at last character. - // Rewind through all the zeros and, if present, the trailing decimal - // point. Py_DTSF_ADD_DOT_0 ensures we won't go past the start of str. - while (*c == '0') { - --c; - } - if (*c == '.') { - --c; - } - try { - buffer.append(str, c + 1); - } catch (std::bad_alloc& e) { - PyMem_Free(str); - throw e; - } + char *str = PyOS_double_to_string( + val, format_code, precision, Py_DTSF_ADD_DOT_0, nullptr); + // Delete trailing zeros and decimal point + char *c = str + strlen(str) - 1; // Start at last character. + // Rewind through all the zeros and, if present, the trailing decimal + // point. Py_DTSF_ADD_DOT_0 ensures we won't go past the start of str. + while (*c == '0') { + --c; + } + if (*c == '.') { + --c; + } + try { + buffer.append(str, c + 1); + } catch (std::bad_alloc& e) { PyMem_Free(str); + throw e; } + PyMem_Free(str); } template bool __convert_to_string(PathIterator &path, int precision, - char **codes, + const std::array &codes, bool postfix, std::string& buffer) { const char format_code = 'f'; - double x[3]; - double y[3]; + std::array x; + std::array y; double last_x = 0.0; double last_y = 0.0; unsigned code; - while ((code = path.vertex(&x[0], &y[0])) != agg::path_cmd_stop) { + while ((code = path.vertex(&std::get<0>(x), &std::get<0>(y))) != agg::path_cmd_stop) { if (code == CLOSEPOLY) { - buffer += codes[4]; + buffer += std::get<4>(codes); } else if (code < 5) { size_t size = NUM_VERTICES[code]; for (size_t i = 1; i < size; ++i) { - unsigned subcode = path.vertex(&x[i], &y[i]); + unsigned subcode = path.vertex(&x.at(i), &y.at(i)); if (subcode != code) { return false; } @@ -1142,29 +1072,29 @@ bool __convert_to_string(PathIterator &path, /* For formats that don't support quad curves, convert to cubic curves */ if (code == CURVE3 && codes[code - 1][0] == '\0') { - quad2cubic(last_x, last_y, x[0], y[0], x[1], y[1], x, y); + quad2cubic(last_x, last_y, x.at(0), y.at(0), x.at(1), y.at(1), x, y); code++; size = 3; } if (!postfix) { - buffer += codes[code - 1]; + buffer += codes.at(code - 1); buffer += ' '; } for (size_t i = 0; i < size; ++i) { - __add_number(x[i], format_code, precision, buffer); + __add_number(x.at(i), format_code, precision, buffer); buffer += ' '; - __add_number(y[i], format_code, precision, buffer); + __add_number(y.at(i), format_code, precision, buffer); buffer += ' '; } if (postfix) { - buffer += codes[code - 1]; + buffer += codes.at(code - 1); } - last_x = x[size - 1]; - last_y = y[size - 1]; + last_x = x.at(size - 1); + last_y = y.at(size - 1); } else { // Unknown code value return false; @@ -1183,26 +1113,18 @@ bool convert_to_string(PathIterator &path, bool simplify, SketchParams sketch_params, int precision, - char **codes, + const std::array &codes, bool postfix, std::string& buffer) { - size_t buffersize; - typedef agg::conv_transform transformed_path_t; - typedef PathNanRemover nan_removal_t; - typedef PathClipper clipped_t; - typedef PathSimplifier simplify_t; - typedef agg::conv_curve curve_t; - typedef Sketch sketch_t; - bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2); - transformed_path_t tpath(path, trans); - nan_removal_t nan_removed(tpath, true, path.has_codes()); - clipped_t clipped(nan_removed, do_clip, clip_rect); - simplify_t simplified(clipped, simplify, path.simplify_threshold()); + auto tpath = agg::conv_transform{path, trans}; + auto nan_removed = PathNanRemover{tpath, true, path.has_codes()}; + auto clipped = PathClipper{nan_removed, do_clip, clip_rect}; + auto simplified = PathSimplifier{clipped, simplify, path.simplify_threshold()}; - buffersize = (size_t) path.total_vertices() * (precision + 5) * 4; + size_t buffersize = (size_t) path.total_vertices() * (precision + 5) * 4; if (buffersize == 0) { return true; } @@ -1216,25 +1138,23 @@ bool convert_to_string(PathIterator &path, if (sketch_params.scale == 0.0) { return __convert_to_string(simplified, precision, codes, postfix, buffer); } else { - curve_t curve(simplified); - sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness); + auto curve = agg::conv_curve{simplified}; + auto sketch = Sketch{ + curve, sketch_params.scale, sketch_params.length, sketch_params.randomness}; return __convert_to_string(sketch, precision, codes, postfix, buffer); } - } template -bool is_sorted_and_has_non_nan(PyArrayObject *array) +bool is_sorted_and_has_non_nan(py::array_t array) { - char* ptr = PyArray_BYTES(array); - npy_intp size = PyArray_DIM(array, 0), - stride = PyArray_STRIDE(array, 0); + auto size = array.shape(0); using limits = std::numeric_limits; T last = limits::has_infinity ? -limits::infinity() : limits::min(); bool found_non_nan = false; - for (npy_intp i = 0; i < size; ++i, ptr += stride) { - T current = *(T*)ptr; + for (auto i = 0; i < size; ++i) { + T current = *array.data(i); // The following tests !isnan(current), but also works for integral // types. (The isnan(IntegralType) overload is absent on MSVC.) if (current == current) { diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index b4eb5d19177f..802189c428d3 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -7,14 +7,11 @@ #include #include -#include "numpy_cpp.h" - #include "_path.h" #include "_backend_agg_basic_types.h" #include "py_adaptors.h" #include "py_converters.h" -#include "py_converters_11.h" namespace py = pybind11; using namespace pybind11::literals; @@ -44,17 +41,9 @@ static py::array_t Py_points_in_path(py::array_t points_obj, double r, mpl::PathIterator path, agg::trans_affine trans) { - numpy::array_view points; - - if (!convert_points(points_obj.ptr(), &points)) { - throw py::error_already_set(); - } + auto points = convert_points(points_obj); - if (!check_trailing_shape(points, "points", 2)) { - throw py::error_already_set(); - } - - py::ssize_t dims[] = { static_cast(points.size()) }; + py::ssize_t dims[] = { points.shape(0) }; py::array_t results(dims); auto results_mutable = results.mutable_unchecked<1>(); @@ -63,123 +52,46 @@ Py_points_in_path(py::array_t points_obj, double r, mpl::PathIterator pa return results; } -static py::tuple -Py_update_path_extents(mpl::PathIterator path, agg::trans_affine trans, - agg::rect_d rect, py::array_t minpos, bool ignore) -{ - bool changed; - - if (minpos.ndim() != 1) { - throw py::value_error( - "minpos must be 1D, got " + std::to_string(minpos.ndim())); - } - if (minpos.shape(0) != 2) { - throw py::value_error( - "minpos must be of length 2, got " + std::to_string(minpos.shape(0))); - } - - extent_limits e; - - if (ignore) { - reset_limits(e); - } else { - if (rect.x1 > rect.x2) { - e.x0 = std::numeric_limits::infinity(); - e.x1 = -std::numeric_limits::infinity(); - } else { - e.x0 = rect.x1; - e.x1 = rect.x2; - } - if (rect.y1 > rect.y2) { - e.y0 = std::numeric_limits::infinity(); - e.y1 = -std::numeric_limits::infinity(); - } else { - e.y0 = rect.y1; - e.y1 = rect.y2; - } - e.xm = *minpos.data(0); - e.ym = *minpos.data(1); - } - - update_path_extents(path, trans, e); - - changed = (e.x0 != rect.x1 || e.y0 != rect.y1 || e.x1 != rect.x2 || e.y1 != rect.y2 || - e.xm != *minpos.data(0) || e.ym != *minpos.data(1)); - - py::ssize_t extentsdims[] = { 2, 2 }; - py::array_t outextents(extentsdims); - *outextents.mutable_data(0, 0) = e.x0; - *outextents.mutable_data(0, 1) = e.y0; - *outextents.mutable_data(1, 0) = e.x1; - *outextents.mutable_data(1, 1) = e.y1; - - py::ssize_t minposdims[] = { 2 }; - py::array_t outminpos(minposdims); - *outminpos.mutable_data(0) = e.xm; - *outminpos.mutable_data(1) = e.ym; - - return py::make_tuple(outextents, outminpos, changed); -} - static py::tuple Py_get_path_collection_extents(agg::trans_affine master_transform, - py::object paths_obj, py::object transforms_obj, - py::object offsets_obj, agg::trans_affine offset_trans) + mpl::PathGenerator paths, + py::array_t transforms_obj, + py::array_t offsets_obj, + agg::trans_affine offset_trans) { - mpl::PathGenerator paths; - numpy::array_view transforms; - numpy::array_view offsets; + auto transforms = convert_transforms(transforms_obj); + auto offsets = convert_points(offsets_obj); extent_limits e; - if (!convert_pathgen(paths_obj.ptr(), &paths)) { - throw py::error_already_set(); - } - if (!convert_transforms(transforms_obj.ptr(), &transforms)) { - throw py::error_already_set(); - } - if (!convert_points(offsets_obj.ptr(), &offsets)) { - throw py::error_already_set(); - } - get_path_collection_extents( master_transform, paths, transforms, offsets, offset_trans, e); py::ssize_t dims[] = { 2, 2 }; py::array_t extents(dims); - *extents.mutable_data(0, 0) = e.x0; - *extents.mutable_data(0, 1) = e.y0; - *extents.mutable_data(1, 0) = e.x1; - *extents.mutable_data(1, 1) = e.y1; + *extents.mutable_data(0, 0) = e.start.x; + *extents.mutable_data(0, 1) = e.start.y; + *extents.mutable_data(1, 0) = e.end.x; + *extents.mutable_data(1, 1) = e.end.y; py::ssize_t minposdims[] = { 2 }; py::array_t minpos(minposdims); - *minpos.mutable_data(0) = e.xm; - *minpos.mutable_data(1) = e.ym; + *minpos.mutable_data(0) = e.minpos.x; + *minpos.mutable_data(1) = e.minpos.y; return py::make_tuple(extents, minpos); } static py::object Py_point_in_path_collection(double x, double y, double radius, - agg::trans_affine master_transform, py::object paths_obj, - py::object transforms_obj, py::object offsets_obj, + agg::trans_affine master_transform, mpl::PathGenerator paths, + py::array_t transforms_obj, + py::array_t offsets_obj, agg::trans_affine offset_trans, bool filled) { - mpl::PathGenerator paths; - numpy::array_view transforms; - numpy::array_view offsets; + auto transforms = convert_transforms(transforms_obj); + auto offsets = convert_points(offsets_obj); std::vector result; - if (!convert_pathgen(paths_obj.ptr(), &paths)) { - throw py::error_already_set(); - } - if (!convert_transforms(transforms_obj.ptr(), &transforms)) { - throw py::error_already_set(); - } - if (!convert_points(offsets_obj.ptr(), &offsets)) { - throw py::error_already_set(); - } - point_in_path_collection(x, y, radius, master_transform, paths, transforms, offsets, offset_trans, filled, result); @@ -197,9 +109,7 @@ Py_path_in_path(mpl::PathIterator a, agg::trans_affine atrans, static py::list Py_clip_path_to_rect(mpl::PathIterator path, agg::rect_d rect, bool inside) { - std::vector result; - - clip_path_to_rect(path, rect, inside, result); + auto result = clip_path_to_rect(path, rect, inside); return convert_polygon_vector(result); } @@ -211,9 +121,7 @@ Py_affine_transform(py::array_t(); - if(!check_trailing_shape(vertices, "vertices", 2)) { - throw py::error_already_set(); - } + check_trailing_shape(vertices, "vertices", 2); py::ssize_t dims[] = { vertices.shape(0), 2 }; py::array_t result(dims); @@ -237,13 +145,9 @@ Py_affine_transform(py::array_t bboxes_obj) { - numpy::array_view bboxes; - - if (!convert_bboxes(bboxes_obj.ptr(), &bboxes)) { - throw py::error_already_set(); - } + auto bboxes = convert_bboxes(bboxes_obj); return count_bboxes_overlapping_bbox(bbox, bboxes); } @@ -298,7 +202,7 @@ Py_cleanup_path(mpl::PathIterator path, agg::trans_affine trans, bool remove_nan bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2); std::vector vertices; - std::vector codes; + std::vector codes; cleanup_path(path, trans, remove_nans, do_clip, clip_rect, snap_mode, stroke_width, *simplify, return_curves, sketch, vertices, codes); @@ -346,16 +250,11 @@ static py::object Py_convert_to_string(mpl::PathIterator path, agg::trans_affine trans, agg::rect_d cliprect, std::optional simplify, SketchParams sketch, int precision, - std::array codes_obj, bool postfix) + const std::array &codes, bool postfix) { - char *codes[5]; std::string buffer; bool status; - for (auto i = 0; i < 5; ++i) { - codes[i] = const_cast(codes_obj[i].c_str()); - } - if (!simplify.has_value()) { simplify = path.should_simplify(); } @@ -381,60 +280,35 @@ Py_is_sorted_and_has_non_nan(py::object obj) { bool result; - PyArrayObject *array = (PyArrayObject *)PyArray_CheckFromAny( - obj.ptr(), NULL, 1, 1, NPY_ARRAY_NOTSWAPPED, NULL); - - if (array == NULL) { - throw py::error_already_set(); + py::array array = py::array::ensure(obj); + if (array.ndim() != 1) { + throw std::invalid_argument("array must be 1D"); } + auto dtype = array.dtype(); /* Handle just the most common types here, otherwise coerce to double */ - switch (PyArray_TYPE(array)) { - case NPY_INT: - result = is_sorted_and_has_non_nan(array); - break; - case NPY_LONG: - result = is_sorted_and_has_non_nan(array); - break; - case NPY_LONGLONG: - result = is_sorted_and_has_non_nan(array); - break; - case NPY_FLOAT: - result = is_sorted_and_has_non_nan(array); - break; - case NPY_DOUBLE: - result = is_sorted_and_has_non_nan(array); - break; - default: - Py_DECREF(array); - array = (PyArrayObject *)PyArray_FromObject(obj.ptr(), NPY_DOUBLE, 1, 1); - if (array == NULL) { - throw py::error_already_set(); - } - result = is_sorted_and_has_non_nan(array); + if (dtype.equal(py::dtype::of())) { + result = is_sorted_and_has_non_nan(array); + } else if (dtype.equal(py::dtype::of())) { + result = is_sorted_and_has_non_nan(array); + } else if (dtype.equal(py::dtype::of())) { + result = is_sorted_and_has_non_nan(array); + } else if (dtype.equal(py::dtype::of())) { + result = is_sorted_and_has_non_nan(array); + } else { + array = py::array_t::ensure(obj); + result = is_sorted_and_has_non_nan(array); } - Py_DECREF(array); - return result; } -PYBIND11_MODULE(_path, m) +PYBIND11_MODULE(_path, m, py::mod_gil_not_used()) { - auto ia = [m]() -> const void* { - import_array(); - return &m; - }; - if (ia() == NULL) { - throw py::error_already_set(); - } - m.def("point_in_path", &Py_point_in_path, "x"_a, "y"_a, "radius"_a, "path"_a, "trans"_a); m.def("points_in_path", &Py_points_in_path, "points"_a, "radius"_a, "path"_a, "trans"_a); - m.def("update_path_extents", &Py_update_path_extents, - "path"_a, "trans"_a, "rect"_a, "minpos"_a, "ignore"_a); m.def("get_path_collection_extents", &Py_get_path_collection_extents, "master_transform"_a, "paths"_a, "transforms"_a, "offsets"_a, "offset_transform"_a); diff --git a/src/_qhull_wrapper.cpp b/src/_qhull_wrapper.cpp index 9784a1698ba1..f8a3103b65f1 100644 --- a/src/_qhull_wrapper.cpp +++ b/src/_qhull_wrapper.cpp @@ -167,13 +167,13 @@ delaunay_impl(py::ssize_t npoints, const double* x, const double* y, } /* qhull expects a FILE* to write errors to. */ - FILE* error_file = NULL; + FILE* error_file = nullptr; if (hide_qhull_errors) { /* qhull errors are ignored by writing to OS-equivalent of /dev/null. * Rather than have OS-specific code here, instead it is determined by * meson.build and passed in via the macro MPL_DEVNULL. */ error_file = fopen(STRINGIFY(MPL_DEVNULL), "w"); - if (error_file == NULL) { + if (error_file == nullptr) { throw std::runtime_error("Could not open devnull"); } } @@ -186,7 +186,7 @@ delaunay_impl(py::ssize_t npoints, const double* x, const double* y, QhullInfo info(error_file, qh); qh_zero(qh, error_file); exitcode = qh_new_qhull(qh, ndim, (int)npoints, points.data(), False, - (char*)"qhull d Qt Qbb Qc Qz", NULL, error_file); + (char*)"qhull d Qt Qbb Qc Qz", nullptr, error_file); if (exitcode != qh_ERRnone) { std::string msg = py::str("Error in qhull Delaunay triangulation calculation: {} (exitcode={})") @@ -276,7 +276,8 @@ delaunay(const CoordArray& x, const CoordArray& y, int verbose) return delaunay_impl(npoints, x.data(), y.data(), verbose == 0); } -PYBIND11_MODULE(_qhull, m) { +PYBIND11_MODULE(_qhull, m, py::mod_gil_not_used()) +{ m.doc() = "Computing Delaunay triangulations.\n"; m.def("delaunay", &delaunay, "x"_a, "y"_a, "verbose"_a, diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index e35502fe23ff..955ce2103f90 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -7,7 +7,7 @@ // and methods of operation are now quite different. Because our review of // the codebase showed that all the code that came from PIL was removed or // rewritten, we have removed the PIL licensing information. If you want PIL, -// you can get it at https://python-pillow.org/ +// you can get it at https://python-pillow.github.io #include #include @@ -19,7 +19,14 @@ #define WIN32_LEAN_AND_MEAN // Windows 8.1 #define WINVER 0x0603 -#define _WIN32_WINNT 0x0603 +#if defined(_WIN32_WINNT) +#if _WIN32_WINNT < WINVER +#undef _WIN32_WINNT +#define _WIN32_WINNT WINVER +#endif +#else +#define _WIN32_WINNT WINVER +#endif #endif #include @@ -85,6 +92,7 @@ static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK; // Global vars for Tcl functions. We load these symbols from the tkinter // extension module or loaded Tcl libraries at run-time. static Tcl_SetVar_t TCL_SETVAR; +static Tcl_SetVar2_t TCL_SETVAR2; static void mpl_tk_blit(py::object interp_obj, const char *photo_name, @@ -166,7 +174,15 @@ DpiSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, std::string dpi = std::to_string(LOWORD(wParam)); Tcl_Interp* interp = (Tcl_Interp*)dwRefData; - TCL_SETVAR(interp, var_name.c_str(), dpi.c_str(), 0); + if (TCL_SETVAR) { + TCL_SETVAR(interp, var_name.c_str(), dpi.c_str(), 0); + } else if (TCL_SETVAR2) { + TCL_SETVAR2(interp, var_name.c_str(), NULL, dpi.c_str(), 0); + } else { + // This should be prevented at import time, and therefore unreachable. + // But defensively throw just in case. + throw std::runtime_error("Unable to call Tcl_SetVar or Tcl_SetVar2"); + } } return 0; case WM_NCDESTROY: @@ -239,13 +255,16 @@ bool load_tcl_tk(T lib) if (auto ptr = dlsym(lib, "Tcl_SetVar")) { TCL_SETVAR = (Tcl_SetVar_t)ptr; } + if (auto ptr = dlsym(lib, "Tcl_SetVar2")) { + TCL_SETVAR2 = (Tcl_SetVar2_t)ptr; + } if (auto ptr = dlsym(lib, "Tk_FindPhoto")) { TK_FIND_PHOTO = (Tk_FindPhoto_t)ptr; } if (auto ptr = dlsym(lib, "Tk_PhotoPutBlock")) { TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)ptr; } - return TCL_SETVAR && TK_FIND_PHOTO && TK_PHOTO_PUT_BLOCK; + return (TCL_SETVAR || TCL_SETVAR2) && TK_FIND_PHOTO && TK_PHOTO_PUT_BLOCK; } #ifdef WIN32_DLL @@ -293,7 +312,7 @@ load_tkinter_funcs() // Load tkinter global funcs from tkinter compiled module. // Try loading from the main program namespace first. - auto main_program = dlopen(NULL, RTLD_LAZY); + auto main_program = dlopen(nullptr, RTLD_LAZY); auto success = load_tcl_tk(main_program); // We don't need to keep a reference open as the main program always exists. if (dlclose(main_program)) { @@ -326,7 +345,7 @@ load_tkinter_funcs() } #endif // end not Windows -PYBIND11_MODULE(_tkagg, m) +PYBIND11_MODULE(_tkagg, m, py::mod_gil_not_used()) { try { load_tkinter_funcs(); @@ -336,8 +355,8 @@ PYBIND11_MODULE(_tkagg, m) throw py::error_already_set(); } - if (!TCL_SETVAR) { - throw py::import_error("Failed to load Tcl_SetVar"); + if (!(TCL_SETVAR || TCL_SETVAR2)) { + throw py::import_error("Failed to load Tcl_SetVar or Tcl_SetVar2"); } else if (!TK_FIND_PHOTO) { throw py::import_error("Failed to load Tk_FindPhoto"); } else if (!TK_PHOTO_PUT_BLOCK) { diff --git a/src/_tkmini.h b/src/_tkmini.h index 85f245815e4c..1c74cf9720f8 100644 --- a/src/_tkmini.h +++ b/src/_tkmini.h @@ -104,6 +104,9 @@ typedef int (*Tk_PhotoPutBlock_t) (Tcl_Interp *interp, Tk_PhotoHandle handle, /* Tcl_SetVar typedef */ typedef const char *(*Tcl_SetVar_t)(Tcl_Interp *interp, const char *varName, const char *newValue, int flags); +/* Tcl_SetVar2 typedef */ +typedef const char *(*Tcl_SetVar2_t)(Tcl_Interp *interp, const char *part1, const char *part2, + const char *newValue, int flags); #ifdef __cplusplus } diff --git a/src/_ttconv.cpp b/src/_ttconv.cpp deleted file mode 100644 index a99ea9d1c891..000000000000 --- a/src/_ttconv.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* - _ttconv.c - - Python wrapper for TrueType conversion library in ../ttconv. - */ - -#include -#include "pprdrv.h" -#include - -namespace py = pybind11; -using namespace pybind11::literals; - -/** - * An implementation of TTStreamWriter that writes to a Python - * file-like object. - */ -class PythonFileWriter : public TTStreamWriter -{ - py::function _write_method; - - public: - PythonFileWriter(py::object& file_object) - : _write_method(file_object.attr("write")) {} - - virtual void write(const char *a) - { - PyObject* decoded = PyUnicode_DecodeLatin1(a, strlen(a), ""); - if (decoded == NULL) { - throw py::error_already_set(); - } - _write_method(py::handle(decoded)); - Py_DECREF(decoded); - } -}; - -static void convert_ttf_to_ps( - const char *filename, - py::object &output, - int fonttype, - py::iterable* glyph_ids) -{ - PythonFileWriter output_(output); - - std::vector glyph_ids_; - if (glyph_ids) { - for (py::handle glyph_id: *glyph_ids) { - glyph_ids_.push_back(glyph_id.cast()); - } - } - - if (fonttype != 3 && fonttype != 42) { - throw py::value_error( - "fonttype must be either 3 (raw Postscript) or 42 (embedded Truetype)"); - } - - try - { - insert_ttfont(filename, output_, static_cast(fonttype), glyph_ids_); - } - catch (TTException &e) - { - throw std::runtime_error(e.getMessage()); - } - catch (...) - { - throw std::runtime_error("Unknown C++ exception"); - } -} - -PYBIND11_MODULE(_ttconv, m) { - m.doc() = "Module to handle converting and subsetting TrueType " - "fonts to Postscript Type 3, Postscript Type 42 and " - "Pdf Type 3 fonts."; - m.def("convert_ttf_to_ps", &convert_ttf_to_ps, - "filename"_a, - "output"_a, - "fonttype"_a, - "glyph_ids"_a = py::none(), - "Converts the Truetype font into a Type 3 or Type 42 Postscript font, " - "optionally subsetting the font to only the desired set of characters.\n" - "\n" - "filename is the path to a TTF font file.\n" - "output is a Python file-like object with a write method that the Postscript " - "font data will be written to.\n" - "fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. " - "Type 42 is an embedded Truetype font. Glyph subsetting is not supported " - "for Type 42 fonts within this module (needs to be done externally).\n" - "glyph_ids (optional) is a list of glyph ids (integers) to keep when " - "subsetting to a Type 3 font. If glyph_ids is not provided or is None, " - "then all glyphs will be included. If any of the glyphs specified are " - "composite glyphs, then the component glyphs will also be included." - ); -} diff --git a/src/agg_workaround.h b/src/agg_workaround.h index 476219519280..f1cba6f570d8 100644 --- a/src/agg_workaround.h +++ b/src/agg_workaround.h @@ -2,52 +2,13 @@ #define MPL_AGG_WORKAROUND_H #include "agg_pixfmt_rgba.h" +#include "agg_trans_affine.h" /********************************************************************** WORKAROUND: This class is to workaround a bug in Agg SVN where the blending of RGBA32 pixels does not preserve enough precision */ -template -struct fixed_blender_rgba_pre : agg::conv_rgba_pre -{ - typedef ColorT color_type; - typedef Order order_type; - typedef typename color_type::value_type value_type; - typedef typename color_type::calc_type calc_type; - typedef typename color_type::long_type long_type; - enum base_scale_e - { - base_shift = color_type::base_shift, - base_mask = color_type::base_mask - }; - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, - value_type alpha, agg::cover_type cover) - { - blend_pix(p, - color_type::mult_cover(cr, cover), - color_type::mult_cover(cg, cover), - color_type::mult_cover(cb, cover), - color_type::mult_cover(alpha, cover)); - } - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, - value_type alpha) - { - alpha = base_mask - alpha; - p[Order::R] = (value_type)(((p[Order::R] * alpha) >> base_shift) + cr); - p[Order::G] = (value_type)(((p[Order::G] * alpha) >> base_shift) + cg); - p[Order::B] = (value_type)(((p[Order::B] * alpha) >> base_shift) + cb); - p[Order::A] = (value_type)(base_mask - ((alpha * (base_mask - p[Order::A])) >> base_shift)); - } -}; - - template struct fixed_blender_rgba_plain : agg::conv_rgba_plain { @@ -82,4 +43,96 @@ struct fixed_blender_rgba_plain : agg::conv_rgba_plain } }; + +/********************************************************************** + This class provides higher-accuracy nearest-neighbor interpolation for + affine transforms than span_interpolator_linear by using + floating-point-based interpolation instead of integer-based +*/ + +template +class accurate_interpolator_affine_nn +{ +public: + typedef Transformer trans_type; + + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + //-------------------------------------------------------------------- + accurate_interpolator_affine_nn() {} + accurate_interpolator_affine_nn(trans_type& trans) : m_trans(&trans) {} + accurate_interpolator_affine_nn(trans_type& trans, + double x, double y, unsigned len) : + m_trans(&trans) + { + begin(x, y, len); + } + + //---------------------------------------------------------------- + const trans_type& transformer() const { return *m_trans; } + void transformer(trans_type& trans) { m_trans = &trans; } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + m_len = len - 1; + m_cur = 0; + + m_stx1 = x; + m_sty1 = y; + m_trans->transform(&m_stx1, &m_sty1); + m_stx1 *= subpixel_scale; + m_sty1 *= subpixel_scale; + + m_stx2 = x + m_len; + m_sty2 = y; + m_trans->transform(&m_stx2, &m_sty2); + m_stx2 *= subpixel_scale; + m_sty2 *= subpixel_scale; + } + + //---------------------------------------------------------------- + void resynchronize(double xe, double ye, unsigned len) + { + m_len = len - 1; + m_cur = 0; + + m_trans->transform(&xe, &ye); + m_stx2 = xe * subpixel_scale; + m_sty2 = ye * subpixel_scale; + } + + //---------------------------------------------------------------- + void operator++() + { + m_cur++; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + // Truncate instead of round because this interpolator needs to + // match the definitions for nearest-neighbor interpolation + if (m_cur == m_len) + { + *x = int(m_stx2); + *y = int(m_sty2); + } + else + { + // Add a tiny fudge amount to account for numerical precision loss + *x = int(m_stx1 + (m_stx2 - m_stx1) * m_cur / m_len + 1e-8); + *y = int(m_sty1 + (m_sty2 - m_sty1) * m_cur / m_len + 1e-8); + } + } + +private: + trans_type* m_trans; + unsigned m_len, m_cur; + double m_stx1, m_sty1, m_stx2, m_sty2; +}; #endif diff --git a/src/array.h b/src/array.h index 97d66dd4a6d2..0e8db3c4cac7 100644 --- a/src/array.h +++ b/src/array.h @@ -56,9 +56,7 @@ class empty public: typedef empty sub_t; - empty() - { - } + empty() = default; T &operator()(int i, int j = 0, int k = 0) { diff --git a/src/ft2font.cpp b/src/ft2font.cpp index b20f224715bf..dc9397dd75f0 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -1,18 +1,16 @@ /* -*- mode: c++; c-basic-offset: 4 -*- */ -#define NO_IMPORT_ARRAY +#include "ft2font.h" +#include "mplutils.h" #include +#include #include +#include #include -#include #include #include - -#include "ft2font.h" -#include "mplutils.h" -#include "numpy_cpp.h" -#include "py_exceptions.h" +#include #ifndef M_PI #define M_PI 3.14159265358979323846264338328 @@ -45,73 +43,23 @@ FT_Library _ft2Library; -// FreeType error codes; loaded as per fterror.h. -static char const* ft_error_string(FT_Error error) { -#undef __FTERRORS_H__ -#define FT_ERROR_START_LIST switch (error) { -#define FT_ERRORDEF( e, v, s ) case v: return s; -#define FT_ERROR_END_LIST default: return NULL; } -#include FT_ERRORS_H -} - -void throw_ft_error(std::string message, FT_Error error) { - char const* s = ft_error_string(error); - std::ostringstream os(""); - if (s) { - os << message << " (" << s << "; error code 0x" << std::hex << error << ")"; - } else { // Should not occur, but don't add another error from failed lookup. - os << message << " (error code 0x" << std::hex << error << ")"; - } - throw std::runtime_error(os.str()); -} - -FT2Image::FT2Image() : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) -{ -} - FT2Image::FT2Image(unsigned long width, unsigned long height) - : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) + : m_buffer((unsigned char *)calloc(width * height, 1)), m_width(width), m_height(height) { - resize(width, height); } FT2Image::~FT2Image() { - delete[] m_buffer; + free(m_buffer); } -void FT2Image::resize(long width, long height) +void draw_bitmap( + py::array_t im, FT_Bitmap *bitmap, FT_Int x, FT_Int y) { - if (width <= 0) { - width = 1; - } - if (height <= 0) { - height = 1; - } - size_t numBytes = width * height; - - if ((unsigned long)width != m_width || (unsigned long)height != m_height) { - if (numBytes > m_width * m_height) { - delete[] m_buffer; - m_buffer = NULL; - m_buffer = new unsigned char[numBytes]; - } - - m_width = (unsigned long)width; - m_height = (unsigned long)height; - } - - if (numBytes && m_buffer) { - memset(m_buffer, 0, numBytes); - } - - m_dirty = true; -} + auto buf = im.mutable_data(0); -void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) -{ - FT_Int image_width = (FT_Int)m_width; - FT_Int image_height = (FT_Int)m_height; + FT_Int image_width = (FT_Int)im.shape(1); + FT_Int image_height = (FT_Int)im.shape(0); FT_Int char_width = bitmap->width; FT_Int char_height = bitmap->rows; @@ -125,14 +73,14 @@ void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) { for (FT_Int i = y1; i < y2; ++i) { - unsigned char *dst = m_buffer + (i * image_width + x1); + unsigned char *dst = buf + (i * image_width + x1); unsigned char *src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start); for (FT_Int j = x1; j < x2; ++j, ++dst, ++src) *dst |= *src; } } else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { for (FT_Int i = y1; i < y2; ++i) { - unsigned char *dst = m_buffer + (i * image_width + x1); + unsigned char *dst = buf + (i * image_width + x1); unsigned char *src = bitmap->buffer + ((i - y_offset) * bitmap->pitch); for (FT_Int j = x1; j < x2; ++j, ++dst) { int x = (j - x1 + x_start); @@ -143,29 +91,6 @@ void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) } else { throw std::runtime_error("Unknown pixel mode"); } - - m_dirty = true; -} - -void FT2Image::draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1) -{ - if (x0 > m_width || x1 > m_width || y0 > m_height || y1 > m_height) { - throw std::runtime_error("Rect coords outside image bounds"); - } - - size_t top = y0 * m_width; - size_t bottom = y1 * m_width; - for (size_t i = x0; i < x1 + 1; ++i) { - m_buffer[i + top] = 255; - m_buffer[i + bottom] = 255; - } - - for (size_t j = y0 + 1; j < y1; ++j) { - m_buffer[x0 + j * m_width] = 255; - m_buffer[x1 + j * m_width] = 255; - } - - m_dirty = true; } void @@ -181,63 +106,28 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, m_buffer[i + j * m_width] = 255; } } - - m_dirty = true; } -static void ft_glyph_warn(FT_ULong charcode, std::set family_names) -{ - PyObject *text_helpers = NULL, *tmp = NULL; - std::set::iterator it = family_names.begin(); - std::stringstream ss; - ss<<*it; - while(++it != family_names.end()){ - ss<<", "<<*it; - } - - if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) || - !(tmp = PyObject_CallMethod(text_helpers, - "warn_on_missing_glyph", "(k, s)", - charcode, ss.str().c_str()))) { - goto exit; - } -exit: - Py_XDECREF(text_helpers); - Py_XDECREF(tmp); - if (PyErr_Occurred()) { - throw mpl::exception(); - } -} - -// ft_outline_decomposer should be passed to FT_Outline_Decompose. On the -// first pass, vertices and codes are set to NULL, and index is simply -// incremented for each vertex that should be inserted, so that it is set, at -// the end, to the total number of vertices. On a second pass, vertices and -// codes should point to correctly sized arrays, and index set again to zero, -// to get fill vertices and codes with the outline decomposition. +// ft_outline_decomposer should be passed to FT_Outline_Decompose. struct ft_outline_decomposer { - int index; - double* vertices; - unsigned char* codes; + std::vector &vertices; + std::vector &codes; }; static int ft_outline_move_to(FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - if (d->index) { - // Appending CLOSEPOLY is important to make patheffects work. - *(d->vertices++) = 0; - *(d->vertices++) = 0; - *(d->codes++) = CLOSEPOLY; - } - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = MOVETO; - } - d->index += d->index ? 2 : 1; + if (!d->vertices.empty()) { + // Appending CLOSEPOLY is important to make patheffects work. + d->vertices.push_back(0); + d->vertices.push_back(0); + d->codes.push_back(CLOSEPOLY); + } + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(MOVETO); return 0; } @@ -245,12 +135,9 @@ static int ft_outline_line_to(FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = LINETO; - } - d->index++; + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(LINETO); return 0; } @@ -258,15 +145,12 @@ static int ft_outline_conic_to(FT_Vector const* control, FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = control->x * (1. / 64.); - *(d->vertices++) = control->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE3; - *(d->codes++) = CURVE3; - } - d->index += 2; + d->vertices.push_back(control->x * (1. / 64.)); + d->vertices.push_back(control->y * (1. / 64.)); + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(CURVE3); + d->codes.push_back(CURVE3); return 0; } @@ -275,18 +159,15 @@ ft_outline_cubic_to( FT_Vector const* c1, FT_Vector const* c2, FT_Vector const* to, void* user) { ft_outline_decomposer* d = reinterpret_cast(user); - if (d->codes) { - *(d->vertices++) = c1->x * (1. / 64.); - *(d->vertices++) = c1->y * (1. / 64.); - *(d->vertices++) = c2->x * (1. / 64.); - *(d->vertices++) = c2->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - } - d->index += 3; + d->vertices.push_back(c1->x * (1. / 64.)); + d->vertices.push_back(c1->y * (1. / 64.)); + d->vertices.push_back(c2->x * (1. / 64.)); + d->vertices.push_back(c2->y * (1. / 64.)); + d->vertices.push_back(to->x * (1. / 64.)); + d->vertices.push_back(to->y * (1. / 64.)); + d->codes.push_back(CURVE4); + d->codes.push_back(CURVE4); + d->codes.push_back(CURVE4); return 0; } @@ -296,172 +177,143 @@ static FT_Outline_Funcs ft_outline_funcs = { ft_outline_conic_to, ft_outline_cubic_to}; -PyObject* -FT2Font::get_path() +void +FT2Font::get_path(std::vector &vertices, std::vector &codes) { if (!face->glyph) { - PyErr_SetString(PyExc_RuntimeError, "No glyph loaded"); - return NULL; - } - ft_outline_decomposer decomposer = {}; - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; - } - if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs. - npy_intp vertices_dims[2] = { 0, 2 }; - numpy::array_view vertices(vertices_dims); - npy_intp codes_dims[1] = { 0 }; - numpy::array_view codes(codes_dims); - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); - } - npy_intp vertices_dims[2] = { decomposer.index + 1, 2 }; - numpy::array_view vertices(vertices_dims); - npy_intp codes_dims[1] = { decomposer.index + 1 }; - numpy::array_view codes(codes_dims); - decomposer.index = 0; - decomposer.vertices = vertices.data(); - decomposer.codes = codes.data(); - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; - } - *(decomposer.vertices++) = 0; - *(decomposer.vertices++) = 0; - *(decomposer.codes++) = CLOSEPOLY; - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); -} - -FT2Font::FT2Font(FT_Open_Args &open_args, - long hinting_factor_, - std::vector &fallback_list) - : image(), face(NULL) -{ - clear(); - - FT_Error error = FT_Open_Face(_ft2Library, &open_args, 0, &face); - if (error) { - throw_ft_error("Can not load face", error); + throw std::runtime_error("No glyph loaded"); + } + ft_outline_decomposer decomposer = { + vertices, + codes, + }; + // We can make a close-enough estimate based on number of points and number of + // contours (which produce a MOVETO each), though it's slightly underestimating due + // to higher-order curves. + size_t estimated_points = static_cast(face->glyph->outline.n_contours) + + static_cast(face->glyph->outline.n_points); + vertices.reserve(2 * estimated_points); + codes.reserve(estimated_points); + if (FT_Error error = FT_Outline_Decompose( + &face->glyph->outline, &ft_outline_funcs, &decomposer)) { + throw std::runtime_error("FT_Outline_Decompose failed with error " + + std::to_string(error)); + } + if (vertices.empty()) { // Don't append CLOSEPOLY to null glyphs. + return; } + vertices.push_back(0); + vertices.push_back(0); + codes.push_back(CLOSEPOLY); +} - // set default kerning factor to 0, i.e., no kerning manipulation - kerning_factor = 0; - - // set a default fontsize 12 pt at 72dpi - hinting_factor = hinting_factor_; +FT2Font::FT2Font(long hinting_factor_, std::vector &fallback_list, + bool warn_if_used) + : warn_if_used(warn_if_used), image({1, 1}), face(nullptr), fallbacks(fallback_list), + hinting_factor(hinting_factor_), + // set default kerning factor to 0, i.e., no kerning manipulation + kerning_factor(0) +{ + clear(); +} - error = FT_Set_Char_Size(face, 12 * 64, 0, 72 * (unsigned int)hinting_factor, 72); - if (error) { - FT_Done_Face(face); - throw_ft_error("Could not set the fontsize", error); - } +FT2Font::~FT2Font() +{ + close(); +} - if (open_args.stream != NULL) { +void FT2Font::open(FT_Open_Args &open_args, FT_Long face_index) +{ + FT_CHECK(FT_Open_Face, _ft2Library, &open_args, face_index, &face); + if (open_args.stream != nullptr) { face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; } - FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; - FT_Set_Transform(face, &transform, 0); - - // Set fallbacks - std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); + // This allows us to get back to our data if we need it, though it makes a pointer + // loop, so don't set a free-function for it. + face->generic.data = this; + face->generic.finalizer = nullptr; } -FT2Font::~FT2Font() +void FT2Font::close() { - for (size_t i = 0; i < glyphs.size(); i++) { - FT_Done_Glyph(glyphs[i]); + // This should be idempotent, in case a user manually calls close before the + // destructor does. Note for example, that PyFT2Font _does_ call this before the + // base destructor to ensure internal pointers are cleared early enough. + + for (auto & glyph : glyphs) { + FT_Done_Glyph(glyph); } + glyphs.clear(); if (face) { FT_Done_Face(face); + face = nullptr; } } void FT2Font::clear() { - pen.x = 0; - pen.y = 0; + pen.x = pen.y = 0; + bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; + advance = 0; - for (size_t i = 0; i < glyphs.size(); i++) { - FT_Done_Glyph(glyphs[i]); + for (auto & glyph : glyphs) { + FT_Done_Glyph(glyph); } glyphs.clear(); - glyph_to_font.clear(); char_to_font.clear(); - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->clear(); + for (auto & fallback : fallbacks) { + fallback->clear(); } } void FT2Font::set_size(double ptsize, double dpi) { - FT_Error error = FT_Set_Char_Size( + FT_CHECK( + FT_Set_Char_Size, face, (FT_F26Dot6)(ptsize * 64), 0, (FT_UInt)(dpi * hinting_factor), (FT_UInt)dpi); - if (error) { - throw_ft_error("Could not set the fontsize", error); - } FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; - FT_Set_Transform(face, &transform, 0); + FT_Set_Transform(face, &transform, nullptr); - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->set_size(ptsize, dpi); + for (auto & fallback : fallbacks) { + fallback->set_size(ptsize, dpi); } } -void FT2Font::set_charmap(int i) +void FT2Font::_set_transform( + std::array, 2> matrix, std::array delta) { - if (i >= face->num_charmaps) { - throw std::runtime_error("i exceeds the available number of char maps"); - } - FT_CharMap charmap = face->charmaps[i]; - if (FT_Error error = FT_Set_Charmap(face, charmap)) { - throw_ft_error("Could not set the charmap", error); + FT_Matrix m = {matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1]}; + FT_Vector d = {delta[0], delta[1]}; + FT_Set_Transform(face, &m, &d); + for (auto & fallback : fallbacks) { + fallback->_set_transform(matrix, delta); } } -void FT2Font::select_charmap(unsigned long i) +void FT2Font::set_charmap(int i) { - if (FT_Error error = FT_Select_Charmap(face, (FT_Encoding)i)) { - throw_ft_error("Could not set the charmap", error); + if (i >= face->num_charmaps) { + throw std::runtime_error("i exceeds the available number of char maps"); } + FT_CHECK(FT_Set_Charmap, face, face->charmaps[i]); } -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback = false) +void FT2Font::select_charmap(unsigned long i) { - if (fallback && glyph_to_font.find(left) != glyph_to_font.end() && - glyph_to_font.find(right) != glyph_to_font.end()) { - FT2Font *left_ft_object = glyph_to_font[left]; - FT2Font *right_ft_object = glyph_to_font[right]; - if (left_ft_object != right_ft_object) { - // we do not know how to do kerning between different fonts - return 0; - } - // if left_ft_object is the same as right_ft_object, - // do the exact same thing which set_text does. - return right_ft_object->get_kerning(left, right, mode, false); - } - else - { - FT_Vector delta; - return get_kerning(left, right, mode, delta); - } + FT_CHECK(FT_Select_Charmap, face, (FT_Encoding)i); } -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta) +int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode) { if (!FT_HAS_KERNING(face)) { return 0; } + FT_Vector delta; if (!FT_Get_Kerning(face, left, right, mode, &delta)) { return (int)(delta.x) / (hinting_factor << kerning_factor); } else { @@ -472,13 +324,150 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &d void FT2Font::set_kerning_factor(int factor) { kerning_factor = factor; - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->set_kerning_factor(factor); + for (auto & fallback : fallbacks) { + fallback->set_kerning_factor(factor); + } +} + +std::vector FT2Font::layout( + std::u32string_view text, FT_Int32 flags, + std::optional> features, LanguageType languages, + std::set& glyph_seen_fonts) +{ + clear(); + + auto rq = raqm_create(); + if (!rq) { + throw std::runtime_error("failed to compute text layout"); + } + [[maybe_unused]] auto const& rq_cleanup = + std::unique_ptr, decltype(&raqm_destroy)>( + rq, raqm_destroy); + + if (!raqm_set_text(rq, reinterpret_cast(text.data()), + text.size())) + { + throw std::runtime_error("failed to set text for layout"); + } + if (!raqm_set_freetype_face(rq, face)) { + throw std::runtime_error("failed to set text face for layout"); + } + if (!raqm_set_freetype_load_flags(rq, flags)) { + throw std::runtime_error("failed to set text flags for layout"); + } + if (features) { + for (auto const& feature : *features) { + if (!raqm_add_font_feature(rq, feature.c_str(), feature.size())) { + throw std::runtime_error("failed to set font feature {}"_s.format(feature)); + } + } + } + if (languages) { + for (auto & [lang_str, start, end] : *languages) { + if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) { + throw std::runtime_error( + "failed to set language between {} and {} characters "_s + "to {!r} for layout"_s.format( + start, end, lang_str)); + } + } + } + if (!raqm_layout(rq)) { + throw std::runtime_error("failed to layout text"); + } + + std::vector> face_substitutions; + glyph_seen_fonts.insert(face->family_name); + + // Attempt to use fallback fonts if necessary. + for (auto const& fallback : fallbacks) { + size_t num_glyphs = 0; + auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs); + bool new_fallback_used = false; + + // Sort clusters (n.b. std::map is ordered), as RTL text will be returned in + // display, not source, order. + std::map cluster_missing; + for (size_t i = 0; i < num_glyphs; i++) { + auto const& rglyph = rq_glyphs[i]; + + // Sometimes multiple glyphs are necessary for a single cluster; if any are + // not found, we want to "poison" the whole set and keep them missing. + cluster_missing[rglyph.cluster] |= (rglyph.index == 0); + } + + for (auto it = cluster_missing.cbegin(); it != cluster_missing.cend(); ) { + auto [cluster, missing] = *it; + ++it; // Early change so we can access the next cluster below. + if (missing) { + auto next = (it != cluster_missing.cend()) ? it->first : text.size(); + for (auto i = cluster; i < next; i++) { + face_substitutions.emplace_back(i, fallback->face); + } + new_fallback_used = true; + } + } + + if (!new_fallback_used) { + // If we never used a fallback, then we're good to go with the existing + // layout we have already made. + break; + } + + // If a fallback was used, then re-attempt the layout with the new fonts. + if (!fallback->warn_if_used) { + glyph_seen_fonts.insert(fallback->face->family_name); + } + + raqm_clear_contents(rq); + if (!raqm_set_text(rq, + reinterpret_cast(text.data()), + text.size())) + { + throw std::runtime_error("failed to set text for layout"); + } + if (!raqm_set_freetype_face(rq, face)) { + throw std::runtime_error("failed to set text face for layout"); + } + for (auto [cluster, fallback] : face_substitutions) { + raqm_set_freetype_face_range(rq, fallback, cluster, 1); + } + if (!raqm_set_freetype_load_flags(rq, flags)) { + throw std::runtime_error("failed to set text flags for layout"); + } + if (features) { + for (auto const& feature : *features) { + if (!raqm_add_font_feature(rq, feature.c_str(), feature.size())) { + throw std::runtime_error( + "failed to set font feature {}"_s.format(feature)); + } + } + } + if (languages) { + for (auto & [lang_str, start, end] : *languages) { + if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) { + throw std::runtime_error( + "failed to set language between {} and {} characters "_s + "to {!r} for layout"_s.format( + start, end, lang_str)); + } + } + } + if (!raqm_layout(rq)) { + throw std::runtime_error("failed to layout text"); + } } + + size_t num_glyphs = 0; + auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs); + + return std::vector(rq_glyphs, rq_glyphs + num_glyphs); } void FT2Font::set_text( - size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys) + std::u32string_view text, double angle, FT_Int32 flags, + std::optional> features, LanguageType languages, + std::vector &xys) { FT_Matrix matrix; /* transformation matrix */ @@ -493,53 +482,44 @@ void FT2Font::set_text( matrix.yx = (FT_Fixed)sinangle; matrix.yy = (FT_Fixed)cosangle; - clear(); + std::set glyph_seen_fonts; + auto rq_glyphs = layout(text, flags, features, languages, glyph_seen_fonts); bbox.xMin = bbox.yMin = 32000; bbox.xMax = bbox.yMax = -32000; - FT_UInt previous = 0; - FT2Font *previous_ft_object = NULL; - - for (size_t n = 0; n < N; n++) { - FT_UInt glyph_index = 0; - FT_BBox glyph_bbox; - FT_Pos last_advance; - - FT_Error charcode_error, glyph_error; - std::set glyph_seen_fonts; - FT2Font *ft_object_with_glyph = this; - bool was_found = load_char_with_fallback(ft_object_with_glyph, glyph_index, glyphs, - char_to_font, glyph_to_font, codepoints[n], flags, - charcode_error, glyph_error, glyph_seen_fonts, false); - if (!was_found) { - ft_glyph_warn((FT_ULong)codepoints[n], glyph_seen_fonts); - // render missing glyph tofu - // come back to top-most font - ft_object_with_glyph = this; - char_to_font[codepoints[n]] = ft_object_with_glyph; - glyph_to_font[glyph_index] = ft_object_with_glyph; - ft_object_with_glyph->load_glyph(glyph_index, flags, ft_object_with_glyph, false); + for (auto const& rglyph : rq_glyphs) { + // Warn for missing glyphs. + if (rglyph.index == 0) { + ft_glyph_warn(text[rglyph.cluster], glyph_seen_fonts); + continue; } - - // retrieve kerning distance and move pen position - if ((ft_object_with_glyph == previous_ft_object) && // if both fonts are the same - ft_object_with_glyph->has_kerning() && // if the font knows how to kern - previous && glyph_index // and we really have 2 glyphs - ) { - FT_Vector delta; - pen.x += ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT, delta); + FT2Font *wrapped_font = static_cast(rglyph.ftface->generic.data); + if (wrapped_font->warn_if_used) { + ft_glyph_warn(text[rglyph.cluster], glyph_seen_fonts); } // extract glyph image and store it in our table - FT_Glyph &thisGlyph = glyphs[glyphs.size() - 1]; + FT_Error error; + error = FT_Load_Glyph(rglyph.ftface, rglyph.index, flags); + if (error) { + throw std::runtime_error("failed to load glyph"); + } + FT_Glyph thisGlyph; + error = FT_Get_Glyph(rglyph.ftface->glyph, &thisGlyph); + if (error) { + throw std::runtime_error("failed to get glyph"); + } - last_advance = ft_object_with_glyph->get_face()->glyph->advance.x; - FT_Glyph_Transform(thisGlyph, 0, &pen); - FT_Glyph_Transform(thisGlyph, &matrix, 0); + pen.x += rglyph.x_offset; + pen.y += rglyph.y_offset; + + FT_Glyph_Transform(thisGlyph, nullptr, &pen); + FT_Glyph_Transform(thisGlyph, &matrix, nullptr); xys.push_back(pen.x); xys.push_back(pen.y); + FT_BBox glyph_bbox; FT_Glyph_Get_CBox(thisGlyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph_bbox); bbox.xMin = std::min(bbox.xMin, glyph_bbox.xMin); @@ -547,11 +527,14 @@ void FT2Font::set_text( bbox.yMin = std::min(bbox.yMin, glyph_bbox.yMin); bbox.yMax = std::max(bbox.yMax, glyph_bbox.yMax); - pen.x += last_advance; - - previous = glyph_index; - previous_ft_object = ft_object_with_glyph; + if ((flags & FT_LOAD_NO_HINTING) != 0) { + pen.x += rglyph.x_advance - rglyph.x_offset; + } else { + pen.x += hinting_factor * rglyph.x_advance - rglyph.x_offset; + } + pen.y += rglyph.y_advance - rglyph.y_offset; + glyphs.push_back(thisGlyph); } FT_Vector_Transform(&pen, &matrix); @@ -571,24 +554,26 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool if (fallback && char_to_font.find(charcode) != char_to_font.end()) { ft_object = char_to_font[charcode]; // since it will be assigned to ft_object anyway - FT2Font *throwaway = NULL; + FT2Font *throwaway = nullptr; ft_object->load_char(charcode, flags, throwaway, false); } else if (fallback) { FT_UInt final_glyph_index; FT_Error charcode_error, glyph_error; FT2Font *ft_object_with_glyph = this; bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, - glyphs, char_to_font, glyph_to_font, + glyphs, char_to_font, charcode, flags, charcode_error, glyph_error, - glyph_seen_fonts, true); + glyph_seen_fonts); if (!was_found) { ft_glyph_warn(charcode, glyph_seen_fonts); if (charcode_error) { - throw_ft_error("Could not load charcode", charcode_error); + THROW_FT_ERROR("charcode loading", charcode_error); } else if (glyph_error) { - throw_ft_error("Could not load charcode", glyph_error); + THROW_FT_ERROR("charcode loading", glyph_error); } + } else if (ft_object_with_glyph->warn_if_used) { + ft_glyph_warn(charcode, glyph_seen_fonts); } ft_object = ft_object_with_glyph; } else { @@ -596,16 +581,12 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool ft_object = this; FT_UInt glyph_index = FT_Get_Char_Index(face, (FT_ULong) charcode); if (!glyph_index){ - glyph_seen_fonts.insert((face != NULL)?face->family_name: NULL); + glyph_seen_fonts.insert((face != nullptr)?face->family_name: nullptr); ft_glyph_warn((FT_ULong)charcode, glyph_seen_fonts); } - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load charcode", error); - } + FT_CHECK(FT_Load_Glyph, face, glyph_index, flags); FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); - } + FT_CHECK(FT_Get_Glyph, face->glyph, &thisGlyph); glyphs.push_back(thisGlyph); } } @@ -639,18 +620,18 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, std::unordered_map &parent_char_to_font, - std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, FT_Error &glyph_error, - std::set &glyph_seen_fonts, - bool override = false) + std::set &glyph_seen_fonts) { FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - glyph_seen_fonts.insert(face->family_name); + if (!warn_if_used) { + glyph_seen_fonts.insert(face->family_name); + } - if (glyph_index || override) { + if (glyph_index) { charcode_error = FT_Load_Glyph(face, glyph_index, flags); if (charcode_error) { return false; @@ -667,17 +648,16 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, // need to store this for anytime a character is loaded from a parent // FT2Font object or to generate a mapping of individual characters to fonts ft_object_with_glyph = this; - parent_glyph_to_font[final_glyph_index] = this; parent_char_to_font[charcode] = this; parent_glyphs.push_back(thisGlyph); return true; } else { - for (size_t i = 0; i < fallbacks.size(); ++i) { - bool was_found = fallbacks[i]->load_char_with_fallback( + for (auto & fallback : fallbacks) { + bool was_found = fallback->load_char_with_fallback( ft_object_with_glyph, final_glyph_index, parent_glyphs, - parent_char_to_font, parent_glyph_to_font, charcode, flags, - charcode_error, glyph_error, glyph_seen_fonts, override); + parent_char_to_font, charcode, flags, + charcode_error, glyph_error, glyph_seen_fonts); if (was_found) { return true; } @@ -686,36 +666,17 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, } } -void FT2Font::load_glyph(FT_UInt glyph_index, - FT_Int32 flags, - FT2Font *&ft_object, - bool fallback = false) -{ - // cache is only for parent FT2Font - if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - ft_object = glyph_to_font[glyph_index]; - } else { - ft_object = this; - } - - ft_object->load_glyph(glyph_index, flags); -} - void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load glyph", error); - } + FT_CHECK(FT_Load_Glyph, face, glyph_index, flags); FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); - } + FT_CHECK(FT_Get_Glyph, face->glyph, &thisGlyph); glyphs.push_back(thisGlyph); } FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) { - FT2Font *ft_object = NULL; + FT2Font *ft_object = nullptr; if (fallback && char_to_font.find(charcode) != char_to_font.end()) { // fallback denotes whether we want to search fallback list. // should call set_text/load_char_with_fallback to parent FT2Font before @@ -729,10 +690,9 @@ FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) return FT_Get_Char_Index(ft_object->get_face(), charcode); } -void FT2Font::get_width_height(long *width, long *height) +std::tuple FT2Font::get_width_height() { - *width = advance; - *height = bbox.yMax - bbox.yMin; + return {advance, bbox.yMax - bbox.yMin}; } long FT2Font::get_descent() @@ -740,10 +700,9 @@ long FT2Font::get_descent() return -bbox.yMin; } -void FT2Font::get_bitmap_offset(long *x, long *y) +std::tuple FT2Font::get_bitmap_offset() { - *x = bbox.xMin; - *y = 0; + return {bbox.xMin, 0}; } void FT2Font::draw_glyphs_to_bitmap(bool antialiased) @@ -751,50 +710,27 @@ void FT2Font::draw_glyphs_to_bitmap(bool antialiased) long width = (bbox.xMax - bbox.xMin) / 64 + 2; long height = (bbox.yMax - bbox.yMin) / 64 + 2; - image.resize(width, height); - - for (size_t n = 0; n < glyphs.size(); n++) { - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } + image = py::array_t{{height, width}}; + std::memset(image.mutable_data(0), 0, image.nbytes()); - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n]; + for (auto & glyph: glyphs) { + FT_CHECK( + FT_Glyph_To_Bitmap, + &glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1); + FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph; // now, draw to our target surface (convert position) // bitmap left and top in pixel, string bbox in subpixel FT_Int x = (FT_Int)(bitmap->left - (bbox.xMin * (1. / 64.))); FT_Int y = (FT_Int)((bbox.yMax * (1. / 64.)) - bitmap->top + 1); - image.draw_bitmap(&bitmap->bitmap, x, y); - } -} - -void FT2Font::get_xys(bool antialiased, std::vector &xys) -{ - for (size_t n = 0; n < glyphs.size(); n++) { - - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n]; - - // bitmap left and top in pixel, string bbox in subpixel - FT_Int x = (FT_Int)(bitmap->left - bbox.xMin * (1. / 64.)); - FT_Int y = (FT_Int)(bbox.yMax * (1. / 64.) - bitmap->top + 1); - // make sure the index is non-neg - x = x < 0 ? 0 : x; - y = y < 0 ? 0 : y; - xys.push_back(x); - xys.push_back(y); + draw_bitmap(image, &bitmap->bitmap, x, y); } } -void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased) +void FT2Font::draw_glyph_to_bitmap( + py::array_t im, + int x, int y, size_t glyphInd, bool antialiased) { FT_Vector sub_offset; sub_offset.x = 0; // int((xd - (double)x) * 64.0); @@ -804,38 +740,40 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, throw std::runtime_error("glyph num is out of range"); } - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[glyphInd], - antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, - &sub_offset, // additional translation - 1 // destroy image - ); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - + FT_CHECK( + FT_Glyph_To_Bitmap, + &glyphs[glyphInd], + antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, + &sub_offset, // additional translation + 1); // destroy image FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyphInd]; - im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y); + draw_bitmap(im, &bitmap->bitmap, x + bitmap->left, y); } -void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false) +std::string FT2Font::get_glyph_name(unsigned int glyph_number) { - if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) { - // cache is only for parent FT2Font - FT2Font *ft_object = glyph_to_font[glyph_number]; - ft_object->get_glyph_name(glyph_number, buffer, false); - return; - } + std::string buffer; + buffer.resize(128); + if (!FT_HAS_GLYPH_NAMES(face)) { /* Note that this generated name must match the name that is generated by ttconv in ttfont_CharStrings_getname. */ - PyOS_snprintf(buffer, 128, "uni%08x", glyph_number); + auto len = snprintf(buffer.data(), buffer.size(), "uni%08x", glyph_number); + if (len >= 0) { + buffer.resize(len); + } else { + throw std::runtime_error("Failed to convert glyph to standard name"); + } } else { - if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) { - throw_ft_error("Could not get glyph names", error); + FT_CHECK(FT_Get_Glyph_Name, face, glyph_number, buffer.data(), buffer.size()); + auto len = buffer.find('\0'); + if (len != buffer.npos) { + buffer.resize(len); } } + + return buffer; } long FT2Font::get_name_index(char *name) diff --git a/src/ft2font.h b/src/ft2font.h index 66b218316e90..3facec0fb244 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -6,16 +6,20 @@ #ifndef MPL_FT2FONT_H #define MPL_FT2FONT_H -#define PY_SSIZE_T_CLEAN -#include +#include +#include -#include +#include #include +#include +#include +#include #include #include extern "C" { #include +#include FT_BITMAP_H #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_OUTLINE_H @@ -24,23 +28,50 @@ extern "C" { #include FT_TRUETYPE_TABLES_H } -/* - By definition, FT_FIXED as 2 16bit values stored in a single long. - */ +#include + +namespace py = pybind11; + +// By definition, FT_FIXED as 2 16bit values stored in a single long. #define FIXED_MAJOR(val) (signed short)((val & 0xffff0000) >> 16) #define FIXED_MINOR(val) (unsigned short)(val & 0xffff) +// Error handling (error codes are loaded as described in fterror.h). +inline char const* ft_error_string(FT_Error error) { +#undef __FTERRORS_H__ +#define FT_ERROR_START_LIST switch (error) { +#define FT_ERRORDEF( e, v, s ) case v: return s; +#define FT_ERROR_END_LIST default: return NULL; } +#include FT_ERRORS_H +} + +// No more than 16 hex digits + "0x" + null byte for a 64-bit int error. +#define THROW_FT_ERROR(name, err) { \ + std::string path{__FILE__}; \ + char buf[20] = {0}; \ + snprintf(buf, sizeof buf, "%#04x", err); \ + throw std::runtime_error{ \ + name " (" \ + + path.substr(path.find_last_of("/\\") + 1) \ + + " line " + std::to_string(__LINE__) + ") failed with error " \ + + std::string{buf} + ": " + std::string{ft_error_string(err)}}; \ +} (void)0 + +#define FT_CHECK(func, ...) { \ + if (auto const& error_ = func(__VA_ARGS__)) { \ + THROW_FT_ERROR(#func, error_); \ + } \ +} (void)0 + // the FreeType string rendered into a width, height buffer class FT2Image { public: - FT2Image(); FT2Image(unsigned long width, unsigned long height); virtual ~FT2Image(); void resize(long width, long height); void draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y); - void draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); void draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); unsigned char *get_buffer() @@ -57,7 +88,6 @@ class FT2Image } private: - bool m_dirty; unsigned char *m_buffer; unsigned long m_width; unsigned long m_height; @@ -71,45 +101,52 @@ extern FT_Library _ft2Library; class FT2Font { - public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list); + using LanguageRange = std::tuple; + using LanguageType = std::optional>; + + FT2Font(long hinting_factor, std::vector &fallback_list, + bool warn_if_used); virtual ~FT2Font(); + void open(FT_Open_Args &open_args, FT_Long face_index); + void close(); void clear(); void set_size(double ptsize, double dpi); + void _set_transform( + std::array, 2> matrix, std::array delta); void set_charmap(int i); void select_charmap(unsigned long i); - void set_text( - size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector &xys); - int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback); - int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta); + std::vector layout(std::u32string_view text, FT_Int32 flags, + std::optional> features, + LanguageType languages, + std::set& glyph_seen_fonts); + void set_text(std::u32string_view codepoints, double angle, FT_Int32 flags, + std::optional> features, + LanguageType languages, std::vector &xys); + int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode); void set_kerning_factor(int factor); void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback); bool load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, std::unordered_map &parent_char_to_font, - std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, FT_Error &glyph_error, - std::set &glyph_seen_fonts, - bool override); - void load_glyph(FT_UInt glyph_index, FT_Int32 flags, FT2Font *&ft_object, bool fallback); + std::set &glyph_seen_fonts); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); - void get_width_height(long *width, long *height); - void get_bitmap_offset(long *x, long *y); + std::tuple get_width_height(); + std::tuple get_bitmap_offset(); long get_descent(); - // TODO: Since we know the size of the array upfront, we probably don't - // need to dynamically allocate like this - void get_xys(bool antialiased, std::vector &xys); void draw_glyphs_to_bitmap(bool antialiased); - void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback); + void draw_glyph_to_bitmap( + py::array_t im, + int x, int y, size_t glyphInd, bool antialiased); + std::string get_glyph_name(unsigned int glyph_number); long get_name_index(char *name); FT_UInt get_char_index(FT_ULong charcode, bool fallback); - PyObject* get_path(); + void get_path(std::vector &vertices, std::vector &codes); bool get_char_fallback_index(FT_ULong charcode, int& index) const; FT_Face const &get_face() const @@ -117,10 +154,14 @@ class FT2Font return face; } - FT2Image &get_image() + py::array_t &get_image() { return image; } + std::vector &get_glyphs() + { + return glyphs; + } FT_Glyph const &get_last_glyph() const { return glyphs.back(); @@ -142,13 +183,15 @@ class FT2Font return FT_HAS_KERNING(face); } + protected: + virtual void ft_glyph_warn(FT_ULong charcode, std::set family_names) = 0; private: - FT2Image image; + bool warn_if_used; + py::array_t image; FT_Face face; FT_Vector pen; /* untransformed origin */ std::vector glyphs; std::vector fallbacks; - std::unordered_map glyph_to_font; std::unordered_map char_to_font; FT_BBox bbox; FT_Pos advance; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 0fdb0165b462..bf345cd1d044 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1,159 +1,327 @@ -#include "mplutils.h" -#include "ft2font.h" -#include "py_converters.h" -#include "py_exceptions.h" +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include +#include -// From Python -#include +#include "ft2font.h" +#include "_enums.h" #include - -static PyObject *convert_xys_to_array(std::vector &xys) -{ - npy_intp dims[] = {(npy_intp)xys.size() / 2, 2 }; - if (dims[0] > 0) { - return PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, &xys[0]); +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +template +using double_or_ = std::variant; + +template +static T +_double_to_(const char *name, double_or_ &var) +{ + if (auto value = std::get_if(&var)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a=name, "obj_type"_a="parameter as float", + "alternative"_a="int({})"_s.format(name)); + return static_cast(*value); + } else if (auto value = std::get_if(&var)) { + return *value; } else { - return PyArray_SimpleNew(2, dims, NPY_DOUBLE); + // pybind11 will have only allowed types that match the variant, so this `else` + // can't happen. We only have this case because older macOS doesn't support + // `std::get` and using the conditional `std::get_if` means an `else` to silence + // compiler warnings about "unhandled" cases. + throw std::runtime_error("Should not happen"); } } /********************************************************************** - * FT2Image + * Enumerations * */ -typedef struct -{ - PyObject_HEAD - FT2Image *x; - Py_ssize_t shape[2]; - Py_ssize_t strides[2]; - Py_ssize_t suboffsets[2]; -} PyFT2Image; +const char *Kerning__doc__ = R"""( + Kerning modes for `.FT2Font.get_kerning`. + + For more information, see `the FreeType documentation + `_. + + .. versionadded:: 3.10 +)"""; + +P11X_DECLARE_ENUM( + "Kerning", "Enum", + {"DEFAULT", FT_KERNING_DEFAULT}, + {"UNFITTED", FT_KERNING_UNFITTED}, + {"UNSCALED", FT_KERNING_UNSCALED}, +); + +const char *FaceFlags__doc__ = R"""( + Flags returned by `FT2Font.face_flags`. + + For more information, see `the FreeType documentation + `_. + + .. versionadded:: 3.10 +)"""; + +#ifndef FT_FACE_FLAG_VARIATION // backcompat: ft 2.9.0. +#define FT_FACE_FLAG_VARIATION (1L << 15) +#endif +#ifndef FT_FACE_FLAG_SVG // backcompat: ft 2.12.0. +#define FT_FACE_FLAG_SVG (1L << 16) +#endif +#ifndef FT_FACE_FLAG_SBIX // backcompat: ft 2.12.0. +#define FT_FACE_FLAG_SBIX (1L << 17) +#endif +#ifndef FT_FACE_FLAG_SBIX_OVERLAY // backcompat: ft 2.12.0. +#define FT_FACE_FLAG_SBIX_OVERLAY (1L << 18) +#endif + +enum class FaceFlags : FT_Long { +#define DECLARE_FLAG(name) name = FT_FACE_FLAG_##name + DECLARE_FLAG(SCALABLE), + DECLARE_FLAG(FIXED_SIZES), + DECLARE_FLAG(FIXED_WIDTH), + DECLARE_FLAG(SFNT), + DECLARE_FLAG(HORIZONTAL), + DECLARE_FLAG(VERTICAL), + DECLARE_FLAG(KERNING), + DECLARE_FLAG(FAST_GLYPHS), + DECLARE_FLAG(MULTIPLE_MASTERS), + DECLARE_FLAG(GLYPH_NAMES), + DECLARE_FLAG(EXTERNAL_STREAM), + DECLARE_FLAG(HINTER), + DECLARE_FLAG(CID_KEYED), + DECLARE_FLAG(TRICKY), + DECLARE_FLAG(COLOR), + DECLARE_FLAG(VARIATION), + DECLARE_FLAG(SVG), + DECLARE_FLAG(SBIX), + DECLARE_FLAG(SBIX_OVERLAY), +#undef DECLARE_FLAG +}; -static PyTypeObject PyFT2ImageType; +P11X_DECLARE_ENUM( + "FaceFlags", "Flag", + {"SCALABLE", FaceFlags::SCALABLE}, + {"FIXED_SIZES", FaceFlags::FIXED_SIZES}, + {"FIXED_WIDTH", FaceFlags::FIXED_WIDTH}, + {"SFNT", FaceFlags::SFNT}, + {"HORIZONTAL", FaceFlags::HORIZONTAL}, + {"VERTICAL", FaceFlags::VERTICAL}, + {"KERNING", FaceFlags::KERNING}, + {"FAST_GLYPHS", FaceFlags::FAST_GLYPHS}, + {"MULTIPLE_MASTERS", FaceFlags::MULTIPLE_MASTERS}, + {"GLYPH_NAMES", FaceFlags::GLYPH_NAMES}, + {"EXTERNAL_STREAM", FaceFlags::EXTERNAL_STREAM}, + {"HINTER", FaceFlags::HINTER}, + {"CID_KEYED", FaceFlags::CID_KEYED}, + {"TRICKY", FaceFlags::TRICKY}, + {"COLOR", FaceFlags::COLOR}, + {"VARIATION", FaceFlags::VARIATION}, + {"SVG", FaceFlags::SVG}, + {"SBIX", FaceFlags::SBIX}, + {"SBIX_OVERLAY", FaceFlags::SBIX_OVERLAY}, +); + +const char *LoadFlags__doc__ = R"""( + Flags for `FT2Font.load_char`, `FT2Font.load_glyph`, and `FT2Font.set_text`. + + For more information, see `the FreeType documentation + `_. + + .. versionadded:: 3.10 +)"""; + +#ifndef FT_LOAD_COMPUTE_METRICS // backcompat: ft 2.6.1. +#define FT_LOAD_COMPUTE_METRICS (1L << 21) +#endif +#ifndef FT_LOAD_BITMAP_METRICS_ONLY // backcompat: ft 2.7.1. +#define FT_LOAD_BITMAP_METRICS_ONLY (1L << 22) +#endif +#ifndef FT_LOAD_NO_SVG // backcompat: ft 2.13.1. +#define FT_LOAD_NO_SVG (1L << 24) +#endif + +enum class LoadFlags : FT_Int32 { +#define DECLARE_FLAG(name) name = FT_LOAD_##name + DECLARE_FLAG(DEFAULT), + DECLARE_FLAG(NO_SCALE), + DECLARE_FLAG(NO_HINTING), + DECLARE_FLAG(RENDER), + DECLARE_FLAG(NO_BITMAP), + DECLARE_FLAG(VERTICAL_LAYOUT), + DECLARE_FLAG(FORCE_AUTOHINT), + DECLARE_FLAG(CROP_BITMAP), + DECLARE_FLAG(PEDANTIC), + DECLARE_FLAG(IGNORE_GLOBAL_ADVANCE_WIDTH), + DECLARE_FLAG(NO_RECURSE), + DECLARE_FLAG(IGNORE_TRANSFORM), + DECLARE_FLAG(MONOCHROME), + DECLARE_FLAG(LINEAR_DESIGN), + DECLARE_FLAG(NO_AUTOHINT), + DECLARE_FLAG(COLOR), + DECLARE_FLAG(COMPUTE_METRICS), + DECLARE_FLAG(BITMAP_METRICS_ONLY), + DECLARE_FLAG(NO_SVG), + DECLARE_FLAG(TARGET_NORMAL), + DECLARE_FLAG(TARGET_LIGHT), + DECLARE_FLAG(TARGET_MONO), + DECLARE_FLAG(TARGET_LCD), + DECLARE_FLAG(TARGET_LCD_V), +#undef DECLARE_FLAG +}; -static PyObject *PyFT2Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyFT2Image *self; - self = (PyFT2Image *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} +P11X_DECLARE_ENUM( + "LoadFlags", "Flag", + {"DEFAULT", LoadFlags::DEFAULT}, + {"NO_SCALE", LoadFlags::NO_SCALE}, + {"NO_HINTING", LoadFlags::NO_HINTING}, + {"RENDER", LoadFlags::RENDER}, + {"NO_BITMAP", LoadFlags::NO_BITMAP}, + {"VERTICAL_LAYOUT", LoadFlags::VERTICAL_LAYOUT}, + {"FORCE_AUTOHINT", LoadFlags::FORCE_AUTOHINT}, + {"CROP_BITMAP", LoadFlags::CROP_BITMAP}, + {"PEDANTIC", LoadFlags::PEDANTIC}, + {"IGNORE_GLOBAL_ADVANCE_WIDTH", LoadFlags::IGNORE_GLOBAL_ADVANCE_WIDTH}, + {"NO_RECURSE", LoadFlags::NO_RECURSE}, + {"IGNORE_TRANSFORM", LoadFlags::IGNORE_TRANSFORM}, + {"MONOCHROME", LoadFlags::MONOCHROME}, + {"LINEAR_DESIGN", LoadFlags::LINEAR_DESIGN}, + {"NO_AUTOHINT", LoadFlags::NO_AUTOHINT}, + {"COLOR", LoadFlags::COLOR}, + {"COMPUTE_METRICS", LoadFlags::COMPUTE_METRICS}, + {"BITMAP_METRICS_ONLY", LoadFlags::BITMAP_METRICS_ONLY}, + {"NO_SVG", LoadFlags::NO_SVG}, + // These must be unique, but the others can be OR'd together; I don't know if + // there's any way to really enforce that. + {"TARGET_NORMAL", LoadFlags::TARGET_NORMAL}, + {"TARGET_LIGHT", LoadFlags::TARGET_LIGHT}, + {"TARGET_MONO", LoadFlags::TARGET_MONO}, + {"TARGET_LCD", LoadFlags::TARGET_LCD}, + {"TARGET_LCD_V", LoadFlags::TARGET_LCD_V}, +); + +const char *RenderMode__doc__ = R"""( + Render modes. + + For more information, see `the FreeType documentation + `_. + + .. versionadded:: 3.10 +)"""; + +P11X_DECLARE_ENUM( + "RenderMode", "Enum", + {"NORMAL", FT_RENDER_MODE_NORMAL}, + {"LIGHT", FT_RENDER_MODE_LIGHT}, + {"MONO", FT_RENDER_MODE_MONO}, + {"LCD", FT_RENDER_MODE_LCD}, + {"LCD_V", FT_RENDER_MODE_LCD_V}, + {"SDF", FT_RENDER_MODE_SDF}, +); + +const char *StyleFlags__doc__ = R"""( + Flags returned by `FT2Font.style_flags`. + + For more information, see `the FreeType documentation + `_. + + .. versionadded:: 3.10 +)"""; + +enum class StyleFlags : FT_Long { +#define DECLARE_FLAG(name) name = FT_STYLE_FLAG_##name + NORMAL = 0, + DECLARE_FLAG(ITALIC), + DECLARE_FLAG(BOLD), +#undef DECLARE_FLAG +}; -static int PyFT2Image_init(PyFT2Image *self, PyObject *args, PyObject *kwds) -{ - double width; - double height; +P11X_DECLARE_ENUM( + "StyleFlags", "Flag", + {"NORMAL", StyleFlags::NORMAL}, + {"ITALIC", StyleFlags::ITALIC}, + {"BOLD", StyleFlags::BOLD}, +); - if (!PyArg_ParseTuple(args, "dd:FT2Image", &width, &height)) { - return -1; - } +/********************************************************************** + * FT2Image + * */ - CALL_CPP_INIT("FT2Image", (self->x = new FT2Image(width, height))); +const char *PyFT2Image__doc__ = R"""( + An image buffer for drawing glyphs. +)"""; - return 0; -} +const char *PyFT2Image_init__doc__ = R"""( + Parameters + ---------- + width, height : int + The dimensions of the image buffer. +)"""; -static void PyFT2Image_dealloc(PyFT2Image *self) -{ - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} +const char *PyFT2Image_draw_rect_filled__doc__ = R"""( + Draw a filled rectangle to the image. -const char *PyFT2Image_draw_rect__doc__ = - "draw_rect(self, x0, y0, x1, y1)\n" - "--\n\n" - "Draw an empty rectangle to the image.\n" - "\n" - ".. deprecated:: 3.8\n"; -; + Parameters + ---------- + x0, y0, x1, y1 : float + The bounds of the rectangle from (x0, y0) to (x1, y1). +)"""; -static PyObject *PyFT2Image_draw_rect(PyFT2Image *self, PyObject *args) +static void +PyFT2Image_draw_rect_filled(FT2Image *self, + double_or_ vx0, double_or_ vy0, + double_or_ vx1, double_or_ vy1) { - char const* msg = - "FT2Image.draw_rect is deprecated since Matplotlib 3.8 and will be removed " - "in Matplotlib 3.10 as it is not used in the library. If you rely on it, " - "please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - - double x0, y0, x1, y1; - - if (!PyArg_ParseTuple(args, "dddd:draw_rect", &x0, &y0, &x1, &y1)) { - return NULL; - } + auto x0 = _double_to_("x0", vx0); + auto y0 = _double_to_("y0", vy0); + auto x1 = _double_to_("x1", vx1); + auto y1 = _double_to_("y1", vy1); - CALL_CPP("draw_rect", (self->x->draw_rect(x0, y0, x1, y1))); - - Py_RETURN_NONE; + self->draw_rect_filled(x0, y0, x1, y1); } -const char *PyFT2Image_draw_rect_filled__doc__ = - "draw_rect_filled(self, x0, y0, x1, y1)\n" - "--\n\n" - "Draw a filled rectangle to the image.\n"; +/********************************************************************** + * Positioned Bitmap; owns the FT_Bitmap! + * */ -static PyObject *PyFT2Image_draw_rect_filled(PyFT2Image *self, PyObject *args) -{ - double x0, y0, x1, y1; +struct PyPositionedBitmap { + FT_Int left, top; + bool owning; + FT_Bitmap bitmap; - if (!PyArg_ParseTuple(args, "dddd:draw_rect_filled", &x0, &y0, &x1, &y1)) { - return NULL; + PyPositionedBitmap(FT_GlyphSlot slot) : + left{slot->bitmap_left}, top{slot->bitmap_top}, owning{true} + { + FT_Bitmap_Init(&bitmap); + FT_CHECK(FT_Bitmap_Convert, _ft2Library, &slot->bitmap, &bitmap, 1); } - CALL_CPP("draw_rect_filled", (self->x->draw_rect_filled(x0, y0, x1, y1))); - - Py_RETURN_NONE; -} - -static int PyFT2Image_get_buffer(PyFT2Image *self, Py_buffer *buf, int flags) -{ - FT2Image *im = self->x; - - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = im->get_buffer(); - buf->len = im->get_width() * im->get_height(); - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 2; - self->shape[0] = im->get_height(); - self->shape[1] = im->get_width(); - buf->shape = self->shape; - self->strides[0] = im->get_width(); - self->strides[1] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} - -static PyTypeObject* PyFT2Image_init_type() -{ - static PyMethodDef methods[] = { - {"draw_rect", (PyCFunction)PyFT2Image_draw_rect, METH_VARARGS, PyFT2Image_draw_rect__doc__}, - {"draw_rect_filled", (PyCFunction)PyFT2Image_draw_rect_filled, METH_VARARGS, PyFT2Image_draw_rect_filled__doc__}, - {NULL} - }; + PyPositionedBitmap(FT_BitmapGlyph bg) : + left{bg->left}, top{bg->top}, owning{true} + { + FT_Bitmap_Init(&bitmap); + FT_CHECK(FT_Bitmap_Convert, _ft2Library, &bg->bitmap, &bitmap, 1); + } - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyFT2Image_get_buffer; + PyPositionedBitmap(PyPositionedBitmap& other) = delete; // Non-copyable. - PyFT2ImageType.tp_name = "matplotlib.ft2font.FT2Image"; - PyFT2ImageType.tp_basicsize = sizeof(PyFT2Image); - PyFT2ImageType.tp_dealloc = (destructor)PyFT2Image_dealloc; - PyFT2ImageType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyFT2ImageType.tp_methods = methods; - PyFT2ImageType.tp_new = PyFT2Image_new; - PyFT2ImageType.tp_init = (initproc)PyFT2Image_init; - PyFT2ImageType.tp_as_buffer = &buffer_procs; + PyPositionedBitmap(PyPositionedBitmap&& other) : + left{other.left}, top{other.top}, owning{true}, bitmap{other.bitmap} + { + other.owning = false; // Prevent double deletion. + } - return &PyFT2ImageType; -} + ~PyPositionedBitmap() + { + if (owning) { + FT_Bitmap_Done(_ft2Library, &bitmap); + } + } +}; /********************************************************************** * Glyph @@ -161,7 +329,6 @@ static PyTypeObject* PyFT2Image_init_type() typedef struct { - PyObject_HEAD size_t glyphInd; long width; long height; @@ -175,16 +342,25 @@ typedef struct FT_BBox bbox; } PyGlyph; -static PyTypeObject PyGlyphType; +const char *PyGlyph__doc__ = R"""( + Information about a single glyph. + + You cannot create instances of this object yourself, but must use + `.FT2Font.load_char` or `.FT2Font.load_glyph` to generate one. This object may be + used in a call to `.FT2Font.draw_glyph_to_bitmap`. -static PyObject *PyGlyph_from_FT2Font(const FT2Font *font) + For more information on the various metrics, see `the FreeType documentation + `_. +)"""; + +static PyGlyph * +PyGlyph_from_FT2Font(const FT2Font *font) { const FT_Face &face = font->get_face(); const long hinting_factor = font->get_hinting_factor(); const FT_Glyph &glyph = font->get_last_glyph(); - PyGlyph *self; - self = (PyGlyph *)PyGlyphType.tp_alloc(&PyGlyphType, 0); + PyGlyph *self = new PyGlyph(); self->glyphInd = font->get_last_glyph_index(); FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_subpixels, &self->bbox); @@ -199,90 +375,88 @@ static PyObject *PyGlyph_from_FT2Font(const FT2Font *font) self->vertBearingY = face->glyph->metrics.vertBearingY; self->vertAdvance = face->glyph->metrics.vertAdvance; - return (PyObject *)self; + return self; } -static void PyGlyph_dealloc(PyGlyph *self) +static py::tuple +PyGlyph_get_bbox(PyGlyph *self) { - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *PyGlyph_get_bbox(PyGlyph *self, void *closure) -{ - return Py_BuildValue( - "llll", self->bbox.xMin, self->bbox.yMin, self->bbox.xMax, self->bbox.yMax); -} - -static PyTypeObject *PyGlyph_init_type() -{ - static PyMemberDef members[] = { - {(char *)"width", T_LONG, offsetof(PyGlyph, width), READONLY, (char *)""}, - {(char *)"height", T_LONG, offsetof(PyGlyph, height), READONLY, (char *)""}, - {(char *)"horiBearingX", T_LONG, offsetof(PyGlyph, horiBearingX), READONLY, (char *)""}, - {(char *)"horiBearingY", T_LONG, offsetof(PyGlyph, horiBearingY), READONLY, (char *)""}, - {(char *)"horiAdvance", T_LONG, offsetof(PyGlyph, horiAdvance), READONLY, (char *)""}, - {(char *)"linearHoriAdvance", T_LONG, offsetof(PyGlyph, linearHoriAdvance), READONLY, (char *)""}, - {(char *)"vertBearingX", T_LONG, offsetof(PyGlyph, vertBearingX), READONLY, (char *)""}, - {(char *)"vertBearingY", T_LONG, offsetof(PyGlyph, vertBearingY), READONLY, (char *)""}, - {(char *)"vertAdvance", T_LONG, offsetof(PyGlyph, vertAdvance), READONLY, (char *)""}, - {NULL} - }; - - static PyGetSetDef getset[] = { - {(char *)"bbox", (getter)PyGlyph_get_bbox, NULL, NULL, NULL}, - {NULL} - }; - - PyGlyphType.tp_name = "matplotlib.ft2font.Glyph"; - PyGlyphType.tp_basicsize = sizeof(PyGlyph); - PyGlyphType.tp_dealloc = (destructor)PyGlyph_dealloc; - PyGlyphType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyGlyphType.tp_members = members; - PyGlyphType.tp_getset = getset; - - return &PyGlyphType; + return py::make_tuple(self->bbox.xMin, self->bbox.yMin, + self->bbox.xMax, self->bbox.yMax); } /********************************************************************** * FT2Font * */ -struct PyFT2Font +class PyFT2Font final : public FT2Font { - PyObject_HEAD - FT2Font *x; - PyObject *py_file; + public: + using FT2Font::FT2Font; + + py::object py_file; FT_StreamRec stream; - Py_ssize_t shape[2]; - Py_ssize_t strides[2]; - Py_ssize_t suboffsets[2]; - std::vector fallbacks; + py::list fallbacks; + + ~PyFT2Font() + { + // Because destructors are called from subclass up to base class, we need to + // explicitly close the font here. Otherwise, the instance attributes here will + // be destroyed before the font itself, but those are used in the close callback. + close(); + } + + void ft_glyph_warn(FT_ULong charcode, std::set family_names) + { + std::set::iterator it = family_names.begin(); + std::stringstream ss; + ss<<*it; + while(++it != family_names.end()){ + ss<<", "<<*it; + } + + auto text_helpers = py::module_::import("matplotlib._text_helpers"); + auto warn_on_missing_glyph = text_helpers.attr("warn_on_missing_glyph"); + warn_on_missing_glyph(charcode, ss.str()); + } }; -static PyTypeObject PyFT2FontType; +const char *PyFT2Font__doc__ = R"""( + An object representing a single font face. + + Outside of the font itself and querying its properties, this object provides methods + for processing text strings into glyph shapes. -static unsigned long read_from_file_callback(FT_Stream stream, - unsigned long offset, - unsigned char *buffer, - unsigned long count) + Commonly, one will use `FT2Font.set_text` to load some glyph metrics and outlines. + Then `FT2Font.draw_glyphs_to_bitmap` and `FT2Font.get_image` may be used to get a + rendered form of the loaded string. + + For single characters, `FT2Font.load_char` or `FT2Font.load_glyph` may be used, + either directly for their return values, or to use `FT2Font.draw_glyph_to_bitmap` or + `FT2Font.get_path`. + + Useful metrics may be examined via the `Glyph` return values or + `FT2Font.get_kerning`. Most dimensions are given in 26.6 or 16.6 fixed-point + integers representing subpixels. Divide these values by 64 to produce floating-point + pixels. +)"""; + +static unsigned long +read_from_file_callback(FT_Stream stream, unsigned long offset, unsigned char *buffer, + unsigned long count) { - PyObject *py_file = ((PyFT2Font *)stream->descriptor.pointer)->py_file; - PyObject *seek_result = NULL, *read_result = NULL; + PyFT2Font *self = (PyFT2Font *)stream->descriptor.pointer; Py_ssize_t n_read = 0; - if (!(seek_result = PyObject_CallMethod(py_file, "seek", "k", offset)) - || !(read_result = PyObject_CallMethod(py_file, "read", "k", count))) { - goto exit; - } - char *tmpbuf; - if (PyBytes_AsStringAndSize(read_result, &tmpbuf, &n_read) == -1) { - goto exit; - } - memcpy(buffer, tmpbuf, n_read); -exit: - Py_XDECREF(seek_result); - Py_XDECREF(read_result); - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(py_file); + try { + char *tmpbuf; + auto seek_result = self->py_file.attr("seek")(offset); + auto read_result = self->py_file.attr("read")(count); + if (PyBytes_AsStringAndSize(read_result.ptr(), &tmpbuf, &n_read) == -1) { + throw py::error_already_set(); + } + memcpy(buffer, tmpbuf, n_read); + } catch (py::error_already_set &eas) { + eas.discard_as_unraisable(__func__); if (!count) { return 1; // Non-zero signals error, when count == 0. } @@ -290,1284 +464,1487 @@ static unsigned long read_from_file_callback(FT_Stream stream, return (unsigned long)n_read; } -static void close_file_callback(FT_Stream stream) +static void +close_file_callback(FT_Stream stream) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyFT2Font *self = (PyFT2Font *)stream->descriptor.pointer; - PyObject *close_result = NULL; - if (!(close_result = PyObject_CallMethod(self->py_file, "close", ""))) { - goto exit; - } -exit: - Py_XDECREF(close_result); - Py_CLEAR(self->py_file); - if (PyErr_Occurred()) { - PyErr_WriteUnraisable((PyObject*)self); + try { + self->py_file.attr("close")(); + } catch (py::error_already_set &eas) { + eas.discard_as_unraisable(__func__); } + self->py_file = py::object(); PyErr_Restore(type, value, traceback); } -static PyObject *PyFT2Font_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyFT2Font *self; - self = (PyFT2Font *)type->tp_alloc(type, 0); - self->x = NULL; - self->py_file = NULL; - memset(&self->stream, 0, sizeof(FT_StreamRec)); - return (PyObject *)self; -} +const char *PyFT2Font_init__doc__ = R"""( + Parameters + ---------- + filename : str, bytes, os.PathLike, or io.BinaryIO + The source of the font data in a format (ttf or ttc) that FreeType can read. + + hinting_factor : int, optional + Must be positive. Used to scale the hinting in the x-direction. + + face_index : int, optional + The index of the face in the font file to load. + + _fallback_list : list of FT2Font, optional + A list of FT2Font objects used to find missing glyphs. -const char *PyFT2Font_init__doc__ = - "FT2Font(filename, hinting_factor=8, *, _fallback_list=None, _kerning_factor=0)\n" - "--\n\n" - "Create a new FT2Font object.\n" - "\n" - "Parameters\n" - "----------\n" - "filename : str or file-like\n" - " The source of the font data in a format (ttf or ttc) that FreeType can read\n" - "\n" - "hinting_factor : int, optional\n" - " Must be positive. Used to scale the hinting in the x-direction\n" - "_fallback_list : list of FT2Font, optional\n" - " A list of FT2Font objects used to find missing glyphs.\n" - "\n" - " .. warning::\n" - " This API is both private and provisional: do not use it directly\n" - "\n" - "_kerning_factor : int, optional\n" - " Used to adjust the degree of kerning.\n" - "\n" - " .. warning::\n" - " This API is private: do not use it directly\n" - "\n" - "Attributes\n" - "----------\n" - "num_faces : int\n" - " Number of faces in file.\n" - "face_flags, style_flags : int\n" - " Face and style flags; see the ft2font constants.\n" - "num_glyphs : int\n" - " Number of glyphs in the face.\n" - "family_name, style_name : str\n" - " Face family and style name.\n" - "num_fixed_sizes : int\n" - " Number of bitmap in the face.\n" - "scalable : bool\n" - " Whether face is scalable; attributes after this one are only\n" - " defined for scalable faces.\n" - "bbox : tuple[int, int, int, int]\n" - " Face global bounding box (xmin, ymin, xmax, ymax).\n" - "units_per_EM : int\n" - " Number of font units covered by the EM.\n" - "ascender, descender : int\n" - " Ascender and descender in 26.6 units.\n" - "height : int\n" - " Height in 26.6 units; used to compute a default line spacing\n" - " (baseline-to-baseline distance).\n" - "max_advance_width, max_advance_height : int\n" - " Maximum horizontal and vertical cursor advance for all glyphs.\n" - "underline_position, underline_thickness : int\n" - " Vertical position and thickness of the underline bar.\n" - "postscript_name : str\n" - " PostScript name of the font.\n"; - -static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) + .. warning:: + This API is both private and provisional: do not use it directly. + + _warn_if_used : bool, optional + Used to trigger missing glyph warnings. + + .. warning:: + This API is private: do not use it directly. +)"""; + +static PyFT2Font * +PyFT2Font_init(py::object filename, long hinting_factor = 8, FT_Long face_index = 0, + std::optional> fallback_list = std::nullopt, + std::optional kerning_factor = std::nullopt, + bool warn_if_used = false) { - PyObject *filename = NULL, *open = NULL, *data = NULL, *fallback_list = NULL; - FT_Open_Args open_args; - long hinting_factor = 8; - int kerning_factor = 0; - const char *names[] = { - "filename", "hinting_factor", "_fallback_list", "_kerning_factor", NULL - }; + if (hinting_factor <= 0) { + throw py::value_error("hinting_factor must be greater than 0"); + } + if (kerning_factor) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.11", "name"_a="_kerning_factor", "obj_type"_a="parameter"); + } else { + kerning_factor = 0; + } + + if (face_index < 0 || face_index > 0xffff) { + throw std::range_error("face_index must be between 0 and 65535, inclusive"); + } + std::vector fallback_fonts; - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|l$Oi:FT2Font", (char **)names, &filename, - &hinting_factor, &fallback_list, &kerning_factor)) { - return -1; + if (fallback_list) { + // go through fallbacks to add them to our lists + std::copy(fallback_list->begin(), fallback_list->end(), + std::back_inserter(fallback_fonts)); } - if (hinting_factor <= 0) { - PyErr_SetString(PyExc_ValueError, - "hinting_factor must be greater than 0"); - goto exit; + + auto self = new PyFT2Font(hinting_factor, fallback_fonts, warn_if_used); + self->set_kerning_factor(*kerning_factor); + + if (fallback_list) { + // go through fallbacks to add them to our lists + for (auto item : *fallback_list) { + self->fallbacks.append(item); + } } - self->stream.base = NULL; + memset(&self->stream, 0, sizeof(FT_StreamRec)); + self->stream.base = nullptr; self->stream.size = 0x7fffffff; // Unknown size. self->stream.pos = 0; self->stream.descriptor.pointer = self; self->stream.read = &read_from_file_callback; + FT_Open_Args open_args; memset((void *)&open_args, 0, sizeof(FT_Open_Args)); open_args.flags = FT_OPEN_STREAM; open_args.stream = &self->stream; - if (fallback_list) { - if (!PyList_Check(fallback_list)) { - PyErr_SetString(PyExc_TypeError, "Fallback list must be a list"); - goto exit; - } - Py_ssize_t size = PyList_Size(fallback_list); - - // go through fallbacks once to make sure the types are right - for (Py_ssize_t i = 0; i < size; ++i) { - // this returns a borrowed reference - PyObject* item = PyList_GetItem(fallback_list, i); - if (!PyObject_IsInstance(item, PyObject_Type(reinterpret_cast(self)))) { - PyErr_SetString(PyExc_TypeError, "Fallback fonts must be FT2Font objects."); - goto exit; - } - } - // go through a second time to add them to our lists - for (Py_ssize_t i = 0; i < size; ++i) { - // this returns a borrowed reference - PyObject* item = PyList_GetItem(fallback_list, i); - // Increase the ref count, we will undo this in dealloc this makes - // sure things do not get gc'd under us! - Py_INCREF(item); - self->fallbacks.push_back(item); - // Also (locally) cache the underlying FT2Font objects. As long as - // the Python objects are kept alive, these pointer are good. - FT2Font *fback = reinterpret_cast(item)->x; - fallback_fonts.push_back(fback); - } - } - - if (PyBytes_Check(filename) || PyUnicode_Check(filename)) { - if (!(open = PyDict_GetItemString(PyEval_GetBuiltins(), "open")) // Borrowed reference. - || !(self->py_file = PyObject_CallFunction(open, "Os", filename, "rb"))) { - goto exit; - } + auto PathLike = py::module_::import("os").attr("PathLike"); + if (py::isinstance(filename) || py::isinstance(filename) || + py::isinstance(filename, PathLike)) + { + self->py_file = py::module_::import("io").attr("open")(filename, "rb"); self->stream.close = &close_file_callback; - } else if (!PyObject_HasAttrString(filename, "read") - || !(data = PyObject_CallMethod(filename, "read", "i", 0)) - || !PyBytes_Check(data)) { - PyErr_SetString(PyExc_TypeError, - "First argument must be a path to a font file or a binary-mode file object"); - Py_CLEAR(data); - goto exit; } else { + try { + // This will catch various issues: + // 1. `read` not being an attribute. + // 2. `read` raising an error. + // 3. `read` returning something other than `bytes`. + auto data = filename.attr("read")(0).cast(); + } catch (const std::exception&) { + throw py::type_error( + "First argument must be a path to a font file or a binary-mode file object"); + } self->py_file = filename; - self->stream.close = NULL; - Py_INCREF(filename); + self->stream.close = nullptr; } - Py_CLEAR(data); - CALL_CPP_FULL( - "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, fallback_fonts)), - Py_CLEAR(self->py_file), -1); + self->open(open_args, face_index); - CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor))); - -exit: - return PyErr_Occurred() ? -1 : 0; + return self; } -static void PyFT2Font_dealloc(PyFT2Font *self) +static py::object +PyFT2Font_fname(PyFT2Font *self) { - delete self->x; - for (size_t i = 0; i < self->fallbacks.size(); i++) { - Py_DECREF(self->fallbacks[i]); + if (self->stream.close) { // User passed a filename to the constructor. + return self->py_file.attr("name"); + } else { + return self->py_file; } - - Py_XDECREF(self->py_file); - Py_TYPE(self)->tp_free((PyObject *)self); } const char *PyFT2Font_clear__doc__ = - "clear(self)\n" - "--\n\n" - "Clear all the glyphs, reset for a new call to `.set_text`.\n"; - -static PyObject *PyFT2Font_clear(PyFT2Font *self, PyObject *args) -{ - CALL_CPP("clear", (self->x->clear())); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_set_size__doc__ = - "set_size(self, ptsize, dpi)\n" - "--\n\n" - "Set the point size and dpi of the text.\n"; - -static PyObject *PyFT2Font_set_size(PyFT2Font *self, PyObject *args) -{ - double ptsize; - double dpi; - - if (!PyArg_ParseTuple(args, "dd:set_size", &ptsize, &dpi)) { - return NULL; + "Clear all the glyphs, reset for a new call to `.set_text`."; + +const char *PyFT2Font_set_size__doc__ = R"""( + Set the size of the text. + + Parameters + ---------- + ptsize : float + The size of the text in points. + dpi : float + The DPI used for rendering the text. +)"""; + +const char *PyFT2Font__set_transform__doc__ = R"""( + Set the transform of the text. + + This is a low-level function, where *matrix* and *delta* are directly in + 16.16 and 26.6 formats respectively. Refer to the FreeType docs of + FT_Set_Transform for further description. + + Note, every call to `.font_manager.get_font` will reset the transform to the default + to ensure consistency across cache accesses. + + Parameters + ---------- + matrix : (2, 2) array of int + delta : (2,) array of int +)"""; + +const char *PyFT2Font_set_charmap__doc__ = R"""( + Make the i-th charmap current. + + For more details on character mapping, see the `FreeType documentation + `_. + + Parameters + ---------- + i : int + The charmap number in the range [0, `.num_charmaps`). + + See Also + -------- + .num_charmaps + .select_charmap + .get_charmap +)"""; + +const char *PyFT2Font_select_charmap__doc__ = R"""( + Select a charmap by its FT_Encoding number. + + For more details on character mapping, see the `FreeType documentation + `_. + + Parameters + ---------- + i : int + The charmap in the form defined by FreeType: + https://freetype.org/freetype2/docs/reference/ft2-character_mapping.html#ft_encoding + + See Also + -------- + .set_charmap + .get_charmap +)"""; + +const char *PyFT2Font_get_kerning__doc__ = R"""( + Get the kerning between two glyphs. + + Parameters + ---------- + left, right : int + The glyph indices. Note these are not characters nor character codes. + Use `.get_char_index` to convert character codes to glyph indices. + + mode : Kerning + A kerning mode constant: + + - ``DEFAULT`` - Return scaled and grid-fitted kerning distances. + - ``UNFITTED`` - Return scaled but un-grid-fitted kerning distances. + - ``UNSCALED`` - Return the kerning vector in original font units. + + .. versionchanged:: 3.10 + This now takes a `.ft2font.Kerning` value instead of an `int`. + + Returns + ------- + int + The kerning adjustment between the two glyphs. +)"""; + +static int +PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, + std::variant mode_or_int) +{ + FT_Kerning_Mode mode; + + if (auto value = std::get_if(&mode_or_int)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="mode", "obj_type"_a="parameter as int", + "alternative"_a="Kerning enum values"); + mode = static_cast(*value); + } else if (auto value = std::get_if(&mode_or_int)) { + mode = *value; + } else { + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("mode must be Kerning or int"); } - CALL_CPP("set_size", (self->x->set_size(ptsize, dpi))); - - Py_RETURN_NONE; + return self->get_kerning(left, right, mode); } -const char *PyFT2Font_set_charmap__doc__ = - "set_charmap(self, i)\n" - "--\n\n" - "Make the i-th charmap current.\n"; +const char *PyFT2Font_set_text__doc__ = R"""( + Set the text *string* and *angle*. -static PyObject *PyFT2Font_set_charmap(PyFT2Font *self, PyObject *args) -{ - int i; + You must call this before `.draw_glyphs_to_bitmap`. - if (!PyArg_ParseTuple(args, "i:set_charmap", &i)) { - return NULL; - } + Parameters + ---------- + string : str + The text to prepare rendering information for. + angle : float + The angle at which to render the supplied text. + flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` + Any bitwise-OR combination of the `.LoadFlags` flags. - CALL_CPP("set_charmap", (self->x->set_charmap(i))); + .. versionchanged:: 3.10 + This now takes an `.ft2font.LoadFlags` instead of an int. + features : tuple[str, ...] + The font feature tags to use for the font. - Py_RETURN_NONE; -} + Available font feature tags may be found at + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist -const char *PyFT2Font_select_charmap__doc__ = - "select_charmap(self, i)\n" - "--\n\n" - "Select a charmap by its FT_Encoding number.\n"; + .. versionadded:: 3.11 -static PyObject *PyFT2Font_select_charmap(PyFT2Font *self, PyObject *args) -{ - unsigned long i; + Returns + ------- + np.ndarray[double] + A sequence of x,y glyph positions in 26.6 subpixels; divide by 64 for pixels. +)"""; - if (!PyArg_ParseTuple(args, "k:select_charmap", &i)) { - return NULL; - } - - CALL_CPP("select_charmap", self->x->select_charmap(i)); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_get_kerning__doc__ = - "get_kerning(self, left, right, mode)\n" - "--\n\n" - "Get the kerning between *left* and *right* glyph indices.\n" - "*mode* is a kerning mode constant:\n\n" - "- KERNING_DEFAULT - Return scaled and grid-fitted kerning distances\n" - "- KERNING_UNFITTED - Return scaled but un-grid-fitted kerning distances\n" - "- KERNING_UNSCALED - Return the kerning vector in original font units\n"; - -static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args) +static py::array_t +PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0, + std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT, + std::optional> features = std::nullopt, + std::variant languages_or_str = nullptr) { - FT_UInt left, right, mode; - int result; - int fallback = 1; - - if (!PyArg_ParseTuple(args, "III:get_kerning", &left, &right, &mode)) { - return NULL; - } - - CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, (bool)fallback))); - - return PyLong_FromLong(result); -} - -const char *PyFT2Font_get_fontmap__doc__ = - "_get_fontmap(self, string)\n" - "--\n\n" - "Get a mapping between characters and the font that includes them.\n" - "A dictionary mapping unicode characters to PyFT2Font objects."; -static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyObject *textobj; - const char *names[] = { "string", NULL }; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O:_get_fontmap", (char **)names, &textobj)) { - return NULL; - } - - std::set codepoints; - size_t size; - - if (PyUnicode_Check(textobj)) { - size = PyUnicode_GET_LENGTH(textobj); - for (size_t i = 0; i < size; ++i) { - codepoints.insert(PyUnicode_ReadChar(textobj, i)); - } + std::vector xys; + LoadFlags flags; + + if (auto value = std::get_if(&flags_or_int)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + flags = static_cast(*value); + } else if (auto value = std::get_if(&flags_or_int)) { + flags = *value; } else { - PyErr_SetString(PyExc_TypeError, "string must be str"); - return NULL; + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("flags must be LoadFlags or int"); } - PyObject *char_to_font; - if (!(char_to_font = PyDict_New())) { - return NULL; - } - for (auto it = codepoints.begin(); it != codepoints.end(); ++it) { - auto x = *it; - PyObject* target_font; - int index; - if (self->x->get_char_fallback_index(x, index)) { - if (index >= 0) { - target_font = self->fallbacks[index]; - } else { - target_font = (PyObject *)self; - } - } else { - // TODO Handle recursion! - target_font = (PyObject *)self; - } - PyObject *key = NULL; - bool error = (!(key = PyUnicode_FromFormat("%c", x)) - || (PyDict_SetItem(char_to_font, key, target_font) == -1)); - Py_XDECREF(key); - if (error) { - Py_DECREF(char_to_font); - PyErr_SetString(PyExc_ValueError, "Something went very wrong"); - return NULL; - } + FT2Font::LanguageType languages; + if (auto value = std::get_if(&languages_or_str)) { + languages = std::move(*value); + } else if (auto value = std::get_if(&languages_or_str)) { + languages = std::vector{ + FT2Font::LanguageRange{*value, 0, text.size()} + }; + } else { + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("languages must be str or list of tuple"); } - return char_to_font; -} - -const char *PyFT2Font_set_text__doc__ = - "set_text(self, string, angle=0.0, flags=32)\n" - "--\n\n" - "Set the text *string* and *angle*.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "You must call this before `.draw_glyphs_to_bitmap`.\n" - "A sequence of x,y positions is returned.\n"; + self->set_text(text, angle, static_cast(flags), features, languages, xys); -static PyObject *PyFT2Font_set_text(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyObject *textobj; - double angle = 0.0; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - std::vector xys; - const char *names[] = { "string", "angle", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|di:set_text", (char **)names, &textobj, &angle, &flags)) { - return NULL; + py::ssize_t dims[] = { static_cast(xys.size()) / 2, 2 }; + py::array_t result(dims); + if (xys.size() > 0) { + memcpy(result.mutable_data(), xys.data(), result.nbytes()); } - - std::vector codepoints; - size_t size; - - if (PyUnicode_Check(textobj)) { - size = PyUnicode_GET_LENGTH(textobj); - codepoints.resize(size); - for (size_t i = 0; i < size; ++i) { - codepoints[i] = PyUnicode_ReadChar(textobj, i); - } + return result; +} + +const char *PyFT2Font_get_num_glyphs__doc__ = "Return the number of loaded glyphs."; + +const char *PyFT2Font_load_char__doc__ = R"""( + Load character in current fontfile and set glyph. + + Parameters + ---------- + charcode : int + The character code to prepare rendering information for. This code must be in + the charmap, or else a ``.notdef`` glyph may be returned instead. + flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` + Any bitwise-OR combination of the `.LoadFlags` flags. + + .. versionchanged:: 3.10 + This now takes an `.ft2font.LoadFlags` instead of an int. + + Returns + ------- + Glyph + The glyph information corresponding to the specified character. + + See Also + -------- + .load_glyph + .select_charmap + .set_charmap +)"""; + +static PyGlyph * +PyFT2Font_load_char(PyFT2Font *self, long charcode, + std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT) +{ + bool fallback = true; + FT2Font *ft_object = nullptr; + LoadFlags flags; + + if (auto value = std::get_if(&flags_or_int)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + flags = static_cast(*value); + } else if (auto value = std::get_if(&flags_or_int)) { + flags = *value; } else { - PyErr_SetString(PyExc_TypeError, "set_text requires str-input."); - return NULL; + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("flags must be LoadFlags or int"); } - uint32_t* codepoints_array = NULL; - if (size > 0) { - codepoints_array = &codepoints[0]; - } - CALL_CPP("set_text", self->x->set_text(size, codepoints_array, angle, flags, xys)); - - return convert_xys_to_array(xys); -} - -const char *PyFT2Font_get_num_glyphs__doc__ = - "get_num_glyphs(self)\n" - "--\n\n" - "Return the number of loaded glyphs.\n"; - -static PyObject *PyFT2Font_get_num_glyphs(PyFT2Font *self, PyObject *args) -{ - return PyLong_FromSize_t(self->x->get_num_glyphs()); -} - -const char *PyFT2Font_load_char__doc__ = - "load_char(self, charcode, flags=32)\n" - "--\n\n" - "Load character with *charcode* in current fontfile and set glyph.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "Return value is a Glyph object, with attributes\n\n" - "- width: glyph width\n" - "- height: glyph height\n" - "- bbox: the glyph bbox (xmin, ymin, xmax, ymax)\n" - "- horiBearingX: left side bearing in horizontal layouts\n" - "- horiBearingY: top side bearing in horizontal layouts\n" - "- horiAdvance: advance width for horizontal layout\n" - "- vertBearingX: left side bearing in vertical layouts\n" - "- vertBearingY: top side bearing in vertical layouts\n" - "- vertAdvance: advance height for vertical layout\n"; - -static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - long charcode; - int fallback = 1; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - const char *names[] = { "charcode", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|i:load_char", (char **)names, &charcode, - &flags)) { - return NULL; - } - - FT2Font *ft_object = NULL; - CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); + self->load_char(charcode, static_cast(flags), ft_object, fallback); return PyGlyph_from_FT2Font(ft_object); } -const char *PyFT2Font_load_glyph__doc__ = - "load_glyph(self, glyphindex, flags=32)\n" - "--\n\n" - "Load character with *glyphindex* in current fontfile and set glyph.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "Return value is a Glyph object, with attributes\n\n" - "- width: glyph width\n" - "- height: glyph height\n" - "- bbox: the glyph bbox (xmin, ymin, xmax, ymax)\n" - "- horiBearingX: left side bearing in horizontal layouts\n" - "- horiBearingY: top side bearing in horizontal layouts\n" - "- horiAdvance: advance width for horizontal layout\n" - "- vertBearingX: left side bearing in vertical layouts\n" - "- vertBearingY: top side bearing in vertical layouts\n" - "- vertAdvance: advance height for vertical layout\n"; - -static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - FT_UInt glyph_index; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - int fallback = 1; - const char *names[] = { "glyph_index", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "I|i:load_glyph", (char **)names, &glyph_index, - &flags)) { - return NULL; - } - - FT2Font *ft_object = NULL; - CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); +const char *PyFT2Font_load_glyph__doc__ = R"""( + Load glyph index in current fontfile and set glyph. - return PyGlyph_from_FT2Font(ft_object); -} + Note that the glyph index is specific to a font, and not universal like a Unicode + code point. -const char *PyFT2Font_get_width_height__doc__ = - "get_width_height(self)\n" - "--\n\n" - "Get the width and height in 26.6 subpixels of the current string set by `.set_text`.\n" - "The rotation of the string is accounted for. To get width and height\n" - "in pixels, divide these values by 64.\n"; + Parameters + ---------- + glyph_index : int + The glyph index to prepare rendering information for. + flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` + Any bitwise-OR combination of the `.LoadFlags` flags. -static PyObject *PyFT2Font_get_width_height(PyFT2Font *self, PyObject *args) -{ - long width, height; + .. versionchanged:: 3.10 + This now takes an `.ft2font.LoadFlags` instead of an int. - CALL_CPP("get_width_height", (self->x->get_width_height(&width, &height))); - - return Py_BuildValue("ll", width, height); -} + Returns + ------- + Glyph + The glyph information corresponding to the specified index. -const char *PyFT2Font_get_bitmap_offset__doc__ = - "get_bitmap_offset(self)\n" - "--\n\n" - "Get the (x, y) offset in 26.6 subpixels for the bitmap if ink hangs left or below (0, 0).\n" - "Since Matplotlib only supports left-to-right text, y is always 0.\n"; + See Also + -------- + .load_char +)"""; -static PyObject *PyFT2Font_get_bitmap_offset(PyFT2Font *self, PyObject *args) +static PyGlyph * +PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, + std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT) { - long x, y; + LoadFlags flags; - CALL_CPP("get_bitmap_offset", (self->x->get_bitmap_offset(&x, &y))); + if (auto value = std::get_if(&flags_or_int)) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + flags = static_cast(*value); + } else if (auto value = std::get_if(&flags_or_int)) { + flags = *value; + } else { + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("flags must be LoadFlags or int"); + } + + self->load_glyph(glyph_index, static_cast(flags)); - return Py_BuildValue("ll", x, y); + return PyGlyph_from_FT2Font(self); } -const char *PyFT2Font_get_descent__doc__ = - "get_descent(self)\n" - "--\n\n" - "Get the descent in 26.6 subpixels of the current string set by `.set_text`.\n" - "The rotation of the string is accounted for. To get the descent\n" - "in pixels, divide this value by 64.\n"; +const char *PyFT2Font_get_width_height__doc__ = R"""( + Get the dimensions of the current string set by `.set_text`. -static PyObject *PyFT2Font_get_descent(PyFT2Font *self, PyObject *args) -{ - long descent; + The rotation of the string is accounted for. - CALL_CPP("get_descent", (descent = self->x->get_descent())); + Returns + ------- + width, height : float + The width and height in 26.6 subpixels of the current string. To get width and + height in pixels, divide these values by 64. - return PyLong_FromLong(descent); -} + See Also + -------- + .get_bitmap_offset + .get_descent +)"""; -const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = - "draw_glyphs_to_bitmap(self, antialiased=True)\n" - "--\n\n" - "Draw the glyphs that were loaded by `.set_text` to the bitmap.\n" - "The bitmap size will be automatically set to include the glyphs.\n"; +const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( + Get the (x, y) offset for the bitmap if ink hangs left or below (0, 0). -static PyObject *PyFT2Font_draw_glyphs_to_bitmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - bool antialiased = true; - const char *names[] = { "antialiased", NULL }; + Since Matplotlib only supports left-to-right text, y is always 0. - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:draw_glyphs_to_bitmap", - (char **)names, &convert_bool, &antialiased)) { - return NULL; - } + Returns + ------- + x, y : float + The x and y offset in 26.6 subpixels of the bitmap. To get x and y in pixels, + divide these values by 64. - CALL_CPP("draw_glyphs_to_bitmap", (self->x->draw_glyphs_to_bitmap(antialiased))); + See Also + -------- + .get_width_height + .get_descent +)"""; - Py_RETURN_NONE; -} +const char *PyFT2Font_get_descent__doc__ = R"""( + Get the descent of the current string set by `.set_text`. -const char *PyFT2Font_get_xys__doc__ = - "get_xys(self, antialiased=True)\n" - "--\n\n" - "Get the xy locations of the current glyphs.\n" - "\n" - ".. deprecated:: 3.8\n"; + The rotation of the string is accounted for. -static PyObject *PyFT2Font_get_xys(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - char const* msg = - "FT2Font.get_xys is deprecated since Matplotlib 3.8 and will be removed in " - "Matplotlib 3.10 as it is not used in the library. If you rely on it, " - "please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } + Returns + ------- + int + The descent in 26.6 subpixels of the bitmap. To get the descent in pixels, + divide these values by 64. - bool antialiased = true; - std::vector xys; - const char *names[] = { "antialiased", NULL }; + See Also + -------- + .get_bitmap_offset + .get_width_height +)"""; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:get_xys", - (char **)names, &convert_bool, &antialiased)) { - return NULL; - } +const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( + Draw the glyphs that were loaded by `.set_text` to the bitmap. - CALL_CPP("get_xys", (self->x->get_xys(antialiased, xys))); + The bitmap size will be automatically set to include the glyphs. - return convert_xys_to_array(xys); -} + Parameters + ---------- + antialiased : bool, default: True + Whether to render glyphs 8-bit antialiased or in pure black-and-white. -const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = - "draw_glyph_to_bitmap(self, image, x, y, glyph, antialiased=True)\n" - "--\n\n" - "Draw a single glyph to the bitmap at pixel locations x, y\n" - "Note it is your responsibility to set up the bitmap manually\n" - "with ``set_bitmap_size(w, h)`` before this call is made.\n" - "\n" - "If you want automatic layout, use `.set_text` in combinations with\n" - "`.draw_glyphs_to_bitmap`. This function is instead intended for people\n" - "who want to render individual glyphs (e.g., returned by `.load_char`)\n" - "at precise locations.\n"; - -static PyObject *PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyFT2Image *image; - double xd, yd; - PyGlyph *glyph; - bool antialiased = true; - const char *names[] = { "image", "x", "y", "glyph", "antialiased", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "O!ddO!|O&:draw_glyph_to_bitmap", - (char **)names, - &PyFT2ImageType, - &image, - &xd, - &yd, - &PyGlyphType, - &glyph, - &convert_bool, - &antialiased)) { - return NULL; - } + See Also + -------- + .draw_glyph_to_bitmap +)"""; - CALL_CPP("draw_glyph_to_bitmap", - self->x->draw_glyph_to_bitmap(*(image->x), xd, yd, glyph->glyphInd, antialiased)); +const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""( + Draw a single glyph to the bitmap at pixel locations x, y. - Py_RETURN_NONE; -} + Note it is your responsibility to create the image manually with the correct size + before this call is made. -const char *PyFT2Font_get_glyph_name__doc__ = - "get_glyph_name(self, index)\n" - "--\n\n" - "Retrieve the ASCII name of a given glyph *index* in a face.\n" - "\n" - "Due to Matplotlib's internal design, for fonts that do not contain glyph\n" - "names (per FT_FACE_FLAG_GLYPH_NAMES), this returns a made-up name which\n" - "does *not* roundtrip through `.get_name_index`.\n"; + If you want automatic layout, use `.set_text` in combinations with + `.draw_glyphs_to_bitmap`. This function is instead intended for people who want to + render individual glyphs (e.g., returned by `.load_char`) at precise locations. -static PyObject *PyFT2Font_get_glyph_name(PyFT2Font *self, PyObject *args) -{ - unsigned int glyph_number; - char buffer[128]; - int fallback = 1; - - if (!PyArg_ParseTuple(args, "I:get_glyph_name", &glyph_number)) { - return NULL; - } - CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer, (bool)fallback))); - return PyUnicode_FromString(buffer); -} + Parameters + ---------- + image : 2d array of uint8 + The image buffer on which to draw the glyph. + x, y : int + The position of the glyph's top left corner. + glyph : Glyph + The glyph to draw. + antialiased : bool, default: True + Whether to render glyphs 8-bit antialiased or in pure black-and-white. -const char *PyFT2Font_get_charmap__doc__ = - "get_charmap(self)\n" - "--\n\n" - "Return a dict that maps the character codes of the selected charmap\n" - "(Unicode by default) to their corresponding glyph indices.\n"; + See Also + -------- + .draw_glyphs_to_bitmap +)"""; -static PyObject *PyFT2Font_get_charmap(PyFT2Font *self, PyObject *args) +static void +PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image, + double_or_ vxd, double_or_ vyd, + PyGlyph *glyph, bool antialiased = true) { - PyObject *charmap; - if (!(charmap = PyDict_New())) { - return NULL; - } + auto xd = _double_to_("x", vxd); + auto yd = _double_to_("y", vyd); + + self->draw_glyph_to_bitmap( + py::array_t{image}, + xd, yd, glyph->glyphInd, antialiased); +} + +const char *PyFT2Font_get_glyph_name__doc__ = R"""( + Retrieve the ASCII name of a given glyph *index* in a face. + + Due to Matplotlib's internal design, for fonts that do not contain glyph names (per + ``FT_FACE_FLAG_GLYPH_NAMES``), this returns a made-up name which does *not* + roundtrip through `.get_name_index`. + + Parameters + ---------- + index : int + The glyph number to query. + + Returns + ------- + str + The name of the glyph, or if the font does not contain names, a name synthesized + by Matplotlib. + + See Also + -------- + .get_name_index +)"""; + +const char *PyFT2Font_get_charmap__doc__ = R"""( + Return a mapping of character codes to glyph indices in the font. + + The charmap is Unicode by default, but may be changed by `.set_charmap` or + `.select_charmap`. + + Returns + ------- + dict[int, int] + A dictionary of the selected charmap mapping character codes to their + corresponding glyph indices. +)"""; + +static py::dict +PyFT2Font_get_charmap(PyFT2Font *self) +{ + py::dict charmap; FT_UInt index; - FT_ULong code = FT_Get_First_Char(self->x->get_face(), &index); + FT_ULong code = FT_Get_First_Char(self->get_face(), &index); while (index != 0) { - PyObject *key = NULL, *val = NULL; - bool error = (!(key = PyLong_FromLong(code)) - || !(val = PyLong_FromLong(index)) - || (PyDict_SetItem(charmap, key, val) == -1)); - Py_XDECREF(key); - Py_XDECREF(val); - if (error) { - Py_DECREF(charmap); - return NULL; - } - code = FT_Get_Next_Char(self->x->get_face(), code, &index); + charmap[py::cast(code)] = py::cast(index); + code = FT_Get_Next_Char(self->get_face(), code, &index); } return charmap; } +const char *PyFT2Font_get_char_index__doc__ = R"""( + Return the glyph index corresponding to a character code point. -const char *PyFT2Font_get_char_index__doc__ = - "get_char_index(self, codepoint)\n" - "--\n\n" - "Return the glyph index corresponding to a character *codepoint*.\n"; + Parameters + ---------- + codepoint : int + A character code point in the current charmap (which defaults to Unicode.) + _fallback : bool + Whether to enable fallback fonts while searching for a character. -static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args) -{ - FT_UInt index; - FT_ULong ccode; - int fallback = 1; + Returns + ------- + int + The corresponding glyph index. - if (!PyArg_ParseTuple(args, "k:get_char_index", &ccode)) { - return NULL; - } + See Also + -------- + .set_charmap + .select_charmap + .get_glyph_name + .get_name_index +)"""; - CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, (bool)fallback)); +const char *PyFT2Font_get_sfnt__doc__ = R"""( + Load the entire SFNT names table. - return PyLong_FromLong(index); -} + Returns + ------- + dict[tuple[int, int, int, int], bytes] + The SFNT names table; the dictionary keys are tuples of: + (platform-ID, ISO-encoding-scheme, language-code, description) -const char *PyFT2Font_get_sfnt__doc__ = - "get_sfnt(self)\n" - "--\n\n" - "Load the entire SFNT names table, as a dict whose keys are\n" - "(platform-ID, ISO-encoding-scheme, language-code, and description)\n" - "tuples.\n"; + and the values are the direct information from the font table. +)"""; -static PyObject *PyFT2Font_get_sfnt(PyFT2Font *self, PyObject *args) +static py::dict +PyFT2Font_get_sfnt(PyFT2Font *self) { - PyObject *names; - - if (!(self->x->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { - PyErr_SetString(PyExc_ValueError, "No SFNT name table"); - return NULL; + if (!(self->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { + throw py::value_error("No SFNT name table"); } - size_t count = FT_Get_Sfnt_Name_Count(self->x->get_face()); + size_t count = FT_Get_Sfnt_Name_Count(self->get_face()); - names = PyDict_New(); - if (names == NULL) { - return NULL; - } + py::dict names; for (FT_UInt j = 0; j < count; ++j) { FT_SfntName sfnt; - FT_Error error = FT_Get_Sfnt_Name(self->x->get_face(), j, &sfnt); + FT_Error error = FT_Get_Sfnt_Name(self->get_face(), j, &sfnt); if (error) { - Py_DECREF(names); - PyErr_SetString(PyExc_ValueError, "Could not get SFNT name"); - return NULL; - } - - PyObject *key = Py_BuildValue( - "HHHH", sfnt.platform_id, sfnt.encoding_id, sfnt.language_id, sfnt.name_id); - if (key == NULL) { - Py_DECREF(names); - return NULL; - } - - PyObject *val = PyBytes_FromStringAndSize((const char *)sfnt.string, sfnt.string_len); - if (val == NULL) { - Py_DECREF(key); - Py_DECREF(names); - return NULL; + throw py::value_error("Could not get SFNT name"); } - if (PyDict_SetItem(names, key, val)) { - Py_DECREF(key); - Py_DECREF(val); - Py_DECREF(names); - return NULL; - } - - Py_DECREF(key); - Py_DECREF(val); + auto key = py::make_tuple( + sfnt.platform_id, sfnt.encoding_id, sfnt.language_id, sfnt.name_id); + auto val = py::bytes(reinterpret_cast(sfnt.string), + sfnt.string_len); + names[key] = val; } return names; } -const char *PyFT2Font_get_name_index__doc__ = - "get_name_index(self, name)\n" - "--\n\n" - "Return the glyph index of a given glyph *name*.\n" - "The glyph index 0 means 'undefined character code'.\n"; +const char *PyFT2Font_get_name_index__doc__ = R"""( + Return the glyph index of a given glyph *name*. -static PyObject *PyFT2Font_get_name_index(PyFT2Font *self, PyObject *args) -{ - char *glyphname; - long name_index; - if (!PyArg_ParseTuple(args, "s:get_name_index", &glyphname)) { - return NULL; - } - CALL_CPP("get_name_index", name_index = self->x->get_name_index(glyphname)); - return PyLong_FromLong(name_index); -} + Parameters + ---------- + name : str + The name of the glyph to query. -const char *PyFT2Font_get_ps_font_info__doc__ = - "get_ps_font_info(self)\n" - "--\n\n" - "Return the information in the PS Font Info structure.\n"; + Returns + ------- + int + The corresponding glyph index; 0 means 'undefined character code'. -static PyObject *PyFT2Font_get_ps_font_info(PyFT2Font *self, PyObject *args) -{ - PS_FontInfoRec fontinfo; + See Also + -------- + .get_char_index + .get_glyph_name +)"""; - FT_Error error = FT_Get_PS_Font_Info(self->x->get_face(), &fontinfo); - if (error) { - PyErr_SetString(PyExc_ValueError, "Could not get PS font info"); - return NULL; - } +const char *PyFT2Font_get_ps_font_info__doc__ = R"""( + Return the information in the PS Font Info structure. - return Py_BuildValue("ssssslbhH", - fontinfo.version ? fontinfo.version : "", - fontinfo.notice ? fontinfo.notice : "", - fontinfo.full_name ? fontinfo.full_name : "", - fontinfo.family_name ? fontinfo.family_name : "", - fontinfo.weight ? fontinfo.weight : "", - fontinfo.italic_angle, - fontinfo.is_fixed_pitch, - fontinfo.underline_position, - fontinfo.underline_thickness); -} + For more information, see the `FreeType documentation on this structure + `_. -const char *PyFT2Font_get_sfnt_table__doc__ = - "get_sfnt_table(self, name)\n" - "--\n\n" - "Return one of the following SFNT tables: head, maxp, OS/2, hhea, " - "vhea, post, or pclt.\n"; + Returns + ------- + version : str + notice : str + full_name : str + family_name : str + weight : str + italic_angle : int + is_fixed_pitch : bool + underline_position : int + underline_thickness : int +)"""; -static PyObject *PyFT2Font_get_sfnt_table(PyFT2Font *self, PyObject *args) +static py::tuple +PyFT2Font_get_ps_font_info(PyFT2Font *self) { - char *tagname; - if (!PyArg_ParseTuple(args, "s:get_sfnt_table", &tagname)) { - return NULL; + PS_FontInfoRec fontinfo; + + FT_Error error = FT_Get_PS_Font_Info(self->get_face(), &fontinfo); + if (error) { + throw py::value_error("Could not get PS font info"); } - int tag; - const char *tags[] = { "head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt", NULL }; + return py::make_tuple( + fontinfo.version ? fontinfo.version : "", + fontinfo.notice ? fontinfo.notice : "", + fontinfo.full_name ? fontinfo.full_name : "", + fontinfo.family_name ? fontinfo.family_name : "", + fontinfo.weight ? fontinfo.weight : "", + fontinfo.italic_angle, + fontinfo.is_fixed_pitch, + fontinfo.underline_position, + fontinfo.underline_thickness); +} + +const char *PyFT2Font_get_sfnt_table__doc__ = R"""( + Return one of the SFNT tables. + + Parameters + ---------- + name : {"head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt"} + Which table to return. + + Returns + ------- + dict[str, Any] + The corresponding table; for more information, see `the FreeType documentation + `_. +)"""; + +static std::optional +PyFT2Font_get_sfnt_table(PyFT2Font *self, std::string tagname) +{ + FT_Sfnt_Tag tag; + const std::unordered_map names = { + {"head", FT_SFNT_HEAD}, + {"maxp", FT_SFNT_MAXP}, + {"OS/2", FT_SFNT_OS2}, + {"hhea", FT_SFNT_HHEA}, + {"vhea", FT_SFNT_VHEA}, + {"post", FT_SFNT_POST}, + {"pclt", FT_SFNT_PCLT}, + }; - for (tag = 0; tags[tag] != NULL; tag++) { - if (strncmp(tagname, tags[tag], 5) == 0) { - break; - } + try { + tag = names.at(tagname); + } catch (const std::out_of_range&) { + return std::nullopt; } - void *table = FT_Get_Sfnt_Table(self->x->get_face(), (FT_Sfnt_Tag)tag); + void *table = FT_Get_Sfnt_Table(self->get_face(), tag); if (!table) { - Py_RETURN_NONE; + return std::nullopt; } switch (tag) { - case 0: { - char head_dict[] = - "{s:(h,H), s:(h,H), s:l, s:l, s:H, s:H," - "s:(l,l), s:(l,l), s:h, s:h, s:h, s:h, s:H, s:H, s:h, s:h, s:h}"; - TT_Header *t = (TT_Header *)table; - return Py_BuildValue(head_dict, - "version", FIXED_MAJOR(t->Table_Version), FIXED_MINOR(t->Table_Version), - "fontRevision", FIXED_MAJOR(t->Font_Revision), FIXED_MINOR(t->Font_Revision), - "checkSumAdjustment", t->CheckSum_Adjust, - "magicNumber", t->Magic_Number, - "flags", t->Flags, - "unitsPerEm", t->Units_Per_EM, - "created", t->Created[0], t->Created[1], - "modified", t->Modified[0], t->Modified[1], - "xMin", t->xMin, - "yMin", t->yMin, - "xMax", t->xMax, - "yMax", t->yMax, - "macStyle", t->Mac_Style, - "lowestRecPPEM", t->Lowest_Rec_PPEM, - "fontDirectionHint", t->Font_Direction, - "indexToLocFormat", t->Index_To_Loc_Format, - "glyphDataFormat", t->Glyph_Data_Format); + case FT_SFNT_HEAD: { + auto t = static_cast(table); + return py::dict( + "version"_a=py::make_tuple(FIXED_MAJOR(t->Table_Version), + FIXED_MINOR(t->Table_Version)), + "fontRevision"_a=py::make_tuple(FIXED_MAJOR(t->Font_Revision), + FIXED_MINOR(t->Font_Revision)), + "checkSumAdjustment"_a=t->CheckSum_Adjust, + "magicNumber"_a=t->Magic_Number, + "flags"_a=t->Flags, + "unitsPerEm"_a=t->Units_Per_EM, + // FreeType 2.6.1 defines these two timestamps as FT_Long, but they should + // be unsigned (fixed in 2.10.0): + // https://gitlab.freedesktop.org/freetype/freetype/-/commit/3e8ec291ffcfa03c8ecba1cdbfaa55f5577f5612 + // It's actually read from the file structure as two 32-bit values, so we + // need to cast down in size to prevent sign extension from producing huge + // 64-bit values. + "created"_a=py::make_tuple(static_cast(t->Created[0]), + static_cast(t->Created[1])), + "modified"_a=py::make_tuple(static_cast(t->Modified[0]), + static_cast(t->Modified[1])), + "xMin"_a=t->xMin, + "yMin"_a=t->yMin, + "xMax"_a=t->xMax, + "yMax"_a=t->yMax, + "macStyle"_a=t->Mac_Style, + "lowestRecPPEM"_a=t->Lowest_Rec_PPEM, + "fontDirectionHint"_a=t->Font_Direction, + "indexToLocFormat"_a=t->Index_To_Loc_Format, + "glyphDataFormat"_a=t->Glyph_Data_Format); } - case 1: { - char maxp_dict[] = - "{s:(h,H), s:H, s:H, s:H, s:H, s:H, s:H," - "s:H, s:H, s:H, s:H, s:H, s:H, s:H, s:H}"; - TT_MaxProfile *t = (TT_MaxProfile *)table; - return Py_BuildValue(maxp_dict, - "version", FIXED_MAJOR(t->version), FIXED_MINOR(t->version), - "numGlyphs", t->numGlyphs, - "maxPoints", t->maxPoints, - "maxContours", t->maxContours, - "maxComponentPoints", t->maxCompositePoints, - "maxComponentContours", t->maxCompositeContours, - "maxZones", t->maxZones, - "maxTwilightPoints", t->maxTwilightPoints, - "maxStorage", t->maxStorage, - "maxFunctionDefs", t->maxFunctionDefs, - "maxInstructionDefs", t->maxInstructionDefs, - "maxStackElements", t->maxStackElements, - "maxSizeOfInstructions", t->maxSizeOfInstructions, - "maxComponentElements", t->maxComponentElements, - "maxComponentDepth", t->maxComponentDepth); + case FT_SFNT_MAXP: { + auto t = static_cast(table); + return py::dict( + "version"_a=py::make_tuple(FIXED_MAJOR(t->version), + FIXED_MINOR(t->version)), + "numGlyphs"_a=t->numGlyphs, + "maxPoints"_a=t->maxPoints, + "maxContours"_a=t->maxContours, + "maxComponentPoints"_a=t->maxCompositePoints, + "maxComponentContours"_a=t->maxCompositeContours, + "maxZones"_a=t->maxZones, + "maxTwilightPoints"_a=t->maxTwilightPoints, + "maxStorage"_a=t->maxStorage, + "maxFunctionDefs"_a=t->maxFunctionDefs, + "maxInstructionDefs"_a=t->maxInstructionDefs, + "maxStackElements"_a=t->maxStackElements, + "maxSizeOfInstructions"_a=t->maxSizeOfInstructions, + "maxComponentElements"_a=t->maxComponentElements, + "maxComponentDepth"_a=t->maxComponentDepth); } - case 2: { - char os_2_dict[] = - "{s:H, s:h, s:H, s:H, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:y#, s:(kkkk)," - "s:y#, s:H, s:H, s:H}"; - TT_OS2 *t = (TT_OS2 *)table; - return Py_BuildValue(os_2_dict, - "version", t->version, - "xAvgCharWidth", t->xAvgCharWidth, - "usWeightClass", t->usWeightClass, - "usWidthClass", t->usWidthClass, - "fsType", t->fsType, - "ySubscriptXSize", t->ySubscriptXSize, - "ySubscriptYSize", t->ySubscriptYSize, - "ySubscriptXOffset", t->ySubscriptXOffset, - "ySubscriptYOffset", t->ySubscriptYOffset, - "ySuperscriptXSize", t->ySuperscriptXSize, - "ySuperscriptYSize", t->ySuperscriptYSize, - "ySuperscriptXOffset", t->ySuperscriptXOffset, - "ySuperscriptYOffset", t->ySuperscriptYOffset, - "yStrikeoutSize", t->yStrikeoutSize, - "yStrikeoutPosition", t->yStrikeoutPosition, - "sFamilyClass", t->sFamilyClass, - "panose", t->panose, Py_ssize_t(10), - "ulCharRange", t->ulUnicodeRange1, t->ulUnicodeRange2, t->ulUnicodeRange3, t->ulUnicodeRange4, - "achVendID", t->achVendID, Py_ssize_t(4), - "fsSelection", t->fsSelection, - "fsFirstCharIndex", t->usFirstCharIndex, - "fsLastCharIndex", t->usLastCharIndex); + case FT_SFNT_OS2: { + auto t = static_cast(table); + auto version = t->version; + auto result = py::dict( + "version"_a=version, + "xAvgCharWidth"_a=t->xAvgCharWidth, + "usWeightClass"_a=t->usWeightClass, + "usWidthClass"_a=t->usWidthClass, + "fsType"_a=t->fsType, + "ySubscriptXSize"_a=t->ySubscriptXSize, + "ySubscriptYSize"_a=t->ySubscriptYSize, + "ySubscriptXOffset"_a=t->ySubscriptXOffset, + "ySubscriptYOffset"_a=t->ySubscriptYOffset, + "ySuperscriptXSize"_a=t->ySuperscriptXSize, + "ySuperscriptYSize"_a=t->ySuperscriptYSize, + "ySuperscriptXOffset"_a=t->ySuperscriptXOffset, + "ySuperscriptYOffset"_a=t->ySuperscriptYOffset, + "yStrikeoutSize"_a=t->yStrikeoutSize, + "yStrikeoutPosition"_a=t->yStrikeoutPosition, + "sFamilyClass"_a=t->sFamilyClass, + "panose"_a=py::bytes(reinterpret_cast(t->panose), 10), + "ulUnicodeRange"_a=py::make_tuple(t->ulUnicodeRange1, t->ulUnicodeRange2, + t->ulUnicodeRange3, t->ulUnicodeRange4), + "achVendID"_a=py::bytes(reinterpret_cast(t->achVendID), 4), + "fsSelection"_a=t->fsSelection, + "usFirstCharIndex"_a=t->usFirstCharIndex, + "usLastCharIndex"_a=t->usLastCharIndex, + "sTypoAscender"_a=t->sTypoAscender, + "sTypoDescender"_a=t->sTypoDescender, + "sTypoLineGap"_a=t->sTypoLineGap, + "usWinAscent"_a=t->usWinAscent, + "usWinDescent"_a=t->usWinDescent); + if (version >= 1) { + result["ulCodePageRange"] = py::make_tuple(t->ulCodePageRange1, + t->ulCodePageRange2); + } + if (version >= 2) { + result["sxHeight"] = t->sxHeight; + result["sCapHeight"] = t->sCapHeight; + result["usDefaultChar"] = t->usDefaultChar; + result["usBreakChar"] = t->usBreakChar; + result["usMaxContext"] = t->usMaxContext; + } + if (version >= 5) { + result["usLowerOpticalPointSize"] = t->usLowerOpticalPointSize; + result["usUpperOpticalPointSize"] = t->usUpperOpticalPointSize; + } + return result; } - case 3: { - char hhea_dict[] = - "{s:(h,H), s:h, s:h, s:h, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:H}"; - TT_HoriHeader *t = (TT_HoriHeader *)table; - return Py_BuildValue(hhea_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "ascent", t->Ascender, - "descent", t->Descender, - "lineGap", t->Line_Gap, - "advanceWidthMax", t->advance_Width_Max, - "minLeftBearing", t->min_Left_Side_Bearing, - "minRightBearing", t->min_Right_Side_Bearing, - "xMaxExtent", t->xMax_Extent, - "caretSlopeRise", t->caret_Slope_Rise, - "caretSlopeRun", t->caret_Slope_Run, - "caretOffset", t->caret_Offset, - "metricDataFormat", t->metric_Data_Format, - "numOfLongHorMetrics", t->number_Of_HMetrics); + case FT_SFNT_HHEA: { + auto t = static_cast(table); + return py::dict( + "version"_a=py::make_tuple(FIXED_MAJOR(t->Version), + FIXED_MINOR(t->Version)), + "ascent"_a=t->Ascender, + "descent"_a=t->Descender, + "lineGap"_a=t->Line_Gap, + "advanceWidthMax"_a=t->advance_Width_Max, + "minLeftBearing"_a=t->min_Left_Side_Bearing, + "minRightBearing"_a=t->min_Right_Side_Bearing, + "xMaxExtent"_a=t->xMax_Extent, + "caretSlopeRise"_a=t->caret_Slope_Rise, + "caretSlopeRun"_a=t->caret_Slope_Run, + "caretOffset"_a=t->caret_Offset, + "metricDataFormat"_a=t->metric_Data_Format, + "numOfLongHorMetrics"_a=t->number_Of_HMetrics); } - case 4: { - char vhea_dict[] = - "{s:(h,H), s:h, s:h, s:h, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:H}"; - TT_VertHeader *t = (TT_VertHeader *)table; - return Py_BuildValue(vhea_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "vertTypoAscender", t->Ascender, - "vertTypoDescender", t->Descender, - "vertTypoLineGap", t->Line_Gap, - "advanceHeightMax", t->advance_Height_Max, - "minTopSideBearing", t->min_Top_Side_Bearing, - "minBottomSizeBearing", t->min_Bottom_Side_Bearing, - "yMaxExtent", t->yMax_Extent, - "caretSlopeRise", t->caret_Slope_Rise, - "caretSlopeRun", t->caret_Slope_Run, - "caretOffset", t->caret_Offset, - "metricDataFormat", t->metric_Data_Format, - "numOfLongVerMetrics", t->number_Of_VMetrics); + case FT_SFNT_VHEA: { + auto t = static_cast(table); + return py::dict( + "version"_a=py::make_tuple(FIXED_MAJOR(t->Version), + FIXED_MINOR(t->Version)), + "vertTypoAscender"_a=t->Ascender, + "vertTypoDescender"_a=t->Descender, + "vertTypoLineGap"_a=t->Line_Gap, + "advanceHeightMax"_a=t->advance_Height_Max, + "minTopSideBearing"_a=t->min_Top_Side_Bearing, + "minBottomSideBearing"_a=t->min_Bottom_Side_Bearing, + "yMaxExtent"_a=t->yMax_Extent, + "caretSlopeRise"_a=t->caret_Slope_Rise, + "caretSlopeRun"_a=t->caret_Slope_Run, + "caretOffset"_a=t->caret_Offset, + "metricDataFormat"_a=t->metric_Data_Format, + "numOfLongVerMetrics"_a=t->number_Of_VMetrics); } - case 5: { - char post_dict[] = "{s:(h,H), s:(h,H), s:h, s:h, s:k, s:k, s:k, s:k, s:k}"; - TT_Postscript *t = (TT_Postscript *)table; - return Py_BuildValue(post_dict, - "format", FIXED_MAJOR(t->FormatType), FIXED_MINOR(t->FormatType), - "italicAngle", FIXED_MAJOR(t->italicAngle), FIXED_MINOR(t->italicAngle), - "underlinePosition", t->underlinePosition, - "underlineThickness", t->underlineThickness, - "isFixedPitch", t->isFixedPitch, - "minMemType42", t->minMemType42, - "maxMemType42", t->maxMemType42, - "minMemType1", t->minMemType1, - "maxMemType1", t->maxMemType1); + case FT_SFNT_POST: { + auto t = static_cast(table); + return py::dict( + "format"_a=py::make_tuple(FIXED_MAJOR(t->FormatType), + FIXED_MINOR(t->FormatType)), + "italicAngle"_a=py::make_tuple(FIXED_MAJOR(t->italicAngle), + FIXED_MINOR(t->italicAngle)), + "underlinePosition"_a=t->underlinePosition, + "underlineThickness"_a=t->underlineThickness, + "isFixedPitch"_a=t->isFixedPitch, + "minMemType42"_a=t->minMemType42, + "maxMemType42"_a=t->maxMemType42, + "minMemType1"_a=t->minMemType1, + "maxMemType1"_a=t->maxMemType1); } - case 6: { - char pclt_dict[] = - "{s:(h,H), s:k, s:H, s:H, s:H, s:H, s:H, s:H, s:y#, s:y#, s:b, " - "s:b, s:b}"; - TT_PCLT *t = (TT_PCLT *)table; - return Py_BuildValue(pclt_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "fontNumber", t->FontNumber, - "pitch", t->Pitch, - "xHeight", t->xHeight, - "style", t->Style, - "typeFamily", t->TypeFamily, - "capHeight", t->CapHeight, - "symbolSet", t->SymbolSet, - "typeFace", t->TypeFace, Py_ssize_t(16), - "characterComplement", t->CharacterComplement, Py_ssize_t(8), - "strokeWeight", t->StrokeWeight, - "widthType", t->WidthType, - "serifStyle", t->SerifStyle); + case FT_SFNT_PCLT: { + auto t = static_cast(table); + return py::dict( + "version"_a=py::make_tuple(FIXED_MAJOR(t->Version), + FIXED_MINOR(t->Version)), + "fontNumber"_a=t->FontNumber, + "pitch"_a=t->Pitch, + "xHeight"_a=t->xHeight, + "style"_a=t->Style, + "typeFamily"_a=t->TypeFamily, + "capHeight"_a=t->CapHeight, + "symbolSet"_a=t->SymbolSet, + "typeFace"_a=py::bytes(reinterpret_cast(t->TypeFace), 16), + "characterComplement"_a=py::bytes( + reinterpret_cast(t->CharacterComplement), 8), + "strokeWeight"_a=t->StrokeWeight, + "widthType"_a=t->WidthType, + "serifStyle"_a=t->SerifStyle); } default: - Py_RETURN_NONE; + return std::nullopt; } } -const char *PyFT2Font_get_path__doc__ = - "get_path(self)\n" - "--\n\n" - "Get the path data from the currently loaded glyph as a tuple of vertices, " - "codes.\n"; +const char *PyFT2Font_get_path__doc__ = R"""( + Get the path data from the currently loaded glyph. -static PyObject *PyFT2Font_get_path(PyFT2Font *self, PyObject *args) -{ - CALL_CPP("get_path", return self->x->get_path()); -} - -const char *PyFT2Font_get_image__doc__ = - "get_image(self)\n" - "--\n\n" - "Return the underlying image buffer for this font object.\n"; + Returns + ------- + vertices : np.ndarray[double] + The (N, 2) array of vertices describing the current glyph. + codes : np.ndarray[np.uint8] + The (N, ) array of codes corresponding to the vertices. -static PyObject *PyFT2Font_get_image(PyFT2Font *self, PyObject *args) -{ - FT2Image &im = self->x->get_image(); - npy_intp dims[] = {(npy_intp)im.get_height(), (npy_intp)im.get_width() }; - return PyArray_SimpleNewFromData(2, dims, NPY_UBYTE, im.get_buffer()); -} + See Also + -------- + .get_image + .load_char + .load_glyph + .set_text +)"""; -static PyObject *PyFT2Font_postscript_name(PyFT2Font *self, void *closure) +static py::tuple +PyFT2Font_get_path(PyFT2Font *self) { - const char *ps_name = FT_Get_Postscript_Name(self->x->get_face()); - if (ps_name == NULL) { - ps_name = "UNAVAILABLE"; - } - - return PyUnicode_FromString(ps_name); -} + std::vector vertices; + std::vector codes; -static PyObject *PyFT2Font_num_faces(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_faces); -} + self->get_path(vertices, codes); -static PyObject *PyFT2Font_family_name(PyFT2Font *self, void *closure) -{ - const char *name = self->x->get_face()->family_name; - if (name == NULL) { - name = "UNAVAILABLE"; + py::ssize_t length = codes.size(); + py::ssize_t vertices_dims[2] = { length, 2 }; + py::array_t vertices_arr(vertices_dims); + if (length > 0) { + memcpy(vertices_arr.mutable_data(), vertices.data(), vertices_arr.nbytes()); } - return PyUnicode_FromString(name); -} - -static PyObject *PyFT2Font_style_name(PyFT2Font *self, void *closure) -{ - const char *name = self->x->get_face()->style_name; - if (name == NULL) { - name = "UNAVAILABLE"; + py::ssize_t codes_dims[1] = { length }; + py::array_t codes_arr(codes_dims); + if (length > 0) { + memcpy(codes_arr.mutable_data(), codes.data(), codes_arr.nbytes()); } - return PyUnicode_FromString(name); -} -static PyObject *PyFT2Font_face_flags(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->face_flags); + return py::make_tuple(vertices_arr, codes_arr); } -static PyObject *PyFT2Font_style_flags(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->style_flags); -} +const char *PyFT2Font_get_image__doc__ = R"""( + Return the underlying image buffer for this font object. -static PyObject *PyFT2Font_num_glyphs(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_glyphs); -} + Returns + ------- + np.ndarray[int] -static PyObject *PyFT2Font_num_fixed_sizes(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_fixed_sizes); -} + See Also + -------- + .get_path +)"""; -static PyObject *PyFT2Font_num_charmaps(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_charmaps); -} +const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""( + Return a list mapping CharString indices of a Type 1 font to FreeType glyph indices. -static PyObject *PyFT2Font_scalable(PyFT2Font *self, void *closure) + Returns + ------- + list[int] +)"""; + +static std::array +PyFT2Font__get_type1_encoding_vector(PyFT2Font *self) { - if (FT_IS_SCALABLE(self->x->get_face())) { - Py_RETURN_TRUE; + auto face = self->get_face(); + auto indices = std::array{}; + for (auto i = 0u; i < indices.size(); ++i) { + auto len = FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, nullptr, 0); + if (len == -1) { + // Explicitly ignore missing entries (mapped to glyph 0 = .notdef). + continue; + } + auto buf = std::make_unique(len); + FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, buf.get(), len); + indices[i] = FT_Get_Name_Index(face, buf.get()); } - Py_RETURN_FALSE; + return indices; } -static PyObject *PyFT2Font_units_per_EM(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->units_per_EM); -} +/********************************************************************** + * Layout items + * */ -static PyObject *PyFT2Font_get_bbox(PyFT2Font *self, void *closure) -{ - FT_BBox *bbox = &(self->x->get_face()->bbox); +struct LayoutItem { + PyFT2Font *ft_object; + std::u32string character; + int glyph_index; + double x; + double y; + double prev_kern; - return Py_BuildValue("llll", - bbox->xMin, bbox->yMin, bbox->xMax, bbox->yMax); -} + LayoutItem(PyFT2Font *f, std::u32string c, int i, double x, double y, double k) : + ft_object(f), character(c), glyph_index(i), x(x), y(y), prev_kern(k) {} +}; -static PyObject *PyFT2Font_ascender(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->ascender); -} +const char *PyFT2Font_layout__doc__ = R"""( + Layout a string and yield information about each used glyph. + + .. warning:: + This API uses the fallback list and is both private and provisional: do not use + it directly. + + .. versionadded:: 3.11 + + Parameters + ---------- + text : str + The characters for which to find fonts. + flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` + Any bitwise-OR combination of the `.LoadFlags` flags. + features : tuple[str, ...], optional + The font feature tags to use for the font. + + Available font feature tags may be found at + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + language : str, optional + The language of the text in a format accepted by libraqm, namely `a BCP47 + language code `_. + + Returns + ------- + list[LayoutItem] +)"""; + +static auto +PyFT2Font_layout(PyFT2Font *self, std::u32string text, LoadFlags flags, + std::optional> features = std::nullopt, + std::variant languages_or_str = nullptr) +{ + const auto hinting_factor = self->get_hinting_factor(); + const auto load_flags = static_cast(flags); + + FT2Font::LanguageType languages; + if (auto value = std::get_if(&languages_or_str)) { + languages = std::move(*value); + } else if (auto value = std::get_if(&languages_or_str)) { + languages = std::vector{ + FT2Font::LanguageRange{*value, 0, text.size()} + }; + } else { + // NOTE: this can never happen as pybind11 would have checked the type in the + // Python wrapper before calling this function, but we need to keep the + // std::get_if instead of std::get for macOS 10.12 compatibility. + throw py::type_error("languages must be str or list of tuple"); + } -static PyObject *PyFT2Font_descender(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->descender); -} + std::set glyph_seen_fonts; + auto glyphs = self->layout(text, load_flags, features, languages, glyph_seen_fonts); -static PyObject *PyFT2Font_height(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->height); -} + std::set clusters; + for (auto &glyph : glyphs) { + clusters.emplace(glyph.cluster); + } -static PyObject *PyFT2Font_max_advance_width(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->max_advance_width); -} + std::vector items; -static PyObject *PyFT2Font_max_advance_height(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->max_advance_height); -} + double x = 0.0; + double y = 0.0; + std::optional prev_advance = std::nullopt; + double prev_x = 0.0; + for (auto &glyph : glyphs) { + auto ft_object = static_cast(glyph.ftface->generic.data); -static PyObject *PyFT2Font_underline_position(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->underline_position); -} + ft_object->load_glyph(glyph.index, load_flags); -static PyObject *PyFT2Font_underline_thickness(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->underline_thickness); -} + double prev_kern = 0.0; + if (prev_advance) { + double actual_advance = (x + glyph.x_offset) - prev_x; + prev_kern = actual_advance - *prev_advance; + } -static PyObject *PyFT2Font_fname(PyFT2Font *self, void *closure) -{ - if (self->stream.close) { // Called passed a filename to the constructor. - return PyObject_GetAttrString(self->py_file, "name"); - } else { - Py_INCREF(self->py_file); - return self->py_file; + auto next = clusters.upper_bound(glyph.cluster); + auto end = (next != clusters.end()) ? *next : text.size(); + auto substr = text.substr(glyph.cluster, end - glyph.cluster); + + items.emplace_back(ft_object, substr, glyph.index, + (x + glyph.x_offset) / 64.0, (y + glyph.y_offset) / 64.0, + prev_kern / 64.0); + prev_x = x + glyph.x_offset; + x += glyph.x_advance; + y += glyph.y_advance; + // Note, linearHoriAdvance is a 16.16 instead of 26.6 fixed-point value. + prev_advance = ft_object->get_face()->glyph->linearHoriAdvance / 1024.0 / hinting_factor; } -} -static int PyFT2Font_get_buffer(PyFT2Font *self, Py_buffer *buf, int flags) -{ - FT2Image &im = self->x->get_image(); - - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = im.get_buffer(); - buf->len = im.get_width() * im.get_height(); - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 2; - self->shape[0] = im.get_height(); - self->shape[1] = im.get_width(); - buf->shape = self->shape; - self->strides[0] = im.get_width(); - self->strides[1] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; + return items; } -static PyTypeObject *PyFT2Font_init_type() -{ - static PyGetSetDef getset[] = { - {(char *)"postscript_name", (getter)PyFT2Font_postscript_name, NULL, NULL, NULL}, - {(char *)"num_faces", (getter)PyFT2Font_num_faces, NULL, NULL, NULL}, - {(char *)"family_name", (getter)PyFT2Font_family_name, NULL, NULL, NULL}, - {(char *)"style_name", (getter)PyFT2Font_style_name, NULL, NULL, NULL}, - {(char *)"face_flags", (getter)PyFT2Font_face_flags, NULL, NULL, NULL}, - {(char *)"style_flags", (getter)PyFT2Font_style_flags, NULL, NULL, NULL}, - {(char *)"num_glyphs", (getter)PyFT2Font_num_glyphs, NULL, NULL, NULL}, - {(char *)"num_fixed_sizes", (getter)PyFT2Font_num_fixed_sizes, NULL, NULL, NULL}, - {(char *)"num_charmaps", (getter)PyFT2Font_num_charmaps, NULL, NULL, NULL}, - {(char *)"scalable", (getter)PyFT2Font_scalable, NULL, NULL, NULL}, - {(char *)"units_per_EM", (getter)PyFT2Font_units_per_EM, NULL, NULL, NULL}, - {(char *)"bbox", (getter)PyFT2Font_get_bbox, NULL, NULL, NULL}, - {(char *)"ascender", (getter)PyFT2Font_ascender, NULL, NULL, NULL}, - {(char *)"descender", (getter)PyFT2Font_descender, NULL, NULL, NULL}, - {(char *)"height", (getter)PyFT2Font_height, NULL, NULL, NULL}, - {(char *)"max_advance_width", (getter)PyFT2Font_max_advance_width, NULL, NULL, NULL}, - {(char *)"max_advance_height", (getter)PyFT2Font_max_advance_height, NULL, NULL, NULL}, - {(char *)"underline_position", (getter)PyFT2Font_underline_position, NULL, NULL, NULL}, - {(char *)"underline_thickness", (getter)PyFT2Font_underline_thickness, NULL, NULL, NULL}, - {(char *)"fname", (getter)PyFT2Font_fname, NULL, NULL, NULL}, - {NULL} - }; - - static PyMethodDef methods[] = { - {"clear", (PyCFunction)PyFT2Font_clear, METH_NOARGS, PyFT2Font_clear__doc__}, - {"set_size", (PyCFunction)PyFT2Font_set_size, METH_VARARGS, PyFT2Font_set_size__doc__}, - {"set_charmap", (PyCFunction)PyFT2Font_set_charmap, METH_VARARGS, PyFT2Font_set_charmap__doc__}, - {"select_charmap", (PyCFunction)PyFT2Font_select_charmap, METH_VARARGS, PyFT2Font_select_charmap__doc__}, - {"get_kerning", (PyCFunction)PyFT2Font_get_kerning, METH_VARARGS, PyFT2Font_get_kerning__doc__}, - {"set_text", (PyCFunction)PyFT2Font_set_text, METH_VARARGS|METH_KEYWORDS, PyFT2Font_set_text__doc__}, - {"_get_fontmap", (PyCFunction)PyFT2Font_get_fontmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_fontmap__doc__}, - {"get_num_glyphs", (PyCFunction)PyFT2Font_get_num_glyphs, METH_NOARGS, PyFT2Font_get_num_glyphs__doc__}, - {"load_char", (PyCFunction)PyFT2Font_load_char, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_char__doc__}, - {"load_glyph", (PyCFunction)PyFT2Font_load_glyph, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_glyph__doc__}, - {"get_width_height", (PyCFunction)PyFT2Font_get_width_height, METH_NOARGS, PyFT2Font_get_width_height__doc__}, - {"get_bitmap_offset", (PyCFunction)PyFT2Font_get_bitmap_offset, METH_NOARGS, PyFT2Font_get_bitmap_offset__doc__}, - {"get_descent", (PyCFunction)PyFT2Font_get_descent, METH_NOARGS, PyFT2Font_get_descent__doc__}, - {"draw_glyphs_to_bitmap", (PyCFunction)PyFT2Font_draw_glyphs_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyphs_to_bitmap__doc__}, - {"get_xys", (PyCFunction)PyFT2Font_get_xys, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_xys__doc__}, - {"draw_glyph_to_bitmap", (PyCFunction)PyFT2Font_draw_glyph_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyph_to_bitmap__doc__}, - {"get_glyph_name", (PyCFunction)PyFT2Font_get_glyph_name, METH_VARARGS, PyFT2Font_get_glyph_name__doc__}, - {"get_charmap", (PyCFunction)PyFT2Font_get_charmap, METH_NOARGS, PyFT2Font_get_charmap__doc__}, - {"get_char_index", (PyCFunction)PyFT2Font_get_char_index, METH_VARARGS, PyFT2Font_get_char_index__doc__}, - {"get_sfnt", (PyCFunction)PyFT2Font_get_sfnt, METH_NOARGS, PyFT2Font_get_sfnt__doc__}, - {"get_name_index", (PyCFunction)PyFT2Font_get_name_index, METH_VARARGS, PyFT2Font_get_name_index__doc__}, - {"get_ps_font_info", (PyCFunction)PyFT2Font_get_ps_font_info, METH_NOARGS, PyFT2Font_get_ps_font_info__doc__}, - {"get_sfnt_table", (PyCFunction)PyFT2Font_get_sfnt_table, METH_VARARGS, PyFT2Font_get_sfnt_table__doc__}, - {"get_path", (PyCFunction)PyFT2Font_get_path, METH_NOARGS, PyFT2Font_get_path__doc__}, - {"get_image", (PyCFunction)PyFT2Font_get_image, METH_NOARGS, PyFT2Font_get_image__doc__}, - {NULL} - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyFT2Font_get_buffer; - - PyFT2FontType.tp_name = "matplotlib.ft2font.FT2Font"; - PyFT2FontType.tp_doc = PyFT2Font_init__doc__; - PyFT2FontType.tp_basicsize = sizeof(PyFT2Font); - PyFT2FontType.tp_dealloc = (destructor)PyFT2Font_dealloc; - PyFT2FontType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyFT2FontType.tp_methods = methods; - PyFT2FontType.tp_getset = getset; - PyFT2FontType.tp_new = PyFT2Font_new; - PyFT2FontType.tp_init = (initproc)PyFT2Font_init; - PyFT2FontType.tp_as_buffer = &buffer_procs; - - return &PyFT2FontType; -} - -static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "ft2font" }; +/********************************************************************** + * Deprecations + * */ -PyMODINIT_FUNC PyInit_ft2font(void) +static py::object +ft2font__getattr__(std::string name) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + +#define DEPRECATE_ATTR_FROM_ENUM(attr_, alternative_, real_value_) \ + do { \ + if (name == #attr_) { \ + warn("since"_a="3.10", "name"_a=#attr_, "obj_type"_a="attribute", \ + "alternative"_a=#alternative_); \ + return py::cast(static_cast(real_value_)); \ + } \ + } while(0) + DEPRECATE_ATTR_FROM_ENUM(KERNING_DEFAULT, Kerning.DEFAULT, FT_KERNING_DEFAULT); + DEPRECATE_ATTR_FROM_ENUM(KERNING_UNFITTED, Kerning.UNFITTED, FT_KERNING_UNFITTED); + DEPRECATE_ATTR_FROM_ENUM(KERNING_UNSCALED, Kerning.UNSCALED, FT_KERNING_UNSCALED); + +#undef DEPRECATE_ATTR_FROM_ENUM + +#define DEPRECATE_ATTR_FROM_FLAG(attr_, enum_, value_) \ + do { \ + if (name == #attr_) { \ + warn("since"_a="3.10", "name"_a=#attr_, "obj_type"_a="attribute", \ + "alternative"_a=#enum_ "." #value_); \ + return py::cast(enum_::value_); \ + } \ + } while(0) + + DEPRECATE_ATTR_FROM_FLAG(LOAD_DEFAULT, LoadFlags, DEFAULT); + DEPRECATE_ATTR_FROM_FLAG(LOAD_NO_SCALE, LoadFlags, NO_SCALE); + DEPRECATE_ATTR_FROM_FLAG(LOAD_NO_HINTING, LoadFlags, NO_HINTING); + DEPRECATE_ATTR_FROM_FLAG(LOAD_RENDER, LoadFlags, RENDER); + DEPRECATE_ATTR_FROM_FLAG(LOAD_NO_BITMAP, LoadFlags, NO_BITMAP); + DEPRECATE_ATTR_FROM_FLAG(LOAD_VERTICAL_LAYOUT, LoadFlags, VERTICAL_LAYOUT); + DEPRECATE_ATTR_FROM_FLAG(LOAD_FORCE_AUTOHINT, LoadFlags, FORCE_AUTOHINT); + DEPRECATE_ATTR_FROM_FLAG(LOAD_CROP_BITMAP, LoadFlags, CROP_BITMAP); + DEPRECATE_ATTR_FROM_FLAG(LOAD_PEDANTIC, LoadFlags, PEDANTIC); + DEPRECATE_ATTR_FROM_FLAG(LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, LoadFlags, + IGNORE_GLOBAL_ADVANCE_WIDTH); + DEPRECATE_ATTR_FROM_FLAG(LOAD_NO_RECURSE, LoadFlags, NO_RECURSE); + DEPRECATE_ATTR_FROM_FLAG(LOAD_IGNORE_TRANSFORM, LoadFlags, IGNORE_TRANSFORM); + DEPRECATE_ATTR_FROM_FLAG(LOAD_MONOCHROME, LoadFlags, MONOCHROME); + DEPRECATE_ATTR_FROM_FLAG(LOAD_LINEAR_DESIGN, LoadFlags, LINEAR_DESIGN); + DEPRECATE_ATTR_FROM_FLAG(LOAD_NO_AUTOHINT, LoadFlags, NO_AUTOHINT); + + DEPRECATE_ATTR_FROM_FLAG(LOAD_TARGET_NORMAL, LoadFlags, TARGET_NORMAL); + DEPRECATE_ATTR_FROM_FLAG(LOAD_TARGET_LIGHT, LoadFlags, TARGET_LIGHT); + DEPRECATE_ATTR_FROM_FLAG(LOAD_TARGET_MONO, LoadFlags, TARGET_MONO); + DEPRECATE_ATTR_FROM_FLAG(LOAD_TARGET_LCD, LoadFlags, TARGET_LCD); + DEPRECATE_ATTR_FROM_FLAG(LOAD_TARGET_LCD_V, LoadFlags, TARGET_LCD_V); + + DEPRECATE_ATTR_FROM_FLAG(SCALABLE, FaceFlags, SCALABLE); + DEPRECATE_ATTR_FROM_FLAG(FIXED_SIZES, FaceFlags, FIXED_SIZES); + DEPRECATE_ATTR_FROM_FLAG(FIXED_WIDTH, FaceFlags, FIXED_WIDTH); + DEPRECATE_ATTR_FROM_FLAG(SFNT, FaceFlags, SFNT); + DEPRECATE_ATTR_FROM_FLAG(HORIZONTAL, FaceFlags, HORIZONTAL); + DEPRECATE_ATTR_FROM_FLAG(VERTICAL, FaceFlags, VERTICAL); + DEPRECATE_ATTR_FROM_FLAG(KERNING, FaceFlags, KERNING); + DEPRECATE_ATTR_FROM_FLAG(FAST_GLYPHS, FaceFlags, FAST_GLYPHS); + DEPRECATE_ATTR_FROM_FLAG(MULTIPLE_MASTERS, FaceFlags, MULTIPLE_MASTERS); + DEPRECATE_ATTR_FROM_FLAG(GLYPH_NAMES, FaceFlags, GLYPH_NAMES); + DEPRECATE_ATTR_FROM_FLAG(EXTERNAL_STREAM, FaceFlags, EXTERNAL_STREAM); + + DEPRECATE_ATTR_FROM_FLAG(ITALIC, StyleFlags, ITALIC); + DEPRECATE_ATTR_FROM_FLAG(BOLD, StyleFlags, BOLD); +#undef DEPRECATE_ATTR_FROM_FLAG + + throw py::attribute_error( + "module 'matplotlib.ft2font' has no attribute {!r}"_s.format(name)); +} + +PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) { - import_array(); - if (FT_Init_FreeType(&_ft2Library)) { // initialize library - return PyErr_Format( - PyExc_RuntimeError, "Could not initialize the freetype2 library"); + throw std::runtime_error("Could not initialize the freetype2 library"); } FT_Int major, minor, patch; char version_string[64]; FT_Library_Version(_ft2Library, &major, &minor, &patch); snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch); - PyObject *m; - if (!(m = PyModule_Create(&moduledef)) || - prepare_and_add_type(PyFT2Image_init_type(), m) || - prepare_and_add_type(PyFT2Font_init_type(), m) || - // Glyph is not constructible from Python, thus not added to the module. - PyType_Ready(PyGlyph_init_type()) || - PyModule_AddStringConstant(m, "__freetype_version__", version_string) || - PyModule_AddStringConstant(m, "__freetype_build_type__", FREETYPE_BUILD_TYPE) || - PyModule_AddIntConstant(m, "SCALABLE", FT_FACE_FLAG_SCALABLE) || - PyModule_AddIntConstant(m, "FIXED_SIZES", FT_FACE_FLAG_FIXED_SIZES) || - PyModule_AddIntConstant(m, "FIXED_WIDTH", FT_FACE_FLAG_FIXED_WIDTH) || - PyModule_AddIntConstant(m, "SFNT", FT_FACE_FLAG_SFNT) || - PyModule_AddIntConstant(m, "HORIZONTAL", FT_FACE_FLAG_HORIZONTAL) || - PyModule_AddIntConstant(m, "VERTICAL", FT_FACE_FLAG_VERTICAL) || - PyModule_AddIntConstant(m, "KERNING", FT_FACE_FLAG_KERNING) || - PyModule_AddIntConstant(m, "FAST_GLYPHS", FT_FACE_FLAG_FAST_GLYPHS) || - PyModule_AddIntConstant(m, "MULTIPLE_MASTERS", FT_FACE_FLAG_MULTIPLE_MASTERS) || - PyModule_AddIntConstant(m, "GLYPH_NAMES", FT_FACE_FLAG_GLYPH_NAMES) || - PyModule_AddIntConstant(m, "EXTERNAL_STREAM", FT_FACE_FLAG_EXTERNAL_STREAM) || - PyModule_AddIntConstant(m, "ITALIC", FT_STYLE_FLAG_ITALIC) || - PyModule_AddIntConstant(m, "BOLD", FT_STYLE_FLAG_BOLD) || - PyModule_AddIntConstant(m, "KERNING_DEFAULT", FT_KERNING_DEFAULT) || - PyModule_AddIntConstant(m, "KERNING_UNFITTED", FT_KERNING_UNFITTED) || - PyModule_AddIntConstant(m, "KERNING_UNSCALED", FT_KERNING_UNSCALED) || - PyModule_AddIntConstant(m, "LOAD_DEFAULT", FT_LOAD_DEFAULT) || - PyModule_AddIntConstant(m, "LOAD_NO_SCALE", FT_LOAD_NO_SCALE) || - PyModule_AddIntConstant(m, "LOAD_NO_HINTING", FT_LOAD_NO_HINTING) || - PyModule_AddIntConstant(m, "LOAD_RENDER", FT_LOAD_RENDER) || - PyModule_AddIntConstant(m, "LOAD_NO_BITMAP", FT_LOAD_NO_BITMAP) || - PyModule_AddIntConstant(m, "LOAD_VERTICAL_LAYOUT", FT_LOAD_VERTICAL_LAYOUT) || - PyModule_AddIntConstant(m, "LOAD_FORCE_AUTOHINT", FT_LOAD_FORCE_AUTOHINT) || - PyModule_AddIntConstant(m, "LOAD_CROP_BITMAP", FT_LOAD_CROP_BITMAP) || - PyModule_AddIntConstant(m, "LOAD_PEDANTIC", FT_LOAD_PEDANTIC) || - PyModule_AddIntConstant(m, "LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH", FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH) || - PyModule_AddIntConstant(m, "LOAD_NO_RECURSE", FT_LOAD_NO_RECURSE) || - PyModule_AddIntConstant(m, "LOAD_IGNORE_TRANSFORM", FT_LOAD_IGNORE_TRANSFORM) || - PyModule_AddIntConstant(m, "LOAD_MONOCHROME", FT_LOAD_MONOCHROME) || - PyModule_AddIntConstant(m, "LOAD_LINEAR_DESIGN", FT_LOAD_LINEAR_DESIGN) || - PyModule_AddIntConstant(m, "LOAD_NO_AUTOHINT", (unsigned long)FT_LOAD_NO_AUTOHINT) || - PyModule_AddIntConstant(m, "LOAD_TARGET_NORMAL", (unsigned long)FT_LOAD_TARGET_NORMAL) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LIGHT", (unsigned long)FT_LOAD_TARGET_LIGHT) || - PyModule_AddIntConstant(m, "LOAD_TARGET_MONO", (unsigned long)FT_LOAD_TARGET_MONO) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LCD", (unsigned long)FT_LOAD_TARGET_LCD) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LCD_V", (unsigned long)FT_LOAD_TARGET_LCD_V)) { - FT_Done_FreeType(_ft2Library); - Py_XDECREF(m); - return NULL; - } - - return m; + p11x::bind_enums(m); + p11x::enums["Kerning"].attr("__doc__") = Kerning__doc__; + p11x::enums["LoadFlags"].attr("__doc__") = LoadFlags__doc__; + p11x::enums["RenderMode"].attr("__doc__") = RenderMode__doc__; + p11x::enums["FaceFlags"].attr("__doc__") = FaceFlags__doc__; + p11x::enums["StyleFlags"].attr("__doc__") = StyleFlags__doc__; + + py::class_(m, "FT2Image", py::is_final(), py::buffer_protocol(), + PyFT2Image__doc__) + .def(py::init( + [](double_or_ width, double_or_ height) { + auto warn = + py::module_::import("matplotlib._api").attr("warn_deprecated"); + warn("since"_a="3.11", "name"_a="FT2Image", "obj_type"_a="class", + "alternative"_a="a 2D uint8 ndarray"); + return new FT2Image( + _double_to_("width", width), + _double_to_("height", height) + ); + }), + "width"_a, "height"_a, PyFT2Image_init__doc__) + .def("draw_rect_filled", &PyFT2Image_draw_rect_filled, + "x0"_a, "y0"_a, "x1"_a, "y1"_a, + PyFT2Image_draw_rect_filled__doc__) + .def_buffer([](FT2Image &self) -> py::buffer_info { + std::vector shape { self.get_height(), self.get_width() }; + std::vector strides { self.get_width(), 1 }; + return py::buffer_info(self.get_buffer(), shape, strides); + }); + + py::class_(m, "_PositionedBitmap", py::is_final()) + .def_readonly("left", &PyPositionedBitmap::left) + .def_readonly("top", &PyPositionedBitmap::top) + .def_property_readonly( + "buffer", [](PyPositionedBitmap &self) -> py::array { + return {{self.bitmap.rows, self.bitmap.width}, + {self.bitmap.pitch, 1}, + self.bitmap.buffer}; + }) + ; + + py::class_(m, "Glyph", py::is_final(), PyGlyph__doc__) + .def(py::init<>([]() -> PyGlyph { + // Glyph is not useful from Python, so mark it as not constructible. + throw std::runtime_error("Glyph is not constructible"); + })) + .def_readonly("width", &PyGlyph::width, "The glyph's width.") + .def_readonly("height", &PyGlyph::height, "The glyph's height.") + .def_readonly("horiBearingX", &PyGlyph::horiBearingX, + "Left side bearing for horizontal layout.") + .def_readonly("horiBearingY", &PyGlyph::horiBearingY, + "Top side bearing for horizontal layout.") + .def_readonly("horiAdvance", &PyGlyph::horiAdvance, + "Advance width for horizontal layout.") + .def_readonly("linearHoriAdvance", &PyGlyph::linearHoriAdvance, + "The advance width of the unhinted glyph.") + .def_readonly("vertBearingX", &PyGlyph::vertBearingX, + "Left side bearing for vertical layout.") + .def_readonly("vertBearingY", &PyGlyph::vertBearingY, + "Top side bearing for vertical layout.") + .def_readonly("vertAdvance", &PyGlyph::vertAdvance, + "Advance height for vertical layout.") + .def_property_readonly("bbox", &PyGlyph_get_bbox, + "The control box of the glyph."); + + py::class_(m, "LayoutItem", py::is_final()) + .def(py::init<>([]() -> LayoutItem { + // LayoutItem is not useful from Python, so mark it as not constructible. + throw std::runtime_error("LayoutItem is not constructible"); + })) + .def_readonly("ft_object", &LayoutItem::ft_object, + "The FT_Face of the item.") + .def_readonly("char", &LayoutItem::character, + "The character code for the item.") + .def_readonly("glyph_index", &LayoutItem::glyph_index, + "The glyph index for the item.") + .def_readonly("x", &LayoutItem::x, + "The x position of the item.") + .def_readonly("y", &LayoutItem::y, + "The y position of the item.") + .def_readonly("prev_kern", &LayoutItem::prev_kern, + "The kerning between this item and the previous one.") + .def("__str__", + [](const LayoutItem& item) { + return + "LayoutItem(ft_object={}, char={!r}, glyph_index={}, "_s + "x={}, y={}, prev_kern={})"_s.format( + PyFT2Font_fname(item.ft_object), item.character, + item.glyph_index, item.x, item.y, item.prev_kern); + }); + + auto cls = py::class_(m, "FT2Font", py::is_final(), py::buffer_protocol(), + PyFT2Font__doc__) + .def(py::init(&PyFT2Font_init), + "filename"_a, "hinting_factor"_a=8, py::kw_only(), "face_index"_a=0, + "_fallback_list"_a=py::none(), "_kerning_factor"_a=py::none(), + "_warn_if_used"_a=false, + PyFT2Font_init__doc__) + .def("clear", &PyFT2Font::clear, PyFT2Font_clear__doc__) + .def("set_size", &PyFT2Font::set_size, "ptsize"_a, "dpi"_a, + PyFT2Font_set_size__doc__) + .def("_set_transform", &PyFT2Font::_set_transform, "matrix"_a, "delta"_a, + PyFT2Font__set_transform__doc__) + .def("set_charmap", &PyFT2Font::set_charmap, "i"_a, + PyFT2Font_set_charmap__doc__) + .def("select_charmap", &PyFT2Font::select_charmap, "i"_a, + PyFT2Font_select_charmap__doc__) + .def("get_kerning", &PyFT2Font_get_kerning, "left"_a, "right"_a, "mode"_a, + PyFT2Font_get_kerning__doc__) + .def("_layout", &PyFT2Font_layout, "string"_a, "flags"_a, py::kw_only(), + "features"_a=nullptr, "language"_a=nullptr, + PyFT2Font_layout__doc__) + .def("set_text", &PyFT2Font_set_text, + "string"_a, "angle"_a=0.0, "flags"_a=LoadFlags::FORCE_AUTOHINT, py::kw_only(), + "features"_a=nullptr, "language"_a=nullptr, + PyFT2Font_set_text__doc__) + .def("get_num_glyphs", &PyFT2Font::get_num_glyphs, + PyFT2Font_get_num_glyphs__doc__) + .def("load_char", &PyFT2Font_load_char, + "charcode"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, + PyFT2Font_load_char__doc__) + .def("load_glyph", &PyFT2Font_load_glyph, + "glyph_index"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, + PyFT2Font_load_glyph__doc__) + .def("get_width_height", &PyFT2Font::get_width_height, + PyFT2Font_get_width_height__doc__) + .def("get_bitmap_offset", &PyFT2Font::get_bitmap_offset, + PyFT2Font_get_bitmap_offset__doc__) + .def("get_descent", &PyFT2Font::get_descent, PyFT2Font_get_descent__doc__) + .def("draw_glyphs_to_bitmap", &PyFT2Font::draw_glyphs_to_bitmap, + py::kw_only(), "antialiased"_a=true, + PyFT2Font_draw_glyphs_to_bitmap__doc__); + // The generated docstring uses an unqualified "Buffer" as type hint, + // which causes an error in sphinx. This is fixed as of pybind11 + // master (since #5566) which now uses "collections.abc.Buffer"; + // restore the signature once that version is released. + { + py::options options{}; + options.disable_function_signatures(); + cls + .def("draw_glyph_to_bitmap", &PyFT2Font_draw_glyph_to_bitmap, + "image"_a, "x"_a, "y"_a, "glyph"_a, py::kw_only(), "antialiased"_a=true, + PyFT2Font_draw_glyph_to_bitmap__doc__); + } + cls + .def("get_glyph_name", &PyFT2Font::get_glyph_name, "index"_a, + PyFT2Font_get_glyph_name__doc__) + .def("get_charmap", &PyFT2Font_get_charmap, PyFT2Font_get_charmap__doc__) + .def("get_char_index", &PyFT2Font::get_char_index, + "codepoint"_a, py::kw_only(), "_fallback"_a=true, + PyFT2Font_get_char_index__doc__) + .def("get_sfnt", &PyFT2Font_get_sfnt, PyFT2Font_get_sfnt__doc__) + .def("get_name_index", &PyFT2Font::get_name_index, "name"_a, + PyFT2Font_get_name_index__doc__) + .def("get_ps_font_info", &PyFT2Font_get_ps_font_info, + PyFT2Font_get_ps_font_info__doc__) + .def("get_sfnt_table", &PyFT2Font_get_sfnt_table, "name"_a, + PyFT2Font_get_sfnt_table__doc__) + .def("get_path", &PyFT2Font_get_path, PyFT2Font_get_path__doc__) + .def("get_image", &PyFT2Font::get_image, PyFT2Font_get_image__doc__) + .def("_get_type1_encoding_vector", &PyFT2Font__get_type1_encoding_vector, + PyFT2Font__get_type1_encoding_vector__doc__) + + .def_property_readonly( + "postscript_name", [](PyFT2Font *self) { + if (const char *name = FT_Get_Postscript_Name(self->get_face())) { + return name; + } else { + return "UNAVAILABLE"; + } + }, "PostScript name of the font.") + .def_property_readonly( + "num_faces", [](PyFT2Font *self) { + return self->get_face()->num_faces & 0xffff; + }, "Number of faces in file.") + .def_property_readonly( + "face_index", [](PyFT2Font *self) { + return self->get_face()->face_index; + }, "The index of the font in the file.") + .def_property_readonly( + "family_name", [](PyFT2Font *self) { + if (const char *name = self->get_face()->family_name) { + return name; + } else { + return "UNAVAILABLE"; + } + }, "Face family name.") + .def_property_readonly( + "style_name", [](PyFT2Font *self) { + if (const char *name = self->get_face()->style_name) { + return name; + } else { + return "UNAVAILABLE"; + } + }, "Style name.") + .def_property_readonly( + "face_flags", [](PyFT2Font *self) { + return static_cast(self->get_face()->face_flags); + }, "Face flags; see `.FaceFlags`.") + .def_property_readonly( + "style_flags", [](PyFT2Font *self) { + return static_cast(self->get_face()->style_flags & 0xffff); + }, "Style flags; see `.StyleFlags`.") + .def_property_readonly( + "num_named_instances", [](PyFT2Font *self) { + return (self->get_face()->style_flags & 0x7fff0000) >> 16; + }, "Number of named instances in the face.") + .def_property_readonly( + "num_glyphs", [](PyFT2Font *self) { + return self->get_face()->num_glyphs; + }, "Number of glyphs in the face.") + .def_property_readonly( + "num_fixed_sizes", [](PyFT2Font *self) { + return self->get_face()->num_fixed_sizes; + }, "Number of bitmap in the face.") + .def_property_readonly( + "num_charmaps", [](PyFT2Font *self) { + return self->get_face()->num_charmaps; + }, "Number of charmaps in the face.") + .def_property_readonly( + "scalable", [](PyFT2Font *self) { + return bool(FT_IS_SCALABLE(self->get_face())); + }, "Whether face is scalable; attributes after this one " + "are only defined for scalable faces.") + .def_property_readonly( + "units_per_EM", [](PyFT2Font *self) { + return self->get_face()->units_per_EM; + }, "Number of font units covered by the EM.") + .def_property_readonly( + "bbox", [](PyFT2Font *self) { + FT_BBox bbox = self->get_face()->bbox; + return py::make_tuple(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); + }, "Face global bounding box (xmin, ymin, xmax, ymax).") + .def_property_readonly( + "ascender", [](PyFT2Font *self) { + return self->get_face()->ascender; + }, "Ascender in 26.6 units.") + .def_property_readonly( + "descender", [](PyFT2Font *self) { + return self->get_face()->descender; + }, "Descender in 26.6 units.") + .def_property_readonly( + "height", [](PyFT2Font *self) { + return self->get_face()->height; + }, "Height in 26.6 units; used to compute a default line spacing " + "(baseline-to-baseline distance).") + .def_property_readonly( + "max_advance_width", [](PyFT2Font *self) { + return self->get_face()->max_advance_width; + }, "Maximum horizontal cursor advance for all glyphs.") + .def_property_readonly( + "max_advance_height", [](PyFT2Font *self) { + return self->get_face()->max_advance_height; + }, "Maximum vertical cursor advance for all glyphs.") + .def_property_readonly( + "underline_position", [](PyFT2Font *self) { + return self->get_face()->underline_position; + }, "Vertical position of the underline bar.") + .def_property_readonly( + "underline_thickness", [](PyFT2Font *self) { + return self->get_face()->underline_thickness; + }, "Thickness of the underline bar.") + .def_property_readonly( + "fname", &PyFT2Font_fname, + "The original filename for this object.") + .def_property_readonly( + "_hinting_factor", &PyFT2Font::get_hinting_factor, + "The hinting factor.") + + .def_buffer([](PyFT2Font &self) -> py::buffer_info { + return self.get_image().request(); + }) + + .def("_render_glyph", + [](PyFT2Font *self, FT_UInt idx, LoadFlags flags, FT_Render_Mode render_mode) { + auto face = self->get_face(); + FT_CHECK(FT_Load_Glyph, face, idx, static_cast(flags)); + FT_CHECK(FT_Render_Glyph, face->glyph, render_mode); + return PyPositionedBitmap{face->glyph}; + }) + ; + + m.attr("__freetype_version__") = version_string; + m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE; + m.attr("__libraqm_version__") = raqm_version_string(); + auto py_int = py::module_::import("builtins").attr("int"); + m.attr("CharacterCodeType") = py_int; + m.attr("GlyphIndexType") = py_int; + m.def("__getattr__", ft2font__getattr__); } diff --git a/src/meson.build b/src/meson.build index bbef93c13d92..8b52bf739c03 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,42 +1,3 @@ -# NumPy include directory - needed in all submodules -# The try-except is needed because when things are split across drives on Windows, there -# is no relative path and an exception gets raised. There may be other such cases, so add -# a catch-all and switch to an absolute path. Relative paths are needed when for example -# a virtualenv is placed inside the source tree; Meson rejects absolute paths to places -# inside the source tree. -# For cross-compilation it is often not possible to run the Python interpreter in order -# to retrieve numpy's include directory. It can be specified in the cross file instead: -# -# [properties] -# numpy-include-dir = /abspath/to/host-pythons/site-packages/numpy/core/include -# -# This uses the path as is, and avoids running the interpreter. -incdir_numpy = meson.get_external_property('numpy-include-dir', 'not-given') -if incdir_numpy == 'not-given' - incdir_numpy = run_command(py3, - [ - '-c', - '''import os -import numpy as np -try: - incdir = os.path.relpath(np.get_include()) -except Exception: - incdir = np.get_include() -print(incdir)''' - ], - check: true - ).stdout().strip() -endif -numpy_dep = declare_dependency( - compile_args: [ - '-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION', - # Allow NumPy's printf format specifiers in C++. - '-D__STDC_FORMAT_MACROS=1', - ], - include_directories: include_directories(incdir_numpy), - dependencies: py3_dep, -) - # For cross-compilation it is often not possible to run the Python interpreter in order # to retrieve the platform-specific /dev/null. It can be specified in the cross file # instead: @@ -73,11 +34,10 @@ extension_data = { '_backend_agg': { 'subdir': 'matplotlib/backends', 'sources': files( - 'py_converters.cpp', '_backend_agg.cpp', '_backend_agg_wrapper.cpp', ), - 'dependencies': [agg_dep, numpy_dep, freetype_dep], + 'dependencies': [agg_dep, pybind11_dep], }, '_c_internal_utils': { 'subdir': 'matplotlib', @@ -91,10 +51,9 @@ extension_data = { 'sources': files( 'ft2font.cpp', 'ft2font_wrapper.cpp', - 'py_converters.cpp', ), 'dependencies': [ - freetype_dep, numpy_dep, agg_dep.partial_dependency(includes: true), + freetype_dep, libraqm_dep, pybind11_dep, agg_dep.partial_dependency(includes: true), ], 'cpp_args': [ '-DFREETYPE_BUILD_TYPE="@0@"'.format( @@ -106,7 +65,7 @@ extension_data = { 'subdir': 'matplotlib', 'sources': files( '_image_wrapper.cpp', - 'py_converters_11.cpp', + 'py_converters.cpp', ), 'dependencies': [ pybind11_dep, @@ -117,11 +76,9 @@ extension_data = { '_path': { 'subdir': 'matplotlib', 'sources': files( - 'py_converters.cpp', - 'py_converters_11.cpp', '_path_wrapper.cpp', ), - 'dependencies': [numpy_dep, agg_dep, pybind11_dep], + 'dependencies': [agg_dep, pybind11_dep], }, '_qhull': { 'subdir': 'matplotlib', @@ -151,30 +108,26 @@ extension_data = { ), 'dependencies': [pybind11_dep], }, - '_ttconv': { - 'subdir': 'matplotlib', - 'sources': files( - '_ttconv.cpp', - ), - 'dependencies': [ttconv_dep, pybind11_dep], - }, } -cpp_special_arguments = [] -if cpp.get_id() == 'msvc' and get_option('buildtype') != 'plain' - # Disable FH4 Exception Handling implementation so that we don't require - # VCRUNTIME140_1.dll. For more details, see: - # https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ - # https://github.com/joerick/cibuildwheel/issues/423#issuecomment-677763904 - cpp_special_arguments += ['/d2FH4-'] +if cpp.get_id() == 'msvc' + # This flag fixes some bugs with the macro processing, namely + # https://learn.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview?view=msvc-170#macro-arguments-are-unpacked + if cpp.has_argument('/Zc:preprocessor') + # This flag was added in MSVC 2019 version 16.5, which deprecated the one below. + new_preprocessor = '/Zc:preprocessor' + else + # Since we currently support any version of MSVC 2019 (vc142), we'll stick with the + # older flag, added in MSVC 2017 version 15.8. + new_preprocessor = '/experimental:preprocessor' + endif +else + new_preprocessor = [] endif foreach ext, kwargs : extension_data - # Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each extension. - unique_array_api = '-DPY_ARRAY_UNIQUE_SYMBOL=MPL_@0@_ARRAY_API'.format(ext.replace('.', '_')) additions = { - 'c_args': [unique_array_api] + kwargs.get('c_args', []), - 'cpp_args': cpp_special_arguments + [unique_array_api] + kwargs.get('cpp_args', []), + 'cpp_args': [new_preprocessor] + kwargs.get('cpp_args', []), } py3.extension_module( ext, diff --git a/src/mplutils.h b/src/mplutils.h index 58eb32d190ec..95d9a2d9eb90 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -48,48 +48,70 @@ enum { CLOSEPOLY = 0x4f }; -inline int prepare_and_add_type(PyTypeObject *type, PyObject *module) -{ - if (PyType_Ready(type)) { - return -1; - } - char const* ptr = strrchr(type->tp_name, '.'); - if (!ptr) { - PyErr_SetString(PyExc_ValueError, "tp_name should be a qualified name"); - return -1; - } - if (PyModule_AddObject(module, ptr + 1, (PyObject *)type)) { - return -1; - } - return 0; -} - #ifdef __cplusplus // not for macosx.m // Check that array has shape (N, d1) or (N, d1, d2). We cast d1, d2 to longs // so that we don't need to access the NPY_INTP_FMT macro here. +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; template -inline bool check_trailing_shape(T array, char const* name, long d1) +inline void check_trailing_shape(T array, char const* name, long d1) { + if (array.ndim() != 2) { + throw py::value_error( + "Expected 2-dimensional array, got %d"_s.format(array.ndim())); + } + if (array.size() == 0) { + // Sometimes things come through as atleast_2d, etc., but they're empty, so + // don't bother enforcing the trailing shape. + return; + } if (array.shape(1) != d1) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld), got (%ld, %ld)", - name, d1, array.shape(0), array.shape(1)); - return false; + throw py::value_error( + "%s must have shape (N, %d), got (%d, %d)"_s.format( + name, d1, array.shape(0), array.shape(1))); } - return true; } template -inline bool check_trailing_shape(T array, char const* name, long d1, long d2) +inline void check_trailing_shape(T array, char const* name, long d1, long d2) { + if (array.ndim() != 3) { + throw py::value_error( + "Expected 3-dimensional array, got %d"_s.format(array.ndim())); + } + if (array.size() == 0) { + // Sometimes things come through as atleast_3d, etc., but they're empty, so + // don't bother enforcing the trailing shape. + return; + } if (array.shape(1) != d1 || array.shape(2) != d2) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld, %ld), got (%ld, %ld, %ld)", - name, d1, d2, array.shape(0), array.shape(1), array.shape(2)); - return false; + throw py::value_error( + "%s must have shape (N, %d, %d), got (%d, %d, %d)"_s.format( + name, d1, d2, array.shape(0), array.shape(1), array.shape(2))); + } +} + +/* In most cases, code should use safe_first_shape(obj) instead of obj.shape(0), since + safe_first_shape(obj) == 0 when any dimension is 0. */ +template +py::ssize_t +safe_first_shape(const py::detail::unchecked_reference &a) +{ + bool empty = (ND == 0); + for (py::ssize_t i = 0; i < ND; i++) { + if (a.shape(i) == 0) { + empty = true; + } + } + if (empty) { + return 0; + } else { + return a.shape(0); } - return true; } #endif diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h deleted file mode 100644 index 6165789b7603..000000000000 --- a/src/numpy_cpp.h +++ /dev/null @@ -1,582 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_NUMPY_CPP_H -#define MPL_NUMPY_CPP_H -#define PY_SSIZE_T_CLEAN -/*************************************************************************** - * This file is based on original work by Mark Wiebe, available at: - * - * http://github.com/mwiebe/numpy-cpp - * - * However, the needs of matplotlib wrappers, such as treating an - * empty array as having the correct dimensions, have made this rather - * matplotlib-specific, so it's no longer compatible with the - * original. - */ - -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif -#ifndef _AIX -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif -#endif - -// Prevent multiple conflicting definitions of swab from stdlib.h and unistd.h -#if defined(__sun) || defined(sun) -#if defined(_XPG4) -#undef _XPG4 -#endif -#if defined(_XPG3) -#undef _XPG3 -#endif -#endif - -#include -#include - -#include "py_exceptions.h" - -#include - -namespace numpy -{ - -// Type traits for the NumPy types -template -struct type_num_of; - -/* Be careful with bool arrays as python has sizeof(npy_bool) == 1, but it is - * not always the case that sizeof(bool) == 1. Using the array_view_accessors - * is always fine regardless of sizeof(bool), so do this rather than using - * array.data() and pointer arithmetic which will not work correctly if - * sizeof(bool) != 1. */ -template <> struct type_num_of -{ - enum { - value = NPY_BOOL - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_BYTE - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_UBYTE - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_SHORT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_USHORT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_INT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_UINT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_LONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_ULONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_LONGLONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_ULONGLONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_FLOAT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_DOUBLE - }; -}; -#if NPY_LONGDOUBLE != NPY_DOUBLE -template <> -struct type_num_of -{ - enum { - value = NPY_LONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_CDOUBLE - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CDOUBLE - }; -}; -#if NPY_CLONGDOUBLE != NPY_CDOUBLE -template <> -struct type_num_of -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of -{ - enum { - value = NPY_OBJECT - }; -}; -template -struct type_num_of -{ - enum { - value = type_num_of::value - }; -}; -template -struct type_num_of -{ - enum { - value = type_num_of::value - }; -}; - -template -struct is_const -{ - enum { - value = false - }; -}; -template -struct is_const -{ - enum { - value = true - }; -}; - -namespace detail -{ -template