Skip to content
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

Copy font info attributes from default font to instance selectively #578

Merged
merged 2 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 82 additions & 5 deletions Lib/fontmake/instantiator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import fontMath
import fontTools.designspaceLib as designspaceLib
import fontTools.misc.fixedTools
import fontTools.ufoLib as ufoLib
import fontTools.varLib as varLib
import ufoLib2

Expand Down Expand Up @@ -76,6 +75,78 @@
200: 9,
}

# Font info fields that are not interpolated and should be copied from the
# default font to the instance.
#
# fontMath at the time of this writing handles the following attributes:
# https://github.com/robotools/fontMath/blob/0.5.0/Lib/fontMath/mathInfo.py#L360-L422
#
# From the attributes that are left, we skip instance-specific ones on purpose:
# - guidelines
# - postscriptFontName
# - styleMapFamilyName
# - styleMapStyleName
# - styleName
# - openTypeNameCompatibleFullName
# - openTypeNamePreferredFamilyName
# - openTypeNamePreferredSubfamilyName
# - openTypeNameUniqueID
# - openTypeNameWWSFamilyName
# - openTypeNameWWSSubfamilyName
# - openTypeOS2Panose
# - postscriptFullName
# - postscriptUniqueID
# - woffMetadataUniqueID
#
# Some, we skip because they are deprecated:
# - macintoshFONDFamilyID
# - macintoshFONDName
# - year
#
# This means we implicitly require the `stylename` attribute in the Designspace
# `<instance>` element.
UFO_INFO_ATTRIBUTES_TO_COPY_TO_INSTANCES = {
"copyright",
"familyName",
"note",
"openTypeGaspRangeRecords",
"openTypeHeadCreated",
"openTypeHeadFlags",
"openTypeNameDescription",
"openTypeNameDesigner",
"openTypeNameDesignerURL",
"openTypeNameLicense",
"openTypeNameLicenseURL",
"openTypeNameManufacturer",
"openTypeNameManufacturerURL",
"openTypeNameRecords",
"openTypeNameSampleText",
"openTypeNameVersion",
"openTypeOS2CodePageRanges",
"openTypeOS2FamilyClass",
"openTypeOS2Selection",
"openTypeOS2Type",
"openTypeOS2UnicodeRanges",
"openTypeOS2VendorID",
"postscriptDefaultCharacter",
"postscriptForceBold",
"postscriptIsFixedPitch",
"postscriptWindowsCharacterSet",
"trademark",
"versionMajor",
"versionMinor",
"woffMajorVersion",
"woffMetadataCopyright",
"woffMetadataCredits",
"woffMetadataDescription",
"woffMetadataExtensions",
"woffMetadataLicense",
"woffMetadataLicensee",
"woffMetadataTrademark",
"woffMetadataVendor",
"woffMinorVersion",
}


# Custom exception for this module
class InstantiatorError(Exception):
Expand Down Expand Up @@ -292,9 +363,7 @@ def _generate_instance_info(
info_instance.extractInfo(font.info)

# Copy non-interpolating metadata from the default font.
for attribute in ufoLib.fontInfoAttributesVersion3:
if hasattr(info_instance, attribute):
continue # Skip interpolated info attributes.
for attribute in UFO_INFO_ATTRIBUTES_TO_COPY_TO_INSTANCES:
if hasattr(self.copy_info, attribute):
setattr(
font.info,
Expand All @@ -305,7 +374,15 @@ def _generate_instance_info(
# TODO: multilingual names to replace possibly existing name records.
if instance.familyName:
font.info.familyName = instance.familyName
if instance.styleName:
if instance.styleName is None:
logger.warning(
"The given instance or instance at location %s is missing the "
"stylename attribute, which is required. Copying over the styleName "
"from the default font, which is probably wrong.",
location,
)
font.info.styleName = self.copy_info.styleName
else:
font.info.styleName = instance.styleName
if instance.postScriptFontName:
font.info.postscriptFontName = instance.postScriptFontName
Expand Down
50 changes: 44 additions & 6 deletions tests/test_instantiator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

import fontTools.designspaceLib as designspaceLib
import pytest
import ufoLib2
Expand Down Expand Up @@ -299,15 +301,18 @@ def test_instance_attributes(data_dir):
assert instance_font.info.styleMapStyleName == "xxx"


def test_instance_no_attributes(data_dir):
def test_instance_no_attributes(data_dir, caplog):
designspace = designspaceLib.DesignSpaceDocument.fromfile(
data_dir / "DesignspaceTest" / "DesignspaceTest-bare.designspace"
)
generator = fontmake.instantiator.Instantiator.from_designspace(
designspace, round_geometry=True
)

instance_font = generator.generate_instance(designspace.instances[0])
with caplog.at_level(logging.WARNING):
instance_font = generator.generate_instance(designspace.instances[0])
assert "missing the stylename attribute" in caplog.text

assert instance_font.info.familyName == "MyFont"
assert instance_font.info.styleName == "Light"
assert instance_font.info.postscriptFontName is None
Expand Down Expand Up @@ -459,10 +464,8 @@ def test_data_independence(data_dir):

assert generator.copy_info.openTypeOS2Panose == [2, 11, 5, 4, 2, 2, 2, 2, 2, 4]
generator.copy_info.openTypeOS2Panose.append(1000)
assert instance_font1.info.openTypeOS2Panose == [2, 11, 5, 4, 2, 2, 2, 2, 2, 4]
assert instance_font2.info.openTypeOS2Panose == [2, 11, 5, 4, 2, 2, 2, 2, 2, 4]
instance_font1.info.openTypeOS2Panose.append(2000)
assert instance_font2.info.openTypeOS2Panose == [2, 11, 5, 4, 2, 2, 2, 2, 2, 4]
assert instance_font1.info.openTypeOS2Panose is None
assert instance_font2.info.openTypeOS2Panose is None

# copy_feature_text not tested because it is a(n immutable) string

Expand All @@ -472,3 +475,38 @@ def test_data_independence(data_dir):
assert not instance_font2.lib["public.skipExportGlyphs"]
instance_font1.lib["public.skipExportGlyphs"].append("z")
assert not instance_font2.lib["public.skipExportGlyphs"]


def test_skipped_fontinfo_attributes():
"""Test that we consider all available font info attributes for copying."""
import fontTools.ufoLib
import fontMath.mathInfo

SKIPPED_ATTRS = {
"guidelines",
"macintoshFONDFamilyID",
"macintoshFONDName",
"openTypeNameCompatibleFullName",
"openTypeNamePreferredFamilyName",
"openTypeNamePreferredSubfamilyName",
"openTypeNameUniqueID",
"openTypeNameWWSFamilyName",
"openTypeNameWWSSubfamilyName",
"openTypeOS2Panose",
"postscriptFontName",
"postscriptFullName",
"postscriptUniqueID",
"styleMapFamilyName",
"styleMapStyleName",
"styleName",
"woffMetadataUniqueID",
"year",
}

assert (
fontTools.ufoLib.fontInfoAttributesVersion3
- set(fontMath.mathInfo._infoAttrs.keys())
- {"postscriptWeightName"} # Handled in fontMath specially.
- fontmake.instantiator.UFO_INFO_ATTRIBUTES_TO_COPY_TO_INSTANCES
== SKIPPED_ATTRS
)