-
Notifications
You must be signed in to change notification settings - Fork 19
Add support for font features and variants #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v0
Are you sure you want to change the base?
Conversation
Here's an script you can use to generate example outputs: #!/usr/bin/env python3
"""
Focused test for numeral OpenType features in ManimPango.
"""
import manimpango
import os
def test_numeral_features():
"""Test various numeral-related OpenType features."""
print("=== Testing Numeral Features ===")
# Text with numbers that should show differences
numeral_text = "0123456789"
mixed_text = "Price: $1,234.56 (March 2023)"
numeral_features = {
"default": None,
"oldstyle": "onum=1", # Old-style/text figures
"lining": "lnum=1", # Lining figures
"tabular": "tnum=1", # Tabular (monospaced) figures
"proportional": "pnum=1", # Proportional figures
"tabular_oldstyle": "tnum=1, onum=1",
"tabular_lining": "tnum=1, lnum=1"
}
# Test fonts that are more likely to support numeral features
fonts_to_test = [
"Times New Roman",
"Georgia",
"SF Pro Text",
"Avenir",
]
for font in fonts_to_test:
print(f"\nTesting font: {font}")
for feature_name, features in numeral_features.items():
try:
filename = f"numerals_{font.replace(' ', '_').lower()}_{feature_name}.svg"
result = manimpango.MarkupUtils.text2svg(
text=mixed_text,
font=font,
slant="NORMAL",
weight="NORMAL",
size=48, # Large size to see differences clearly
_=None,
disable_liga=False,
file_name=filename,
START_X=10,
START_Y=60,
width=800,
height=100,
font_features=features
)
print(f" ✓ Created: {filename} ({feature_name})")
except Exception as e:
print(f" ✗ Error with {feature_name}: {e}")
# Also test pure numbers
try:
filename = f"pure_numbers_{font.replace(' ', '_').lower()}.svg"
result = manimpango.MarkupUtils.text2svg(
text=numeral_text,
font=font,
slant="NORMAL",
weight="NORMAL",
size=64,
_=None,
disable_liga=False,
file_name=filename,
START_X=10,
START_Y=80,
width=600,
height=120,
font_features="onum=1" # Test old-style figures
)
print(f" ✓ Created: {filename} (pure numbers with onum)")
except Exception as e:
print(f" ✗ Error with pure numbers: {e}")
def compare_numeral_files():
"""Compare file sizes and content for numeral features."""
print("\n=== Comparing Numeral Files ===")
# Get all numerals files
numeral_files = [f for f in os.listdir(".") if f.startswith("numerals_") and f.endswith(".svg")]
if not numeral_files:
print("No numeral files found to compare")
return
# Group by font
fonts = {}
for f in numeral_files:
parts = f.split("_")
if len(parts) >= 3:
font_key = "_".join(parts[1:-1]) # everything except 'numerals' and feature name
if font_key not in fonts:
fonts[font_key] = {}
feature = parts[-1].replace(".svg", "")
fonts[font_key][feature] = f
for font, files in fonts.items():
print(f"\nFont: {font}")
# Compare file sizes
file_sizes = {}
for feature, filename in files.items():
try:
size = os.path.getsize(filename)
file_sizes[feature] = size
print(f" {feature}: {size} bytes")
except:
print(f" {feature}: file not found")
# Look for size differences (indicating different rendering)
if len(file_sizes) > 1:
sizes = list(file_sizes.values())
if len(set(sizes)) > 1:
print(f" ✓ Size differences detected - features may be working!")
else:
print(f" ? All files same size - features may not be working")
def test_markup_numerals():
"""Test numeral features using markup."""
print("\n=== Testing Numerals in Markup ===")
markup_tests = [
("Default numbers", "Numbers: 0123456789"),
("Old-style figures", "<span font_features='onum=1'>Numbers: 0123456789</span>"),
("Tabular figures", "<span font_features='tnum=1'>123.45 678.90 234.56</span>"),
("Combined features", "<span font_features='onum=1, tnum=1'>Old+Tab: 0123456789</span>"),
]
for name, markup in markup_tests:
try:
# Validate markup first
error = manimpango.MarkupUtils.validate(markup)
if error:
print(f"✗ Markup error for '{name}': {error}")
continue
filename = f"markup_numerals_{name.replace(' ', '_').replace('+', '_').lower()}.svg"
result = manimpango.MarkupUtils.text2svg(
text=markup,
font="Times New Roman",
slant="NORMAL",
weight="NORMAL",
size=36,
_=None,
disable_liga=False,
file_name=filename,
START_X=10,
START_Y=50,
width=600,
height=80
)
print(f"✓ Created: {filename} ({name})")
except Exception as e:
print(f"✗ Error with '{name}': {e}")
def analyze_svg_numerals():
"""Analyze SVG content specifically for numeral differences."""
print("\n=== Analyzing SVG Numeral Content ===")
# Find files to compare
default_file = None
onum_file = None
for f in os.listdir("."):
if "numerals_times_new_roman_default.svg" in f:
default_file = f
elif "numerals_times_new_roman_oldstyle.svg" in f:
onum_file = f
if default_file and onum_file:
try:
with open(default_file, 'r') as f:
default_content = f.read()
with open(onum_file, 'r') as f:
onum_content = f.read()
# Look for differences in glyph usage
default_glyphs = set()
onum_glyphs = set()
# Extract glyph references
import re
glyph_pattern = r'xlink:href="#glyph-\d+-(\d+)"'
default_matches = re.findall(glyph_pattern, default_content)
onum_matches = re.findall(glyph_pattern, onum_content)
print(f"Default file glyphs used: {sorted(set(default_matches))}")
print(f"Old-style file glyphs used: {sorted(set(onum_matches))}")
if set(default_matches) != set(onum_matches):
print("✓ Different glyphs used - numeral features are working!")
else:
print("? Same glyphs used - numeral features may not be working")
# Check file sizes
default_size = len(default_content)
onum_size = len(onum_content)
print(f"File sizes: default={default_size}, oldstyle={onum_size}")
except Exception as e:
print(f"Error analyzing files: {e}")
else:
print("Could not find both default and oldstyle files to compare")
def main():
"""Run all numeral tests."""
print("ManimPango Numeral Features Test")
print("=" * 40)
try:
test_numeral_features()
test_markup_numerals()
compare_numeral_files()
analyze_svg_numerals()
print("\n" + "=" * 40)
print("Numeral test completed!")
print("Check the generated SVG files to see numeral differences.")
print("Look for files with 'oldstyle', 'tabular' variations.")
except Exception as e:
print(f"\nError running numeral tests: {e}")
if __name__ == "__main__":
main() |
Hi @matt, Thanks for your contribution. I'll take a look at this PR later today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, sorry for the late review. I thought I already reviewed it, and it looks like I've forgotten 😅
I've some questions, please answer and I'll merge these changes and create a new release.
|
||
class MarkupUtils: | ||
@staticmethod | ||
def build_font_features( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Is this function used anywhere, or are we exposing it as an API?
def validate_font_variant(variant: str) -> bool: | ||
"""Validate a font_variant value. | ||
|
||
Parameters | ||
========== | ||
variant : str | ||
The font variant to validate | ||
|
||
Returns | ||
======= | ||
bool | ||
True if valid, False otherwise | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Are we using this anywhere? Or is it a new API? If it's a new API, I don't see the use of this one much; the validate
method itself should suffice this need.
This PR adds support for OpenType Font features and font variants using Pango markup attributes.
For example, with the changes in this PR, I can now render proportional and tabular numerals:
Proportional figures (
pnum=1
;default
)Tabular / monospaced figures (
tnum=1
)