TTFQuery 2.0.1a1__tar.gz → 2.1.0__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.
Files changed (32) hide show
  1. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/PKG-INFO +9 -3
  2. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/PKG-INFO +9 -3
  3. ttfquery-2.1.0/pyproject.toml +102 -0
  4. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/setup.py +4 -4
  5. ttfquery-2.1.0/tests/test_describe.py +56 -0
  6. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/tests/test_findsystem_linux.py +3 -3
  7. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/tests/test_findsystem_win32.py +4 -1
  8. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/tests/test_glyphquery.py +10 -8
  9. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/tests/test_registry.py +6 -5
  10. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/__init__.py +1 -1
  11. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/_scriptregistry.py +3 -3
  12. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/describe.py +33 -27
  13. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/findsystem.py +24 -21
  14. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/glyph.py +5 -5
  15. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/glyphquery.py +8 -8
  16. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/guessdescription.py +5 -3
  17. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/scriptregistry.py +3 -2
  18. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/ttffamily.py +1 -1
  19. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/ttffiles.py +23 -24
  20. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/ttfgroups.py +9 -6
  21. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/ttfquery/ttfmetadata.py +3 -3
  22. ttfquery-2.0.1a1/pyproject.toml +0 -55
  23. ttfquery-2.0.1a1/tests/test_describe.py +0 -20
  24. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/MANIFEST.in +0 -0
  25. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/README.md +0 -0
  26. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/SOURCES.txt +0 -0
  27. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/dependency_links.txt +0 -0
  28. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/entry_points.txt +0 -0
  29. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/requires.txt +0 -0
  30. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/TTFQuery.egg-info/top_level.txt +0 -0
  31. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/license.txt +0 -0
  32. {ttfquery-2.0.1a1 → ttfquery-2.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TTFQuery
3
- Version: 2.0.1a1
3
+ Version: 2.1.0
4
4
  Summary: FontTools-based package for querying system fonts
5
5
  Home-page: http://ttfquery.sourceforge.net/
6
6
  Author: Mike C. Fletcher
@@ -46,12 +46,18 @@ License: THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY
46
46
  Project-URL: Homepage, http://ttfquery.sourceforge.net/
47
47
  Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
48
48
  Platform: Any
49
- Classifier: Programming Language :: Python
49
+ Classifier: Programming Language :: Python :: 3
50
+ Classifier: Programming Language :: Python :: 3 :: Only
51
+ Classifier: Programming Language :: Python :: 3.9
52
+ Classifier: Programming Language :: Python :: 3.10
53
+ Classifier: Programming Language :: Python :: 3.11
54
+ Classifier: Programming Language :: Python :: 3.12
55
+ Classifier: Programming Language :: Python :: 3.13
50
56
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
51
57
  Classifier: Intended Audience :: Developers
52
58
  Classifier: Operating System :: OS Independent
53
59
  Classifier: Topic :: Multimedia :: Graphics
54
- Requires-Python: >=3.7
60
+ Requires-Python: >=3.9
55
61
  Description-Content-Type: text/markdown
56
62
  Requires-Dist: fonttools
57
63
  Requires-Dist: numpy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TTFQuery
3
- Version: 2.0.1a1
3
+ Version: 2.1.0
4
4
  Summary: FontTools-based package for querying system fonts
5
5
  Home-page: http://ttfquery.sourceforge.net/
6
6
  Author: Mike C. Fletcher
@@ -46,12 +46,18 @@ License: THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY
46
46
  Project-URL: Homepage, http://ttfquery.sourceforge.net/
47
47
  Keywords: fonttools,ttf,truetype,outline,font,curve,system fonts
48
48
  Platform: Any
49
- Classifier: Programming Language :: Python
49
+ Classifier: Programming Language :: Python :: 3
50
+ Classifier: Programming Language :: Python :: 3 :: Only
51
+ Classifier: Programming Language :: Python :: 3.9
52
+ Classifier: Programming Language :: Python :: 3.10
53
+ Classifier: Programming Language :: Python :: 3.11
54
+ Classifier: Programming Language :: Python :: 3.12
55
+ Classifier: Programming Language :: Python :: 3.13
50
56
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
51
57
  Classifier: Intended Audience :: Developers
52
58
  Classifier: Operating System :: OS Independent
53
59
  Classifier: Topic :: Multimedia :: Graphics
54
- Requires-Python: >=3.7
60
+ Requires-Python: >=3.9
55
61
  Description-Content-Type: text/markdown
56
62
  Requires-Dist: fonttools
57
63
  Requires-Dist: numpy
@@ -0,0 +1,102 @@
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.9"
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 :: 3",
30
+ "Programming Language :: Python :: 3 :: Only",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Topic :: Software Development :: Libraries :: Python Modules",
37
+ "Intended Audience :: Developers",
38
+ "Operating System :: OS Independent",
39
+ "Topic :: Multimedia :: Graphics",
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "http://ttfquery.sourceforge.net/"
44
+
45
+ [project.optional-dependencies]
46
+ test = [
47
+ "pytest",
48
+ "pytest-cov",
49
+ ]
50
+
51
+ [project.scripts]
52
+ ttffiles = "ttfquery.ttffiles:main"
53
+ ttffamily = "ttfquery.ttffamily:main"
54
+ ttfgroups = "ttfquery.ttfgroups:main"
55
+ ttfmetadata = "ttfquery.ttfmetadata:main"
56
+
57
+ [tool.setuptools]
58
+ packages = ["ttfquery"]
59
+
60
+ [tool.setuptools.dynamic]
61
+ version = { attr = "ttfquery.__version__" }
62
+
63
+ # uv decides whether its cached metadata for a source tree is still current
64
+ # from pyproject.toml, setup.py and setup.cfg alone. This project's version
65
+ # lives in ttfquery/__init__.py, so name that file too: without it a version
66
+ # bump leaves uv answering with the previous number, and a dependent requiring
67
+ # the new one resolves against PyPI rather than this checkout.
68
+ [tool.uv]
69
+ cache-keys = [{ file = "pyproject.toml" }, { file = "ttfquery/__init__.py" }]
70
+
71
+ # Named explicitly rather than left to ruff's default, which has widened
72
+ # between releases: a gate whose meaning moves when a tool is upgraded is not a
73
+ # gate. `E,W,F,B` is what the other pre-2010 projects here hold to.
74
+ [tool.ruff.lint]
75
+ select = ["E", "W", "F", "B"]
76
+ # `_name` as well as `_`: an underscore prefix is how Python says a name is
77
+ # bound but deliberately unused.
78
+ dummy-variable-rgx = "^(_[a-zA-Z0-9_]*|err)$"
79
+ ignore = [
80
+ 'E501', # long lines, kept readable by hand
81
+ ]
82
+
83
+ # The package's own declarations, checked as a consumer's checker reads them.
84
+ # `check_untyped_defs` rather than `disallow_untyped_defs`: this package is
85
+ # older than annotations, and its bodies being checked is the half that finds
86
+ # defects -- the other half is a job of its own, tracked as one.
87
+ [tool.mypy]
88
+ # Named here so that `mypy` with no arguments checks the package: the tox
89
+ # environment and a developer's bare `mypy` are then the same run.
90
+ files = ["ttfquery"]
91
+ python_version = "3.12"
92
+ check_untyped_defs = true
93
+ warn_unused_ignores = true
94
+ ignore_missing_imports = true
95
+
96
+ # `findsystem` reads the Windows font directories out of the registry, and
97
+ # `winreg` is a module Linux does not have. Checked as Windows sees it in a
98
+ # second run -- see tools/preflight.toml -- so this keeps the Linux run from
99
+ # reporting nine absent names as defects.
100
+ [[tool.mypy.overrides]]
101
+ module = "ttfquery.findsystem"
102
+ disable_error_code = ["attr-defined"]
@@ -5,7 +5,7 @@ Run:
5
5
  python setup.py install
6
6
  to install the package from the source archive.
7
7
  """
8
- import sys, os, string
8
+ import os
9
9
  from setuptools import setup
10
10
  HERE = os.path.dirname(__file__)
11
11
 
@@ -32,9 +32,9 @@ if __name__ == "__main__":
32
32
  'ttfquery':'ttfquery',
33
33
  },
34
34
  packages = [
35
- 'ttfquery',
35
+ 'ttfquery',
36
36
  ],
37
- install_requires = ['fonttools','numpy'],
37
+ install_requires = ['fonttools','numpy'],
38
38
  classifiers= [
39
39
  """Programming Language :: Python""",
40
40
  """Topic :: Software Development :: Libraries :: Python Modules""",
@@ -74,4 +74,4 @@ which TTFQuery was refactored.
74
74
  ]
75
75
  }
76
76
  )
77
-
77
+
@@ -0,0 +1,56 @@
1
+ from __future__ import print_function
2
+ from ttfquery import describe, findsystem
3
+ import unittest
4
+
5
+ class TestDescribe(unittest.TestCase):
6
+ def test_describe_system_fonts(self):
7
+ for fontfile in list(findsystem.findFonts())[:100]:
8
+ try:
9
+ font = describe.openFont(fontfile)
10
+ except Exception as err:
11
+ err.args += ('Error opening font', font)
12
+ raise
13
+ else:
14
+ short = describe.shortName( font )
15
+ assert short, "Null name for font %s"%(fontfile,)
16
+ family = describe.family( font )
17
+ assert len(family)==2, family
18
+ modifiers = describe.modifiers( font )
19
+ assert describe.weightName(modifiers[0])
20
+
21
+
22
+ class TestTheNameIsText:
23
+ """`shortName` answers what the font calls itself, as text.
24
+
25
+ A name record is bytes in one of two encodings, and answering those bytes
26
+ put `b'Open Sans'` into every message that named a font and left the font
27
+ registry decoding what this had just encoded.
28
+ """
29
+
30
+ def a_font(self):
31
+ from ttfquery import findsystem
32
+ for path in findsystem.findFonts():
33
+ try:
34
+ return describe.openFont(path)
35
+ except Exception:
36
+ continue
37
+ return None
38
+
39
+ def test_both_halves_are_strings(self):
40
+ font = self.a_font()
41
+ if font is None:
42
+ import pytest
43
+ pytest.skip('no readable font on this machine')
44
+ name, family = describe.shortName(font)
45
+ assert isinstance(name, str), repr(name)
46
+ assert isinstance(family, str), repr(family)
47
+
48
+ def test_a_two_byte_record_is_read_as_utf_16(self):
49
+ assert describe._recordText('Ångstrom'.encode('utf-16-be')) == \
50
+ 'Ångstrom'
51
+
52
+ def test_a_single_byte_record_is_read_as_latin_1(self):
53
+ assert describe._recordText(b'Open Sans') == 'Open Sans'
54
+
55
+ def test_text_is_left_as_it_is(self):
56
+ assert describe._recordText('Open Sans') == 'Open Sans'
@@ -1,9 +1,10 @@
1
1
  from __future__ import print_function
2
2
  from ttfquery import findsystem
3
3
  import pytest
4
- import sys, glob, unittest
4
+ import sys
5
+ import unittest
5
6
  pytestmark = pytest.mark.skipif(
6
- not sys.platform.startswith('linux'),
7
+ not sys.platform.startswith('linux'),
7
8
  reason='Linux tests'
8
9
  )
9
10
 
@@ -15,4 +16,3 @@ class TestLinux(unittest.TestCase):
15
16
  def test_fonts_from_registry(self):
16
17
  fonts = findsystem.findFonts()
17
18
  assert fonts, "No fonts were found in our default font-search paths"
18
-
@@ -1,7 +1,10 @@
1
1
  from __future__ import print_function
2
2
  from ttfquery import findsystem
3
3
  import pytest
4
- import sys, glob, unittest
4
+ import os
5
+ import sys
6
+ import glob
7
+ import unittest
5
8
  pytestmark = pytest.mark.skipif(not sys.platform == 'win32', reason='Win32 tests')
6
9
 
7
10
  class TestWin32(unittest.TestCase):
@@ -1,7 +1,9 @@
1
1
  from __future__ import print_function
2
2
  from ttfquery import ttffiles, describe, glyphquery, glyph
3
- import pytest
4
- import os, sys, glob, unittest, tempfile, shutil
3
+ import os
4
+ import unittest
5
+ import tempfile
6
+ import shutil
5
7
 
6
8
  class TestGlyphQuery(unittest.TestCase):
7
9
  def setUp(self):
@@ -17,15 +19,15 @@ class TestGlyphQuery(unittest.TestCase):
17
19
 
18
20
  def test_has_a_glyph(self):
19
21
  has,total = 0,0
20
- for name,metadata in self.registry.specificFonts.items():
22
+ for _name,metadata in self.registry.specificFonts.items():
21
23
  font = describe.openFont(metadata.file_name)
22
- height = glyphquery.charHeight(font)
23
- line = glyphquery.lineHeight(font)
24
- desc = glyphquery.charDescent(font)
24
+ glyphquery.charHeight(font)
25
+ glyphquery.lineHeight(font)
26
+ glyphquery.charDescent(font)
25
27
  if glyphquery.hasGlyph(font,'a'):
26
28
  glyphName = glyphquery.explicitGlyph(font,'a')
27
29
  if glyphName:
28
- width = glyphquery.width(font,glyphName)
30
+ glyphquery.width(font,glyphName)
29
31
  has += 1
30
32
 
31
33
  shape = glyph.Glyph(glyphName)
@@ -38,7 +40,7 @@ class TestGlyphQuery(unittest.TestCase):
38
40
  else:
39
41
  glyphName = glyphquery.glyphName(font,'a')
40
42
  assert glyphName, """Didn't get a placeholder for the 'a' character"""
41
- width = glyphquery.width(font,glyphName)
43
+ glyphquery.width(font,glyphName)
42
44
  total += 1
43
45
  assert total, "No specific fonts on this system?"
44
46
  assert has/float(total) > .2, "More than 4/5 of fonts on this system are missing a glyph for `a`? %s/%s"%(has,total)
@@ -1,7 +1,9 @@
1
1
  from __future__ import print_function
2
2
  from ttfquery import ttffiles, ttfgroups, ttfmetadata, ttffamily
3
- import pytest
4
- import os, sys, glob, unittest, tempfile, shutil
3
+ import os
4
+ import unittest
5
+ import tempfile
6
+ import shutil
5
7
 
6
8
  class TestRegistry(unittest.TestCase):
7
9
  def setUp(self):
@@ -30,10 +32,10 @@ class TestRegistry(unittest.TestCase):
30
32
  except KeyError:
31
33
  pass
32
34
  if font:
33
- break
35
+ break
34
36
  if not font:
35
37
  raise RuntimeError("Unable to find any of Arial/Helvetica/SANS")
36
-
38
+
37
39
  def test_ttfgroups(self):
38
40
  registry = self.registry
39
41
  table = ttfgroups.buildTable(registry)
@@ -56,4 +58,3 @@ class TestRegistry(unittest.TestCase):
56
58
  ttffamily.search(registry,family)
57
59
  for subfam in subfams.keys():
58
60
  ttffamily.search(registry,family,subfam)
59
-
@@ -1,2 +1,2 @@
1
1
  """Fonttools package for querying and sorting system fonts"""
2
- __version__ = "2.0.1a1"
2
+ __version__ = "2.1.0"
@@ -1,7 +1,7 @@
1
1
  """The singleton "registry" object (an instance of :class:`ttfquery.ttffiles.Registry`
2
- is the only member of note). This registry with be created in a file
3
- in the user's $HOME directory ``.font.cache`` if the $HOME environment variable
4
- is defined. Otherwise will be created in the ttfquery source code directory
2
+ is the only member of note). This registry with be created in a file
3
+ in the user's $HOME directory ``.font.cache`` if the $HOME environment variable
4
+ is defined. Otherwise will be created in the ttfquery source code directory
5
5
  (which is obviously not a very good solution).
6
6
 
7
7
  .. note::
@@ -1,16 +1,13 @@
1
1
  """Extract meta-data from a font-file to describe the font"""
2
- from fontTools import ttLib
2
+ import logging
3
3
  import sys
4
+ from typing import Dict, List
5
+
6
+ from fontTools import ttLib
4
7
 
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
8
+ unicode = str
9
+ long = int
10
+ log = logging.getLogger(__name__)
14
11
 
15
12
 
16
13
  def openFont(filename):
@@ -24,21 +21,33 @@ FONT_SPECIFIER_NAME_ID = 4
24
21
  FONT_SPECIFIER_FAMILY_ID = 1
25
22
 
26
23
 
24
+ def _recordText(value):
25
+ """A name record as text, whichever way the font stored it.
26
+
27
+ A record is bytes in one of two encodings and the font does not say which:
28
+ UTF-16BE where it carries the NUL bytes of one, and a single-byte encoding
29
+ otherwise -- Latin-1, which is what the Macintosh Roman a name table names
30
+ agrees with for the characters a font name uses.
31
+ """
32
+ if not isinstance(value, bytes):
33
+ return value
34
+ return (value.decode("utf-16-be") if b"\000" in value
35
+ else value.decode("latin-1"))
36
+
37
+
27
38
  def shortName(font):
28
- """Get the short name from the font's names table"""
39
+ """The font's own name and its family's, as text
40
+
41
+ ``('Open Sans Bold Italic', 'Open Sans')`` -- what the font calls itself,
42
+ and the family it belongs to.
43
+ """
29
44
  name = ""
30
45
  family = ""
31
46
  for record in font["name"].names:
32
47
  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
48
+ name = _recordText(record.string)
37
49
  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
50
+ family = _recordText(record.string)
42
51
  if name and family:
43
52
  break
44
53
  return name, family
@@ -183,7 +192,9 @@ WEIGHT_NAMES = {
183
192
  "black": 900,
184
193
  "heavy": 900,
185
194
  }
186
- WEIGHT_NUMBERS = {}
195
+ #: Every name for each weight, so a number reads back as the words a font
196
+ #: might have used for it.
197
+ WEIGHT_NUMBERS: Dict[int, List[str]] = {}
187
198
  for key, value in WEIGHT_NAMES.items():
188
199
  WEIGHT_NUMBERS.setdefault(value, []).append(key)
189
200
 
@@ -231,8 +242,7 @@ def familyNames(familyID, subFamilyID=0):
231
242
  """Convert family integers to human-readable names"""
232
243
  familyName, subFamilies = FAMILY_NAMES.get(familyID, ("RESERVED", None))
233
244
  if familyName == "RESERVED":
234
- if text_log:
235
- text_log.warn("Font has invalid (reserved) familyID: %s", familyID)
245
+ log.warning("Font has invalid (reserved) familyID: %s", familyID)
236
246
  if subFamilies:
237
247
  subFamily = subFamilies.get(subFamilyID, "RESERVED")
238
248
  else:
@@ -279,11 +289,7 @@ def guessEncoding(font, given=None):
279
289
  """
280
290
  if isinstance(given, tuple) and given:
281
291
  if len(given) == 2:
282
- if __debug__:
283
- if text_log:
284
- text_log.info(
285
- """Checking for explicitly required encoding %r""", given
286
- )
292
+ log.debug("Checking for explicitly required encoding %r", given)
287
293
  if not font["cmap"].getcmap(*given):
288
294
  raise ValueError(
289
295
  """The specified font encoding %r does not appear to be available within the font %r. Available encodings: %s"""
@@ -1,36 +1,37 @@
1
1
  """Find system fonts (only works on Linux and Win32 at the moment)"""
2
2
  from __future__ import print_function
3
- try:
4
- unicode
5
- except NameError:
6
- unicode = str
7
- import sys, os, glob, re
3
+ import sys
4
+ import os
5
+ import glob
6
+ import re
7
+
8
+ unicode = str
8
9
 
9
10
  def win32FontDirectory( ):
10
11
  """Get User-specific font directory on Win32"""
11
12
  try:
12
- import _winreg
13
+ import winreg
13
14
  except ImportError:
14
15
  return os.path.join(os.environ['WINDIR'], 'Fonts')
15
16
  else:
16
- k = _winreg.OpenKey(
17
- _winreg.HKEY_CURRENT_USER,
17
+ k = winreg.OpenKey(
18
+ winreg.HKEY_CURRENT_USER,
18
19
  r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
19
20
  )
20
21
  try:
21
22
  # should check that k is valid? How?
22
- return _winreg.QueryValueEx( k, "Fonts" )[0]
23
+ return winreg.QueryValueEx( k, "Fonts" )[0]
23
24
  finally:
24
- _winreg.CloseKey( k )
25
+ winreg.CloseKey( k )
25
26
 
26
27
  def win32InstalledFonts( fontDirectory = None ):
27
28
  """Get list of explicitly *installed* font names
28
-
29
+
29
30
  IFF there is no registry entry for installed fonts,
30
31
  likely due to a too-new windows install, then we'll
31
32
  just return all .ttf files in fontDirectory
32
33
  """
33
- import _winreg
34
+ import winreg
34
35
  if fontDirectory is None:
35
36
  fontDirectory = win32FontDirectory()
36
37
  k = None
@@ -40,32 +41,34 @@ def win32InstalledFonts( fontDirectory = None ):
40
41
  r"SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts",
41
42
  ):
42
43
  try:
43
- k = _winreg.OpenKey(
44
- _winreg.HKEY_LOCAL_MACHINE,
44
+ k = winreg.OpenKey(
45
+ winreg.HKEY_LOCAL_MACHINE,
45
46
  keyName
46
47
  )
47
48
  except OSError as err:
48
49
  pass
50
+ else:
51
+ break
49
52
  if not k:
50
53
  # couldn't open either WinNT or Win98 key???
51
54
  return glob.glob( os.path.join(fontDirectory, '*.ttf'))
52
55
  try:
53
56
  # should check that k is valid? How?
54
- for index in range( _winreg.QueryInfoKey(k)[1]):
55
- key,value,_ = _winreg.EnumValue( k, index )
57
+ for index in range( winreg.QueryInfoKey(k)[1]):
58
+ key,value,_ = winreg.EnumValue( k, index )
56
59
  if not os.path.dirname( value ):
57
60
  value = os.path.join( fontDirectory, value )
58
61
  value = os.path.abspath( value ).lower()
59
62
  if value[-4:] == '.ttf':
60
63
  items[ value ] = 1
61
- return items.keys()
64
+ return list(items.keys())
62
65
  finally:
63
- _winreg.CloseKey( k )
64
-
66
+ winreg.CloseKey( k )
67
+
65
68
 
66
69
  def linuxFontDirectories( ):
67
70
  """Get system font directories on Linux/Unix
68
-
71
+
69
72
  Uses /usr/sbin/chkfontpath to get the list
70
73
  of system-font directories, note that many
71
74
  of these will *not* be truetype font directories.
@@ -101,7 +104,7 @@ def linuxFontDirectories( ):
101
104
  "/System/Library/Fonts/",
102
105
  "System Folder:Fonts:",
103
106
  ]
