plotpress 0.23.2__py3-none-any.whl
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.
- plotpress/__init__.py +108 -0
- plotpress/_interactive.py +2849 -0
- plotpress/_spectral.py +154 -0
- plotpress/_version.py +1 -0
- plotpress/artists.py +1382 -0
- plotpress/axes.py +3221 -0
- plotpress/colors.py +498 -0
- plotpress/figure.py +3084 -0
- plotpress/fonts/__init__.py +51 -0
- plotpress/fonts/families.py +192 -0
- plotpress/fonts/installed.py +82 -0
- plotpress/fonts/metrics.py +265 -0
- plotpress/png.py +93 -0
- plotpress/polar.py +240 -0
- plotpress/primitives.py +335 -0
- plotpress/qt.py +427 -0
- plotpress/raster.py +1316 -0
- plotpress/style.py +91 -0
- plotpress/svg.py +2589 -0
- plotpress/ticker.py +212 -0
- plotpress/transform.py +85 -0
- plotpress/vega.py +1324 -0
- plotpress/vega_lite.py +1199 -0
- plotpress-0.23.2.dist-info/METADATA +378 -0
- plotpress-0.23.2.dist-info/RECORD +28 -0
- plotpress-0.23.2.dist-info/WHEEL +5 -0
- plotpress-0.23.2.dist-info/licenses/LICENSE +21 -0
- plotpress-0.23.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Bundled font metrics.
|
|
2
|
+
|
|
3
|
+
SVG-first rendering means we never rasterize glyphs -- the viewer's renderer
|
|
4
|
+
does that from the ``<text>`` elements we emit. We only need *metrics* (glyph
|
|
5
|
+
advance widths) to size the canvas and align tick/axis labels. Bundled width
|
|
6
|
+
tables keep layout deterministic across machines without a font-file
|
|
7
|
+
dependency.
|
|
8
|
+
|
|
9
|
+
``text_width`` measures the base-14 metric families -- Helvetica, Times and
|
|
10
|
+
Courier, each in regular / bold / italic / bold-italic -- plus DejaVu Sans.
|
|
11
|
+
That covers the metric-compatible clones too: Arial and Liberation Sans are
|
|
12
|
+
Helvetica, Liberation Serif and Tinos are Times, Liberation Mono and Cousine
|
|
13
|
+
are Courier.
|
|
14
|
+
|
|
15
|
+
Known limitation
|
|
16
|
+
----------------
|
|
17
|
+
Because layout happens before anything draws the glyphs, plotpress has to
|
|
18
|
+
*predict* how wide text will be, from the bundled tables only:
|
|
19
|
+
|
|
20
|
+
* **Families outside those groups are measured as Helvetica.** Verdana, Tahoma,
|
|
21
|
+
Arial Black and Arial Narrow have proprietary metrics that match nothing
|
|
22
|
+
bundled here, so they still render but their legend boxes and axis margins are
|
|
23
|
+
sized for Helvetica.
|
|
24
|
+
* **Small sizes quantize.** Renderers round glyph advances to whole pixels, so
|
|
25
|
+
even a perfectly matched face drifts a few tenths of a percent at tick-label
|
|
26
|
+
sizes. This is inherent to laying out text you do not rasterize; it shows up
|
|
27
|
+
as a little slack in margins, never as overlap.
|
|
28
|
+
|
|
29
|
+
Lifting the first limitation for arbitrary families means measuring real font
|
|
30
|
+
files, which makes layout depend on which fonts happen to be installed -- the
|
|
31
|
+
very thing these tables exist to avoid. It is therefore offered as an opt-in
|
|
32
|
+
rather than refused: see :mod:`plotpress.fonts.installed` and
|
|
33
|
+
``Style(measure_installed_fonts=True)``.
|
|
34
|
+
|
|
35
|
+
Module layout
|
|
36
|
+
-------------
|
|
37
|
+
``families``
|
|
38
|
+
The single registry of font families: which width table measures a CSS
|
|
39
|
+
stack, and which files should draw it. Declared together so layout and the
|
|
40
|
+
raster backend cannot disagree about what a family is.
|
|
41
|
+
``metrics``
|
|
42
|
+
The bundled width tables and :func:`text_width`. Generated -- see
|
|
43
|
+
``tools/gen_font_metrics.py`` for the sources.
|
|
44
|
+
``installed``
|
|
45
|
+
Opt-in measurement of the real font files on this machine.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from .families import font_files, resolve_family
|
|
49
|
+
from .metrics import text_width
|
|
50
|
+
|
|
51
|
+
__all__ = ["font_files", "resolve_family", "text_width"]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""The one place that knows about font families.
|
|
2
|
+
|
|
3
|
+
Two consumers need to answer questions about a CSS font stack, and they must
|
|
4
|
+
not disagree:
|
|
5
|
+
|
|
6
|
+
* **layout** (``fonts.metrics``) asks *which width table describes this stack*,
|
|
7
|
+
because it sizes margins and legend boxes before anything is drawn;
|
|
8
|
+
* **the raster backend** asks *which font file should draw these glyphs*,
|
|
9
|
+
because PNG export rasterizes them itself.
|
|
10
|
+
|
|
11
|
+
Splitting those tables across two modules is how you get glyphs drawn from a
|
|
12
|
+
face that does not match the space reserved for them. So a family is declared
|
|
13
|
+
once here, with both answers attached, and the fallback chain is derived from
|
|
14
|
+
the declaration rather than written out by hand.
|
|
15
|
+
|
|
16
|
+
The derivation rule: a stack's candidate files always end with faces belonging
|
|
17
|
+
to its own *metric* family. That is what keeps the fallback safe. DejaVu Serif
|
|
18
|
+
is 29% wider than Times and DejaVu Sans is 14% wider than Helvetica, so putting
|
|
19
|
+
either at the end of a chain whose layout was computed from Times or Helvetica
|
|
20
|
+
metrics silently overflows every box on a machine that has no better face
|
|
21
|
+
installed.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from collections import namedtuple
|
|
27
|
+
|
|
28
|
+
# ``metrics`` is the key into the bundled width tables; ``regular``/``bold`` are
|
|
29
|
+
# candidate file names for the raster backend, best first.
|
|
30
|
+
_Family = namedtuple("_Family", "metrics regular bold")
|
|
31
|
+
|
|
32
|
+
# Faces belonging to each metric family, in rough platform order (macOS,
|
|
33
|
+
# Windows, Linux). Every one of these agrees with the width table it is filed
|
|
34
|
+
# under, which is what makes them safe fallbacks.
|
|
35
|
+
HELVETICA_FILES = (
|
|
36
|
+
"Helvetica.ttc", "Helvetica.ttf",
|
|
37
|
+
"arial.ttf", "Arial.ttf",
|
|
38
|
+
"LiberationSans-Regular.ttf",
|
|
39
|
+
"Arimo-Regular.ttf",
|
|
40
|
+
)
|
|
41
|
+
HELVETICA_FILES_BOLD = (
|
|
42
|
+
"Helvetica-Bold.ttf",
|
|
43
|
+
"arialbd.ttf", "Arial Bold.ttf",
|
|
44
|
+
"LiberationSans-Bold.ttf",
|
|
45
|
+
"Arimo-Bold.ttf",
|
|
46
|
+
)
|
|
47
|
+
TIMES_FILES = (
|
|
48
|
+
"Times New Roman.ttf", "times.ttf",
|
|
49
|
+
"LiberationSerif-Regular.ttf",
|
|
50
|
+
"Tinos-Regular.ttf",
|
|
51
|
+
)
|
|
52
|
+
TIMES_FILES_BOLD = (
|
|
53
|
+
"Times New Roman Bold.ttf", "timesbd.ttf",
|
|
54
|
+
"LiberationSerif-Bold.ttf",
|
|
55
|
+
"Tinos-Bold.ttf",
|
|
56
|
+
)
|
|
57
|
+
# DejaVu Sans Mono earns its place here: it advances 602/1000 em against
|
|
58
|
+
# Courier's flat 600, so it is Courier-metric to within a third of a percent.
|
|
59
|
+
# Its sans and serif siblings are not, and are deliberately absent elsewhere.
|
|
60
|
+
COURIER_FILES = (
|
|
61
|
+
"Courier New.ttf", "cour.ttf",
|
|
62
|
+
"LiberationMono-Regular.ttf",
|
|
63
|
+
"Cousine-Regular.ttf",
|
|
64
|
+
"DejaVuSansMono.ttf",
|
|
65
|
+
)
|
|
66
|
+
COURIER_FILES_BOLD = (
|
|
67
|
+
"Courier New Bold.ttf", "courbd.ttf",
|
|
68
|
+
"LiberationMono-Bold.ttf",
|
|
69
|
+
"Cousine-Bold.ttf",
|
|
70
|
+
"DejaVuSansMono-Bold.ttf",
|
|
71
|
+
)
|
|
72
|
+
DEJAVU_FILES = ("DejaVuSans.ttf",)
|
|
73
|
+
DEJAVU_FILES_BOLD = ("DejaVuSans-Bold.ttf",)
|
|
74
|
+
|
|
75
|
+
_METRIC_FILES = {
|
|
76
|
+
"helvetica": (HELVETICA_FILES, HELVETICA_FILES_BOLD),
|
|
77
|
+
"times": (TIMES_FILES, TIMES_FILES_BOLD),
|
|
78
|
+
"courier": (COURIER_FILES, COURIER_FILES_BOLD),
|
|
79
|
+
"dejavu sans": (DEJAVU_FILES, DEJAVU_FILES_BOLD),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
DEFAULT_METRIC_FAMILY = "helvetica"
|
|
83
|
+
|
|
84
|
+
_FAMILIES = {
|
|
85
|
+
# -- Helvetica and its metric-compatible clones -----------------------
|
|
86
|
+
"helvetica": _Family("helvetica", HELVETICA_FILES, HELVETICA_FILES_BOLD),
|
|
87
|
+
"helvetica neue": _Family("helvetica", ("HelveticaNeue.ttc",),
|
|
88
|
+
("HelveticaNeue-Bold.ttf",)),
|
|
89
|
+
"arial": _Family("helvetica", ("arial.ttf", "Arial.ttf"),
|
|
90
|
+
("arialbd.ttf", "Arial Bold.ttf")),
|
|
91
|
+
"liberation sans": _Family("helvetica", ("LiberationSans-Regular.ttf",),
|
|
92
|
+
("LiberationSans-Bold.ttf",)),
|
|
93
|
+
"arimo": _Family("helvetica", ("Arimo-Regular.ttf",), ("Arimo-Bold.ttf",)),
|
|
94
|
+
"nimbus sans": _Family("helvetica", ("NimbusSans-Regular.otf",),
|
|
95
|
+
("NimbusSans-Bold.otf",)),
|
|
96
|
+
"sans-serif": _Family("helvetica", HELVETICA_FILES, HELVETICA_FILES_BOLD),
|
|
97
|
+
|
|
98
|
+
# -- Times and its metric-compatible clones ---------------------------
|
|
99
|
+
"times": _Family("times", TIMES_FILES, TIMES_FILES_BOLD),
|
|
100
|
+
"times new roman": _Family("times", ("Times New Roman.ttf", "times.ttf"),
|
|
101
|
+
("Times New Roman Bold.ttf", "timesbd.ttf")),
|
|
102
|
+
"liberation serif": _Family("times", ("LiberationSerif-Regular.ttf",),
|
|
103
|
+
("LiberationSerif-Bold.ttf",)),
|
|
104
|
+
"tinos": _Family("times", ("Tinos-Regular.ttf",), ("Tinos-Bold.ttf",)),
|
|
105
|
+
"nimbus roman": _Family("times", ("NimbusRoman-Regular.otf",),
|
|
106
|
+
("NimbusRoman-Bold.otf",)),
|
|
107
|
+
"serif": _Family("times", TIMES_FILES, TIMES_FILES_BOLD),
|
|
108
|
+
|
|
109
|
+
# -- Courier and its metric-compatible clones -------------------------
|
|
110
|
+
"courier": _Family("courier", COURIER_FILES, COURIER_FILES_BOLD),
|
|
111
|
+
"courier new": _Family("courier", ("Courier New.ttf", "cour.ttf"),
|
|
112
|
+
("Courier New Bold.ttf", "courbd.ttf")),
|
|
113
|
+
"liberation mono": _Family("courier", ("LiberationMono-Regular.ttf",),
|
|
114
|
+
("LiberationMono-Bold.ttf",)),
|
|
115
|
+
"cousine": _Family("courier", ("Cousine-Regular.ttf",), ("Cousine-Bold.ttf",)),
|
|
116
|
+
"nimbus mono": _Family("courier", ("NimbusMonoPS-Regular.otf",),
|
|
117
|
+
("NimbusMonoPS-Bold.otf",)),
|
|
118
|
+
"monospace": _Family("courier", COURIER_FILES, COURIER_FILES_BOLD),
|
|
119
|
+
|
|
120
|
+
# -- DejaVu -----------------------------------------------------------
|
|
121
|
+
"dejavu sans": _Family("dejavu sans", DEJAVU_FILES, DEJAVU_FILES_BOLD),
|
|
122
|
+
|
|
123
|
+
# -- Declared, but not measurable -------------------------------------
|
|
124
|
+
# Proprietary metrics that match no bundled table, so layout measures them
|
|
125
|
+
# as Helvetica -- see the limitations docs. They are listed anyway so PNG
|
|
126
|
+
# export can still draw their real glyphs where the system has them; the
|
|
127
|
+
# derivation rule then tails the chain with Helvetica-metric faces rather
|
|
128
|
+
# than with whatever happens to look similar.
|
|
129
|
+
"verdana": _Family("helvetica", ("verdana.ttf", "Verdana.ttf"),
|
|
130
|
+
("verdanab.ttf", "Verdana Bold.ttf")),
|
|
131
|
+
"tahoma": _Family("helvetica", ("tahoma.ttf", "Tahoma.ttf"),
|
|
132
|
+
("tahomabd.ttf", "Tahoma Bold.ttf")),
|
|
133
|
+
"arial narrow": _Family("helvetica", ("ARIALN.TTF", "Arial Narrow.ttf"),
|
|
134
|
+
("ARIALNB.TTF", "Arial Narrow Bold.ttf")),
|
|
135
|
+
"arial black": _Family("helvetica", ("ariblk.ttf", "Arial Black.ttf"), ()),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _names(family):
|
|
140
|
+
"""The stack, lowercased and unquoted, in order."""
|
|
141
|
+
return [n.strip().strip("'\"").lower()
|
|
142
|
+
for n in (family or "").split(",") if n.strip()]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def resolve_family(family):
|
|
146
|
+
"""The metric family describing the first recognized name in a CSS stack.
|
|
147
|
+
|
|
148
|
+
Unknown names are skipped, so a stack naming an unmeasurable face first
|
|
149
|
+
still finds a measurable fallback behind it. A stack with no recognized
|
|
150
|
+
name at all resolves to Helvetica, which is what layout has always assumed.
|
|
151
|
+
"""
|
|
152
|
+
for name in _names(family):
|
|
153
|
+
entry = _FAMILIES.get(name)
|
|
154
|
+
if entry is not None:
|
|
155
|
+
return entry.metrics
|
|
156
|
+
return DEFAULT_METRIC_FAMILY
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def font_files(family, bold=False):
|
|
160
|
+
"""Candidate font files for a CSS stack, best first.
|
|
161
|
+
|
|
162
|
+
The chain is: the faces each named family actually is, then the faces of
|
|
163
|
+
the metric family layout measured it with, then -- for a bold request --
|
|
164
|
+
the same chain at regular weight, since the right glyphs at the wrong
|
|
165
|
+
weight beat the wrong glyphs entirely.
|
|
166
|
+
|
|
167
|
+
It ends with the Helvetica faces as a last resort. For a non-Helvetica
|
|
168
|
+
metric family that is a compromise rather than a match: it keeps a machine
|
|
169
|
+
with no serif or mono face installed rendering, but Helvetica glyphs are
|
|
170
|
+
wider than the Times metrics the layout used, so text can overflow. It
|
|
171
|
+
still beats Pillow's built-in bitmap default, which is both wider and
|
|
172
|
+
unscalable.
|
|
173
|
+
"""
|
|
174
|
+
out = []
|
|
175
|
+
|
|
176
|
+
def add(names):
|
|
177
|
+
for f in names:
|
|
178
|
+
if f not in out:
|
|
179
|
+
out.append(f)
|
|
180
|
+
|
|
181
|
+
for name in _names(family):
|
|
182
|
+
entry = _FAMILIES.get(name)
|
|
183
|
+
if entry is not None:
|
|
184
|
+
add(entry.bold if bold else entry.regular)
|
|
185
|
+
|
|
186
|
+
regular_files, bold_files = _METRIC_FILES[resolve_family(family)]
|
|
187
|
+
add(bold_files if bold else regular_files)
|
|
188
|
+
if bold:
|
|
189
|
+
add(font_files(family, bold=False))
|
|
190
|
+
add(HELVETICA_FILES_BOLD if bold else ())
|
|
191
|
+
add(HELVETICA_FILES)
|
|
192
|
+
return out
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Opt-in measurement of the fonts actually installed on this machine.
|
|
2
|
+
|
|
3
|
+
Off by default, and deliberately so. plotpress's bundled width tables exist to
|
|
4
|
+
make layout *deterministic*: the same script produces the same margins on every
|
|
5
|
+
machine, because it never asks the machine anything. Turning this on trades that
|
|
6
|
+
guarantee for fidelity, and it is a real trade -- a figure laid out here may not
|
|
7
|
+
match one laid out on a colleague's box, or on CI.
|
|
8
|
+
|
|
9
|
+
Turn it on when the fidelity is worth more than the reproducibility: you are
|
|
10
|
+
setting ``Style.font_family`` to a face plotpress cannot measure (Verdana,
|
|
11
|
+
Tahoma, Arial Black, Arial Narrow) and you would rather have correct margins on
|
|
12
|
+
your own machine than portable ones.
|
|
13
|
+
|
|
14
|
+
fig, ax = plotpress.subplots(
|
|
15
|
+
style=plotpress.Style(font_family="Verdana, sans-serif",
|
|
16
|
+
measure_installed_fonts=True))
|
|
17
|
+
|
|
18
|
+
Implementation note: this needs no new dependency. Pillow is already required
|
|
19
|
+
for PNG export, it already resolves a bare font file name against the system
|
|
20
|
+
font directories, and it already measures glyph advances -- the same machinery
|
|
21
|
+
the raster backend draws with. Reusing it is what keeps layout and PNG output
|
|
22
|
+
agreeing about what a face is: both go through
|
|
23
|
+
:func:`plotpress.fonts.families.font_files`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
# Measured once at a large em square and scaled down, rather than at each label's
|
|
29
|
+
# real size: advances are linear in size, so one probe serves every size, and a
|
|
30
|
+
# big probe makes per-glyph integer rounding negligible.
|
|
31
|
+
_PROBE_EM = 1000
|
|
32
|
+
|
|
33
|
+
_ASCII = [chr(c) for c in range(32, 127)]
|
|
34
|
+
|
|
35
|
+
_table_cache = {}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def installed_table(family, bold=False, italic=False):
|
|
39
|
+
"""``{char: advance per 1000 em}`` from a real font file, or ``None``.
|
|
40
|
+
|
|
41
|
+
Returns ``None`` -- meaning "fall back to the bundled tables" -- when no
|
|
42
|
+
candidate file resolves, or when Pillow was built without FreeType and so
|
|
43
|
+
cannot open a scalable face at all.
|
|
44
|
+
|
|
45
|
+
``italic`` is accepted for signature parity with the bundled path but does
|
|
46
|
+
not select a different file: the family registry tracks weight, not slant,
|
|
47
|
+
since nothing in the default styling draws italic.
|
|
48
|
+
"""
|
|
49
|
+
key = (family, bool(bold))
|
|
50
|
+
if key in _table_cache:
|
|
51
|
+
return _table_cache[key]
|
|
52
|
+
|
|
53
|
+
table = _measure(family, bool(bold))
|
|
54
|
+
_table_cache[key] = table
|
|
55
|
+
return table
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _measure(family, bold):
|
|
59
|
+
try:
|
|
60
|
+
from PIL import ImageFont
|
|
61
|
+
except ImportError: # pragma: no cover - Pillow is required
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
from .families import font_files
|
|
65
|
+
|
|
66
|
+
for name in font_files(family, bold):
|
|
67
|
+
try:
|
|
68
|
+
font = ImageFont.truetype(name, _PROBE_EM)
|
|
69
|
+
except (OSError, ImportError):
|
|
70
|
+
# Not installed here, or this Pillow has no FreeType. Either way the
|
|
71
|
+
# next candidate might still work.
|
|
72
|
+
continue
|
|
73
|
+
try:
|
|
74
|
+
return {ch: font.getlength(ch) for ch in _ASCII}
|
|
75
|
+
except (OSError, ValueError): # pragma: no cover - malformed face
|
|
76
|
+
continue
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def clear_cache():
|
|
81
|
+
"""Forget measured faces. Only useful in tests, or if fonts change on disk."""
|
|
82
|
+
_table_cache.clear()
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Advance-width tables for the fonts plotpress can measure.
|
|
2
|
+
|
|
3
|
+
GENERATED by ``tools/gen_font_metrics.py`` -- do not edit by hand.
|
|
4
|
+
|
|
5
|
+
Only the metric matters for layout: we place ``<text>`` and let the renderer
|
|
6
|
+
draw glyphs. ``text_width`` estimates a string's rendered width so labels can be
|
|
7
|
+
centered / right-aligned and margins sized.
|
|
8
|
+
|
|
9
|
+
Widths are in 1/1000 em. Sources are the URW base-14 clones (metric-compatible
|
|
10
|
+
with Adobe Helvetica / Times / Courier) and the DejaVu Sans TTFs; see the
|
|
11
|
+
generator for why they are read by glyph name rather than by character code.
|
|
12
|
+
|
|
13
|
+
Which table describes a given CSS font stack is decided in ``families.py``,
|
|
14
|
+
alongside which files should draw it -- one declaration, so layout and the
|
|
15
|
+
raster backend cannot disagree.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from .families import DEFAULT_METRIC_FAMILY, resolve_family
|
|
21
|
+
|
|
22
|
+
_DEJAVU_SANS = {
|
|
23
|
+
' ': 318, '!': 401, '"': 460, '#': 838, '$': 636, '%': 950, '&': 780, "'": 275,
|
|
24
|
+
'(': 390, ')': 390, '*': 500, '+': 838, ',': 318, '-': 361, '.': 318, '/': 337,
|
|
25
|
+
'0': 636, '1': 636, '2': 636, '3': 636, '4': 636, '5': 636, '6': 636, '7': 636,
|
|
26
|
+
'8': 636, '9': 636, ':': 337, ';': 337, '<': 838, '=': 838, '>': 838, '?': 531,
|
|
27
|
+
'@': 1000, 'A': 684, 'B': 686, 'C': 698, 'D': 770, 'E': 632, 'F': 575, 'G': 775,
|
|
28
|
+
'H': 752, 'I': 295, 'J': 295, 'K': 656, 'L': 557, 'M': 863, 'N': 748, 'O': 787,
|
|
29
|
+
'P': 603, 'Q': 787, 'R': 695, 'S': 635, 'T': 611, 'U': 732, 'V': 684, 'W': 989,
|
|
30
|
+
'X': 685, 'Y': 611, 'Z': 685, '[': 390, '\\': 337, ']': 390, '^': 838, '_': 500,
|
|
31
|
+
'`': 500, 'a': 613, 'b': 635, 'c': 550, 'd': 635, 'e': 615, 'f': 352, 'g': 635,
|
|
32
|
+
'h': 634, 'i': 278, 'j': 278, 'k': 579, 'l': 278, 'm': 974, 'n': 634, 'o': 612,
|
|
33
|
+
'p': 635, 'q': 635, 'r': 411, 's': 521, 't': 392, 'u': 634, 'v': 592, 'w': 818,
|
|
34
|
+
'x': 592, 'y': 592, 'z': 525, '{': 636, '|': 337, '}': 636, '~': 838,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_DEJAVU_SANS_ITALIC = {
|
|
38
|
+
' ': 318, '!': 401, '"': 460, '#': 838, '$': 636, '%': 950, '&': 780, "'": 275,
|
|
39
|
+
'(': 390, ')': 390, '*': 500, '+': 838, ',': 318, '-': 361, '.': 318, '/': 337,
|
|
40
|
+
'0': 636, '1': 636, '2': 636, '3': 636, '4': 636, '5': 636, '6': 636, '7': 636,
|
|
41
|
+
'8': 636, '9': 636, ':': 337, ';': 337, '<': 838, '=': 838, '>': 838, '?': 531,
|
|
42
|
+
'@': 1000, 'A': 684, 'B': 686, 'C': 698, 'D': 770, 'E': 632, 'F': 575, 'G': 775,
|
|
43
|
+
'H': 752, 'I': 295, 'J': 295, 'K': 656, 'L': 557, 'M': 863, 'N': 748, 'O': 787,
|
|
44
|
+
'P': 603, 'Q': 787, 'R': 695, 'S': 635, 'T': 611, 'U': 732, 'V': 684, 'W': 989,
|
|
45
|
+
'X': 685, 'Y': 611, 'Z': 685, '[': 390, '\\': 337, ']': 390, '^': 838, '_': 500,
|
|
46
|
+
'`': 500, 'a': 613, 'b': 635, 'c': 550, 'd': 635, 'e': 615, 'f': 352, 'g': 635,
|
|
47
|
+
'h': 634, 'i': 278, 'j': 278, 'k': 579, 'l': 278, 'm': 974, 'n': 634, 'o': 612,
|
|
48
|
+
'p': 635, 'q': 635, 'r': 411, 's': 521, 't': 392, 'u': 634, 'v': 592, 'w': 818,
|
|
49
|
+
'x': 592, 'y': 592, 'z': 525, '{': 636, '|': 337, '}': 636, '~': 838,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_DEJAVU_SANS_BOLD = {
|
|
53
|
+
' ': 348, '!': 456, '"': 521, '#': 838, '$': 696, '%': 1002, '&': 872, "'": 306,
|
|
54
|
+
'(': 457, ')': 457, '*': 523, '+': 838, ',': 380, '-': 415, '.': 380, '/': 365,
|
|
55
|
+
'0': 696, '1': 696, '2': 696, '3': 696, '4': 696, '5': 696, '6': 696, '7': 696,
|
|
56
|
+
'8': 696, '9': 696, ':': 400, ';': 400, '<': 838, '=': 838, '>': 838, '?': 580,
|
|
57
|
+
'@': 1000, 'A': 774, 'B': 762, 'C': 734, 'D': 830, 'E': 683, 'F': 683, 'G': 821,
|
|
58
|
+
'H': 837, 'I': 372, 'J': 372, 'K': 775, 'L': 637, 'M': 995, 'N': 837, 'O': 850,
|
|
59
|
+
'P': 733, 'Q': 850, 'R': 770, 'S': 720, 'T': 682, 'U': 812, 'V': 774, 'W': 1103,
|
|
60
|
+
'X': 771, 'Y': 724, 'Z': 725, '[': 457, '\\': 365, ']': 457, '^': 838, '_': 500,
|
|
61
|
+
'`': 500, 'a': 675, 'b': 716, 'c': 593, 'd': 716, 'e': 678, 'f': 435, 'g': 716,
|
|
62
|
+
'h': 712, 'i': 343, 'j': 343, 'k': 665, 'l': 343, 'm': 1042, 'n': 712, 'o': 687,
|
|
63
|
+
'p': 716, 'q': 716, 'r': 493, 's': 595, 't': 478, 'u': 712, 'v': 652, 'w': 924,
|
|
64
|
+
'x': 645, 'y': 652, 'z': 582, '{': 712, '|': 365, '}': 712, '~': 838,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_DEJAVU_SANS_BOLD_ITALIC = {
|
|
68
|
+
' ': 348, '!': 456, '"': 521, '#': 696, '$': 696, '%': 1002, '&': 872, "'": 306,
|
|
69
|
+
'(': 457, ')': 457, '*': 523, '+': 838, ',': 380, '-': 415, '.': 380, '/': 365,
|
|
70
|
+
'0': 696, '1': 696, '2': 696, '3': 696, '4': 696, '5': 696, '6': 696, '7': 696,
|
|
71
|
+
'8': 696, '9': 696, ':': 400, ';': 400, '<': 838, '=': 838, '>': 838, '?': 580,
|
|
72
|
+
'@': 1000, 'A': 774, 'B': 762, 'C': 734, 'D': 830, 'E': 683, 'F': 683, 'G': 821,
|
|
73
|
+
'H': 837, 'I': 372, 'J': 372, 'K': 775, 'L': 637, 'M': 995, 'N': 837, 'O': 850,
|
|
74
|
+
'P': 733, 'Q': 850, 'R': 770, 'S': 720, 'T': 682, 'U': 812, 'V': 774, 'W': 1103,
|
|
75
|
+
'X': 771, 'Y': 724, 'Z': 725, '[': 457, '\\': 365, ']': 457, '^': 838, '_': 500,
|
|
76
|
+
'`': 500, 'a': 675, 'b': 716, 'c': 593, 'd': 716, 'e': 678, 'f': 435, 'g': 716,
|
|
77
|
+
'h': 712, 'i': 343, 'j': 343, 'k': 665, 'l': 343, 'm': 1042, 'n': 712, 'o': 687,
|
|
78
|
+
'p': 716, 'q': 716, 'r': 493, 's': 595, 't': 478, 'u': 712, 'v': 652, 'w': 924,
|
|
79
|
+
'x': 645, 'y': 652, 'z': 582, '{': 712, '|': 365, '}': 712, '~': 838,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_HELVETICA = {
|
|
83
|
+
' ': 278, '!': 278, '"': 355, '#': 556, '$': 556, '%': 889, '&': 667, "'": 191,
|
|
84
|
+
'(': 333, ')': 333, '*': 389, '+': 584, ',': 278, '-': 333, '.': 278, '/': 278,
|
|
85
|
+
'0': 556, '1': 556, '2': 556, '3': 556, '4': 556, '5': 556, '6': 556, '7': 556,
|
|
86
|
+
'8': 556, '9': 556, ':': 278, ';': 278, '<': 584, '=': 584, '>': 584, '?': 556,
|
|
87
|
+
'@': 1015, 'A': 667, 'B': 667, 'C': 722, 'D': 722, 'E': 667, 'F': 611, 'G': 778,
|
|
88
|
+
'H': 722, 'I': 278, 'J': 500, 'K': 667, 'L': 556, 'M': 833, 'N': 722, 'O': 778,
|
|
89
|
+
'P': 667, 'Q': 778, 'R': 722, 'S': 667, 'T': 611, 'U': 722, 'V': 667, 'W': 944,
|
|
90
|
+
'X': 667, 'Y': 667, 'Z': 611, '[': 278, '\\': 278, ']': 278, '^': 469, '_': 556,
|
|
91
|
+
'`': 333, 'a': 556, 'b': 556, 'c': 500, 'd': 556, 'e': 556, 'f': 278, 'g': 556,
|
|
92
|
+
'h': 556, 'i': 222, 'j': 222, 'k': 500, 'l': 222, 'm': 833, 'n': 556, 'o': 556,
|
|
93
|
+
'p': 556, 'q': 556, 'r': 333, 's': 500, 't': 278, 'u': 556, 'v': 500, 'w': 722,
|
|
94
|
+
'x': 500, 'y': 500, 'z': 500, '{': 334, '|': 260, '}': 334, '~': 584,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_HELVETICA_ITALIC = {
|
|
98
|
+
' ': 278, '!': 278, '"': 355, '#': 556, '$': 556, '%': 889, '&': 667, "'": 191,
|
|
99
|
+
'(': 333, ')': 333, '*': 389, '+': 584, ',': 278, '-': 333, '.': 278, '/': 278,
|
|
100
|
+
'0': 556, '1': 556, '2': 556, '3': 556, '4': 556, '5': 556, '6': 556, '7': 556,
|
|
101
|
+
'8': 556, '9': 556, ':': 278, ';': 278, '<': 584, '=': 584, '>': 584, '?': 556,
|
|
102
|
+
'@': 1015, 'A': 667, 'B': 667, 'C': 722, 'D': 722, 'E': 667, 'F': 611, 'G': 778,
|
|
103
|
+
'H': 722, 'I': 278, 'J': 500, 'K': 667, 'L': 556, 'M': 833, 'N': 722, 'O': 778,
|
|
104
|
+
'P': 667, 'Q': 778, 'R': 722, 'S': 667, 'T': 611, 'U': 722, 'V': 667, 'W': 944,
|
|
105
|
+
'X': 667, 'Y': 667, 'Z': 611, '[': 278, '\\': 278, ']': 278, '^': 469, '_': 556,
|
|
106
|
+
'`': 333, 'a': 556, 'b': 556, 'c': 500, 'd': 556, 'e': 556, 'f': 278, 'g': 556,
|
|
107
|
+
'h': 556, 'i': 222, 'j': 222, 'k': 500, 'l': 222, 'm': 833, 'n': 556, 'o': 556,
|
|
108
|
+
'p': 556, 'q': 556, 'r': 333, 's': 500, 't': 278, 'u': 556, 'v': 500, 'w': 722,
|
|
109
|
+
'x': 500, 'y': 500, 'z': 500, '{': 334, '|': 260, '}': 334, '~': 584,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_HELVETICA_BOLD = {
|
|
113
|
+
' ': 278, '!': 333, '"': 474, '#': 556, '$': 556, '%': 889, '&': 722, "'": 238,
|
|
114
|
+
'(': 333, ')': 333, '*': 389, '+': 584, ',': 278, '-': 333, '.': 278, '/': 278,
|
|
115
|
+
'0': 556, '1': 556, '2': 556, '3': 556, '4': 556, '5': 556, '6': 556, '7': 556,
|
|
116
|
+
'8': 556, '9': 556, ':': 333, ';': 333, '<': 584, '=': 584, '>': 584, '?': 611,
|
|
117
|
+
'@': 975, 'A': 722, 'B': 722, 'C': 722, 'D': 722, 'E': 667, 'F': 611, 'G': 778,
|
|
118
|
+
'H': 722, 'I': 278, 'J': 556, 'K': 722, 'L': 611, 'M': 833, 'N': 722, 'O': 778,
|
|
119
|
+
'P': 667, 'Q': 778, 'R': 722, 'S': 667, 'T': 611, 'U': 722, 'V': 667, 'W': 944,
|
|
120
|
+
'X': 667, 'Y': 667, 'Z': 611, '[': 333, '\\': 278, ']': 333, '^': 584, '_': 556,
|
|
121
|
+
'`': 333, 'a': 556, 'b': 611, 'c': 556, 'd': 611, 'e': 556, 'f': 333, 'g': 611,
|
|
122
|
+
'h': 611, 'i': 278, 'j': 278, 'k': 556, 'l': 278, 'm': 889, 'n': 611, 'o': 611,
|
|
123
|
+
'p': 611, 'q': 611, 'r': 389, 's': 556, 't': 333, 'u': 611, 'v': 556, 'w': 778,
|
|
124
|
+
'x': 556, 'y': 556, 'z': 500, '{': 389, '|': 280, '}': 389, '~': 584,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
_HELVETICA_BOLD_ITALIC = {
|
|
128
|
+
' ': 278, '!': 333, '"': 474, '#': 556, '$': 556, '%': 889, '&': 722, "'": 238,
|
|
129
|
+
'(': 333, ')': 333, '*': 389, '+': 584, ',': 278, '-': 333, '.': 278, '/': 278,
|
|
130
|
+
'0': 556, '1': 556, '2': 556, '3': 556, '4': 556, '5': 556, '6': 556, '7': 556,
|
|
131
|
+
'8': 556, '9': 556, ':': 333, ';': 333, '<': 584, '=': 584, '>': 584, '?': 611,
|
|
132
|
+
'@': 975, 'A': 722, 'B': 722, 'C': 722, 'D': 722, 'E': 667, 'F': 611, 'G': 778,
|
|
133
|
+
'H': 722, 'I': 278, 'J': 556, 'K': 722, 'L': 611, 'M': 833, 'N': 722, 'O': 778,
|
|
134
|
+
'P': 667, 'Q': 778, 'R': 722, 'S': 667, 'T': 611, 'U': 722, 'V': 667, 'W': 944,
|
|
135
|
+
'X': 667, 'Y': 667, 'Z': 611, '[': 333, '\\': 278, ']': 333, '^': 584, '_': 556,
|
|
136
|
+
'`': 333, 'a': 556, 'b': 611, 'c': 556, 'd': 611, 'e': 556, 'f': 333, 'g': 611,
|
|
137
|
+
'h': 611, 'i': 278, 'j': 278, 'k': 556, 'l': 278, 'm': 889, 'n': 611, 'o': 611,
|
|
138
|
+
'p': 611, 'q': 611, 'r': 389, 's': 556, 't': 333, 'u': 611, 'v': 556, 'w': 778,
|
|
139
|
+
'x': 556, 'y': 556, 'z': 500, '{': 389, '|': 280, '}': 389, '~': 584,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_TIMES = {
|
|
143
|
+
' ': 250, '!': 333, '"': 408, '#': 500, '$': 500, '%': 833, '&': 778, "'": 180,
|
|
144
|
+
'(': 333, ')': 333, '*': 500, '+': 564, ',': 250, '-': 333, '.': 250, '/': 278,
|
|
145
|
+
'0': 500, '1': 500, '2': 500, '3': 500, '4': 500, '5': 500, '6': 500, '7': 500,
|
|
146
|
+
'8': 500, '9': 500, ':': 278, ';': 278, '<': 564, '=': 564, '>': 564, '?': 444,
|
|
147
|
+
'@': 921, 'A': 722, 'B': 667, 'C': 667, 'D': 722, 'E': 611, 'F': 556, 'G': 722,
|
|
148
|
+
'H': 722, 'I': 333, 'J': 389, 'K': 722, 'L': 611, 'M': 889, 'N': 722, 'O': 722,
|
|
149
|
+
'P': 556, 'Q': 722, 'R': 667, 'S': 556, 'T': 611, 'U': 722, 'V': 722, 'W': 944,
|
|
150
|
+
'X': 722, 'Y': 722, 'Z': 611, '[': 333, '\\': 278, ']': 333, '^': 469, '_': 500,
|
|
151
|
+
'`': 333, 'a': 444, 'b': 500, 'c': 444, 'd': 500, 'e': 444, 'f': 333, 'g': 500,
|
|
152
|
+
'h': 500, 'i': 278, 'j': 278, 'k': 500, 'l': 278, 'm': 778, 'n': 500, 'o': 500,
|
|
153
|
+
'p': 500, 'q': 500, 'r': 333, 's': 389, 't': 278, 'u': 500, 'v': 500, 'w': 722,
|
|
154
|
+
'x': 500, 'y': 500, 'z': 444, '{': 480, '|': 200, '}': 480, '~': 541,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_TIMES_ITALIC = {
|
|
158
|
+
' ': 250, '!': 333, '"': 420, '#': 500, '$': 500, '%': 833, '&': 778, "'": 214,
|
|
159
|
+
'(': 333, ')': 333, '*': 500, '+': 675, ',': 250, '-': 333, '.': 250, '/': 278,
|
|
160
|
+
'0': 500, '1': 500, '2': 500, '3': 500, '4': 500, '5': 500, '6': 500, '7': 500,
|
|
161
|
+
'8': 500, '9': 500, ':': 333, ';': 333, '<': 675, '=': 675, '>': 675, '?': 500,
|
|
162
|
+
'@': 920, 'A': 611, 'B': 611, 'C': 667, 'D': 722, 'E': 611, 'F': 611, 'G': 722,
|
|
163
|
+
'H': 722, 'I': 333, 'J': 444, 'K': 667, 'L': 556, 'M': 833, 'N': 667, 'O': 722,
|
|
164
|
+
'P': 611, 'Q': 722, 'R': 611, 'S': 500, 'T': 556, 'U': 722, 'V': 611, 'W': 833,
|
|
165
|
+
'X': 611, 'Y': 556, 'Z': 556, '[': 389, '\\': 278, ']': 389, '^': 422, '_': 500,
|
|
166
|
+
'`': 333, 'a': 500, 'b': 500, 'c': 444, 'd': 500, 'e': 444, 'f': 278, 'g': 500,
|
|
167
|
+
'h': 500, 'i': 278, 'j': 278, 'k': 444, 'l': 278, 'm': 722, 'n': 500, 'o': 500,
|
|
168
|
+
'p': 500, 'q': 500, 'r': 389, 's': 389, 't': 278, 'u': 500, 'v': 444, 'w': 667,
|
|
169
|
+
'x': 444, 'y': 444, 'z': 389, '{': 400, '|': 275, '}': 400, '~': 541,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_TIMES_BOLD = {
|
|
173
|
+
' ': 250, '!': 333, '"': 555, '#': 500, '$': 500, '%': 1000, '&': 833, "'": 278,
|
|
174
|
+
'(': 333, ')': 333, '*': 500, '+': 570, ',': 250, '-': 333, '.': 250, '/': 278,
|
|
175
|
+
'0': 500, '1': 500, '2': 500, '3': 500, '4': 500, '5': 500, '6': 500, '7': 500,
|
|
176
|
+
'8': 500, '9': 500, ':': 333, ';': 333, '<': 570, '=': 570, '>': 570, '?': 500,
|
|
177
|
+
'@': 930, 'A': 722, 'B': 667, 'C': 722, 'D': 722, 'E': 667, 'F': 611, 'G': 778,
|
|
178
|
+
'H': 778, 'I': 389, 'J': 500, 'K': 778, 'L': 667, 'M': 944, 'N': 722, 'O': 778,
|
|
179
|
+
'P': 611, 'Q': 778, 'R': 722, 'S': 556, 'T': 667, 'U': 722, 'V': 722, 'W': 1000,
|
|
180
|
+
'X': 722, 'Y': 722, 'Z': 667, '[': 333, '\\': 278, ']': 333, '^': 581, '_': 500,
|
|
181
|
+
'`': 333, 'a': 500, 'b': 556, 'c': 444, 'd': 556, 'e': 444, 'f': 333, 'g': 500,
|
|
182
|
+
'h': 556, 'i': 278, 'j': 333, 'k': 556, 'l': 278, 'm': 833, 'n': 556, 'o': 500,
|
|
183
|
+
'p': 556, 'q': 556, 'r': 444, 's': 389, 't': 333, 'u': 556, 'v': 500, 'w': 722,
|
|
184
|
+
'x': 500, 'y': 500, 'z': 444, '{': 394, '|': 220, '}': 394, '~': 520,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_TIMES_BOLD_ITALIC = {
|
|
188
|
+
' ': 250, '!': 389, '"': 555, '#': 500, '$': 500, '%': 833, '&': 778, "'": 278,
|
|
189
|
+
'(': 333, ')': 333, '*': 500, '+': 570, ',': 250, '-': 333, '.': 250, '/': 278,
|
|
190
|
+
'0': 500, '1': 500, '2': 500, '3': 500, '4': 500, '5': 500, '6': 500, '7': 500,
|
|
191
|
+
'8': 500, '9': 500, ':': 333, ';': 333, '<': 570, '=': 570, '>': 570, '?': 500,
|
|
192
|
+
'@': 832, 'A': 667, 'B': 667, 'C': 667, 'D': 722, 'E': 667, 'F': 667, 'G': 722,
|
|
193
|
+
'H': 778, 'I': 389, 'J': 500, 'K': 667, 'L': 611, 'M': 889, 'N': 722, 'O': 722,
|
|
194
|
+
'P': 611, 'Q': 722, 'R': 667, 'S': 556, 'T': 611, 'U': 722, 'V': 667, 'W': 889,
|
|
195
|
+
'X': 667, 'Y': 611, 'Z': 611, '[': 333, '\\': 278, ']': 333, '^': 570, '_': 500,
|
|
196
|
+
'`': 333, 'a': 500, 'b': 500, 'c': 444, 'd': 500, 'e': 444, 'f': 333, 'g': 500,
|
|
197
|
+
'h': 556, 'i': 278, 'j': 278, 'k': 500, 'l': 278, 'm': 778, 'n': 556, 'o': 500,
|
|
198
|
+
'p': 500, 'q': 500, 'r': 389, 's': 389, 't': 278, 'u': 556, 'v': 444, 'w': 667,
|
|
199
|
+
'x': 500, 'y': 444, 'z': 389, '{': 348, '|': 220, '}': 348, '~': 570,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Courier and its variants are monospaced: every ASCII glyph is 600/1000 em.
|
|
203
|
+
_COURIER_ADVANCE = 600
|
|
204
|
+
|
|
205
|
+
# Fallback for characters outside the tables -- anything non-ASCII, so degree
|
|
206
|
+
# signs, Greek and micro. Each family uses its own digit advance: digits are a
|
|
207
|
+
# single uniform width per family and sit mid-range, which makes them a better
|
|
208
|
+
# stand-in than a constant borrowed from Helvetica.
|
|
209
|
+
_DEFAULTS = {
|
|
210
|
+
"helvetica": 556,
|
|
211
|
+
"times": 500,
|
|
212
|
+
"dejavu sans": 636,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# (metric family, bold, italic) -> width table. Oblique/italic keys are present
|
|
216
|
+
# for every family, so a lookup never has to fall back across a style boundary.
|
|
217
|
+
_TABLES = {
|
|
218
|
+
("helvetica", False, False): _HELVETICA,
|
|
219
|
+
("helvetica", True, False): _HELVETICA_BOLD,
|
|
220
|
+
("helvetica", False, True): _HELVETICA_ITALIC,
|
|
221
|
+
("helvetica", True, True): _HELVETICA_BOLD_ITALIC,
|
|
222
|
+
("times", False, False): _TIMES,
|
|
223
|
+
("times", True, False): _TIMES_BOLD,
|
|
224
|
+
("times", False, True): _TIMES_ITALIC,
|
|
225
|
+
("times", True, True): _TIMES_BOLD_ITALIC,
|
|
226
|
+
("dejavu sans", False, False): _DEJAVU_SANS,
|
|
227
|
+
("dejavu sans", True, False): _DEJAVU_SANS_BOLD,
|
|
228
|
+
("dejavu sans", False, True): _DEJAVU_SANS_ITALIC,
|
|
229
|
+
("dejavu sans", True, True): _DEJAVU_SANS_BOLD_ITALIC,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
def text_width(text, font_size, family=None, bold=False, italic=False,
|
|
233
|
+
measure_installed=False):
|
|
234
|
+
"""Estimated rendered width of ``text`` in pixels at ``font_size`` px.
|
|
235
|
+
|
|
236
|
+
``family`` is a CSS font stack; it is resolved to the nearest bundled
|
|
237
|
+
metric family. ``bold`` / ``italic`` select the matching face, which matters
|
|
238
|
+
because bold Helvetica runs several percent wider than regular.
|
|
239
|
+
|
|
240
|
+
``measure_installed`` opts out of the bundled tables and measures the font
|
|
241
|
+
file actually present on this machine, which is more faithful for families
|
|
242
|
+
plotpress cannot otherwise measure but makes layout depend on what is
|
|
243
|
+
installed. It silently falls back to the bundled tables when no face
|
|
244
|
+
resolves. See :mod:`plotpress.fonts.installed`.
|
|
245
|
+
"""
|
|
246
|
+
if measure_installed:
|
|
247
|
+
from .installed import installed_table
|
|
248
|
+
|
|
249
|
+
table = installed_table(family, bold, italic)
|
|
250
|
+
if table is not None:
|
|
251
|
+
default = table.get("0", _DEFAULTS[DEFAULT_METRIC_FAMILY])
|
|
252
|
+
units = 0
|
|
253
|
+
for ch in text:
|
|
254
|
+
units += table.get(ch, default)
|
|
255
|
+
return units / 1000.0 * font_size
|
|
256
|
+
|
|
257
|
+
resolved = resolve_family(family)
|
|
258
|
+
if resolved == "courier":
|
|
259
|
+
return len(text) * _COURIER_ADVANCE / 1000.0 * font_size
|
|
260
|
+
table = _TABLES[(resolved, bool(bold), bool(italic))]
|
|
261
|
+
default = _DEFAULTS[resolved]
|
|
262
|
+
units = 0
|
|
263
|
+
for ch in text:
|
|
264
|
+
units += table.get(ch, default)
|
|
265
|
+
return units / 1000.0 * font_size
|