Skip to content

Commit

Permalink
Beginnings of a designspace profile
Browse files Browse the repository at this point in the history
  • Loading branch information
simoncozens authored and felipesanches committed Aug 25, 2021
1 parent 92ed5be commit 0f2bae8
Show file tree
Hide file tree
Showing 16 changed files with 461 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Lib/fontbakery/commands/check_designspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python
import sys
from functools import partial

from fontbakery.commands.check_profile import (
runner_factory as super_runner_factory, main as super_main)
from fontbakery.profiles.designspace import profile

# The values dict will probably get one or more specific blacklists
# for the google font project. It would be good if it was not necessary
# to copy paste this kind of configuration, thus a central init for
# the google/fonts repository is good.
GOOGLEFONTS_SPECIFICS = {}


# runner_factory is used by the fontbakery dashboard.
# It is here in order to have a single place from which
# the profile is configured for the CLI and the worker.
def runner_factory(fonts):
values = {}
values.update(GOOGLEFONTS_SPECIFICS)
values['fonts'] = fonts
return super_runner_factory(profile, values=values)


main = partial(super_main, profile, values=GOOGLEFONTS_SPECIFICS)

if __name__ == '__main__':
sys.exit(main())
173 changes: 173 additions & 0 deletions Lib/fontbakery/profiles/designspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import os

from fontbakery.checkrunner import Section, PASS, FAIL, WARN, ERROR, INFO, SKIP, Profile
from fontbakery.callable import condition, check, disable
from fontbakery.callable import FontBakeryExpectedValue as ExpectedValue
from fontbakery.message import Message
from fontbakery.fonts_profile import profile_factory
from fontbakery.utils import pretty_print_list
import defcon

profile_imports = ".shared_conditions"


class DesignspaceProfile(Profile):
def setup_argparse(self, argument_parser):
"""Set up custom arguments needed for this profile."""
import glob
import logging
import argparse

def get_fonts(pattern):

fonts_to_check = []
# use glob.glob to accept *.designsapce

for fullpath in glob.glob(pattern):
fullpath_absolute = os.path.abspath(fullpath)
if fullpath_absolute.lower().endswith(
".designspace"
) and os.path.isfile(fullpath_absolute):
fonts_to_check.append(fullpath)
else:
logging.warning(
(
"Skipping '{}' as it does not seem to be"
" valid Designspace file."
).format(fullpath)
)
return fonts_to_check

class MergeAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
target = [item for l in values for item in l]
setattr(namespace, self.dest, target)

argument_parser.add_argument(
"fonts",
# To allow optional commands like "-L" to work without other input
# files:
nargs="*",
type=get_fonts,
action=MergeAction,
help="font file path(s) to check."
" Wildcards like *.designspace are allowed.",
)

return ("fonts",)


fonts_expected_value = ExpectedValue(
"fonts",
default=[],
description="A list of the designspace file paths to check.",
validator=lambda fonts: (True, None) if len(fonts) else (False, "Value is empty."),
)

# ----------------------------------------------------------------------------
# This variable serves as an exportable anchor point, see e.g. the
# Lib/fontbakery/commands/check_ufo_sources.py script.
profile = DesignspaceProfile(
default_section=Section("Default"),
iterargs={"font": "fonts"},
expected_values={fonts_expected_value.name: fonts_expected_value},
)

register_check = profile.register_check
register_condition = profile.register_condition

basic_checks = Section("Basic Designspace checks")


@register_condition
@condition
def designspace(font):
from fontTools.designspaceLib import DesignSpaceDocument

ds = DesignSpaceDocument.fromfile(font)
return ds


@register_condition
@condition
def sources(designspace):
return designspace.loadSourceFonts(defcon.Font)


@register_check(section=basic_checks)
@check(id="com.google.fonts/check/designspace_has_sources")
def com_google_fonts_check_designspace_has_sources(sources):
"""See if we can actually load the source files."""
if not sources:
yield FAIL, "Unable to load source files."
else:
yield PASS, "We have sources."


@register_check(section=basic_checks)
@check(id="com.google.fonts/check/designspace_has_default_master")
def com_google_fonts_check_designspace_has_default_master(designspace):
"""Ensure a default master is defined."""
if not designspace.findDefault():
yield FAIL, "Unable to find a default master."
else:
yield PASS, "We located a default master."


@register_check(section=basic_checks)
@check(id="com.google.fonts/check/designspace_has_consistent_glyphset")
def com_google_fonts_check_designspace_has_consistent_glyphset(designspace, sources):
"""Ensure non-default masters don't have glyphs not present in the default."""
default_glyphset = set(designspace.findDefault().font.keys())
failures = []
for source in designspace.sources:
master_glyphset = set(source.font.keys())
outliers = master_glyphset - default_glyphset
if outliers:
failures.append(
"Source %s has glyphs not present in the default master: %s"
% (
source.filename,
", ".join(list(outliers)),
)
)
if failures:
formatted_list = "\t* " + pretty_print_list(failures, sep="\n\t* ")
yield FAIL, Message(
"inconsistent-glyphset", "Glyphsets were not consistent:\n" + formatted_list
)
else:
yield PASS, "Glyphsets were consistent."