104
-
107
+
105
108
  set = []
106
109
  for directory in directories:
107
110
  directory = directory = os.path.expanduser( os.path.expandvars(directory))
@@ -21,7 +21,7 @@ class Glyph( object):
21
21
  ]
22
22
  self.width = glyphquery.width( font, self.glyphName )
23
23
  self.height = glyphquery.lineHeight( font )
24
-
24
+
25
25
  def calculateContours( self, font ):
26
26
  """Given a character, determine contours to draw
27
27
 
@@ -85,7 +85,7 @@ class Glyph( object):
85
85
  list(flags[last:])+list(flags[last:])
86
86
  ) ))
87
87
  return contours
88
-
88
+
89
89
  def decomposeOutline( contour, steps=3 ):
90
90
  """Decompose a single TrueType contour to a line-loop
91
91
 
@@ -118,14 +118,14 @@ def decomposeOutline( contour, steps=3 ):
118
118
  set = contour[:]
119
119
  def on( record ):
120
120
  """Is this record on the contour?
121
-
121
+
122
122
  record = ((Ax,Ay),Af)
123
123
  """
124
124
  return record[-1] == 1
125
125
 
126
126
  def merge( first, second):
127
127
  """Merge two off-point records into an on-point record"""
128
- ((Ax,Ay),Af) = first
128
+ ((Ax,Ay),Af) = first
129
129
  ((Bx,By),Bf) = second
