Skip to content

Commit

Permalink
Add initial support to search for fonts on mac with rubicon
Browse files Browse the repository at this point in the history
  • Loading branch information
goanpeca committed Jan 3, 2020
1 parent 3fad53c commit c4cc7bb
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 16 deletions.
26 changes: 25 additions & 1 deletion colosseum/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import sys

from .validators import (ValidationError, is_color, is_font_family,
is_integer, is_length,
is_number, is_percentage)
Expand Down Expand Up @@ -328,8 +330,30 @@ def __str__(self):

GENERIC_FAMILY_FONTS = [SERIF, SANS_SERIF, CURSIVE, FANTASY, MONOSPACE]


def available_font_families():
"""List available font family names."""
if sys.platform == 'darwin':
return _available_font_families_mac()
return []


def _available_font_families_mac():
"""List available font family names on mac."""
from ctypes import cdll, util
from rubicon.objc import ObjCClass
appkit = cdll.LoadLibrary(util.find_library('AppKit'))
NSFontManager = ObjCClass("NSFontManager")
NSFontManager.declare_class_property('sharedFontManager')
NSFontManager.declare_class_property("sharedFontManager")
NSFontManager.declare_property("availableFontFamilies")
manager = NSFontManager.sharedFontManager
return list(sorted(str(item) for item in manager.availableFontFamilies))


AVAILABLE_FONT_FAMILIES = available_font_families()
FONT_FAMILY_CHOICES = Choices(
validators=[is_font_family(generic_family=GENERIC_FAMILY_FONTS)],
validators=[is_font_family(generic_family=GENERIC_FAMILY_FONTS, font_families=AVAILABLE_FONT_FAMILIES)],
explicit_defaulting_constants=[INHERIT, INITIAL],
)

Expand Down
2 changes: 2 additions & 0 deletions colosseum/fonts.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import sys

from .constants import (FONT_FAMILY_CHOICES, FONT_SIZE_CHOICES,
FONT_STYLE_CHOICES, FONT_VARIANT_CHOICES,
FONT_WEIGHT_CHOICES, INHERIT, INITIAL_FONT_VALUES,
Expand Down
12 changes: 9 additions & 3 deletions colosseum/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ def is_color(value):
_CSS_IDENTIFIER_RE = re.compile(r'^[a-zA-Z][a-zA-Z0-9\-\_]+$')


def is_font_family(value=None, generic_family=None):
def is_font_family(value=None, generic_family=None, font_families=None):
"""
Validate that value is a valid font family.
This validator returns a list.
"""
generic_family = generic_family or []
font_families = font_families or []

def validator(font_value):
font_value = ' '.join(font_value.strip().split())
Expand All @@ -135,12 +136,17 @@ def validator(font_value):

if (val.startswith('"') and val.endswith('"')
or val.startswith("'") and val.endswith("'")):
# TODO: Check that the font exists?
try:
ast.literal_eval(val)
no_quotes_val = ast.literal_eval(val)
checked_values.append(val)
except ValueError:
raise ValidationError

if no_quotes_val not in font_families:
print(font_families)
raise ValidationError('Font family "{font_value}"'
' not found on system!'.format(font_value=no_quotes_val))

elif val in generic_family:
checked_values.append(val)
else:
Expand Down
10 changes: 5 additions & 5 deletions tests/test_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def test_list_property(self):
# Check valid values
node.style.font_family = ['caption']
node.style.font_family = ['serif']
node.style.font_family = ["'Lucida Foo Bar'", 'serif']
node.style.font_family = ["'Arial Black'", 'serif']

# TODO: This will coerce to a list, is this a valid behavior?
node.style.font_family = 'just-a-string'
Expand All @@ -454,7 +454,7 @@ def test_list_property(self):

# Check invalid values
with self.assertRaises(ValueError):
node.style.font_family = ['Lucida Foo Bar']
node.style.font_family = ['Arial Black']

# Check the error message
try:
Expand Down Expand Up @@ -712,9 +712,9 @@ def test_font_shorthand_property(self):
node.style.font_variant = 'small-caps'
node.style.font_size = '10px'
node.style.line_height = '1.5'
node.style.font_family = ['"Foo Bar Spam"', 'serif']
node.style.font_family = ['"Arial Black"', 'serif']
# TODO: Is this the behavior we want?
self.assertEqual(node.style.font, 'italic small-caps bold 10.0px/1.5 "Foo Bar Spam", serif')
self.assertEqual(node.style.font, 'italic small-caps bold 10.0px/1.5 "Arial Black", serif')

# Check setting the shorthand resets values
node.style.font = '9px serif'
Expand All @@ -734,7 +734,7 @@ def test_font_shorthand_property(self):
node.style.font_variant = 'small-caps'
node.style.font_size = '10px'
node.style.line_height = '1.5'
node.style.font_family = ['"Foo Bar Spam"', 'serif']
node.style.font_family = ['"Arial Black"', 'serif']
self.assertEqual(node.style.font, '9px serif')

# Check invalid values
Expand Down
6 changes: 3 additions & 3 deletions tests/test_fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@
'line_height': '120%',
'font_family': ['fantasy'],
},
r'x-large/110% "New Century Schoolbook",serif': {
r'x-large/110% "Arial Black",serif': {
'font_style': 'normal',
'font_variant': 'normal',
'font_weight': 'normal',
'font_size': 'x-large',
'line_height': '110%',
'font_family': ['"New Century Schoolbook"', 'serif'],
'font_family': ['"Arial Black"', 'serif'],
},
}

Expand All @@ -58,7 +58,7 @@ def test_parse_font_shorthand(self):

# Test extra spaces
parse_font_property(r' normal normal normal 12px/12px serif ')
parse_font_property(r' normal normal normal 12px/12px "New Foo Bar", serif ')
parse_font_property(r' normal normal normal 12px/12px " Arial Black ", serif ')

# Test valid single part
for part in SYSTEM_FONT_KEYWORDS:
Expand Down
8 changes: 4 additions & 4 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ def test_number(self):

class FontTests(TestCase):
def test_font_family_name_valid(self):
validator = is_font_family(generic_family=GENERIC_FAMILY_FONTS)
self.assertEqual(validator('"New Century Schoolbook", serif'), ['"New Century Schoolbook"', 'serif'])
self.assertEqual(validator("'21st Century',fantasy"), ["'21st Century'", 'fantasy'])
self.assertEqual(validator(" ' 21st Century ' , fantasy "), ["'21st Century'", 'fantasy'])
validator = is_font_family(generic_family=GENERIC_FAMILY_FONTS, font_families=['Arial Black'])
self.assertEqual(validator('"Arial Black", serif'), ['"Arial Black"', 'serif'])
self.assertEqual(validator("'Arial Black',fantasy"), ["'Arial Black'", 'fantasy'])
self.assertEqual(validator(" ' Arial Black ' , fantasy "), ["'Arial Black'", 'fantasy'])

def test_font_family_name_invalid(self):
validator = is_font_family(generic_family=GENERIC_FAMILY_FONTS)
Expand Down

0 comments on commit c4cc7bb

Please sign in to comment.