├── .github ├── dependabot.yml └── workflows │ └── wheels.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── example ├── LICENSE.roboto ├── Roboto-Medium.ttf └── example.py ├── peru.yaml ├── pyproject.toml ├── src └── pkgIndex.tcl.in ├── tests ├── README.overhaul ├── overhaul.ttf └── test_tkextrafont.py ├── third_party └── extrafont │ ├── docs │ └── doc.txt │ ├── license.terms │ └── src │ ├── extrafont.c │ ├── extrafont.tcl │ ├── fontnameinfo.tcl │ ├── futmp.tcl │ └── pkgIndex.tcl └── tkextrafont └── __init__.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_wheels: 7 | name: Build wheels on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macos-12, macos-13, macos-14] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | submodules: true 17 | 18 | - name: Set up QEMU 19 | if: runner.os == 'Linux' 20 | uses: docker/setup-qemu-action@v3 21 | with: 22 | platforms: all 23 | 24 | - name: Build wheels 25 | uses: pypa/cibuildwheel@v2.21.3 26 | env: 27 | CIBW_ARCHS_LINUX: auto aarch64 ppc64le s390x 28 | CIBW_ARCHS_WINDOWS: all 29 | 30 | - uses: actions/upload-artifact@v4 31 | with: 32 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 33 | path: ./wheelhouse/*.whl 34 | 35 | make_sdist: 36 | name: Make SDist 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v4 40 | with: 41 | submodules: true 42 | 43 | - name: Build SDist 44 | run: pipx run build --sdist 45 | 46 | - uses: actions/upload-artifact@v4 47 | with: 48 | name: cibw-sdist 49 | path: dist/*.tar.gz 50 | 51 | upload_all: 52 | needs: [build_wheels, make_sdist] 53 | environment: pypi 54 | permissions: 55 | id-token: write 56 | runs-on: ubuntu-latest 57 | if: github.event_name == 'release' && github.event.action == 'published' 58 | steps: 59 | - uses: actions/download-artifact@v4 60 | with: 61 | pattern: cibw-* 62 | path: dist 63 | merge-multiple: true 64 | 65 | - uses: pypa/gh-action-pypi-publish@release/v1 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python ### 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # pdm 106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 107 | #pdm.lock 108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 109 | # in version control. 110 | # https://pdm.fming.dev/#use-with-ide 111 | .pdm.toml 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | 163 | ### Python Patch ### 164 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 165 | poetry.toml 166 | 167 | # ruff 168 | .ruff_cache/ 169 | 170 | # LSP config files 171 | pyrightconfig.json 172 | 173 | .peru/ 174 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | # =========================================================================== 4 | # Project Information 5 | # =========================================================================== 6 | project( 7 | extrafont 8 | LANGUAGES C 9 | VERSION 1.3) 10 | 11 | # =========================================================================== 12 | # Locate Tcl/Tk 13 | # =========================================================================== 14 | if(MSVC) 15 | find_package(TCL QUIET) 16 | if(NOT TCL_FOUND) 17 | include(FetchContent) 18 | FetchContent_Declare( 19 | cpython-tcltk-win32 20 | GIT_REPOSITORY https://github.com/python/cpython-bin-deps 21 | GIT_TAG tcltk) 22 | FetchContent_MakeAvailable(cpython-tcltk-win32) 23 | 24 | if(cpython-tcltk-win32_POPULATED) 25 | if(MSVC_C_ARCHITECTURE_ID STREQUAL "ARM64") 26 | set(CMAKE_PREFIX_PATH 27 | ${CMAKE_PREFIX_PATH};${cpython-tcltk-win32_SOURCE_DIR}/arm64) 28 | elseif(MSVC_C_ARCHITECTURE_ID STREQUAL "x64") 29 | set(CMAKE_PREFIX_PATH 30 | ${CMAKE_PREFIX_PATH};${cpython-tcltk-win32_SOURCE_DIR}/amd64) 31 | elseif(MSVC_C_ARCHITECTURE_ID STREQUAL "X86") 32 | set(CMAKE_PREFIX_PATH 33 | ${CMAKE_PREFIX_PATH};${cpython-tcltk-win32_SOURCE_DIR}/win32) 34 | else() 35 | message( 36 | WARNING 37 | "No Tcl/Tk prebuilt binaries from CPython could be found for your platform (MSVC ${MSVC_C_ARCHITECTURE_ID})." 38 | "If the build fails, Ensure a MSVC-built Tcl/Tk is available on CMAKE_PREFIX_PATH." 39 | ) 40 | endif() 41 | endif() 42 | endif() 43 | endif() 44 | 45 | find_package(TCL REQUIRED) 46 | find_package(TclStub REQUIRED) 47 | 48 | # =========================================================================== 49 | # Target: tkextrafont shared library 50 | # =========================================================================== 51 | configure_file(third_party/extrafont/src/extrafont.c 52 | ${CMAKE_CURRENT_BINARY_DIR}/extrafont.c) 53 | add_library(tkextrafont SHARED ${CMAKE_CURRENT_BINARY_DIR}/extrafont.c) 54 | target_link_libraries(tkextrafont PRIVATE ${TCL_STUB_LIBRARY}) 55 | target_link_libraries(tkextrafont PRIVATE ${TK_STUB_LIBRARY}) 56 | 57 | if(APPLE) 58 | find_library(CoreText_LIBRARY CoreText REQUIRED) 59 | find_library(CoreFoundation_LIBRARY CoreFoundation REQUIRED) 60 | target_link_libraries(tkextrafont PRIVATE ${CoreText_LIBRARY} ${CoreFoundation_LIBRARY}) 61 | elseif(WIN32) 62 | target_link_libraries(tkextrafont PRIVATE gdi32) 63 | elseif(UNIX) 64 | find_package(Fontconfig REQUIRED) 65 | target_link_libraries(tkextrafont PRIVATE ${Fontconfig_LIBRARIES}) 66 | endif() 67 | 68 | target_compile_definitions(tkextrafont PRIVATE -DUSE_TCL_STUBS) 69 | target_compile_definitions(tkextrafont PRIVATE -DUSE_TK_STUBS) 70 | target_compile_definitions(tkextrafont 71 | PRIVATE -DPACKAGE_NAME="${CMAKE_PROJECT_NAME}") 72 | target_compile_definitions(tkextrafont 73 | PRIVATE -DPACKAGE_VERSION="${CMAKE_PROJECT_VERSION}") 74 | target_include_directories(tkextrafont PRIVATE ${TCL_INCLUDE_PATH}) 75 | target_include_directories(tkextrafont PRIVATE ${TK_INCLUDE_PATH}) 76 | target_include_directories(tkextrafont PRIVATE ${Fontconfig_INCLUDE_DIRS}) 77 | 78 | block() 79 | set(PACKAGE_NAME ${CMAKE_PROJECT_NAME}) 80 | set(PACKAGE_VERSION ${CMAKE_PROJECT_VERSION}) 81 | set(PKG_LIB_FILE 82 | ${CMAKE_SHARED_LIBRARY_PREFIX}tkextrafont${CMAKE_SHARED_LIBRARY_SUFFIX}) 83 | configure_file(src/pkgIndex.tcl.in pkgIndex.tcl @ONLY) 84 | endblock() 85 | 86 | configure_file(third_party/extrafont/src/extrafont.tcl extrafont.tcl) 87 | configure_file(third_party/extrafont/src/fontnameinfo.tcl fontnameinfo.tcl) 88 | configure_file(third_party/extrafont/src/futmp.tcl futmp.tcl) 89 | 90 | if(WIN32) 91 | install(TARGETS tkextrafont RUNTIME DESTINATION tkextrafont) 92 | else() 93 | install(TARGETS tkextrafont LIBRARY DESTINATION tkextrafont) 94 | endif() 95 | install( 96 | FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgIndex.tcl 97 | ${CMAKE_CURRENT_BINARY_DIR}/extrafont.tcl 98 | ${CMAKE_CURRENT_BINARY_DIR}/fontnameinfo.tcl 99 | ${CMAKE_CURRENT_BINARY_DIR}/futmp.tcl 100 | DESTINATION tkextrafont) 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2020 A. Buratti 4 | Copyright (c) 2021 RedFantom 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all vendor build publish 2 | 3 | all: vendor build 4 | 5 | build: 6 | hatch build 7 | 8 | publish: build 9 | hatch publish 10 | 11 | vendor: .peru/lastimports 12 | peru reup 13 | 14 | .peru/lastimports: peru.yaml 15 | peru sync 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tkextrafont 2 | [![Build status](https://ci.appveyor.com/api/projects/status/s64648offncdbmbk/branch/master?svg=true)](https://ci.appveyor.com/project/RedFantom/python-tkextrafont/branch/master) 3 | [![Build status](https://api.travis-ci.com/TkinterEP/python-tkextrafont.svg?branch=master)](https://travis-ci.com/github/TkinterEP/python-tkextrafont/) 4 | [![PyPI version](https://badge.fury.io/py/tkextrafont.svg)](https://pypi.python.org/pypi/tkextrafont) 5 | [![License](https://img.shields.io/badge/license-MIT%20License-green)](https://github.com/TkinterEP/python-tksvg/blob/master/LICENSE.md) 6 | [![PyPI Format](https://img.shields.io/pypi/format/tksvg?color=purple)](https://pypi.python.org/pypi/tksvg) 7 | 8 | From the basic concept of [the old version](https://github.com/TkinterEP/python-tkextrafont.old) 9 | of this repository and the [build system](https://github.com/TkinterEP/python-gttk/blob/master/CMakeLists.txt) 10 | created for [`python-gttk`](https://github.com/TkinterEP/python-gttk) 11 | now comes a version of the `extrafont` package that is packaged for 12 | Python on both Windows and Linux platforms and run on CI platforms. 13 | 14 | ## Building & Installation 15 | The package is built with `CMake` and [`scikit-build`](https://scikit-build.readthedocs.io/en/latest/), 16 | and on Windows an installation of [`MYS2`](https://www.msys2.org/) is 17 | required. The original build system for the `extrafont` package with 18 | a build system that depends on Visual Studio is not available in this 19 | repository, though you might be able to build the `extrafont` binary 20 | separately and then copy it as you require. 21 | 22 | ### Linux 23 | This example is given for Ubuntu 20.04, change the names of packages and 24 | commands as required for your distribution of choice. 25 | ```bash 26 | sudo apt install python3-dev tcl-dev tk-dev \ 27 | fontconfig libfontconfig1 libfontconfig1-dev \ 28 | cmake cmake-data extra-cmake-modules build-essential 29 | python -m pip install scikit-build 30 | python setup.py install 31 | ``` 32 | 33 | ### Windows 34 | See the `setup.py` and `.appveyor.yml` for more detailed installation 35 | instructions. After installing the dependencies (`tk`, `cmake`, 36 | `toolchain`, `fontconfig`), run in the MSYS shell: 37 | ``` 38 | python setup..py build 39 | python setup.py install 40 | ``` 41 | 42 | ## Usage 43 | ```python 44 | import tkinter as tk 45 | from tkextrafont import Font 46 | 47 | window = tk.Tk() 48 | font = Font(file="tests/overhaul.ttf", family="Overhaul") 49 | tk.Label(window, text="Hello", font=font).pack() 50 | window.mainloop() 51 | ``` 52 | 53 | ## License & Copyright 54 | This library is available under the MIT License, as described in 55 | `LICENSE.md`. `extrafont` is originally available under a different 56 | license, which is also found in `LICENSE.md`. The build system 57 | (`setup.py`, `.appveyor.yml` and `.travis.yml`) are available under 58 | GNU GPLv3 only. 59 | -------------------------------------------------------------------------------- /example/LICENSE.roboto: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /example/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkinterEP/python-tkextrafont/aee61bec47b8f981d2cc3625141a8a9d254f5531/example/Roboto-Medium.ttf -------------------------------------------------------------------------------- /example/example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: RedFantom 3 | License: GNU GPLv3 4 | Copyright (c) 2018 RedFantom 5 | """ 6 | import tkinter as tk 7 | from tkinter import ttk 8 | from tkextrafont import Font 9 | 10 | window = tk.Tk() 11 | font = Font(file="Roboto-Medium.ttf") 12 | assert font.is_font_available("Roboto") 13 | assert "Roboto Medium" in font.loaded_fonts() 14 | assert font.font_info("Roboto-Medium.ttf")["copyright"] 15 | ttk.Label(window, text="Roboto Font", font=("Roboto", 12)).grid() 16 | ttk.Label(window, text="Normal Font", font=("default", 12)).grid() 17 | window.mainloop() 18 | -------------------------------------------------------------------------------- /peru.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | extrafont: third_party/extrafont/ 3 | roboto: example/ 4 | overhaul: tests/ 5 | 6 | svn module extrafont: 7 | url: https://svn.code.sf.net/p/irrational-numbers/code/pkgs/extrafont-devkit/trunk/ 8 | rev: "1218" 9 | pick: 10 | - license.terms 11 | - docs/ 12 | - src/ 13 | 14 | curl module roboto: 15 | url: https://github.com/googlefonts/roboto/releases/download/v2.138/roboto-android.zip 16 | unpack: zip 17 | move: 18 | LICENSE: LICENSE.roboto 19 | pick: 20 | - LICENSE.roboto 21 | - Roboto-Medium.ttf 22 | sha1: 0453421f680022954c48ca21c45bffc18749e6bc 23 | 24 | curl module overhaul: 25 | url: https://dl.dafont.com/dl/?f=overhaul 26 | unpack: zip 27 | move: 28 | Overhaul.ttf: overhaul.ttf 29 | Readme.txt: README.overhaul 30 | pick: 31 | - overhaul.ttf 32 | - README.overhaul 33 | sha1: 9d3a95aed46aa9808f0850ffa8ed35bb46f3da46 34 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core"] 3 | build-backend = "scikit_build_core.build" 4 | 5 | [project] 6 | name = "tkextrafont" 7 | version = "1.3.0" 8 | description = "Fonts loader for Tkinter" 9 | author = "The extrafont and tkextrafont authors" 10 | url = "https://github.com/TkinterEP/tkextrafont" 11 | 12 | [tool.cibuildwheel] 13 | skip = ["cp36-*", "cp37-*"] 14 | 15 | [tool.cibuildwheel.linux] 16 | before-all = "yum install -y tcl-devel tk-devel fontconfig-devel" 17 | 18 | [[tool.cibuildwheel.overrides]] 19 | select = "*-musllinux*" 20 | before-all = "apk add tcl-dev tk-dev fontconfig-dev" 21 | 22 | [tool.cibuildwheel.macos] 23 | # https://github.com/actions/runner-images/issues/9441#issuecomment-1976928303 24 | before-all = "brew install tcl-tk" 25 | -------------------------------------------------------------------------------- /src/pkgIndex.tcl.in: -------------------------------------------------------------------------------- 1 | package ifneeded @PACKAGE_NAME@ @PACKAGE_VERSION@ \ 2 | "source [file join $dir extrafont.tcl]; \ 3 | source [file join $dir futmp.tcl]; \ 4 | load [file join $dir @PKG_LIB_FILE@] [string totitle @PACKAGE_NAME@]; \ 5 | package provide @PACKAGE_NAME@ @PACKAGE_VERSION@;" 6 | -------------------------------------------------------------------------------- /tests/README.overhaul: -------------------------------------------------------------------------------- 1 | Overhaul Font 2 | 3 | ------------------------------------------------------------------------------------------------ 4 | Caution: This font is heavy to load. On some computers, the font can crash or freeze the system. 5 | ------------------------------------------------------------------------------------------------ 6 | 7 | Overhaul is a sans-serif grunge font. Like all other JLH Fonts, this font is in the public domain and comes with the Euro sign. 8 | 9 | Check out our other fonts, such as: 10 | 11 | - Hand Drawn Shapes 12 | - Gold Plated 13 | - Chalk Line 14 | - Portmanteau 15 | - Pretzel 16 | - Sierra Nevada Road 17 | - Seattle Avenue 18 | - Marker Scribbles (symbol) -------------------------------------------------------------------------------- /tests/overhaul.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkinterEP/python-tkextrafont/aee61bec47b8f981d2cc3625141a8a9d254f5531/tests/overhaul.ttf -------------------------------------------------------------------------------- /tests/test_tkextrafont.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: RedFantom 3 | License: MIT License 4 | Copyright (c) 2021 RedFantom 5 | """ 6 | import sys 7 | sys.path = sys.path[2:] 8 | import tkinter as tk 9 | import tkextrafont 10 | from unittest import TestCase 11 | import os 12 | 13 | 14 | class TestTkExtraFont(TestCase): 15 | """Test the compiled extrafont binary with a public-domain font""" 16 | 17 | PATH = os.path.abspath(os.path.dirname(__file__)) 18 | 19 | def setUp(self): 20 | self.window = tk.Tk() 21 | tkextrafont.load(self.window) 22 | 23 | def test_font_load(self): 24 | font = tkextrafont.Font() 25 | assert not font.is_font_available("Overhaul") 26 | loaded = set(font.loaded_fonts()) 27 | loaded_font = tkextrafont.Font(file=os.path.join(self.PATH, "overhaul.ttf"), family="Overhaul") 28 | assert len(list(set(font.loaded_fonts()) - loaded)) != 0 29 | assert font.is_font_available("Overhaul") 30 | assert "Overhaul" in font.loaded_fonts() 31 | label = tk.Label(self.window, text="Overhaul font", font=loaded_font) 32 | label.pack() 33 | self.window.update() 34 | label.destroy() 35 | 36 | label = tk.Label(self.window, text="Overhaul font", font=("Overhaul", 12, "bold")) 37 | label.pack() 38 | self.window.update() 39 | -------------------------------------------------------------------------------- /third_party/extrafont/docs/doc.txt: -------------------------------------------------------------------------------- 1 | extrafont - 1.3 2 | ================ 3 | 4 | extrafont is a package designed to provide "private fonts" for Tk apps. 5 | 6 | "Private fonts" are fonts usually delivered with an app. 7 | They don't need to be installed in some 'standard' system-wide directories; 8 | once these fonsts are loaded, they can be used in the same way of pre-installed fonts. 9 | These loaded fonts are only visible by the process (app) who loaded'em, and then 10 | disappear when the app terminates. 11 | 12 | This package provides an homogeneous multi platform mechamism for such purpose. 13 | Supported tcltk runtimes are 14 | * Windows (32/64 bit) 15 | * Linux (32/64 bit) 16 | * MacOS 17 | You don't need to choose a specific binary runtime; it is automatically selected 18 | when you run 19 | package require extrafont 20 | Note that a specific runtime support (e.g. "Linux 32") is not referred to the 21 | hosting O.S. architecture, but it's referred to the architecture of the TclTk 22 | interpreter. 23 | E.g. if you have a 32-bit TclTk interpreter running on a 64-bit Linux, 24 | then the binary extension for linux-x32 will be automaticaaly selected. 25 | 26 | ======= 27 | 28 | The extrafont package provides these commands: 29 | extrafont::load 30 | extrafont::unload 31 | extrafont::loaded (*deprecated obsolete*) 32 | extrafont::query 33 | extrafont::nameinfo 34 | extrafont::nametable::nameIDs 35 | extrafont::cleanup 36 | extrafont::isAvailable 37 | extrafont::availableFamilies 38 | 39 | extrafont::load _filename_ 40 | Loads all the fonts contained in filename. These fonts will be visible to the current process only 41 | and they will automatically disappear when the process terminates. 42 | After loading filename, all the fonts contained in filename will be available to the current Tk app. 43 | This command returns the list of the font-families loaded. 44 | An error is raised if filename represents an invalid font-file, or if filename has been already loaded as an extrafont. 45 | 46 | extrafont::unload _filename_ 47 | Unloads all the fonts previosly loaded with filename. 48 | Note that if a widget is using these fonts, it may display them correctly, as long text or font-properties (e.g. size) are not changed; 49 | in these latter cases, Tk will replace the displayed text using a default font. 50 | 51 | extrafont::loaded 52 | (This command is obsolete and its use is deprecated. See extrafont::query command) 53 | Returns a list containing the names of all currently loaded 'extrafont' font-files 54 | 55 | extrafont::query _kind_ ?_selector_ _pattern_? 56 | Returns lists of different kinds (files, families, fullnames, details) about 57 | the loaded fonts (just about the extrafont-loaded fonts), matching the optional 58 | selection-pattern. 59 | A selection-pattern is made by a selector (-file, -family, -fullname) and a 60 | glob-style pattern. 61 | Examples: 62 | * list all the (extrafont) loaded font-files: 63 | extrafont::query files 64 | * list all the (extrafont) loaded font-families from font-files "Ariel*.ttf"" 65 | extrafont::query families -file "*/Ariel*.ttf" 66 | * list all the details of the font-family "Ariel*" 67 | extrafont::query details -family "Ariel*" 68 | 69 | extrafont::nameinfo _fontfile_ ?fontPlatformName? 70 | Returns a list of font-details. One font-detail (a dictionary) for each font 71 | contained in $fontfile. 72 | fontPlatformName can be ("" (default) "win" "mac" ). 73 | If fontPlatformName is "" then the extracted info are those required for 74 | the current platform (i.e "win" for "windows" or "mac" for all other platforms) 75 | 76 | extrafont::nametable::nameIDs 77 | Returns all the valid keys used for the font-details dictionary 78 | 79 | extrafont::cleanup 80 | Unloads all the loaded extrafonts. 81 | 82 | extrafont::isAvailable _fontFamily_ 83 | Returns true if fontFamily is avaiable. 84 | **WARNING** - on MacOSX after loading/unloading one or more fonts, the list 85 | of the availables fonts won't be updated till the next event-loop update. 86 | For this reason, if your script needs to call isAvalable/availableFamilies 87 | just after loading/unloading a fontfile, you need to call the "update" command. 88 | 89 | 90 | extrafont::availableFamilies ?_fontFamilyPattern_? 91 | Returns the list of font-families matching the glob-style fontFamilyPattern. 92 | e.g. 93 | extrafont::availableFamilies co* 94 | returns 95 | Courier {Comic Sans MS} ..... 96 | **WARNING** - on MacOSX after loading/unloading one or more fonts, the list 97 | of the availables fonts won't be updated till the next event-loop update. 98 | For this reason, if your script needs to call isAvalable/availableFamilies 99 | just after loading/unloading a fontfile, you need to call the "update" command. 100 | 101 | 102 | One important distinction to keep in mind is among 103 | font-filename 104 | font-family 105 | fontname (or tk-fontname) 106 | 107 | Font-filename is used just for loading/unloading an external font: 108 | set fontfamilies [extrafont::load "c:/tmp/Monoton-regular.ttf"] 109 | 110 | This font-file contains just one font. The font-family-name can be extracted as 111 | result of the extrafont::load command 112 | foreach fontfamily $fontfamilies { 113 | puts "Loaded font-family: $fontfamily" 114 | } 115 | # just get the 1st font-familiy 116 | set myNewFontFamily [lindex $fontfamilies 0] ;# --> "Monoton" 117 | 118 | When you want to use this new font, you should create or configure 119 | a tk-fontname (using the standard 'font' command) 120 | 121 | set myfontname "tk_monoton" ;# ... choose the name you want .. 122 | font create $myfontname -family $myNewFontFamily -size 20 123 | # or, let tk choose a fontname for you ... 124 | set myfontname [font create -family $myNewFontFamily -size 20] 125 | # then use $myfontname for a new widget ... 126 | label .mylabel -font $myfontname -text ABC ....... 127 | -------------------------------------------------------------------------------- /third_party/extrafont/license.terms: -------------------------------------------------------------------------------- 1 | == Extrafont == 2 | A multi-platform binary package for loading "private fonts" 3 | 4 | Copyright (c) 2017,2022 by A.Buratti 5 | 6 | This library is free software; you can use, modify, and redistribute it 7 | for any purpose, provided that existing copyright notices are retained 8 | in all copies and that this notice is included verbatim in any 9 | distributions. 10 | 11 | This software is distributed WITHOUT ANY WARRANTY; without even the 12 | implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | -------------------------------------------------------------------------------- /third_party/extrafont/src/extrafont.c: -------------------------------------------------------------------------------- 1 | /* 2 | ========================================================================== 3 | TclTk extension (Win/Linux/MacOSX): 4 | extrafont::load 5 | This command makes available to TclTk apps a new font without isntalling it. 6 | --- 7 | July 2017 - A.Buratti fecit 8 | May 2018 - BugFix for MY_FcConfigAppFontRemoveFile (linux only) 9 | Nov 2023 - Replaced __WIN32__ with _WIN32 (this is valid also for Win-64bit) 10 | ========================================================================== 11 | */ 12 | 13 | #include 14 | 15 | #if defined(_WIN32) 16 | #include 17 | #include 18 | #elif defined(__linux__) 19 | #include 20 | #include 21 | #include 22 | #elif defined(__APPLE__) 23 | #include 24 | #include 25 | #else 26 | #error "Cannot detect the target platform: Neither _WIN32, nor __linux__, nor __APPLE__; what else ?" 27 | #endif 28 | 29 | // This macro is a short for the standard parameter-list of Tcl_ObjCmdProc conformant functions. 30 | // Note that the first parameter (ClientData, i.e (void*)) is missing, since it should be 31 | // explicitely defined as a specific pointer. 32 | #define OTHER_CMDPROC_ARGS Tcl_Interp *_interp, int _objc, Tcl_Obj* const _objv[] 33 | 34 | 35 | static 36 | int CMDPROC_loadfont( ClientData unused, OTHER_CMDPROC_ARGS ) { 37 | int r = TCL_OK; 38 | if (_objc != 2) { 39 | Tcl_WrongNumArgs(_interp, 1, _objv, "filename"); 40 | return TCL_ERROR; 41 | } 42 | 43 | int len; 44 | const char *path = Tcl_GetStringFromObj(_objv[1], &len); 45 | int res; 46 | 47 | #if defined(_WIN32) 48 | Tcl_DString ds; 49 | Tcl_Encoding unicode; 50 | 51 | Tcl_DStringInit(&ds); 52 | unicode = Tcl_GetEncoding(_interp, "unicode"); 53 | Tcl_UtfToExternalDString(unicode, path, len, &ds); 54 | res = AddFontResourceExW((LPCWSTR)Tcl_DStringValue(&ds), FR_PRIVATE, NULL); 55 | Tcl_DStringFree(&ds); 56 | Tcl_FreeEncoding(unicode); 57 | 58 | #elif defined(__linux__) 59 | res = FcConfigAppFontAddFile(NULL,path); 60 | 61 | #elif defined(__APPLE__) 62 | CFStringRef filenameStr = CFStringCreateWithCString( NULL, path, kCFStringEncodingUTF8 ); 63 | CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, filenameStr, kCFURLPOSIXPathStyle, false); 64 | 65 | CFRelease(filenameStr); 66 | CFErrorRef error = nil; 67 | res = CTFontManagerRegisterFontsForURL(fileURL,kCTFontManagerScopeProcess,&error); 68 | 69 | CFRelease(fileURL); 70 | 71 | #endif 72 | 73 | if ( ! res ) { 74 | Tcl_SetObjResult( _interp, Tcl_ObjPrintf("error %d - cannot load font \"%s\"", res, path) ); 75 | r = TCL_ERROR; 76 | } 77 | return r; 78 | } 79 | 80 | 81 | 82 | #if defined(__linux__) 83 | /* 84 | * The inverse of FcConfigAppFontAddFile(). 85 | * - Currently (in Fontconfig 2.12.4) this function is missing, 86 | * so, here is my hack. 87 | * WARNING: based on internal code hacking, not documented/supported. 88 | */ 89 | 90 | static 91 | FcBool MY_FcConfigAppFontRemoveFile( FcConfig *config, const FcChar8 *filename) { 92 | FcFontSet *set = FcConfigGetFonts(config,FcSetApplication); 93 | if ( !set ) return FcFalse; 94 | 95 | FcBool res = FcFalse; 96 | int found = 0; 97 | // scan the set->fonts array, and copy all the elements but those with matching 'filename'. 98 | // Finally substitute the new (reduced) array to set->fonts. 99 | if ( set->nfont == 0 ) return FcFalse; 100 | 101 | FcPattern **newFonts = (FcPattern **)calloc( set->sfont, sizeof(FcPattern *)); 102 | FcPattern **newP = newFonts; 103 | for ( int i = 0 ; i < set->nfont ; ++i ) { 104 | FcChar8 *strRef = NULL; 105 | FcPatternGetString( set->fonts[i], FC_FILE, 0, &strRef); 106 | if( strRef && strcmp(strRef,filename)==0 ) { 107 | ++found; 108 | FcPatternDestroy(set->fonts[i]); 109 | } else { 110 | *newP++ = set->fonts[i]; 111 | } 112 | } 113 | if( found > 0 ) { 114 | //free just the array of pointers; don't free the pointed objects (they have been copied in newFonts) 115 | free(set->fonts); 116 | // set newFonts as the new set->fonts 117 | set->fonts = newFonts; 118 | set->nfont -= found; 119 | res = FcTrue; 120 | } else { 121 | free(newFonts); // free just the array 122 | res = FcFalse; 123 | } 124 | return res; 125 | } 126 | #endif 127 | 128 | 129 | static 130 | int CMDPROC_unloadfont( ClientData unused, OTHER_CMDPROC_ARGS ) { 131 | int r = TCL_OK; 132 | if (_objc != 2) { 133 | Tcl_WrongNumArgs(_interp, 1, _objv, "filename"); 134 | return TCL_ERROR; 135 | } 136 | 137 | int len; 138 | const char *path = Tcl_GetStringFromObj(_objv[1], &len); 139 | int res; 140 | 141 | #if defined(_WIN32) 142 | Tcl_DString ds; 143 | Tcl_Encoding unicode; 144 | 145 | Tcl_DStringInit(&ds); 146 | unicode = Tcl_GetEncoding(_interp, "unicode"); 147 | Tcl_UtfToExternalDString(unicode, path, len, &ds); 148 | res = RemoveFontResourceExW( (LPCWSTR)Tcl_DStringValue(&ds), FR_PRIVATE, NULL ); 149 | Tcl_DStringFree(&ds); 150 | Tcl_FreeEncoding(unicode); 151 | 152 | #elif defined(__linux__) 153 | // Given that Fontconfig (2.12.4) lacks of the inverse of FcConfigAppFontAddFile 154 | // we use a custom hacked function. 155 | res = MY_FcConfigAppFontRemoveFile( NULL, path ) ? 1 : 0 ; 156 | 157 | #elif defined(__APPLE__) 158 | CFStringRef filenameStr = CFStringCreateWithCString( NULL, path, kCFStringEncodingUTF8 ); 159 | CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, filenameStr, kCFURLPOSIXPathStyle, false); 160 | 161 | CFRelease(filenameStr); 162 | CFErrorRef error = nil; 163 | res = CTFontManagerUnregisterFontsForURL(fileURL,kCTFontManagerScopeProcess,&error); 164 | 165 | CFRelease(fileURL); 166 | 167 | #endif 168 | 169 | if ( ! res ) { 170 | Tcl_SetObjResult( _interp, Tcl_ObjPrintf("error %d - cannot unload font \"%s\"", res, path) ); 171 | r = TCL_ERROR; 172 | } 173 | return r; 174 | } 175 | 176 | 177 | 178 | DLLEXPORT 179 | int Extrafont_Init(Tcl_Interp *interp) { 180 | if (Tcl_InitStubs(interp, "8.5", 0) == NULL) { 181 | return TCL_ERROR; 182 | } 183 | 184 | if ( ! Tcl_CreateObjCommand(interp, "extrafont::core::load", 185 | CMDPROC_loadfont, 186 | (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL) ) { 187 | return TCL_ERROR; 188 | } 189 | if ( ! Tcl_CreateObjCommand(interp, "extrafont::core::unload", 190 | CMDPROC_unloadfont, 191 | (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL) ) { 192 | return TCL_ERROR; 193 | } 194 | 195 | return TCL_OK; 196 | } 197 | -------------------------------------------------------------------------------- /third_party/extrafont/src/extrafont.tcl: -------------------------------------------------------------------------------- 1 | ## extrafont.tcl -- a multi-platform binary package for loading "private fonts" 2 | 3 | ## Copyright (c) 2017,2018 by A.Buratti 4 | ## 5 | ## This library is free software; you can use, modify, and redistribute it 6 | ## for any purpose, provided that existing copyright notices are retained 7 | ## in all copies and that this notice is included verbatim in any 8 | ## distributions. 9 | ## 10 | ## This software is distributed WITHOUT ANY WARRANTY; without even the 11 | ## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | ## 13 | 14 | namespace eval extrafont { 15 | # FFFD-table is the core data structure holding all the relations between 16 | # font-files, font-familiies, font-fullnames and font-details. 17 | # *** NOTE: we are talking just about fonts loaded with extrafont::load; 18 | # *** fonts loaded at system-level are not included here. 19 | # They first three components FFF (font-file, font-familiy, font-fullname) 20 | # gives you a primary key for the D component (the font-detail dictionary) 21 | # 22 | variable _FFFD_Table ;# array: key is (file,family,fullname) 23 | ;# value is the font-detail 24 | variable _File2TempFile ;# array: key is the originale filename (normalized), 25 | ;# value is its temporary working copy 26 | variable _TempDir 27 | 28 | array unset _FFFD_Table 29 | array unset _File2TempFile 30 | set _TempDir "" 31 | 32 | proc _isVfsFile {filename} { 33 | expr { [lindex [file system $filename] 0] != "native" } 34 | } 35 | 36 | # load thi submodule" fontnameinfo.tcl" in a sub-namespace 37 | # It provides the 'nameinfo' command 38 | namespace eval nametable { 39 | source [file join [file dirname [info script]] fontnameinfo.tcl] 40 | } 41 | # when Tk is destroyed (e.g on exit), then do a cleanup 42 | trace add command "." delete {apply { {args} {extrafont::cleanup} } } 43 | } 44 | 45 | 46 | proc extrafont::_copyToTempFile {filename} { 47 | variable _TempDir 48 | 49 | if { $_TempDir == "" } { 50 | set _TempDir [futmp::mktempdir [futmp::tempdir] extrafont_] 51 | # don't catch error; let it raise 52 | } 53 | 54 | set fd [open $filename r] ;# on error let it raise .. 55 | fconfigure $fd -translation binary 56 | 57 | # note: tempfile returns an open channel ; the filename is returned via upvar (in newfilename var) 58 | set newfilename "" 59 | set wentWrong [catch { 60 | set cacheChannel [futmp::tempfile newfilename $_TempDir cache_ [file extension $filename]] 61 | fconfigure $cacheChannel -translation binary 62 | } errmsg ] 63 | if { $wentWrong } { 64 | close $fd 65 | error $errmsg 66 | } 67 | 68 | set wentWrong [catch { 69 | fcopy $fd $cacheChannel 70 | } errmsg ] 71 | 72 | close $cacheChannel 73 | close $fd 74 | 75 | if { $wentWrong } { 76 | error $errmsg 77 | } 78 | 79 | return $newfilename 80 | } 81 | 82 | # extrafont::load fontfile 83 | # ------------------------ 84 | # install the fonts contained in $fontfile and return a list of font-families 85 | # eg: "{ "Family A" "Family B" ... } 86 | # Usually the returned list holds just one font-family. 87 | # If you load an OpenTypeCollections (*.ttc), it may contains more than one font; 88 | # usuallly this fonts are variants of the same font-family, but nothing prevents a *.ttc 89 | # to include different font-families. Note that the returned list 90 | # may contain duplicates. 91 | # 92 | # In order to discover the differences for these fants of the same font-family, 93 | # use extrafont::query ... for extracting the font-fullnames, or the full details. 94 | proc extrafont::load {fontfile} { 95 | variable _FFFD_Table 96 | variable _File2TempFile 97 | 98 | set fontfile [file normalize $fontfile] 99 | set orig_fontfile $fontfile 100 | if { [array names _FFFD_Table $orig_fontfile,*] != {} } { 101 | error "Fontfile \"$orig_fontfile\" already loaded." 102 | } 103 | 104 | if { [_isVfsFile $orig_fontfile] } { 105 | set fontfile [_copyToTempFile $orig_fontfile] ;# on error let it raise 106 | set _File2TempFile($orig_fontfile) $fontfile 107 | } 108 | if { [catch {core::load $fontfile} errmsg] } { 109 | array unset _File2TempFile $orig_fontfile 110 | error [string map [list $fontfile $orig_fontfile] $errmsg] 111 | } 112 | set fontsInfo {} 113 | # if nameinfo fails, don't stop; return an empty list 114 | catch { 115 | set fontsInfo [nametable::nameinfo $fontfile] 116 | } 117 | set FamList {} 118 | foreach fontInfo $fontsInfo { 119 | set family [dict get $fontInfo "fontFamily"] 120 | set fullname [dict get $fontInfo "fullName"] 121 | set _FFFD_Table($orig_fontfile,$family,$fullname) $fontInfo 122 | lappend FamList $family 123 | } 124 | # ? should I return preferredFamily ? 125 | return $FamList 126 | } 127 | 128 | 129 | # extrafont::unload fontfile 130 | # -------------------------- 131 | # Be careful: since this proc could be called when app exits, 132 | # you cannot rely on other packages (e.g. vfs ), since they could have been destroyed before. 133 | # Therefore DO NOT use within this proc code from other packages 134 | proc extrafont::unload {fontfile} { 135 | variable _FFFD_Table 136 | variable _File2TempFile 137 | 138 | set fontfile [file normalize $fontfile] 139 | 140 | # Fix for MacOSX : 141 | # Since core::unload does not return an error when unloading a not-loaded file, 142 | # we must check-it before 143 | if { $::tcl_platform(os) == "Darwin" } { 144 | if { [query files -file $fontfile] == {} } { 145 | error "error 0 - cannot unload font \"$fontfile\"" 146 | } 147 | } 148 | set orig_fontfile $fontfile 149 | set isVfs [info exists _File2TempFile($orig_fontfile)] 150 | if { $isVfs } { 151 | set fontfile $_File2TempFile($orig_fontfile) 152 | } 153 | if { [catch {core::unload $fontfile} errmsg] } { 154 | error [string map [list $fontfile $orig_fontfile] $errmsg] 155 | } 156 | 157 | if { $isVfs } { 158 | catch {file delete $fontfile} ;# skip errors 159 | unset _File2TempFile($orig_fontfile) 160 | } 161 | array unset _FFFD_Table $fontfile,* 162 | return 163 | } 164 | 165 | 166 | # extrafont::loaded 167 | # returns the list of the currently loaded (extra)font-files. 168 | # 169 | # OBSOLETE 170 | # extrafont::loaded has been obsoleted by the extrafont::query command 171 | # and it is currently suported just for backward compatibility. 172 | # 173 | # extrafont::loaded 174 | # is equivalent to 175 | # extrafont::query files 176 | proc extrafont::loaded {} { 177 | variable _FFFD_Table 178 | return [query files] 179 | } 180 | 181 | 182 | # extrafont::query _kind_ ? _selector_ _pattern_ ? 183 | # ---------------------------------------------- 184 | # returns list of *extrafont-loaded* files,families,fullnames,details 185 | # matching -file,-family,-fullname pattern 186 | # NOTE: system-installed fonts are not excluded; this query deals with 187 | # extrafonts-installed fonts only. 188 | # Example: 189 | # query files 190 | # query files -file Ariel*.ttf 191 | # query files -family Ariel* 192 | # query families 193 | # query families -file Ariel*.ttf 194 | # query fullnames 195 | # query fullnames -family Ariel* 196 | # ... 197 | # query details -family Ariel* 198 | proc extrafont::query { kind args } { 199 | variable _FFFD_Table 200 | 201 | set allowedValues {files families fullnames details} 202 | if { $kind ni $allowedValues } { 203 | error "bad kind \"$kind\": must be [join $allowedValues ","]" 204 | } 205 | 206 | if { $args == {} } { 207 | set selector "(empty)" ;# dummy selector 208 | } elseif { [llength $args] == 2 } { 209 | lassign $args selector selectorVal 210 | set allowedValues {-file -family -fullname} 211 | if { $selector ni $allowedValues } { 212 | error "bad selector \"$selector\": must be [join $allowedValues ","]" 213 | } 214 | } else { 215 | error "wrong params: query _kind_ ?selector value?" 216 | } 217 | 218 | switch -- $selector { 219 | (empty) { set pattern "*" } 220 | -file { set pattern "$selectorVal,*,*" } 221 | -family { set pattern "*,$selectorVal,*" } 222 | -fullname { set pattern "*,*,$selectorVal" } 223 | } 224 | 225 | set L {} 226 | foreach { key detail } [array get _FFFD_Table $pattern] { 227 | lassign [split $key ","] fontfile family fullname 228 | switch -- $kind { 229 | files { lappend L $fontfile } 230 | families { lappend L $family } 231 | fullnames { lappend L $fullname} 232 | details {lappend L $detail } 233 | } 234 | } 235 | lsort -unique $L 236 | } 237 | 238 | 239 | # nameinfo $fontfile ?$fontPlatformID? 240 | # ------------------ 241 | # Returns a list of font-info. One font-info (a dictionary) for each font 242 | # contained in $fontfile. 243 | # 244 | # fontPlatformName can be used for selecting data tailored for a given platform. 245 | # It can be ("" (default) "win" or "mac" - no support for the "Unicode platform"). 246 | # If fontPlatformName is "" then the extracted info are those required for 247 | # the current platform (i.e "win" for windows and "mac" for mac/linux/../restOfTheWorld) 248 | # 249 | # Implementation note: 250 | # if $fontfile is loaded, then the 'cached' font-infos are returned, 251 | # else these are extracted by calling [nametable::nameinfo $fontfile] 252 | proc extrafont::nameinfo {fontfile {fontPlatformName ""}} { 253 | variable _FFFD_Table 254 | 255 | if { $fontPlatformName eq "" } { 256 | if { $::tcl_platform(platform) == "windows" } { 257 | set fontPlatformName "win" 258 | } else { 259 | set fontPlatformName "mac" 260 | } 261 | } 262 | switch -- $fontPlatformName { 263 | "mac" { set fontPlatformID 1 } 264 | "win" { set fontPlatformID 3 } 265 | default { error "invalid fontPlatformName. Valid values are win, mac."} 266 | } 267 | 268 | set fontfile [file normalize $fontfile] 269 | set res [query details -file $fontfile] 270 | if { $res == {} } { 271 | set res [nametable::nameinfo $fontfile $fontPlatformID] 272 | } 273 | return $res 274 | } 275 | 276 | # extrafont::cleanup 277 | # ------------------ 278 | # remove all the loaded extrafonts (with all the underlying OS stuff at OS level) 279 | proc extrafont::cleanup {} { 280 | variable _FFFD_Table 281 | variable _File2TempFile 282 | variable _TempDir 283 | 284 | foreach fontfile [query files] { 285 | catch {unload $fontfile} ;# don't stop it now ! 286 | } 287 | 288 | if { $_TempDir != "" } { 289 | file delete -force $_TempDir ;# brute force 290 | set _TempDir "" 291 | } 292 | # nothing required on the core side 293 | return 294 | } 295 | 296 | 297 | # extrafont::isAvailable $family 298 | # ------------------------------ 299 | # test if a given font-family is available. 300 | # WARNING; on MacOSX after loading/unloading one or more fonts, the list 301 | # of the availables fonts (i.e. [font families]) won't be updated till the next event-loop update. 302 | # For this reason, if your script needs to call isAvalable/availableFamilies 303 | # just after loading/unloading a fontfile, you need to call the "update" command. 304 | proc extrafont::isAvailable {family} { 305 | expr [lsearch -nocase -exact [font families] $family] == -1 ? false : true 306 | } 307 | 308 | 309 | # extrafont::availableFamilies ?pattern? 310 | # -------------------------------------- 311 | # returns the list of available fontfamiles matching pattern. 312 | # NOTE: 313 | # extrafont::availableFamilies and extrafont::query families 314 | # are quit similar, and they bot returns a list of matching font-families. 315 | # They key difference is that 316 | # extrafont::query families -families A* 317 | # matches the loaded extra-fonts ONLY 318 | # whilst 319 | # extrafont::avalableFamilies A* 320 | # matches all the loaded font-families (both system-wide fonts and private-fonts) 321 | # (and it's a case-sensitive matching) 322 | # 323 | proc extrafont::availableFamilies { {familyPattern {*}} } { 324 | lsearch -all -inline -glob -nocase [font families] $familyPattern 325 | } 326 | -------------------------------------------------------------------------------- /third_party/extrafont/src/fontnameinfo.tcl: -------------------------------------------------------------------------------- 1 | # fontnameinfo.tcl 2 | # 3 | # Commands for extracting details from the "name' table of OpenType font-files. 4 | # 5 | # Tested with 6 | # *.otf (including those with PostScript outlines (OTTO)) 7 | # *.ttf 8 | # *.ttc (OpenTypeCollections) 9 | # 10 | # Reference Specification: 11 | # Opentype 1.6 - http://www.microsoft.com/typography/otspec/ 12 | # 13 | # This module derives from the module Glyph 14 | # ( see http://wiki.tcl.tk/37854 ) 15 | # which in turn is inspired by the following works: 16 | # * org.apache.batik.svggen project (Apache License, 2.0) 17 | # * pdf4tcl project 18 | # Copyright (c) 2004 by Frank Richter and 19 | # Jens Ponisch 20 | # Copyright (c) 2006-2012 by Peter Spjuth 21 | # Copyright (c) 2009 by Yaroslav Schekin 22 | # * sfntutil.tcl - by Lars Hellstrom 23 | 24 | 25 | # NameIDs for the name table. 26 | set _NameID2Str [dict create {*}{ 27 | 0 copyright 28 | 1 fontFamily 29 | 2 fontSubfamily 30 | 3 uniqueID 31 | 4 fullName 32 | 5 version 33 | 6 postScriptName 34 | 7 trademark 35 | 8 manufacturer 36 | 9 designer 37 | 10 description 38 | 11 manufacturerURL 39 | 12 designerURL 40 | 13 license 41 | 14 licenseURL 42 | 15 reserved 43 | 16 typographicFamily 44 | 17 typographicSubfamily 45 | 18 compatibleFullName 46 | 19 sampleText 47 | 20 postScriptFindFontName 48 | 21 wwsFamily 49 | 22 wwsSubfamily 50 | 23 lightBackgroundPalette 51 | 24 darkBackgroundPalette 52 | 25 variationsPostScriptNamePrefix 53 | }] 54 | 55 | # return all the valid keys for the font-info dictionary 56 | # NOTE: none of these nameID is mandatory, but the following 57 | # are strongly recommended: 58 | # 1 fontFamily 59 | # 2 fontSubfamily 60 | # 4 fullName 61 | # ? 16 typographicFamily 62 | # ? 17 typographicSubfamily 63 | # 64 | # Reference: 65 | # https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids 66 | # 67 | # Note: currently extrafont requires just the following mandatory nameID 68 | # fontFamily 69 | # fullName 70 | proc nameIDs {} { 71 | variable _NameID2Str 72 | dict values $_NameID2Str 73 | } 74 | 75 | 76 | # nameinfo $fontPath 77 | # ------------------ 78 | # scan the 'name' table(s) of $fontPath, and returns a list of font-info 79 | # One font-info for each name table 80 | # Each font-info is a dictionary 81 | # (see [nameIDS] for the keys; not all the keys are mandatory) 82 | # 83 | # fontPlatformID can be ("" (default) 3 (windows) 1 (everything but windows ). 84 | # If fontPlatformID is "" then the extracted info are those required for 85 | # the current platform (i.e 3 for "windows" or 1 for mac/linux/...) 86 | # 87 | # An error is raised if fontPath cannot be properly parsed. 88 | proc nameinfo {fontPath {fontPlatformID ""}} { 89 | set fd [open $fontPath "r"] 90 | fconfigure $fd -translation binary 91 | set failed [catch {set names [_ReadFontFile $fd $fontPlatformID]} errMsg] 92 | close $fd 93 | 94 | if { $failed } { 95 | error $errMsg 96 | } 97 | return $names 98 | } 99 | 100 | # _ReadFontFile $fd 101 | # ----------------- 102 | # return a list of font-info (usually just one font-info) 103 | # Each font-info is a dictionary 104 | # An error is raised if fontPath cannot be properly parsed. 105 | proc _ReadFontFile { fd {fontPlatformID ""}} { 106 | set fontsInfo {} 107 | set magicTag [read $fd 4] 108 | if { $magicTag == "ttcf" } { 109 | set fontsOffset [_ReadTTC_Header $fd] ;# one elem for each subfont 110 | foreach fontOffset $fontsOffset { 111 | # go to the start of the subfont and skip the initial 'magicTag' 112 | seek $fd [expr {$fontOffset+4}] 113 | lappend fontsInfo [_ReadSimpleFontFile $fd $fontPlatformID] 114 | } 115 | } elseif { $magicTag in { "OTTO" "\x00\x01\x00\x00" "typ1" "true" } } { 116 | lappend fontsInfo [_ReadSimpleFontFile $fd $fontPlatformID] 117 | } else { 118 | error "Unrecognized magic-number for OpenType font: 0x[binary encode hex $magicTag]" 119 | } 120 | return $fontsInfo 121 | } 122 | 123 | 124 | # _ReadTTCHeader $fd 125 | # ------------------ 126 | # scan the TTC header and 127 | # returns a list of fontsOffset ( i.e. where each sub-font starts ) 128 | proc _ReadTTC_Header {fd} { 129 | binary scan [read $fd 8] SuSuIu majorVersion minorVersion numFonts 130 | #extract a list of 32bit integers 131 | binary scan [read $fd [expr {4*$numFonts}]] "Iu*" fontsOffset 132 | 133 | # NOTE: if majorVersion > 2 there can be a trailing digital-signature section 134 | # ... IGNORE IT 135 | 136 | return $fontsOffset 137 | } 138 | 139 | 140 | # _ReadSimpleFontFile $fd 141 | # ----------------------- 142 | # returns a font-info dictionary (or an error ...) 143 | proc _ReadSimpleFontFile {fd {fontPlatformID ""}} { 144 | # Assert: we are at the beginng of the Table-Directory 145 | binary scan [read $fd 8] SuSuSuSu numTables searchRange entrySelector rangeShift 146 | 147 | # scan the Table Directory ... we are just interested with the 'name' table 148 | set tableName {} 149 | for {set n 0} {$n<$numTables} {incr n} { 150 | binary scan [read $fd 16] a4H8IuIu tableName _checksum start length 151 | if { $tableName == "name" } break 152 | } 153 | if { $tableName != "name" } { 154 | error "No \"name\" table found." 155 | } 156 | 157 | seek $fd $start 158 | return [_ReadTable.name $fd $length $fontPlatformID] 159 | } 160 | 161 | 162 | # _convertfromUTF16BE $data 163 | # ------------------------- 164 | # convert strings from UTF16BE to (tcl)Unicode strings. 165 | # NOTE: 166 | # When font-info is extracted from namerecords with platformID==3 (Windows) 167 | # data (binary strings) are originally encoded in UTF16-BE. 168 | # These data should be converted in (tcl)Unicode strings. 169 | # Since the "tcl - unicode encoding" is BigEndian or LittleEndian, depending 170 | # on the current platform, two variants of _convertfromUTF16BE areprovided; 171 | # the right conversion will be choosen once at load-time. 172 | if { $::tcl_platform(byteOrder) == "bigEndian" } { 173 | proc _convertfromUTF16BE {data} { 174 | encoding convertfrom unicode $data 175 | } 176 | } else { 177 | proc _convertfromUTF16BE {data} { 178 | # swp bytes, then call encoding unicode .. 179 | binary scan $data "S*" z 180 | encoding convertfrom unicode [binary format "s*" $z] 181 | } 182 | } 183 | 184 | # _score ... 185 | # -------------- 186 | # return the score (>=0) 187 | # for every tuple of {platformID specificID languageID} 188 | # Scores are weighted based un the target fontPlatformID 189 | proc _score {fontPlatformID platformID specificID languageID} { 190 | switch -- $fontPlatformID { 191 | 1 { ;# macintosh i.e non-windows 192 | set premium(macEnglish) 0x0100 193 | set premium(winEnglish_US) 0x0040 194 | set premium(winEnglish_UK) 0x0030 195 | set premium(winEnglish) 0x0020 196 | } 197 | 3 { ;# windows 198 | set premium(macEnglish) 0x000 199 | set premium(winEnglish_US) 0x0400 200 | set premium(winEnglish_UK) 0x0300 201 | set premium(winEnglish) 0x0200 202 | } 203 | default { error "unsupported target platformID" } 204 | } 205 | 206 | set score 0 207 | switch -- $platformID { 208 | 0 { 209 | # platform Unicode 210 | set score 3 211 | } 212 | 1 { 213 | # platform Macintosh 214 | if { $specificID == 0 } { 215 | # MacEncodingRoman 216 | set score 2 217 | } else { 218 | return 0 ;# NO WAY !! 219 | } 220 | if { $languageID == 0 } { 221 | # MacLanguageEnglish 222 | incr score $premium(macEnglish) 223 | } 224 | } 225 | 3 { 226 | #platform Windows 227 | switch -- $specificID { 228 | 0 { set score 1 } ;# WindowsEncodingSymbol 229 | 1 { set score 4 } ;# WindowsEncodingUCS2 230 | default { return 0 } ;# NO WAY !! 231 | } 232 | # try to augment score based on languageId 233 | if { ($languageID & 0xFF) == 0x09 } { 234 | # .. generic English 235 | switch -- [format "0x%.4x" $languageID] { 236 | 0x0409 { incr score $premium(winEnglish_US) } 237 | 0x0809 { incr score $premium(winEnglish_UK) } 238 | default { incr score $premium(winEnglish) } 239 | } 240 | } 241 | } 242 | } 243 | return $score 244 | } 245 | 246 | 247 | # _ReadTable.name $fd 248 | # -------------------- 249 | # Scan the 'name' table and return a font-info dictionary. 250 | # 251 | # Reference Specification: 252 | # see http://www.microsoft.com/typography/otspec/name.htm 253 | # NOTE: 254 | # New internal logic for selecting values among repeated values 255 | # for different platformID encodingID languageID nameID, 256 | # based on a score system. 257 | proc _ReadTable.name {fd tableSize {fontPlatformID ""}} { 258 | variable _NameID2Str 259 | 260 | if { $fontPlatformID eq "" } { 261 | if { $::tcl_platform(platform) == "windows" } { 262 | set fontPlatformID 3 263 | } else { 264 | set fontPlatformID 1 265 | } 266 | } 267 | 268 | set tableStart [tell $fd] ;# save the start of this Table 269 | set tableEnd [expr {$tableStart+$tableSize}] 270 | binary scan [read $fd 6] "SuSuSu" version count strRegionOffset 271 | # we expect version == 0 ; version == 1 is not supported yet 272 | 273 | set strRegionStart [expr {$tableStart + $strRegionOffset}] 274 | set strRegionSize [expr {$tableSize-$strRegionOffset}] 275 | #Each nameRecord is made of 6 UnsignedShort 276 | binary scan [read $fd [expr {2*6*$count}]] "Su*" nameRecords 277 | 278 | set nameinfo [dict create] 279 | # initialize bestScore array 280 | for {set nameID 0} {$nameID <= 25} {incr nameID} { 281 | set bestScore($nameID) 0 282 | # no need to initialize bestPlatform, bestOffset, bestLength arrays 283 | } 284 | # Assert: nameRecords are sorted by platformID,encodingID,languageID,nameID 285 | foreach { platformID specificID languageID nameID strLength strOffset } $nameRecords { 286 | if { $nameID > 25 } continue 287 | # Offset could be anything if length is zero. 288 | if {$strLength == 0} continue 289 | # Fonts are full of wrong data, if the offset is outside of the string data we simply skip the record. 290 | if { $strOffset >= $strRegionSize || $strOffset+$strLength>$strRegionSize } continue ;# WARNING 291 | 292 | set score [_score $fontPlatformID $platformID $specificID $languageID] 293 | if { $score > $bestScore($nameID) } { 294 | set bestScore($nameID) $score 295 | set bestOffset($nameID) $strOffset 296 | set bestLength($nameID) $strLength 297 | set bestPlatform($nameID) $platformID 298 | } 299 | } 300 | for {set nameID 0} {$nameID <= 25} {incr nameID} { 301 | if { $bestScore($nameID) == 0 } continue; 302 | 303 | set offset $bestOffset($nameID) 304 | set length $bestLength($nameID) 305 | seek $fd [expr {$strRegionStart+$offset}] 306 | binary scan [read $fd $length] "a*" value 307 | 308 | # Windows only: extracted strings from records with platformID == 3 (windows) 309 | # are in UTF-16BE format. They should be converted. 310 | if { $bestPlatform($nameID) == 3 } { 311 | set value [_convertfromUTF16BE $value] 312 | } 313 | 314 | set nameIDstr [dict get $_NameID2Str $nameID] 315 | dict set nameinfo $nameIDstr $value 316 | } 317 | if 0 { 318 | # TODO ... The score logic should consider the current platform 319 | # and then adjust the evaluation. 320 | # BUT current platform should be an 'external' parameter, so that 321 | # it could be used for tuning different platforms. 322 | 323 | # prefer typographicFamily over fontFamily 324 | if { [dict exists $nameinfo typographicFamily] } { 325 | dict set nameinfo fontFamily [dict get $nameinfo typographicFamily] 326 | } 327 | # prefer typographicSubfamily over fontSubfamily 328 | if { [dict exists $nameinfo typographicSubfamily] } { 329 | dict set nameinfo fontSubfamily [dict get $nameinfo typographicSubfamily] 330 | } 331 | } 332 | 333 | # if $version == 1, there should be a 'languageTag section' 334 | # ... IGNORE IT 335 | 336 | return $nameinfo 337 | } 338 | -------------------------------------------------------------------------------- /third_party/extrafont/src/futmp.tcl: -------------------------------------------------------------------------------- 1 | # futmp.tcl - file utilities for working with temporary files. 2 | # 3 | # This package is largely derived from the tcllib's package "fileutil" 1.14.10 4 | # The original commands' synospys has been changed to include an explicit 5 | # base-dir and an optional file-suffix. 6 | # 7 | # futmp::tempdir 8 | # futmp::tempdir _newdir_ 9 | # futmp::mktempdir _basedir_ ?_prefix_? 10 | # futmp::tempfile _basedir_ ?_prefix_? ?_suffix_? 11 | # 12 | # -Aug.2017 - A.Buratti fecit 13 | # 14 | # Credits for the original "fileutil" : 15 | # Copyright (c) 1998-2000 by Ajuba Solutions. 16 | # Copyright (c) 2005-2013 by Andreas Kupries 17 | # 18 | 19 | package require Tcl 8.2 20 | package provide futmp 1.14.10 21 | 22 | namespace eval ::futmp { 23 | variable tempdir {} 24 | variable tempdirSet 0 25 | } 26 | 27 | # ::futmp::tempdir -- 28 | # 29 | # Return the correct directory to use for temporary files. 30 | # Attempt this sequence, which seems logical: 31 | # 32 | # 1. The directory named by the `TMPDIR' environment variable. 33 | # 34 | # 2. The directory named by the `TEMP' environment variable. 35 | # 36 | # 3. The directory named by the `TMP' environment variable. 37 | # 38 | # 4. A platform-specific location: 39 | # * On Macintosh, the `Temporary Items' folder. 40 | # 41 | # * On Windows, the directories `C:\\TEMP', `C:\\TMP', 42 | # `\\TEMP', and `\\TMP', in that order. 43 | # 44 | # * On all other platforms, the directories `/tmp', 45 | # `/var/tmp', and `/usr/tmp', in that order. 46 | # 47 | # 5. As a last resort, the current working directory. 48 | # 49 | # The code here also does 50 | # 51 | # 0. The directory set by invoking tempdir with an argument. 52 | # If this is present it is used exclusively. 53 | # 54 | # Arguments: 55 | # None. 56 | # 57 | # Side Effects: 58 | # None. 59 | # 60 | # Results: 61 | # The directory for temporary files. 62 | 63 | proc ::futmp::tempdir {args} { 64 | if {[llength $args] > 1} { 65 | return -code error {wrong#args: should be "::futmp::tempdir ?path?"} 66 | } elseif {[llength $args] == 1} { 67 | variable tempdir [lindex $args 0] 68 | variable tempdirSet 1 69 | return 70 | } 71 | return [TempDir] 72 | } 73 | 74 | 75 | proc ::futmp::TempDir {} { 76 | global tcl_platform env 77 | variable tempdir 78 | variable tempdirSet 79 | 80 | set attempdirs [list] 81 | set problems {} 82 | 83 | if {$tempdirSet} { 84 | lappend attempdirs $tempdir 85 | lappend problems {User/Application specified tempdir} 86 | } else { 87 | foreach tmp {TMPDIR TEMP TMP} { 88 | if { [info exists env($tmp)] } { 89 | lappend attempdirs $env($tmp) 90 | } else { 91 | lappend problems "No environment variable $tmp" 92 | } 93 | } 94 | 95 | switch $tcl_platform(platform) { 96 | windows { 97 | lappend attempdirs "C:\\TEMP" "C:\\TMP" "\\TEMP" "\\TMP" 98 | } 99 | macintosh { 100 | lappend attempdirs $env(TRASH_FOLDER) ;# a better place? 101 | } 102 | default { 103 | lappend attempdirs \ 104 | [file join / tmp] \ 105 | [file join / var tmp] \ 106 | [file join / usr tmp] 107 | } 108 | } 109 | 110 | lappend attempdirs [pwd] 111 | } 112 | 113 | foreach tmp $attempdirs { 114 | if { [file isdirectory $tmp] && [file writable $tmp] } { 115 | return $tmp 116 | } elseif { ![file isdirectory $tmp] } { 117 | lappend problems "Not a directory: $tmp" 118 | } else { 119 | lappend problems "Not writable: $tmp" 120 | } 121 | } 122 | 123 | # Fail if nothing worked. 124 | return -code error "Unable to determine a proper directory for temporary files\n[join $problems \n]" 125 | } 126 | 127 | 128 | proc futmp::mktempdir { basedir {prefix {}} } { 129 | if {![file writable $basedir]} { 130 | return -code error "Base-Directory $basedir is not writable" 131 | } 132 | 133 | set chars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 134 | set nrand_chars 10 135 | set maxtries 10 136 | 137 | for {set i 0} {$i < $maxtries} {incr i} { 138 | set newname $prefix 139 | for {set j 0} {$j < $nrand_chars} {incr j} { 140 | append newname [string index $chars [expr {int(rand()*62)}]] 141 | } 142 | set newname [file join $basedir $newname] 143 | 144 | if { ! [file exists $newname] } { 145 | # WARNING [file mkdir ..] does not return an error if $newname is already present 146 | # For this reason we test before if the new directory is present. 147 | # This is not perfect, since this should be an atomic operation. It isn't ! 148 | # There's a chance that someone else will create the same $newname between 149 | # these two operations. (Mitigation: the probability that another process 150 | # generates another file with *same random name* in the *same interval (msec)* 151 | # is very low. 152 | if { ! [catch {file mkdir $newname}] } { 153 | return $newname 154 | } 155 | } 156 | } 157 | return -code error "Failed to find an unused temporary file name" 158 | } 159 | 160 | 161 | # ::futmp::tempfile -- 162 | # 163 | # generate a temporary file name suitable for writing to 164 | # the file name will be unique, writable. 165 | # Code derived from http://mini.net/tcl/772 attributed to 166 | # Igor Volobouev and anon. 167 | # 168 | # Arguments: 169 | # basedir - where to put the new filename 170 | # prefix - prefix for the new filename, 171 | # extension - extension for the new filename 172 | # Results: 173 | # returns an opened channed (and the filename via filenameVar) or an error. 174 | # 175 | 176 | proc ::futmp::tempfile { filenameVar basedir {prefix {}} {extension {}} } { 177 | set chars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 178 | set nrand_chars 10 179 | set maxtries 10 180 | set access [list RDWR CREAT EXCL] 181 | set permission 0600 182 | set channel "" 183 | 184 | if {![file writable $basedir]} { 185 | return -code error "Directory $basedir is not writable" 186 | } 187 | 188 | for {set i 0} {$i < $maxtries} {incr i} { 189 | set newname $prefix 190 | for {set j 0} {$j < $nrand_chars} {incr j} { 191 | append newname [string index $chars [expr {int(rand()*62)}]] 192 | } 193 | set newname [file join $basedir $newname] 194 | append newname $extension 195 | if { ! [catch {open $newname $access $permission} channel] } { 196 | # Success 197 | upvar $filenameVar newfilename 198 | set newfilename $newname 199 | return $channel 200 | } 201 | } 202 | return -code error "Failed to find an unused temporary file name" 203 | } 204 | -------------------------------------------------------------------------------- /third_party/extrafont/src/pkgIndex.tcl: -------------------------------------------------------------------------------- 1 | 2 | package ifneeded extrafont 1.3 [list apply { dir { 3 | package require Tk 4 | 5 | set thisDir [file normalize ${dir}] 6 | 7 | set os $::tcl_platform(platform) 8 | switch -- $os { 9 | windows { set os win } 10 | unix { 11 | switch -- $::tcl_platform(os) { 12 | Darwin { set os darwin } 13 | Linux { set os linux } 14 | } 15 | } 16 | } 17 | set tail_libFile extrafont[info sharedlibextension] 18 | # try to guess the tcl-interpreter architecture (32/64 bit) ... 19 | set arch $::tcl_platform(pointerSize) 20 | switch -- $arch { 21 | 4 { set arch x32 } 22 | 8 { set arch x64 } 23 | default { error "extrafont: Unsupported architecture: Unexpected pointer-size $arch!!! "} 24 | } 25 | 26 | 27 | set dir_libFile [file join $thisDir ${os}-${arch}] 28 | if { ! [file isdirectory $dir_libFile ] } { 29 | error "extrafont: Unsupported platform ${os}-${arch}" 30 | } 31 | 32 | set full_libFile [file join $dir_libFile $tail_libFile] 33 | load $full_libFile 34 | 35 | namespace eval extrafont {} 36 | source [file join $thisDir extrafont.tcl] 37 | source [file join $thisDir futmp.tcl] 38 | 39 | package provide extrafont 1.3 40 | 41 | }} $dir] ;# end of lambda apply 42 | 43 | 44 | -------------------------------------------------------------------------------- /tkextrafont/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: RedFantom 3 | License: MIT License 4 | Copyright (c) 2018 RedFantom 5 | 6 | Python/Tkinter wrapper around extrafont library for Tk/Tcl. The original 7 | project files can be found here: 8 | 9 | 10 | Provides interface to load the extrafont library into a Tk instance by 11 | loading it into the Tk interpreter. 12 | """ 13 | import tkinter as tk 14 | import tkinter.font as tkfont 15 | import os 16 | from typing import Dict, List 17 | 18 | 19 | class Font(tkfont.Font): 20 | """ 21 | tk.Font wrapper that allows loading fonts from a file 22 | 23 | Loads tkextrafont if the package has not yet been loaded. 24 | """ 25 | 26 | def __init__(self, root=None, font=None, name=None, exists=False, file=None, **options): 27 | """ 28 | :param file: Path to the file to load a font from 29 | """ 30 | if file is None: # May as well use normal tk.Font 31 | tkfont.Font.__init__(self, root, font, name, exists, **options) 32 | return 33 | self._file = file 34 | root = root or tk._default_root 35 | if root is None: 36 | raise tk.TclError("No Tk instance available to get interpreter from") 37 | if not getattr(root, "_tkextrafont_loaded", False): 38 | load(root) 39 | # Load the font file 40 | root.tk.call("extrafont::load", file) 41 | tkfont.Font.__init__(self, root, font, name, exists, **options) 42 | 43 | def unload(self): 44 | """Unload the current font""" 45 | self._tk.call("extrafont::unload", self.name) # self._tk available after tk.font.Font.__init__ 46 | 47 | def loaded_fonts(self) -> List[str]: 48 | """Return a list of fonts loaded with extrafont""" 49 | return self._tk.call("extrafont::query", "families") 50 | 51 | def font_info(self, fname: str) -> List[Dict[str, str]]: 52 | """Return info of a font file""" 53 | tk_result_list = self._tk.splitlist( 54 | self._tk.call("extrafont::nameinfo", fname)[0] 55 | ) 56 | font_info_dict = {} 57 | 58 | for key, value in zip(tk_result_list[0::2], tk_result_list[1::2]): 59 | font_info_dict[key] = str(value) 60 | return font_info_dict 61 | 62 | def is_font_available(self, font_name) -> bool: 63 | """Return a boolean whether a font is available""" 64 | return self._tk.call("extrafont::isAvailable", font_name) 65 | 66 | 67 | def load(window: tk.Tk): 68 | """Load extrafont into a Tk interpreter""" 69 | local = os.path.abspath(os.path.dirname(__file__)) 70 | window.tk.setvar("dir", local) 71 | window.tk.eval("source [file join $dir pkgIndex.tcl]") 72 | try: 73 | window.tk.eval("package require extrafont") 74 | except tk.TclError as e: 75 | if "libfontconfig" in e.message: 76 | raise tk.TclError("Could not load extrafont due to missing fontconfig - See issue #1 on GitHub: ") 77 | window.tk.unsetvar("dir") 78 | window._tkextrafont_loaded = True 79 | --------------------------------------------------------------------------------