130
130
  return (((Ax+Bx)/2.0),((Ay+By))/2.0),1
131
131
  # create an expanded set so that all adjacent
@@ -152,7 +152,7 @@ def decomposeOutline( contour, steps=3 ):
152
152
  if len(expanded) == 2: #KH
153
153
  assert on(expanded[0]), """Expanded outline finishes off-curve""" #KH
154
154
  result.append( expanded[1][0] ) #KH
155
- del expanded[:1]
155
+ del expanded[:1]
156
156
  break
157
157
  assert on(expanded[2]), "Expanded outline doesn't have proper format!"
158
158
  points = integrateQuadratic( expanded[:3], steps = steps )
@@ -1,9 +1,9 @@
1
1
  """Glyph-specific queries on font-files"""
2
+ import logging
3
+
2
4
  from ttfquery import describe
3
- try:
4
- from OpenGLContext.debug.logs import text_log
5
- except ImportError:
6
- text_log = None
5
+
6
+ log = logging.getLogger(__name__)
7
7
 
8
8
  def hasGlyph( font, char, encoding=None ):
9
9
  """Check to see if font appears to have explicit glyph for char"""
@@ -35,8 +35,8 @@ def glyphName( font, char, encoding=None, warnOnFailure=1 ):
35
35
  glyfName = table.cmap.get( -1)
