From 47c858a2f2b97c1e21daf83b7b226d1eef2d2061 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 23 Jul 2023 17:14:01 -0600 Subject: [PATCH] [varLib.avarPlanner] Add axis-limit sanitization --- Lib/fontTools/varLib/avarPlanner.py | 99 ++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/varLib/avarPlanner.py b/Lib/fontTools/varLib/avarPlanner.py index 3e6c7173e..ba3375b47 100644 --- a/Lib/fontTools/varLib/avarPlanner.py +++ b/Lib/fontTools/varLib/avarPlanner.py @@ -10,6 +10,8 @@ __all__ = [ "planWeightAxis", "planWidthAxis", "planAxis", + "sanitizeWeight", + "sanitizeWidth", "measureBlackness", "measureWidth", "makeDesignspaceSnippet", @@ -114,10 +116,103 @@ def measureWidth(glyphset, glyphs=None): def sanitizeWidth(userTriple, designTriple, pins, measurements): + if len(set(userTriple)) < 3: + return True + + minVal, defaultVal, maxVal = ( + measurements[designTriple[0]], + measurements[designTriple[1]], + measurements[designTriple[2]], + ) + + calculatedMinVal = userTriple[0] * (minVal / defaultVal) + calculatedMaxVal = userTriple[2] * (maxVal / defaultVal) + + log.info("Original axis limits: %g:%g:%g", *userTriple) + log.info( + "Calculated axis limits: %g:%g:%g", + calculatedMinVal, + userTriple[1], + calculatedMaxVal, + ) + + if ( + abs(calculatedMinVal - userTriple[0]) / userTriple[1] > 0.05 + or abs(calculatedMaxVal - userTriple[2]) / userTriple[1] > 0.05 + ): + log.warning("Calculated width axis min/max do not match user input.") + log.warning( + " Suggested axis limits: %g:%g:%g", + calculatedMinVal, + userTriple[1], + calculatedMaxVal, + ) + + return False + return True def sanitizeWeight(userTriple, designTriple, pins, measurements): + if len(set(userTriple)) < 3: + return True + + minVal, defaultVal, maxVal = ( + measurements[designTriple[0]], + measurements[designTriple[1]], + measurements[designTriple[2]], + ) + + logMin = math.log(minVal) + logDefault = math.log(defaultVal) + logMax = math.log(maxVal) + + t = (userTriple[1] - userTriple[0]) / (userTriple[2] - userTriple[0]) + y = math.exp(logMin + t * (logMax - logMin)) + t = (y - minVal) / (maxVal - minVal) + calculatedDefaultVal = userTriple[0] + t * (userTriple[2] - userTriple[0]) + + log.info("Original axis limits: %g:%g:%g", *userTriple) + log.info( + "Calculated axis limits: %g:%g:%g", + userTriple[0], + calculatedDefaultVal, + userTriple[2], + ) + + if abs(calculatedDefaultVal - userTriple[1]) / userTriple[1] > 0.05: + log.warning("Calculated axis default does not match user input.") + log.warning( + " Suggested axis limits, changing default: %g:%g:%g", + userTriple[0], + calculatedDefaultVal, + userTriple[2], + ) + + t = (userTriple[2] - userTriple[0]) / (userTriple[1] - userTriple[0]) + y = math.exp(logMin + t * (logDefault - logMin)) + t = (y - minVal) / (defaultVal - minVal) + calculatedMaxVal = userTriple[0] + t * (userTriple[1] - userTriple[0]) + log.warning( + " Suggested axis limits, changing maximum: %g:%g:%g", + userTriple[0], + userTriple[1], + calculatedMaxVal, + ) + + t = (userTriple[0] - userTriple[2]) / (userTriple[1] - userTriple[2]) + y = math.exp(logMax + t * (logDefault - logMax)) + t = (y - maxVal) / (defaultVal - maxVal) + calculatedMinVal = userTriple[2] + t * (userTriple[1] - userTriple[2]) + log.warning( + " Suggested axis limits, changing minimum: %g:%g:%g", + calculatedMinVal, + userTriple[1], + userTriple[2], + ) + + return False + return True @@ -309,7 +404,7 @@ def planWidthAxis( glyphs=glyphs, designUnits=designUnits, pins=pins, - sanitizeFunc=sanitizeWeight if sanitize else None, + sanitizeFunc=sanitizeWidth if sanitize else None, ) @@ -411,7 +506,7 @@ def main(args=None): options = parser.parse_args(args) configLogger( - level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") + level=("DEBUG" if options.verbose else "WARNING" if options.quiet else "INFO") ) font = TTFont(options.font)