@register_check(section=basic_checks)
@check(id="com.google.fonts/check/designspace_has_consistent_unicodes")
def com_google_fonts_check_designspace_has_consistent_unicodes(designspace, sources):
"""Ensure Unicode assignments are consistent across sources."""
default_source = designspace.findDefault()
default_unicodes = {g.name: g.unicode for g in default_source.font}
failures = []
for source in designspace.sources:
for g in source.font:
if g.name not in default_unicodes:
# Previous test will cover this
continue
if g.unicode != default_unicodes[g.name]:
failures.append(
"Source %s has %s=%s; default master has %s=%s"
% (
source.filename,
g.name,
g.unicode,
g.name,
default_unicodes[g.name],
)
)
if failures:
formatted_list = "\t* " + pretty_print_list(failures, sep="\n\t* ")
yield FAIL, Message(
"inconsistent-unicodes",
"Unicode assignments were not consistent:\n" + formatted_list,
)
else:
yield PASS, "Unicode assignments were consistent."
40 changes: 40 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/fontinfo.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ascender</key>
<integer>800</integer>
<key>capHeight</key>
<integer>700</integer>
<key>descender</key>
<integer>-200</integer>
<key>openTypeHeadCreated</key>
<string>2021/02/05 10:10:29</string>
<key>postscriptBlueValues</key>
<array>
<real>-16.0</real>
<real>0.0</real>
<real>500.0</real>
<real>516.0</real>
<real>700.0</real>
<real>716.0</real>
<real>800.0</real>
<real>816.0</real>
</array>
<key>postscriptOtherBlues</key>
<array>
<real>-216.0</real>
<real>-200.0</real>
</array>
<key>styleName</key>
<string>Regular</string>
<key>unitsPerEm</key>
<integer>1000</integer>
<key>versionMajor</key>
<integer>1</integer>
<key>versionMinor</key>
<integer>0</integer>
<key>xHeight</key>
<integer>500</integer>
</dict>
</plist>
22 changes: 22 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/glyphs/A_.glif
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<glyph name="A" format="2">
<advance width="888"/>
<unicode hex="0041"/>
<outline>
<contour>
<point x="694" y="-8" type="line"/>
<point x="883" y="-8" type="line"/>
<point x="549" y="728" type="line"/>
<point x="339" y="728" type="line"/>
<point x="5" y="-8" type="line"/>
<point x="184" y="-8" type="line"/>
<point x="281" y="207" type="line"/>
<point x="596" y="207" type="line"/>
</contour>
<contour>
<point x="319" y="343" type="line"/>
<point x="439" y="600" type="line"/>
<point x="558" y="343" type="line"/>
</contour>
</outline>
</glyph>
22 changes: 22 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/glyphs/B_.glif
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<glyph name="B" format="2">
<advance width="600"/>
<unicode hex="0042"/>
<outline>
<contour>
<point x="573" y="252" type="line"/>
<point x="809" y="524" type="line"/>
<point x="549" y="728" type="line"/>
<point x="95" y="728" type="line"/>
<point x="63" y="-8" type="line"/>
<point x="281" y="-8" type="line"/>
<point x="399" y="163" type="line"/>
<point x="728" y="-62" type="line"/>
</contour>
<contour>
<point x="281" y="343" type="line"/>
<point x="291" y="600" type="line"/>
<point x="558" y="504" type="line"/>
</contour>
</outline>
</glyph>
10 changes: 10 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/glyphs/contents.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>A</key>
<string>A_.glif</string>
<key>B</key>
<string>B_.glif</string>
</dict>
</plist>
10 changes: 10 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/layercontents.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<array>
<string>public.default</string>
<string>glyphs</string>
</array>
</array>
</plist>
19 changes: 19 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/lib.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.schriftgestaltung.disablesAutomaticAlignment</key>
<false/>
<key>com.schriftgestaltung.fontMasterID</key>
<string>m01</string>
<key>com.schriftgestaltung.glyphOrder</key>
<false/>
<key>com.schriftgestaltung.useNiceNames</key>
<true/>
<key>public.glyphOrder</key>
<array>
<string>A</string>
<string>B</string>
</array>
</dict>
</plist>
10 changes: 10 additions & 0 deletions data/test/stupidfont/Stupid Font Bold.ufo/metainfo.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>creator</key>
<string>com.schriftgestaltung.GlyphsUFOExport</string>
<key>formatVersion</key>
<integer>3</integer>
</dict>
</plist>
40 changes: 40 additions & 0 deletions data/test/stupidfont/Stupid Font Regular.ufo/fontinfo.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ascender</key>
<integer>800</integer>
<key>capHeight</key>
<integer>700</integer>
<key>descender</key>
<integer>-200</integer>
<key>openTypeHeadCreated</key>
<string>2021/02/05 10:10:29</string>
<key>postscriptBlueValues</key>
<array>
<real>-16.0</real>
<real>0.0</real>
<real>500.0</real>
<real>516.0</real>
<real>700.0</real>
<real>716.0</real>
<real>800.0</real>
<real>816.0</real>
</array>
<key>postscriptOtherBlues</key>
<array>
<real>-216.0</real>
<real>-200.0</real>
</array>
<key>styleName</key>
<string>Regular</string>
<key>unitsPerEm</key>
<integer>1000</integer>
<key>versionMajor</key>
<integer>1</integer>
<key>versionMinor</key>
<integer>0</integer>
<key>xHeight</key>
<integer>500</integer>
</dict>
</plist>
Loading

0 comments on commit 0f2bae8

Please sign in to comment.