36
36
  if glyfName is None:
37
37
  glyfName = font['glyf'].glyphOrder[0]
38
- if text_log and warnOnFailure:
39
- text_log.warn(
38
+ if warnOnFailure:
39
+ log.warning(
40
40
  """Unable to find glyph name for %r, in %r using first glyph in table (%r)""",
41
41
  char,
42
42
  describe.shortName(font),
@@ -54,10 +54,10 @@ def width( font, glyphName ):
54
54
  """
55
55
  try:
56
56
  return font['hmtx'].metrics[ glyphName ][0]
57
- except KeyError:
57
+ except KeyError as err:
58
58
  raise ValueError( """Couldn't find glyph for glyphName %r"""%(
59
59
  glyphName,
60
- ))
60
+ )) from err
61
61
 
62
62
  def lineHeight( font ):
63
63
  """Get the base-line to base-line height for the font
@@ -16,6 +16,7 @@ XXX This is English-specific and entirely dependent on the
16
16
  there will be a considerable number of cases where the
17
17
  resulting name and flags will be incorrect.
18
18
  """
19
+ from typing import Any, Dict
19
20
  WEIGHTS = [
20
21
  'demibold', 'extrabold', 'semibold', 'ultrabold', 'bold',
21
22
  'ultralight', 'extralight', 'light',
@@ -48,12 +49,13 @@ WEIGHT_SYNONYMS = [
48
49
  WEIGHT_MODIFIERS = [
49
50
  'extra', 'ultra'
50
51
  ]
51
-
52
+
52
53
 
53
54
  ITALIC_INDICATORS = [
54
55
  'italic', 'ital','itali', 'it', 'bolditalic',
55
56
  ]
56
- SYSTEM_FONTS = {}
57
+ #: The fonts a scan of the system found, by the name it was asked for.
58
+ SYSTEM_FONTS: Dict[str, Any] = {}
57
59
 
58
60
  def interpretModifiers( name ):
59
61
  """Heuristic attempt to get weight and italic data from font-name
@@ -130,6 +132,6 @@ def get( name, style=None ):
130
132
  if key[0] == style[0]:
131
133
  return value
132
134
  # well, there's at least one font here, use it...
133
- for key, value in set.items():
135
+ for _key, value in set.items():
134
136
  return value
135
137
  return None
@@ -1,13 +1,14 @@
1
1
  """Provides a single font metadata cache from which multiple programs can run
2
2
 
3
- Note: on systems without an XDG_CACHE_HOME or a HOME environment variable
3
+ Note: on systems without an XDG_CACHE_HOME or a HOME environment variable
4
4
  this will store the cache in the *code* directory for ttfquery, which is
5
5
  obviously sub-optimal.
6
6
 
7
7
  `registryFile` -- calculated at load time as the location for the cache
8
8
  """
9
9
  from ttfquery import ttffiles
10
- import os, logging
10
+ import os
11
+ import logging
11
12
  log = logging.getLogger( __name__ )
12
13
 
13
14
  ### more robust registry-file location by John Hunter...
@@ -2,7 +2,7 @@
2
2
  """Query for font-members of a particular family"""
3
3
  from __future__ import print_function
4
4
  from ttfquery import ttffiles
5
- import sys, logging
5
+ import logging
6
6
  log = logging.getLogger( __name__ )
7
7
 
8
8
  def get_options():
@@ -1,18 +1,17 @@
1
1
  """Registry of available TrueType font files"""
2
2
  from ttfquery import describe, findsystem
3
- import traceback, os
3
+ import traceback
4
+ import os
4
5
  try:
5
- import cPickle as pickle
6
+ import cPickle as pickle
6
7
  except ImportError:
7
8
  import pickle
8
- try:
9
- unicode
10
- except NameError:
11
- unicode = str
12
- import logging
13
- log =logging.getLogger( __name__ )
9
+ import logging
14
10
  from collections import namedtuple
15
11
 
12
+ unicode = str
13
+ log = logging.getLogger( __name__ )
14
+
16
15
  class FontMetadata(namedtuple(
17
16
  'FontMetadata',
18
17
  (
@@ -26,10 +25,10 @@ class FontMetadata(namedtuple(
26
25
  """Stores specific font metadata
27
26
 
28
27
  A specific font is basically an actual font-file,
29
- that is, it is one of a family of fonts which make
28
+ that is, it is one of a family of fonts which make
30
29
  up the various "modified" versions of a font face.
31
30
 
32
- So `Ubuntu Light Italic` would be a specific font
31
+ So `Ubuntu Light Italic` would be a specific font
33
32
  that is a part of the font-face `Ubuntu` with the modifers
34
33
  for `Light` and `Italic` applied.
35
34
 
@@ -67,7 +66,7 @@ class Registry(object):
67
66
  specific font names
68
67
  shortFiles -- mapping from font filename basenames
69
68
  to font-file-lists
70
-
69
+
71
70
  DIRTY -- flag indicating whether the registry has
72
71
  had a new font registered (i.e. whether it should
73
72
  be saved out to disk).
@@ -136,7 +135,7 @@ class Registry(object):
136
135
  )
137
136
  return FontMetadata(*[
138
137
  (x.decode('utf-8') if isinstance(x,bytes) else x)
139
- for x in result
138
+ for x in result
140
139
  ])
141
140
  def register(
142
141
  self,
@@ -160,7 +159,7 @@ class Registry(object):
160
159
  if filename in self.files and not force:
161
160
  return self.specificFonts.get( self.files[filename] )
162
161
  self.dirty(1)
163
- if modifiers == None:
162
+ if modifiers is None:
164
163
  (filename, modifiers, specificName, fontName, familySpecifier) = self.metadata(filename, force = force)
165
164
  description = FontMetadata(filename, modifiers, specificName, fontName, familySpecifier)
166
165
  try:
@@ -181,13 +180,13 @@ class Registry(object):
181
180
 
182
181
  :param major: string description of major family
183
182
  :param minor: optional string description of minor family
184
-
183
+
185
184
  :rtype: list of font faces in the family
186
185
  """
187
186
  major = major.upper()
188
187
  if not minor:
189
188
  result = []
190
- for key,set in self.families.get(major,{}).items():
189
+ for _key,set in self.families.get(major,{}).items():
191
190
  result.extend( set.keys())
192
191
  return result
193
192
  minor = minor.upper()
@@ -215,7 +214,7 @@ class Registry(object):
215
214
  return result
216
215
  def fontForms( self, fontName ):
217
216
  """Retrieve the set of font-forms (weight,italics) available in a font
218
-
217
+
219
218
  :param fontName: general font name to search
220
219
  :rtype: list of two-tuples of (weight,italics) available in the font
221
220
  """
@@ -223,7 +222,7 @@ class Registry(object):
223
222
 
224
223
  def fontFile( self, specificName ):
225
224
  """Return the absolute path-name for a given specific font
226
-
225
+
227
226
  :param specificName: specific font name to search
228
227
  :rtype: str(file_name) of the FontMetadata instance
229
228
  """
@@ -235,7 +234,7 @@ class Registry(object):
235
234
 
236
235
  def matchName( self, name, single=0 ):
237
236
  """Try to find a general font based on a name
238
-
237
+
239
238
  :param name: name to use to try to find the font, can match on any of
240
239
  specific name, major family, or minor family
241
240
  :param single: return the first match only (not a dictionary of results)
@@ -381,10 +380,10 @@ def get_options():
381
380
 
382
381
 
383
382
  def main():
384
- import logging
383
+ import logging
385
384
  logging.basicConfig(level=logging.INFO)
386
385
  options = get_options().parse_args()
387
- registry = registry_for_options(options)
386
+ registry_for_options(options)
388
387
  return 0
389
388
 
390
389
  def registry_for_options(options):
@@ -395,14 +394,14 @@ def registry_for_options(options):
395
394
  else:
396
395
  registry = Registry()
397
396
  new = True
398
- scan = new
397
+ scan = new
399
398
  if getattr(options,'scan',False):
400
399
  log.info("Forcing rescan of directories")
401
400
  scan = True
402
401
  if scan:
403
- new,failed = registry.scan(
404
- options.directories or None,
405
- printErrors = False,
402
+ new,failed = registry.scan(
403
+ options.directories or None,
404
+ printErrors = False,
406
405
  force = 1
407
406
  )
408
407
  if options.registry and registry.DIRTY:
@@ -1,7 +1,9 @@
1
1
  """Demo script to print ordered set of system fonts"""
2
2
  from __future__ import print_function
3
- from ttfquery import describe, ttffiles
4
- import sys, logging
3
+
4
+ from typing import Any, Dict
5
+ from ttfquery import ttffiles
6
+ import logging
5
7
  log = logging.getLogger( __name__ )
6
8
 
7
9
  def buildTable( registry ):
@@ -11,9 +13,10 @@ def buildTable( registry ):
11
13
  otherwise the full set of system fonts provided
12
14
  by findsystem will be used.
13
15
  """
14
- table = {}
16
+ #: family -> font name -> modifier set -> (font name, file name)
17
+ table: Dict[Any, Dict[Any, Dict[Any, Any]]] = {}
15
18
  for major, minors in registry.families.items():
16
- for minor, fonts in minors.items():
19
+ for _minor, fonts in minors.items():
17
20
  for fontname in fonts.keys():
18
21
  table.setdefault(major,{}).setdefault(fontname, {})
19
22
  font = registry.fonts.get(fontname)
@@ -46,5 +49,5 @@ def run_report(table):
46
49
  fnts = sorted( table[fam].items() )
47
50
  for fnt,modset in fnts:
48
51
  mods = sorted(modset.keys())
49
- mods = ",".join([ '%s%s'%( w, ['','(I)'][i&1]) for (w,i) in mods])
50
- print(' ',fnt.ljust(32), '--', mods)
52
+ shown = ",".join([ '%s%s'%( w, ['','(I)'][i&1]) for (w,i) in mods])
53
+ print(' ',fnt.ljust(32), '--', shown)
@@ -3,7 +3,7 @@
3
3
  from __future__ import print_function
4
4
  #from ttfquery import describe
5
5
  from ttfquery import ttffiles
6
- import sys, logging
6
+ import logging
7
7
 
8
8
  def printMetaData( metadata ):
9
9
  print(' Specific Name:', metadata.specific_name)
@@ -35,7 +35,7 @@ def find_match(name, registry):
35
35
  specifics.sort()
36
36
  for specific in specifics:
37
37
  printMetaData( registry.metadata( registry.fontFile(specific) ))
38
-
39
-
38
+
39
+
40
40
  if __name__ == "__main__":
41
41
  main()
@@ -1,55 +0,0 @@
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__" }
@@ -1,20 +0,0 @@
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])
File without changes
File without changes
File without changes
File without changes