TTFQuery 2.0.0b1__tar.gz → 2.0.1a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ttfquery-2.0.1a1/PKG-INFO +83 -0
- ttfquery-2.0.1a1/README.md +19 -0
- ttfquery-2.0.1a1/TTFQuery.egg-info/PKG-INFO +83 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/TTFQuery.egg-info/SOURCES.txt +7 -1
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/TTFQuery.egg-info/entry_points.txt +0 -1
- ttfquery-2.0.1a1/TTFQuery.egg-info/requires.txt +6 -0
- ttfquery-2.0.1a1/pyproject.toml +55 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/setup.cfg +0 -1
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/setup.py +0 -1
- ttfquery-2.0.1a1/tests/test_describe.py +20 -0
- ttfquery-2.0.1a1/tests/test_findsystem_linux.py +18 -0
- ttfquery-2.0.1a1/tests/test_findsystem_win32.py +17 -0
- ttfquery-2.0.1a1/tests/test_glyphquery.py +45 -0
- ttfquery-2.0.1a1/tests/test_registry.py +59 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/__init__.py +1 -1
- ttfquery-2.0.1a1/ttfquery/describe.py +332 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/findsystem.py +1 -1
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/ttffiles.py +9 -0
- TTFQuery-2.0.0b1/PKG-INFO +0 -38
- TTFQuery-2.0.0b1/TTFQuery.egg-info/PKG-INFO +0 -38
- TTFQuery-2.0.0b1/TTFQuery.egg-info/requires.txt +0 -2
- TTFQuery-2.0.0b1/ttfquery/describe.py +0 -294
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/MANIFEST.in +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/TTFQuery.egg-info/dependency_links.txt +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/TTFQuery.egg-info/top_level.txt +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/license.txt +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/_scriptregistry.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/glyph.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/glyphquery.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/guessdescription.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/scriptregistry.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/ttffamily.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/ttfgroups.py +0 -0
- {TTFQuery-2.0.0b1 → ttfquery-2.0.1a1}/ttfquery/ttfmetadata.py +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: TTFQuery
|
|
3
|
+
Version: 2.0.1a1
|
|
4
|
+
Summary: FontTools-based package for querying system fonts
|
|
5
|
+
Home-page: http://ttfquery.sourceforge.net/
|
|
6
|
+
Author: Mike C. Fletcher
|
|
7
|
+
Author-email: "Mike C. Fletcher" <mcfletch@users.sourceforge.net>
|
|
8
|
+
License: THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY
|
|
9
|
+
SITUATION ENDANGERING HUMAN LIFE OR PROPERTY.
|
|
10
|
+
|
|
11
|
+
TTFQuery License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2003-2018, Michael C. Fletcher and Contributors
|
|
14
|
+
All rights reserved.
|
|
15
|
+
|
|
16
|
+
Redistribution and use in source and binary forms, with or without
|
|
17
|
+
modification, are permitted provided that the following conditions
|
|
18
|
+
are met:
|
|
19
|
+
|
|
20
|
+
Redistributions of source code must retain the above copyright
|
|
21
|
+
notice, this list of conditions and the following disclaimer.
|
|
22
|
+
|
|
23
|
+
Redistributions in binary form must reproduce the above
|
|
24
|
+
copyright notice, this list of conditions and the following
|
|
25
|
+
disclaimer in the documentation and/or other materials
|
|
26
|
+
provided with the distribution.
|
|
27
|
+
|
|
28
|
+
The name of Michael C. Fletcher, or the name of any Contributor,
|
|
29
|
+
may not be used to endorse or promote products derived from this
|
|
30
|
+
software without specific prior written permission.
|
|
31
|
+
|
|
32
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
33
|
+
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
34
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
35
|
+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
36
|
+
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
37
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
38
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
39
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
40
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
41
|
+
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
42
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
43
|
+
OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Project-URL: Homepage, http://ttfquery.sourceforge.net/
|
|
47
|
+
Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
|
|
48
|
+
Platform: Any
|
|
49
|
+
Classifier: Programming Language :: Python
|
|
50
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
51
|
+
Classifier: Intended Audience :: Developers
|
|
52
|
+
Classifier: Operating System :: OS Independent
|
|
53
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
54
|
+
Requires-Python: >=3.7
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
Requires-Dist: fonttools
|
|
57
|
+
Requires-Dist: numpy
|
|
58
|
+
Provides-Extra: test
|
|
59
|
+
Requires-Dist: pytest; extra == "test"
|
|
60
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
61
|
+
Dynamic: author
|
|
62
|
+
Dynamic: home-page
|
|
63
|
+
Dynamic: platform
|
|
64
|
+
|
|
65
|
+
# TTFQuery
|
|
66
|
+
|
|
67
|
+
FontTools-based package for querying system fonts
|
|
68
|
+
|
|
69
|
+
TTFQuery builds on the FontTools package to allow the Python programmer to accomplish a number of tasks:
|
|
70
|
+
|
|
71
|
+
* query the system to find installed fonts
|
|
72
|
+
* retrieve metadata about any TTF font file (even those not yet installed)
|
|
73
|
+
* abstract family type
|
|
74
|
+
* proper font name
|
|
75
|
+
* glyph outlines
|
|
76
|
+
* build simple metadata registries for run-time font matching
|
|
77
|
+
|
|
78
|
+
With these functionalities, it is possible to readily create OpenGL solid-text
|
|
79
|
+
rendering libraries which can accept abstract font-family names as font
|
|
80
|
+
specifiers and deliver platform-specific TTF files to match those libraries.
|
|
81
|
+
|
|
82
|
+
TTFQuery doesn't provide rendering services, but a sample implementation can be
|
|
83
|
+
found in the OpenGLContext project, from which TTFQuery was refactored.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# TTFQuery
|
|
2
|
+
|
|
3
|
+
FontTools-based package for querying system fonts
|
|
4
|
+
|
|
5
|
+
TTFQuery builds on the FontTools package to allow the Python programmer to accomplish a number of tasks:
|
|
6
|
+
|
|
7
|
+
* query the system to find installed fonts
|
|
8
|
+
* retrieve metadata about any TTF font file (even those not yet installed)
|
|
9
|
+
* abstract family type
|
|
10
|
+
* proper font name
|
|
11
|
+
* glyph outlines
|
|
12
|
+
* build simple metadata registries for run-time font matching
|
|
13
|
+
|
|
14
|
+
With these functionalities, it is possible to readily create OpenGL solid-text
|
|
15
|
+
rendering libraries which can accept abstract font-family names as font
|
|
16
|
+
specifiers and deliver platform-specific TTF files to match those libraries.
|
|
17
|
+
|
|
18
|
+
TTFQuery doesn't provide rendering services, but a sample implementation can be
|
|
19
|
+
found in the OpenGLContext project, from which TTFQuery was refactored.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: TTFQuery
|
|
3
|
+
Version: 2.0.1a1
|
|
4
|
+
Summary: FontTools-based package for querying system fonts
|
|
5
|
+
Home-page: http://ttfquery.sourceforge.net/
|
|
6
|
+
Author: Mike C. Fletcher
|
|
7
|
+
Author-email: "Mike C. Fletcher" <mcfletch@users.sourceforge.net>
|
|
8
|
+
License: THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY
|
|
9
|
+
SITUATION ENDANGERING HUMAN LIFE OR PROPERTY.
|
|
10
|
+
|
|
11
|
+
TTFQuery License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2003-2018, Michael C. Fletcher and Contributors
|
|
14
|
+
All rights reserved.
|
|
15
|
+
|
|
16
|
+
Redistribution and use in source and binary forms, with or without
|
|
17
|
+
modification, are permitted provided that the following conditions
|
|
18
|
+
are met:
|
|
19
|
+
|
|
20
|
+
Redistributions of source code must retain the above copyright
|
|
21
|
+
notice, this list of conditions and the following disclaimer.
|
|
22
|
+
|
|
23
|
+
Redistributions in binary form must reproduce the above
|
|
24
|
+
copyright notice, this list of conditions and the following
|
|
25
|
+
disclaimer in the documentation and/or other materials
|
|
26
|
+
provided with the distribution.
|
|
27
|
+
|
|
28
|
+
The name of Michael C. Fletcher, or the name of any Contributor,
|
|
29
|
+
may not be used to endorse or promote products derived from this
|
|
30
|
+
software without specific prior written permission.
|
|
31
|
+
|
|
32
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
33
|
+
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
34
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
35
|
+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
36
|
+
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
37
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
38
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
39
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
40
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
41
|
+
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
42
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
43
|
+
OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Project-URL: Homepage, http://ttfquery.sourceforge.net/
|
|
47
|
+
Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
|
|
48
|
+
Platform: Any
|
|
49
|
+
Classifier: Programming Language :: Python
|
|
50
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
51
|
+
Classifier: Intended Audience :: Developers
|
|
52
|
+
Classifier: Operating System :: OS Independent
|
|
53
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
54
|
+
Requires-Python: >=3.7
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
Requires-Dist: fonttools
|
|
57
|
+
Requires-Dist: numpy
|
|
58
|
+
Provides-Extra: test
|
|
59
|
+
Requires-Dist: pytest; extra == "test"
|
|
60
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
61
|
+
Dynamic: author
|
|
62
|
+
Dynamic: home-page
|
|
63
|
+
Dynamic: platform
|
|
64
|
+
|
|
65
|
+
# TTFQuery
|
|
66
|
+
|
|
67
|
+
FontTools-based package for querying system fonts
|
|
68
|
+
|
|
69
|
+
TTFQuery builds on the FontTools package to allow the Python programmer to accomplish a number of tasks:
|
|
70
|
+
|
|
71
|
+
* query the system to find installed fonts
|
|
72
|
+
* retrieve metadata about any TTF font file (even those not yet installed)
|
|
73
|
+
* abstract family type
|
|
74
|
+
* proper font name
|
|
75
|
+
* glyph outlines
|
|
76
|
+
* build simple metadata registries for run-time font matching
|
|
77
|
+
|
|
78
|
+
With these functionalities, it is possible to readily create OpenGL solid-text
|
|
79
|
+
rendering libraries which can accept abstract font-family names as font
|
|
80
|
+
specifiers and deliver platform-specific TTF files to match those libraries.
|
|
81
|
+
|
|
82
|
+
TTFQuery doesn't provide rendering services, but a sample implementation can be
|
|
83
|
+
found in the OpenGLContext project, from which TTFQuery was refactored.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
MANIFEST.in
|
|
2
|
+
README.md
|
|
2
3
|
license.txt
|
|
3
|
-
|
|
4
|
+
pyproject.toml
|
|
4
5
|
setup.py
|
|
5
6
|
TTFQuery.egg-info/PKG-INFO
|
|
6
7
|
TTFQuery.egg-info/SOURCES.txt
|
|
@@ -8,6 +9,11 @@ TTFQuery.egg-info/dependency_links.txt
|
|
|
8
9
|
TTFQuery.egg-info/entry_points.txt
|
|
9
10
|
TTFQuery.egg-info/requires.txt
|
|
10
11
|
TTFQuery.egg-info/top_level.txt
|
|
12
|
+
tests/test_describe.py
|
|
13
|
+
tests/test_findsystem_linux.py
|
|
14
|
+
tests/test_findsystem_win32.py
|
|
15
|
+
tests/test_glyphquery.py
|
|
16
|
+
tests/test_registry.py
|
|
11
17
|
ttfquery/__init__.py
|
|
12
18
|
ttfquery/_scriptregistry.py
|
|
13
19
|
ttfquery/describe.py
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "TTFQuery"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "FontTools-based package for querying system fonts"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "license.txt" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Mike C. Fletcher", email = "mcfletch@users.sourceforge.net" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.7"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"fonttools",
|
|
17
|
+
"numpy",
|
|
18
|
+
]
|
|
19
|
+
keywords = [
|
|
20
|
+
"fonttools",
|
|
21
|
+
"ttf",
|
|
22
|
+
"truetype",
|
|
23
|
+
"outline",
|
|
24
|
+
"font",
|
|
25
|
+
"curve",
|
|
26
|
+
"system fonts",
|
|
27
|
+
]
|
|
28
|
+
classifiers = [
|
|
29
|
+
"Programming Language :: Python",
|
|
30
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
31
|
+
"Intended Audience :: Developers",
|
|
32
|
+
"Operating System :: OS Independent",
|
|
33
|
+
"Topic :: Multimedia :: Graphics",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "http://ttfquery.sourceforge.net/"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
test = [
|
|
41
|
+
"pytest",
|
|
42
|
+
"pytest-cov",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
ttffiles = "ttfquery.ttffiles:main"
|
|
47
|
+
ttffamily = "ttfquery.ttffamily:main"
|
|
48
|
+
ttfgroups = "ttfquery.ttfgroups:main"
|
|
49
|
+
ttfmetadata = "ttfquery.ttfmetadata:main"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
packages = ["ttfquery"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.dynamic]
|
|
55
|
+
version = { attr = "ttfquery.__version__" }
|
|
@@ -36,7 +36,6 @@ if __name__ == "__main__":
|
|
|
36
36
|
],
|
|
37
37
|
install_requires = ['fonttools','numpy'],
|
|
38
38
|
classifiers= [
|
|
39
|
-
"""License :: OSI Approved :: BSD License""",
|
|
40
39
|
"""Programming Language :: Python""",
|
|
41
40
|
"""Topic :: Software Development :: Libraries :: Python Modules""",
|
|
42
41
|
"""Intended Audience :: Developers""",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
from ttfquery import describe, findsystem
|
|
3
|
+
import pytest
|
|
4
|
+
import sys, glob, unittest
|
|
5
|
+
|
|
6
|
+
class TestDescribe(unittest.TestCase):
|
|
7
|
+
def test_describe_system_fonts(self):
|
|
8
|
+
for fontfile in list(findsystem.findFonts())[:100]:
|
|
9
|
+
try:
|
|
10
|
+
font = describe.openFont(fontfile)
|
|
11
|
+
except Exception as err:
|
|
12
|
+
err.args += ('Error opening font', font)
|
|
13
|
+
raise
|
|
14
|
+
else:
|
|
15
|
+
short = describe.shortName( font )
|
|
16
|
+
assert short, "Null name for font %s"%(fontfile,)
|
|
17
|
+
family = describe.family( font )
|
|
18
|
+
assert len(family)==2, family
|
|
19
|
+
modifiers = describe.modifiers( font )
|
|
20
|
+
assert describe.weightName(modifiers[0])
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
from ttfquery import findsystem
|
|
3
|
+
import pytest
|
|
4
|
+
import sys, glob, unittest
|
|
5
|
+
pytestmark = pytest.mark.skipif(
|
|
6
|
+
not sys.platform.startswith('linux'),
|
|
7
|
+
reason='Linux tests'
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
class TestLinux(unittest.TestCase):
|
|
11
|
+
def test_font_directory(self):
|
|
12
|
+
"""Validate that at least one font directory can be found"""
|
|
13
|
+
directories = findsystem.linuxFontDirectories()
|
|
14
|
+
assert directories, "No font directories were found, *something* should be there"
|
|
15
|
+
def test_fonts_from_registry(self):
|
|
16
|
+
fonts = findsystem.findFonts()
|
|
17
|
+
assert fonts, "No fonts were found in our default font-search paths"
|
|
18
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
from ttfquery import findsystem
|
|
3
|
+
import pytest
|
|
4
|
+
import sys, glob, unittest
|
|
5
|
+
pytestmark = pytest.mark.skipif(not sys.platform == 'win32', reason='Win32 tests')
|
|
6
|
+
|
|
7
|
+
class TestWin32(unittest.TestCase):
|
|
8
|
+
def test_font_directory(self):
|
|
9
|
+
"""Validate that the fonts directory windows reports is not empty"""
|
|
10
|
+
dirname = findsystem.win32FontDirectory()
|
|
11
|
+
assert glob.glob(os.path.join(dirname,'*.ttf')), "No .ttf files found in Win32 font directory %s"%(
|
|
12
|
+
dirname,
|
|
13
|
+
)
|
|
14
|
+
def test_fonts_from_registry(self):
|
|
15
|
+
dirname = 'c:\\should-not-exist-and-if-it-does-you-know-you-did-it'
|
|
16
|
+
fontpaths = findsystem.win32InstalledFonts(dirname)
|
|
17
|
+
assert fontpaths, "Did not find any registry-supplied fonts"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
from ttfquery import ttffiles, describe, glyphquery, glyph
|
|
3
|
+
import pytest
|
|
4
|
+
import os, sys, glob, unittest, tempfile, shutil
|
|
5
|
+
|
|
6
|
+
class TestGlyphQuery(unittest.TestCase):
|
|
7
|
+
def setUp(self):
|
|
8
|
+
self.workdir = tempfile.mkdtemp(prefix='ttfquery-',suffix='-tests')
|
|
9
|
+
self.registry = os.path.join(self.workdir,'fonts.cache')
|
|
10
|
+
class fakeoptions:
|
|
11
|
+
directories = []
|
|
12
|
+
registry = self.registry
|
|
13
|
+
self.options = fakeoptions
|
|
14
|
+
self.registry = ttffiles.registry_for_options(self.options)
|
|
15
|
+
def tearDown(self):
|
|
16
|
+
shutil.rmtree(self.workdir)
|
|
17
|
+
|
|
18
|
+
def test_has_a_glyph(self):
|
|
19
|
+
has,total = 0,0
|
|
20
|
+
for name,metadata in self.registry.specificFonts.items():
|
|
21
|
+
font = describe.openFont(metadata.file_name)
|
|
22
|
+
height = glyphquery.charHeight(font)
|
|
23
|
+
line = glyphquery.lineHeight(font)
|
|
24
|
+
desc = glyphquery.charDescent(font)
|
|
25
|
+
if glyphquery.hasGlyph(font,'a'):
|
|
26
|
+
glyphName = glyphquery.explicitGlyph(font,'a')
|
|
27
|
+
if glyphName:
|
|
28
|
+
width = glyphquery.width(font,glyphName)
|
|
29
|
+
has += 1
|
|
30
|
+
|
|
31
|
+
shape = glyph.Glyph(glyphName)
|
|
32
|
+
shape.compile(font)
|
|
33
|
+
assert shape.contours, "No contours generated"
|
|
34
|
+
assert shape.outlines, "No outlines generated"
|
|
35
|
+
assert shape.width, "Width of `a` character was null"
|
|
36
|
+
assert shape.height, "Width of `a` character was null"
|
|
37
|
+
|
|
38
|
+
else:
|
|
39
|
+
glyphName = glyphquery.glyphName(font,'a')
|
|
40
|
+
assert glyphName, """Didn't get a placeholder for the 'a' character"""
|
|
41
|
+
width = glyphquery.width(font,glyphName)
|
|
42
|
+
total += 1
|
|
43
|
+
assert total, "No specific fonts on this system?"
|
|
44
|
+
assert has/float(total) > .2, "More than 4/5 of fonts on this system are missing a glyph for `a`? %s/%s"%(has,total)
|
|
45
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
from ttfquery import ttffiles, ttfgroups, ttfmetadata, ttffamily
|
|
3
|
+
import pytest
|
|
4
|
+
import os, sys, glob, unittest, tempfile, shutil
|
|
5
|
+
|
|
6
|
+
class TestRegistry(unittest.TestCase):
|
|
7
|
+
def setUp(self):
|
|
8
|
+
self.workdir = tempfile.mkdtemp(prefix='ttfquery-',suffix='-tests')
|
|
9
|
+
self.registry = os.path.join(self.workdir,'fonts.cache')
|
|
10
|
+
class fakeoptions:
|
|
11
|
+
directories = []
|
|
12
|
+
registry = self.registry
|
|
13
|
+
self.options = fakeoptions
|
|
14
|
+
self.registry = ttffiles.registry_for_options(self.options)
|
|
15
|
+
def tearDown(self):
|
|
16
|
+
shutil.rmtree(self.workdir)
|
|
17
|
+
def test_registry_setup(self):
|
|
18
|
+
registry = self.registry
|
|
19
|
+
assert registry.fonts, "No fonts loaded"
|
|
20
|
+
assert registry.families, "No families extracted"
|
|
21
|
+
assert registry.specificFonts, "No final/specific font classes registered"
|
|
22
|
+
assert registry.files, "No files registered/added"
|
|
23
|
+
assert registry.shortFiles, "No short filenames registered"
|
|
24
|
+
assert not registry.DIRTY, "Registry was just saved, should be clean"
|
|
25
|
+
|
|
26
|
+
font = None
|
|
27
|
+
for name in ('Arial','Helvetica','SANS'):
|
|
28
|
+
try:
|
|
29
|
+
font = registry.matchName(name)
|
|
30
|
+
except KeyError:
|
|
31
|
+
pass
|
|
32
|
+
if font:
|
|
33
|
+
break
|
|
34
|
+
if not font:
|
|
35
|
+
raise RuntimeError("Unable to find any of Arial/Helvetica/SANS")
|
|
36
|
+
|
|
37
|
+
def test_ttfgroups(self):
|
|
38
|
+
registry = self.registry
|
|
39
|
+
table = ttfgroups.buildTable(registry)
|
|
40
|
+
ttfgroups.run_report(table)
|
|
41
|
+
|
|
42
|
+
parser = ttfgroups.get_options()
|
|
43
|
+
assert 'font-groups' in parser.description, parser.description
|
|
44
|
+
|
|
45
|
+
def test_ttfmetadata(self):
|
|
46
|
+
registry = self.registry
|
|
47
|
+
for name in registry.fonts.keys():
|
|
48
|
+
ttfmetadata.find_match(name,registry)
|
|
49
|
+
|
|
50
|
+
parser = ttfmetadata.get_options()
|
|
51
|
+
assert 'Query/search' in parser.description, parser.description
|
|
52
|
+
|
|
53
|
+
def test_ttffamily(self):
|
|
54
|
+
registry = self.registry
|
|
55
|
+
for family,subfams in registry.families.items():
|
|
56
|
+
ttffamily.search(registry,family)
|
|
57
|
+
for subfam in subfams.keys():
|
|
58
|
+
ttffamily.search(registry,family,subfam)
|
|
59
|
+
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Fonttools package for querying and sorting system fonts"""
|
|
2
|
-
__version__ = "2.0.
|
|
2
|
+
__version__ = "2.0.1a1"
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""Extract meta-data from a font-file to describe the font"""
|
|
2
|
+
from fontTools import ttLib
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
unicode, long
|
|
7
|
+
except NameError:
|
|
8
|
+
unicode = str
|
|
9
|
+
long = int
|
|
10
|
+
try:
|
|
11
|
+
from OpenGLContext.debug.logs import text_log
|
|
12
|
+
except ImportError:
|
|
13
|
+
text_log = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def openFont(filename):
|
|
17
|
+
"""Get a new font object"""
|
|
18
|
+
if isinstance(filename, (bytes, unicode)):
|
|
19
|
+
filename = open(filename, "rb")
|
|
20
|
+
return ttLib.TTFont(filename)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
FONT_SPECIFIER_NAME_ID = 4
|
|
24
|
+
FONT_SPECIFIER_FAMILY_ID = 1
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def shortName(font):
|
|
28
|
+
"""Get the short name from the font's names table"""
|
|
29
|
+
name = ""
|
|
30
|
+
family = ""
|
|
31
|
+
for record in font["name"].names:
|
|
32
|
+
if record.nameID == FONT_SPECIFIER_NAME_ID and not name:
|
|
33
|
+
if b"\000" in record.string:
|
|
34
|
+
name = unicode(record.string, "utf-16-be").encode("utf-8")
|
|
35
|
+
else:
|
|
36
|
+
name = record.string
|
|
37
|
+
elif record.nameID == FONT_SPECIFIER_FAMILY_ID and not family:
|
|
38
|
+
if b"\000" in record.string:
|
|
39
|
+
family = unicode(record.string, "utf-16-be").encode("utf-8")
|
|
40
|
+
else:
|
|
41
|
+
family = record.string
|
|
42
|
+
if name and family:
|
|
43
|
+
break
|
|
44
|
+
return name, family
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
FAMILY_NAMES = {
|
|
48
|
+
0: ("ANY", {}),
|
|
49
|
+
1: (
|
|
50
|
+
"SERIF-OLD",
|
|
51
|
+
{
|
|
52
|
+
0: "ANY",
|
|
53
|
+
1: "ROUNDED-LEGIBILITY",
|
|
54
|
+
2: "GARALDE",
|
|
55
|
+
3: "VENETIAN",
|
|
56
|
+
4: "VENETIAN-MODIFIED",
|
|
57
|
+
5: "DUTCH-MODERN",
|
|
58
|
+
6: "DUTCH-TRADITIONAL",
|
|
59
|
+
7: "CONTEMPORARY",
|
|
60
|
+
8: "CALLIGRAPHIC",
|
|
61
|
+
15: "MISCELLANEOUS",
|
|
62
|
+
},
|
|
63
|
+
),
|
|
64
|
+
2: (
|
|
65
|
+
"SERIF-TRANSITIONAL",
|
|
66
|
+
{
|
|
67
|
+
0: "ANY",
|
|
68
|
+
1: "DIRECT-LINE",
|
|
69
|
+
2: "SCRIPT",
|
|
70
|
+
15: "MISCELLANEOUS",
|
|
71
|
+
},
|
|
72
|
+
),
|
|
73
|
+
3: (
|
|
74
|
+
"SERIF",
|
|
75
|
+
{
|
|
76
|
+
0: "ANY",
|
|
77
|
+
1: "ITALIAN",
|
|
78
|
+
2: "SCRIPT",
|
|
79
|
+
15: "MISCELLANEOUS",
|
|
80
|
+
},
|
|
81
|
+
),
|
|
82
|
+
4: (
|
|
83
|
+
"SERIF-CLARENDON",
|
|
84
|
+
{
|
|
85
|
+
0: "ANY",
|
|
86
|
+
1: "CLARENDON",
|
|
87
|
+
2: "MODERN",
|
|
88
|
+
3: "TRADITIONAL",
|
|
89
|
+
4: "NEWSPAPER",
|
|
90
|
+
5: "STUB-SERIF",
|
|
91
|
+
6: "MONOTYPE",
|
|
92
|
+
7: "TYPEWRITER",
|
|
93
|
+
15: "MISCELLANEOUS",
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
5: (
|
|
97
|
+
"SERIF-SLAB",
|
|
98
|
+
{
|
|
99
|
+
0: "ANY",
|
|
100
|
+
1: "MONOTONE",
|
|
101
|
+
2: "HUMANIST",
|
|
102
|
+
3: "GEOMETRIC",
|
|
103
|
+
4: "SWISS",
|
|
104
|
+
5: "TYPEWRITER",
|
|
105
|
+
15: "MISCELLANEOUS",
|
|
106
|
+
},
|
|
107
|
+
),
|
|
108
|
+
7: (
|
|
109
|
+
"SERIF-FREEFORM",
|
|
110
|
+
{
|
|
111
|
+
0: "ANY",
|
|
112
|
+
1: "MODERN",
|
|
113
|
+
15: "MISCELLANEOUS",
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
8: (
|
|
117
|
+
"SANS",
|
|
118
|
+
{
|
|
119
|
+
0: "ANY",
|
|
120
|
+
1: "GOTHIC-NEO-GROTESQUE-IBM",
|
|
121
|
+
2: "HUMANIST",
|
|
122
|
+
3: "ROUND-GEOMETRIC-LOW-X",
|
|
123
|
+
4: "ROUND-GEOMETRIC-HIGH-X",
|
|
124
|
+
5: "GOTHIC-NEO-GROTESQUE",
|
|
125
|
+
6: "GOTHIC-NEO-GROTESQUE-MODIFIED",
|
|
126
|
+
9: "GOTHIC-TYPEWRITER",
|
|
127
|
+
10: "MATRIX",
|
|
128
|
+
15: "MISCELLANEOUS",
|
|
129
|
+
},
|
|
130
|
+
),
|
|
131
|
+
9: (
|
|
132
|
+
"ORNAMENTAL",
|
|
133
|
+
{
|
|
134
|
+
0: "ANY",
|
|
135
|
+
1: "ENGRAVER",
|
|
136
|
+
2: "BLACK-LETTER",
|
|
137
|
+
3: "DECORATIVE",
|
|
138
|
+
4: "THREE-DIMENSIONAL",
|
|
139
|
+
15: "MISCELLANEOUS",
|
|
140
|
+
},
|
|
141
|
+
),
|
|
142
|
+
10: (
|
|
143
|
+
"SCRIPT",
|
|
144
|
+
{
|
|
145
|
+
0: "ANY",
|
|
146
|
+
1: "UNCIAL",
|
|
147
|
+
2: "BRUSH-JOINED",
|
|
148
|
+
3: "FORMAL-JOINED",
|
|
149
|
+
4: "MONOTONE-JOINED",
|
|
150
|
+
5: "CALLIGRAPHIC",
|
|
151
|
+
6: "BRUSH-UNJOINED",
|
|
152
|
+
7: "FORMAL-UNJOINED",
|
|
153
|
+
8: "MONOTONE-UNJOINED",
|
|
154
|
+
15: "MISCELLANEOUS",
|
|
155
|
+
},
|
|
156
|
+
),
|
|
157
|
+
12: (
|
|
158
|
+
"SYMBOL",
|
|
159
|
+
{
|
|
160
|
+
0: "ANY",
|
|
161
|
+
3: "MIXED-SERIF",
|
|
162
|
+
6: "OLDSTYLE-SERIF",
|
|
163
|
+
7: "NEO-GROTESQUE-SANS",
|
|
164
|
+
15: "MISCELLANEOUS",
|
|
165
|
+
},
|
|
166
|
+
),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
WEIGHT_NAMES = {
|
|
170
|
+
"thin": 100,
|
|
171
|
+
"extralight": 200,
|
|
172
|
+
"ultralight": 200,
|
|
173
|
+
"light": 300,
|
|
174
|
+
"normal": 400,
|
|
175
|
+
"regular": 400,
|
|
176
|
+
"plain": 400,
|
|
177
|
+
"medium": 500,
|
|
178
|
+
"semibold": 600,
|
|
179
|
+
"demibold": 600,
|
|
180
|
+
"bold": 700,
|
|
181
|
+
"extrabold": 800,
|
|
182
|
+
"ultrabold": 800,
|
|
183
|
+
"black": 900,
|
|
184
|
+
"heavy": 900,
|
|
185
|
+
}
|
|
186
|
+
WEIGHT_NUMBERS = {}
|
|
187
|
+
for key, value in WEIGHT_NAMES.items():
|
|
188
|
+
WEIGHT_NUMBERS.setdefault(value, []).append(key)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def weightNumber(name):
|
|
192
|
+
"""Convert a string-name to a weight number compatible with this module"""
|
|
193
|
+
if isinstance(name, (bytes, unicode)):
|
|
194
|
+
name = name.lower()
|
|
195
|
+
name = name.replace("-", "").replace(" ", "")
|
|
196
|
+
if name and name[-1] == "+":
|
|
197
|
+
name = name[:-1]
|
|
198
|
+
adjust = 50
|
|
199
|
+
elif name and name[-1] == "-":
|
|
200
|
+
name = name[:-1]
|
|
201
|
+
adjust = -50
|
|
202
|
+
else:
|
|
203
|
+
adjust = 0
|
|
204
|
+
return WEIGHT_NAMES[name] + adjust
|
|
205
|
+
else:
|
|
206
|
+
return int(name) or 400 # for cases where number isn't properly specified
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def weightName(number):
|
|
210
|
+
"""Convert integer number to a human-readable weight-name"""
|
|
211
|
+
number = int(number) or 400
|
|
212
|
+
if number in WEIGHT_NUMBERS:
|
|
213
|
+
return WEIGHT_NUMBERS[number]
|
|
214
|
+
name = "thin-"
|
|
215
|
+
for x in range(100, 1000, 100):
|
|
216
|
+
if number >= x:
|
|
217
|
+
name = WEIGHT_NUMBERS[x][0] + "+"
|
|
218
|
+
return name
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def family(font):
|
|
222
|
+
"""Get the family (and sub-family) for a font"""
|
|
223
|
+
HIBYTE = 65280
|
|
224
|
+
LOBYTE = 255
|
|
225
|
+
familyID = (font["OS/2"].sFamilyClass & HIBYTE) >> 8
|
|
226
|
+
subFamilyID = font["OS/2"].sFamilyClass & LOBYTE
|
|
227
|
+
return familyNames(familyID, subFamilyID)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def familyNames(familyID, subFamilyID=0):
|
|
231
|
+
"""Convert family integers to human-readable names"""
|
|
232
|
+
familyName, subFamilies = FAMILY_NAMES.get(familyID, ("RESERVED", None))
|
|
233
|
+
if familyName == "RESERVED":
|
|
234
|
+
if text_log:
|
|
235
|
+
text_log.warn("Font has invalid (reserved) familyID: %s", familyID)
|
|
236
|
+
if subFamilies:
|
|
237
|
+
subFamily = subFamilies.get(subFamilyID, "RESERVED")
|
|
238
|
+
else:
|
|
239
|
+
subFamily = "ANY"
|
|
240
|
+
return (familyName, subFamily)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def modifiers(font):
|
|
244
|
+
"""Get weight and italic modifiers for a font
|
|
245
|
+
|
|
246
|
+
weight is taken from the OS/2 usWeightClass field
|
|
247
|
+
italic is taken from either OS/2 fsSelection or
|
|
248
|
+
head macStyle, if either indicates italics we
|
|
249
|
+
report italics
|
|
250
|
+
"""
|
|
251
|
+
return (
|
|
252
|
+
# weight as an integer
|
|
253
|
+
font["OS/2"].usWeightClass,
|
|
254
|
+
(font["OS/2"].fsSelection & 1 or font["head"].macStyle & 2), # italic
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def guessEncoding(font, given=None):
|
|
259
|
+
"""Attempt to guess/retrieve an encoding from the font itself
|
|
260
|
+
|
|
261
|
+
Basically this will try to get the given encoding
|
|
262
|
+
(unless it is None).
|
|
263
|
+
|
|
264
|
+
If given is a single integer or a single-item tuple,
|
|
265
|
+
we will attempt scan looking for any table matching
|
|
266
|
+
given as the platform ID and returning the first sub
|
|
267
|
+
table.
|
|
268
|
+
|
|
269
|
+
If given is a two-value tuple, we will require
|
|
270
|
+
explicit matching, and raise errors if the encoding
|
|
271
|
+
cannot be retrieved.
|
|
272
|
+
|
|
273
|
+
if given is None, we will return the first encoding
|
|
274
|
+
in the font.
|
|
275
|
+
|
|
276
|
+
XXX This needs some work, particularly for non-win32
|
|
277
|
+
platforms, where there is no preference embodied
|
|
278
|
+
for the native encoding.
|
|
279
|
+
"""
|
|
280
|
+
if isinstance(given, tuple) and given:
|
|
281
|
+
if len(given) == 2:
|
|
282
|
+
if __debug__:
|
|
283
|
+
if text_log:
|
|
284
|
+
text_log.info(
|
|
285
|
+
"""Checking for explicitly required encoding %r""", given
|
|
286
|
+
)
|
|
287
|
+
if not font["cmap"].getcmap(*given):
|
|
288
|
+
raise ValueError(
|
|
289
|
+
"""The specified font encoding %r does not appear to be available within the font %r. Available encodings: %s"""
|
|
290
|
+
% (
|
|
291
|
+
given,
|
|
292
|
+
shortName(font),
|
|
293
|
+
[
|
|
294
|
+
(table.platformID, table.platEncID)
|
|
295
|
+
for table in font["cmap"].tables
|
|
296
|
+
],
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
return given
|
|
300
|
+
elif len(given) > 2:
|
|
301
|
+
raise TypeError(
|
|
302
|
+
"""Encoding must be None, a two-tuple, or an integer, got %r"""
|
|
303
|
+
% (given,)
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
# treat as a single integer, regardless of number of integer's
|
|
307
|
+
given = given[0]
|
|
308
|
+
if isinstance(given, (int, long)):
|
|
309
|
+
for table in font["cmap"].tables:
|
|
310
|
+
if table.platformID == given:
|
|
311
|
+
return (table.platformID, table.platEncID)
|
|
312
|
+
raise ValueError(
|
|
313
|
+
"""Could not find encoding with specified platformID==%s within the font %r. Available encodings: %s"""
|
|
314
|
+
% (
|
|
315
|
+
given,
|
|
316
|
+
shortName(font),
|
|
317
|
+
[(table.platformID, table.platEncID) for table in font["cmap"].tables],
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
if sys.platform == "win32":
|
|
321
|
+
prefered = (3, 1)
|
|
322
|
+
# should have prefered values for Linux and Mac as well...
|
|
323
|
+
for table in font["cmap"].tables:
|
|
324
|
+
if (table.platformID, table.platEncID) == prefered:
|
|
325
|
+
return prefered
|
|
326
|
+
# just retrieve the first table's values
|
|
327
|
+
for table in font["cmap"].tables:
|
|
328
|
+
return (table.platformID, table.platEncID)
|
|
329
|
+
raise ValueError(
|
|
330
|
+
"""There are no encoding tables within the font %r, likely a corrupt font-file"""
|
|
331
|
+
% (shortName(font),)
|
|
332
|
+
)
|
|
@@ -76,7 +76,7 @@ def linuxFontDirectories( ):
|
|
|
76
76
|
executable = '/usr/sbin/chkfontpath'
|
|
77
77
|
if os.path.isfile( executable ):
|
|
78
78
|
data = os.popen( executable ).readlines()
|
|
79
|
-
match = re.compile( '\d+: (.+)')
|
|
79
|
+
match = re.compile( r'\d+: (.+)')
|
|
80
80
|
set = []
|
|
81
81
|
for line in data:
|
|
82
82
|
result = match.match( line )
|
|
@@ -112,6 +112,15 @@ class Registry(object):
|
|
|
112
112
|
if filename in self.files and not force:
|
|
113
113
|
return self.specificFonts.get( self.files[filename] )
|
|
114
114
|
font = describe.openFont(filename)
|
|
115
|
+
if 'glyf' not in font:
|
|
116
|
+
# CFF / OpenType-CFF fonts have no 'glyf' table; ttfquery's glyph
|
|
117
|
+
# outline extraction is TrueType-only, so exclude them rather than
|
|
118
|
+
# crash later when their outlines are requested.
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"Unsupported font (no 'glyf' table, likely CFF/OpenType): %s" % (
|
|
121
|
+
filename,
|
|
122
|
+
)
|
|
123
|
+
)
|
|
115
124
|
try:
|
|
116
125
|
modifiers = describe.modifiers( font )
|
|
117
126
|
except (KeyError,AttributeError):
|
TTFQuery-2.0.0b1/PKG-INFO
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.1
|
|
2
|
-
Name: TTFQuery
|
|
3
|
-
Version: 2.0.0b1
|
|
4
|
-
Summary: FontTools-based package for querying system fonts
|
|
5
|
-
Home-page: http://ttfquery.sourceforge.net/
|
|
6
|
-
Author: Mike C. Fletcher
|
|
7
|
-
Author-email: mcfletch@users.sourceforge.net
|
|
8
|
-
License: BSD-style, see license.txt for details
|
|
9
|
-
Description-Content-Type: UNKNOWN
|
|
10
|
-
Description: FontTools-based package for querying system fonts
|
|
11
|
-
|
|
12
|
-
TTFQuery builds on the FontTools package to allow the Python programmer to accomplish a number of tasks:
|
|
13
|
-
|
|
14
|
-
* query the system to find installed fonts
|
|
15
|
-
* retrieve metadata about any TTF font file (even those not yet
|
|
16
|
-
installed)
|
|
17
|
-
o abstract family type
|
|
18
|
-
o proper font name
|
|
19
|
-
o glyph outlines
|
|
20
|
-
* build simple metadata registries for run-time font matching
|
|
21
|
-
|
|
22
|
-
With these functionalities, it is possible to readily
|
|
23
|
-
create OpenGL solid-text rendering libraries which
|
|
24
|
-
can accept abstract font-family names as font specifiers
|
|
25
|
-
and deliver platform-specific TTF files to match those libraries.
|
|
26
|
-
|
|
27
|
-
TTFQuery doesn't provide rendering services, but a sample
|
|
28
|
-
implementation can be found in the OpenGLContext project, from
|
|
29
|
-
which TTFQuery was refactored.
|
|
30
|
-
|
|
31
|
-
Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
|
|
32
|
-
Platform: Any
|
|
33
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
34
|
-
Classifier: Programming Language :: Python
|
|
35
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
36
|
-
Classifier: Intended Audience :: Developers
|
|
37
|
-
Classifier: Operating System :: OS Independent
|
|
38
|
-
Classifier: Topic :: Multimedia :: Graphics
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.1
|
|
2
|
-
Name: TTFQuery
|
|
3
|
-
Version: 2.0.0b1
|
|
4
|
-
Summary: FontTools-based package for querying system fonts
|
|
5
|
-
Home-page: http://ttfquery.sourceforge.net/
|
|
6
|
-
Author: Mike C. Fletcher
|
|
7
|
-
Author-email: mcfletch@users.sourceforge.net
|
|
8
|
-
License: BSD-style, see license.txt for details
|
|
9
|
-
Description-Content-Type: UNKNOWN
|
|
10
|
-
Description: FontTools-based package for querying system fonts
|
|
11
|
-
|
|
12
|
-
TTFQuery builds on the FontTools package to allow the Python programmer to accomplish a number of tasks:
|
|
13
|
-
|
|
14
|
-
* query the system to find installed fonts
|
|
15
|
-
* retrieve metadata about any TTF font file (even those not yet
|
|
16
|
-
installed)
|
|
17
|
-
o abstract family type
|
|
18
|
-
o proper font name
|
|
19
|
-
o glyph outlines
|
|
20
|
-
* build simple metadata registries for run-time font matching
|
|
21
|
-
|
|
22
|
-
With these functionalities, it is possible to readily
|
|
23
|
-
create OpenGL solid-text rendering libraries which
|
|
24
|
-
can accept abstract font-family names as font specifiers
|
|
25
|
-
and deliver platform-specific TTF files to match those libraries.
|
|
26
|
-
|
|
27
|
-
TTFQuery doesn't provide rendering services, but a sample
|
|
28
|
-
implementation can be found in the OpenGLContext project, from
|
|
29
|
-
which TTFQuery was refactored.
|
|
30
|
-
|
|
31
|
-
Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
|
|
32
|
-
Platform: Any
|
|
33
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
34
|
-
Classifier: Programming Language :: Python
|
|
35
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
36
|
-
Classifier: Intended Audience :: Developers
|
|
37
|
-
Classifier: Operating System :: OS Independent
|
|
38
|
-
Classifier: Topic :: Multimedia :: Graphics
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
"""Extract meta-data from a font-file to describe the font"""
|
|
2
|
-
from fontTools import ttLib
|
|
3
|
-
import sys
|
|
4
|
-
try:
|
|
5
|
-
unicode,long
|
|
6
|
-
except NameError:
|
|
7
|
-
unicode = str
|
|
8
|
-
long = int
|
|
9
|
-
try:
|
|
10
|
-
from OpenGLContext.debug.logs import text_log
|
|
11
|
-
except ImportError:
|
|
12
|
-
text_log = None
|
|
13
|
-
|
|
14
|
-
def openFont( filename ):
|
|
15
|
-
"""Get a new font object"""
|
|
16
|
-
if isinstance( filename, (bytes,unicode)):
|
|
17
|
-
filename = open( filename, 'rb')
|
|
18
|
-
return ttLib.TTFont(filename)
|
|
19
|
-
|
|
20
|
-
FONT_SPECIFIER_NAME_ID = 4
|
|
21
|
-
FONT_SPECIFIER_FAMILY_ID = 1
|
|
22
|
-
def shortName( font ):
|
|
23
|
-
"""Get the short name from the font's names table"""
|
|
24
|
-
name = ""
|
|
25
|
-
family = ""
|
|
26
|
-
for record in font['name'].names:
|
|
27
|
-
if record.nameID == FONT_SPECIFIER_NAME_ID and not name:
|
|
28
|
-
if b'\000' in record.string:
|
|
29
|
-
name = unicode(record.string, 'utf-16-be').encode('utf-8')
|
|
30
|
-
else:
|
|
31
|
-
name = record.string
|
|
32
|
-
elif record.nameID == FONT_SPECIFIER_FAMILY_ID and not family:
|
|
33
|
-
if b'\000' in record.string:
|
|
34
|
-
family = unicode(record.string, 'utf-16-be').encode('utf-8')
|
|
35
|
-
else:
|
|
36
|
-
family = record.string
|
|
37
|
-
if name and family:
|
|
38
|
-
break
|
|
39
|
-
return name, family
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
FAMILY_NAMES = {
|
|
43
|
-
0: ("ANY",{}),
|
|
44
|
-
1: ("SERIF-OLD", {
|
|
45
|
-
0: "ANY",
|
|
46
|
-
1: "ROUNDED-LEGIBILITY",
|
|
47
|
-
2: "GARALDE",
|
|
48
|
-
3: "VENETIAN",
|
|
49
|
-
4: "VENETIAN-MODIFIED",
|
|
50
|
-
5: "DUTCH-MODERN",
|
|
51
|
-
6: "DUTCH-TRADITIONAL",
|
|
52
|
-
7: "CONTEMPORARY",
|
|
53
|
-
8: "CALLIGRAPHIC",
|
|
54
|
-
15: "MISCELLANEOUS",
|
|
55
|
-
}),
|
|
56
|
-
2: ("SERIF-TRANSITIONAL", {
|
|
57
|
-
0: "ANY",
|
|
58
|
-
1: "DIRECT-LINE",
|
|
59
|
-
2: "SCRIPT",
|
|
60
|
-
15: "MISCELLANEOUS",
|
|
61
|
-
}),
|
|
62
|
-
3: ("SERIF", {
|
|
63
|
-
0: "ANY",
|
|
64
|
-
1: "ITALIAN",
|
|
65
|
-
2: "SCRIPT",
|
|
66
|
-
15: "MISCELLANEOUS",
|
|
67
|
-
}),
|
|
68
|
-
4: ("SERIF-CLARENDON",{
|
|
69
|
-
0: "ANY",
|
|
70
|
-
1: "CLARENDON",
|
|
71
|
-
2: "MODERN",
|
|
72
|
-
3: "TRADITIONAL",
|
|
73
|
-
4: "NEWSPAPER",
|
|
74
|
-
5: "STUB-SERIF",
|
|
75
|
-
6: "MONOTYPE",
|
|
76
|
-
7: "TYPEWRITER",
|
|
77
|
-
15: "MISCELLANEOUS",
|
|
78
|
-
}),
|
|
79
|
-
5: ("SERIF-SLAB",{
|
|
80
|
-
0: 'ANY',
|
|
81
|
-
1: 'MONOTONE',
|
|
82
|
-
2: 'HUMANIST',
|
|
83
|
-
3: 'GEOMETRIC',
|
|
84
|
-
4: 'SWISS',
|
|
85
|
-
5: 'TYPEWRITER',
|
|
86
|
-
15: 'MISCELLANEOUS',
|
|
87
|
-
}),
|
|
88
|
-
7: ("SERIF-FREEFORM",{
|
|
89
|
-
0: 'ANY',
|
|
90
|
-
1: 'MODERN',
|
|
91
|
-
15: 'MISCELLANEOUS',
|
|
92
|
-
}),
|
|
93
|
-
8: ("SANS",{
|
|
94
|
-
0: 'ANY',
|
|
95
|
-
1: 'GOTHIC-NEO-GROTESQUE-IBM',
|
|
96
|
-
2: 'HUMANIST',
|
|
97
|
-
3: 'ROUND-GEOMETRIC-LOW-X',
|
|
98
|
-
4: 'ROUND-GEOMETRIC-HIGH-X',
|
|
99
|
-
5: 'GOTHIC-NEO-GROTESQUE',
|
|
100
|
-
6: 'GOTHIC-NEO-GROTESQUE-MODIFIED',
|
|
101
|
-
9: 'GOTHIC-TYPEWRITER',
|
|
102
|
-
10: 'MATRIX',
|
|
103
|
-
15: 'MISCELLANEOUS',
|
|
104
|
-
}),
|
|
105
|
-
9: ("ORNAMENTAL",{
|
|
106
|
-
0: 'ANY',
|
|
107
|
-
1: 'ENGRAVER',
|
|
108
|
-
2: 'BLACK-LETTER',
|
|
109
|
-
3: 'DECORATIVE',
|
|
110
|
-
4: 'THREE-DIMENSIONAL',
|
|
111
|
-
15: 'MISCELLANEOUS',
|
|
112
|
-
}),
|
|
113
|
-
10:("SCRIPT",{
|
|
114
|
-
0: 'ANY',
|
|
115
|
-
1: 'UNCIAL',
|
|
116
|
-
2: 'BRUSH-JOINED',
|
|
117
|
-
3: 'FORMAL-JOINED',
|
|
118
|
-
4: 'MONOTONE-JOINED',
|
|
119
|
-
5: 'CALLIGRAPHIC',
|
|
120
|
-
6: 'BRUSH-UNJOINED',
|
|
121
|
-
7: 'FORMAL-UNJOINED',
|
|
122
|
-
8: 'MONOTONE-UNJOINED',
|
|
123
|
-
15: 'MISCELLANEOUS',
|
|
124
|
-
}),
|
|
125
|
-
12:("SYMBOL",{
|
|
126
|
-
0: 'ANY',
|
|
127
|
-
3: 'MIXED-SERIF',
|
|
128
|
-
6: 'OLDSTYLE-SERIF',
|
|
129
|
-
7: 'NEO-GROTESQUE-SANS',
|
|
130
|
-
15: 'MISCELLANEOUS',
|
|
131
|
-
}),
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
WEIGHT_NAMES = {
|
|
135
|
-
'thin':100,
|
|
136
|
-
'extralight':200,
|
|
137
|
-
'ultralight':200,
|
|
138
|
-
'light':300,
|
|
139
|
-
'normal':400,
|
|
140
|
-
'regular':400,
|
|
141
|
-
'plain':400,
|
|
142
|
-
'medium':500,
|
|
143
|
-
'semibold':600,
|
|
144
|
-
'demibold':600,
|
|
145
|
-
'bold':700,
|
|
146
|
-
'extrabold':800,
|
|
147
|
-
'ultrabold':800,
|
|
148
|
-
'black':900,
|
|
149
|
-
'heavy':900,
|
|
150
|
-
}
|
|
151
|
-
WEIGHT_NUMBERS = {}
|
|
152
|
-
for key,value in WEIGHT_NAMES.items():
|
|
153
|
-
WEIGHT_NUMBERS.setdefault(value,[]).append(key)
|
|
154
|
-
|
|
155
|
-
def weightNumber( name ):
|
|
156
|
-
"""Convert a string-name to a weight number compatible with this module"""
|
|
157
|
-
if isinstance( name, (bytes,unicode)):
|
|
158
|
-
name = name.lower()
|
|
159
|
-
name = name.replace( '-','').replace(' ','')
|
|
160
|
-
if name and name[-1] == '+':
|
|
161
|
-
name = name[:-1]
|
|
162
|
-
adjust = 50
|
|
163
|
-
elif name and name[-1] == '-':
|
|
164
|
-
name = name[:-1]
|
|
165
|
-
adjust = -50
|
|
166
|
-
else:
|
|
167
|
-
adjust = 0
|
|
168
|
-
return WEIGHT_NAMES[ name ]+ adjust
|
|
169
|
-
else:
|
|
170
|
-
return int(name) or 400 # for cases where number isn't properly specified
|
|
171
|
-
|
|
172
|
-
def weightName( number ):
|
|
173
|
-
"""Convert integer number to a human-readable weight-name"""
|
|
174
|
-
number = int(number) or 400
|
|
175
|
-
if number in WEIGHT_NUMBERS:
|
|
176
|
-
return WEIGHT_NUMBERS[number]
|
|
177
|
-
name = 'thin-'
|
|
178
|
-
for x in range(100,1000, 100):
|
|
179
|
-
if number >= x:
|
|
180
|
-
name = WEIGHT_NUMBERS[x]+'+'
|
|
181
|
-
return name
|
|
182
|
-
|
|
183
|
-
def family( font ):
|
|
184
|
-
"""Get the family (and sub-family) for a font"""
|
|
185
|
-
HIBYTE = 65280
|
|
186
|
-
LOBYTE = 255
|
|
187
|
-
familyID = (font['OS/2'].sFamilyClass&HIBYTE)>>8
|
|
188
|
-
subFamilyID = font['OS/2'].sFamilyClass&LOBYTE
|
|
189
|
-
return familyNames( familyID, subFamilyID )
|
|
190
|
-
def familyNames( familyID, subFamilyID=0 ):
|
|
191
|
-
"""Convert family integers to human-readable names"""
|
|
192
|
-
familyName, subFamilies = FAMILY_NAMES.get( familyID, ('RESERVED',None))
|
|
193
|
-
if familyName == 'RESERVED':
|
|
194
|
-
if text_log:
|
|
195
|
-
text_log.warn( 'Font has invalid (reserved) familyID: %s', familyID )
|
|
196
|
-
if subFamilies:
|
|
197
|
-
subFamily = subFamilies.get(subFamilyID, 'RESERVED')
|
|
198
|
-
else:
|
|
199
|
-
subFamily = 'ANY'
|
|
200
|
-
return (
|
|
201
|
-
familyName,
|
|
202
|
-
subFamily
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
def modifiers( font ):
|
|
206
|
-
"""Get weight and italic modifiers for a font
|
|
207
|
-
|
|
208
|
-
weight is taken from the OS/2 usWeightClass field
|
|
209
|
-
italic is taken from either OS/2 fsSelection or
|
|
210
|
-
head macStyle, if either indicates italics we
|
|
211
|
-
report italics
|
|
212
|
-
"""
|
|
213
|
-
return (
|
|
214
|
-
# weight as an integer
|
|
215
|
-
font['OS/2'].usWeightClass,
|
|
216
|
-
( # italic
|
|
217
|
-
font['OS/2'].fsSelection &1 or
|
|
218
|
-
font['head'].macStyle&2
|
|
219
|
-
),
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
def guessEncoding( font, given=None ):
|
|
223
|
-
"""Attempt to guess/retrieve an encoding from the font itself
|
|
224
|
-
|
|
225
|
-
Basically this will try to get the given encoding
|
|
226
|
-
(unless it is None).
|
|
227
|
-
|
|
228
|
-
If given is a single integer or a single-item tuple,
|
|
229
|
-
we will attempt scan looking for any table matching
|
|
230
|
-
given as the platform ID and returning the first sub
|
|
231
|
-
table.
|
|
232
|
-
|
|
233
|
-
If given is a two-value tuple, we will require
|
|
234
|
-
explicit matching, and raise errors if the encoding
|
|
235
|
-
cannot be retrieved.
|
|
236
|
-
|
|
237
|
-
if given is None, we will return the first encoding
|
|
238
|
-
in the font.
|
|
239
|
-
|
|
240
|
-
XXX This needs some work, particularly for non-win32
|
|
241
|
-
platforms, where there is no preference embodied
|
|
242
|
-
for the native encoding.
|
|
243
|
-
"""
|
|
244
|
-
if isinstance( given, tuple) and given:
|
|
245
|
-
if len(given) == 2:
|
|
246
|
-
if __debug__:
|
|
247
|
-
if text_log:
|
|
248
|
-
text_log.info(
|
|
249
|
-
"""Checking for explicitly required encoding %r""",
|
|
250
|
-
given
|
|
251
|
-
)
|
|
252
|
-
if not font['cmap'].getcmap( *given ):
|
|
253
|
-
raise ValueError(
|
|
254
|
-
"""The specified font encoding %r does not appear to be available within the font %r. Available encodings: %s"""% (
|
|
255
|
-
given, shortName(font),
|
|
256
|
-
[
|
|
257
|
-
(table.platformID,table.platEncID)
|
|
258
|
-
for table in font['cmap'].tables
|
|
259
|
-
],
|
|
260
|
-
)
|
|
261
|
-
)
|
|
262
|
-
return given
|
|
263
|
-
elif len(given) > 2:
|
|
264
|
-
raise TypeError("""Encoding must be None, a two-tuple, or an integer, got %r"""%(given,))
|
|
265
|
-
else:
|
|
266
|
-
# treat as a single integer, regardless of number of integer's
|
|
267
|
-
given = given[0]
|
|
268
|
-
if isinstance( given, (int, long)):
|
|
269
|
-
for table in font['cmap'].tables:
|
|
270
|
-
if table.platformID == given:
|
|
271
|
-
return (table.platformID, table.platEncID)
|
|
272
|
-
raise ValueError(
|
|
273
|
-
"""Could not find encoding with specified platformID==%s within the font %r. Available encodings: %s"""% (
|
|
274
|
-
given, shortName(font),
|
|
275
|
-
[
|
|
276
|
-
(table.platformID,table.platEncID)
|
|
277
|
-
for table in font['cmap'].tables
|
|
278
|
-
],
|
|
279
|
-
)
|
|
280
|
-
)
|
|
281
|
-
if sys.platform == 'win32':
|
|
282
|
-
prefered = (3,1)
|
|
283
|
-
# should have prefered values for Linux and Mac as well...
|
|
284
|
-
for table in font['cmap'].tables:
|
|
285
|
-
if (table.platformID, table.platEncID) == prefered:
|
|
286
|
-
return prefered
|
|
287
|
-
# just retrieve the first table's values
|
|
288
|
-
for table in font['cmap'].tables:
|
|
289
|
-
return (table.platformID, table.platEncID)
|
|
290
|
-
raise ValueError(
|
|
291
|
-
"""There are no encoding tables within the font %r, likely a corrupt font-file"""% (
|
|
292
|
-
shortName(font),
|
|
293
|
-
)
|
|
294
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|