node — render it as paths,
- # scaled to fit and centered in the unit box, in both modes. A plain unit
- # keeps the original baseline-anchored text/paths behaviour.
if _has_markup(unit):
- unit_svg = ' ' + _markup_unit_svg(unit)
+ unit_svg = ' ' + _markup_unit_svg(unit, fd)
elif use_paths:
unit_svg = ' ' + _text_to_paths(
- unit, 41.455711, 109.03796, _unit_font_size(unit),
+ unit, 41.455711, 109.03796, _unit_font_size(unit, fd), fd,
wrap_transform=f'scale({_UNIT_SCALE_X},1.0364155)'
)
else:
- fs = _unit_font_size(unit)
+ fs = _unit_font_size(unit, fd)
unit_svg = (
- f' {escape(unit)}'
)
if use_paths:
- parts += [unit_svg, ' ' + _text_to_paths(range_label, 184.79707, 113.6755, 3.60539)]
+ parts += [unit_svg, ' ' + _text_to_paths(range_label, 184.79707, 113.6755, 3.60539, fd)]
else:
parts += [
unit_svg,
- f' {escape(range_label)}',
]
- # Major ticks
for x in big_xs:
parts.append(
f' '
)
- # Minor ticks — placement depends on scale type:
- # linear: uniform physical spacing (= uniform value spacing)
- # log: linear value interpolation between geometric major values
- # (crowds toward high end of each interval)
- # sqrt: sqrt of fractional position within each interval, giving
- # consistent high-end crowding across all intervals; using
- # value interpolation instead would concentrate the crowding
- # only near zero (where sqrt is steepest) and look linear elsewhere.
if small_ticks > 0:
sub = small_ticks + 1
for i in range(big_ticks):
@@ -488,9 +461,6 @@ def generate_svg(unit, min_val, max_val, range_label, big_ticks, small_ticks, la
if scale_type == 'sqrt':
x = big_xs[i] + (j / sub) ** 2 * (big_xs[i + 1] - big_xs[i])
elif scale_type == 'reciprocal' or math.isinf(big_vals[i]):
- # Reciprocal: interpolate in position space (= linear in 1/v).
- # Linear value interpolation would map huge early value ranges
- # through 1/v and pile all ticks against the left wall.
x = big_xs[i] + (j / sub) * (big_xs[i + 1] - big_xs[i])
else:
v = big_vals[i] + j * (big_vals[i + 1] - big_vals[i]) / sub
@@ -500,15 +470,14 @@ def generate_svg(unit, min_val, max_val, range_label, big_ticks, small_ticks, la
f' style="stroke:#1a1a1a;stroke-width:0.302;stroke-linecap:butt" />'
)
- # Numeric labels
for k, idx in enumerate(label_indices):
x = big_xs[idx]
label = custom_labels[k] if custom_labels else _fmt(big_vals[idx])
if use_paths:
- parts.append(' ' + _text_to_paths(label, x, 101.49598, label_size, anchor='middle'))
+ parts.append(' ' + _text_to_paths(label, x, 101.49598, label_size, fd, anchor='middle'))
else:
parts.append(
- f' {escape(label)}'
)
@@ -518,7 +487,9 @@ def generate_svg(unit, min_val, max_val, range_label, big_ticks, small_ticks, la
@app.route('/')
def index():
- return render_template('index.html', logo_b64=_LOGO_B64, version=VERSION)
+ fonts = list(_FONTS.values())
+ return render_template('index.html', logo_b64=_LOGO_B64, version=VERSION,
+ fonts=fonts, default_font=_DEFAULT_FONT)
@app.route('/generate', methods=['POST'])
@@ -532,6 +503,7 @@ def generate():
label_count = max(2, int(request.form.get('label_count', 6)))
use_paths = request.form.get('output_mode', 'paths') == 'paths'
scale_type = request.form.get('scale_type', 'linear')
+ font_key = request.form.get('font', _DEFAULT_FONT)
if request.form.get('max_is_inf') == '1':
max_val = math.inf
@@ -540,7 +512,8 @@ def generate():
try:
svg = generate_svg(unit, min_val, max_val, range_label, big_ticks, small_ticks,
- label_count, use_paths, scale_type, custom_labels, label_size)
+ label_count, use_paths, scale_type, custom_labels, label_size,
+ font_key)
except ValueError as err:
return Response(str(err), status=400, mimetype='text/plain')
return Response(svg, mimetype='image/svg+xml')
diff --git a/web/templates/index.html b/web/templates/index.html
index 323b000..98e6e4b 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -368,6 +368,16 @@
+
+
+
+
+
+
@@ -499,6 +509,8 @@
URL.revokeObjectURL(a.href);
});
+ document.getElementById('font-select').addEventListener('change', doGenerate);
+
doGenerate();