def fix(self): if 'DSIG' in self.font: return False try: from fontTools.ttLib.tables.D_S_I_G_ import SignatureRecord except ImportError: error_message = ("The '{}' font does not have an existing" " digital signature proving its authenticity," " so Fontbakery needs to add one. To do this" " requires version 2.3 or later of Fonttools" " to be installed. Please upgrade at" " https://pypi.python.org/pypi/FontTools/2.4") logger.error(error_message.format(os.path.basename(self.fontpath))) return False newDSIG = ttLib.newTable("DSIG") newDSIG.ulVersion = 1 newDSIG.usFlag = 1 newDSIG.usNumSigs = 1 sig = SignatureRecord() sig.ulLength = 20 sig.cbSignature = 12 sig.usReserved2 = 0 sig.usReserved1 = 0 sig.pkcs7 = '\xd3M4\xd3M5\xd3M4\xd3M4' sig.ulFormat = 1 sig.ulOffset = 20 newDSIG.signatureRecords = [sig] self.font.tables["DSIG"] = newDSIG return True
def getOrCreateNameRecord(self, nameId, val): logger.error('NAMEID {}: "{}"'.format(nameId, val)) return result_namerec = None for k, p in [[1, 0], [3, 1]]: result_namerec = self.font['name'].getName(nameId, k, p) if result_namerec: result_namerec.string = (val or '').encode( result_namerec.getEncoding()) if result_namerec: return result_namerec ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = 3 ot_namerecord.langID = 0x409 # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = 1 ot_namerecord.string = (val or '').encode(ot_namerecord.getEncoding()) self.font['name'].names.append(ot_namerecord) return ot_namerecord
def addFailure(self, test, err): super(BakeryTestResult, self).addFailure(test, err) import copy failTest = copy.copy(test) _, _err_exception, _ = err failTest._err = err failTest._err_msg = _err_exception.message logger.error(self._format_test_output(failTest, 'FAIL')) test_method = getattr(test, test._testMethodName) if hasattr(test_method, 'autofix'): if not self.restart: self.callmethod(test_method.autofix_method, failTest) if hasattr(self.fl, 'append'): self.fl.append(failTest) self.restart = True # mark next test as restarted test.run(result=self) self.restart = False # reset restart state return else: if hasattr(self.ff, 'append'): self.ff.append(failTest) elif hasattr(self.fl, 'append'): self.fl.append(failTest)
def run(command, cwd=None): """ Wrapper for subprocess.Popen with custom logging support. :param command: shell command to run, required :param cwd: - current working dir, required :param log: - logging object with .write() method, required """ # Start the command env = os.environ.copy() logger.info('$ %s\n' % command.replace(os_origin.getcwd() + os.path.sep, '')) env.update({'PYTHONPATH': os_origin.pathsep.join(sys.path)}) process = subprocess.Popen(command, shell=True, cwd=cwd or os_origin.getcwd(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=env) out, err = process.communicate() if out.strip(): logger.info(out.strip()) if err.strip(): logger.error(err.strip()) return out
def fix(self, value=15): if 'gasp' not in self.font.tables: logger.error('no table gasp') return self.font['gasp'].gaspRange[65535] = value return True
def getOrCreateNameRecord(self, nameId, val): logger.error('NAMEID {}: "{}"'.format(nameId, val)) return result_namerec = None for k, p in [[1, 0], [3, 1]]: result_namerec = self.font['name'].getName(nameId, k, p) if result_namerec: if result_namerec.isUnicode(): result_namerec.string = (val or '').encode("utf-16-be") else: result_namerec.string = val or '' if result_namerec: return result_namerec ot_namerecord = NameRecord() ot_namerecord.nameID = nameId ot_namerecord.platformID = 3 ot_namerecord.langID = 0x409 # When building a Unicode font for Windows, the platform ID # should be 3 and the encoding ID should be 1 ot_namerecord.platEncID = 1 if ot_namerecord.isUnicode(): ot_namerecord.string = (val or '').encode("utf-16-be") else: ot_namerecord.string = val or '' self.font['name'].names.append(ot_namerecord) return ot_namerecord
def func(*args, **kwargs): logger.debug('\n$ ' + shell_cmd_repr(value, args)) try: result = getattr(cls.__originmodule__, value)(*args, **kwargs) return result except Exception as e: logger.error('Error: %s' % e.message) raise e
def fix(self, path, value=15): try: table = self.font.get('gasp') table.gaspRange[65535] = value return True except: logger.error('ER: {}: no table gasp'.format(path)) return
def show(self): if 'gasp' not in self.font.tables: logger.error('no table gasp') return try: logger.info(self.font['gasp'].gaspRange[65535]) except IndexError: logger.error('no index 65535')
def addError(self, test, err): super(BakeryTestResult, self).addError(test, err) _, _err_exception, _ = err test._err = err test._err_msg = _err_exception.message if hasattr(self.el, 'append'): self.el.append(test) if hasattr(test, 'operator'): logger.error(self._format_test_output(test, 'ERROR'))
def show(self, path): try: table = self.font.get('gasp') except: logger.error('ER: {}: no table gasp'.format(path)) return try: logger.info(self.font.get('gasp').gaspRange[65535]) except IndexError: logger.error('ER: {}: no index 65535'.format(path))
def load_config(self, config): """ Loading settings from yaml bake configuration. """ if isinstance(config, dict): self.config = config or {} else: try: configfile = open(config, 'r') except OSError: configfile = open(BAKERY_CONFIGURATION_DEFAULTS, 'r') logger.error(('Cannot read configuration file.' ' Using defaults')) self.config = yaml.safe_load(configfile)
def is_valid(self): content = {} with io.open(self.fontpath, 'r', encoding="utf-8") as fp: content = json.load(fp, object_pairs_hook=collections.OrderedDict) if len(content.get('designer', '').split()) > 4: logger.error( 'ER: Designer key is too long. Fix to "Multiple Designer"') return False if ' and ' in content.get('designer', ''): logger.error( 'ER: Several designers in designer key. Fix to "Multiple Designer"' ) return False if ',' in content.get('designer', ''): logger.error( 'ER: Several designers in designer key. Fix to "Multiple Designer"' ) return False if '.' in content.get('designer', ''): logger.error( 'ER: Several designers in designer key. Fix to "Multiple Designer"' ) return False logger.info(u'OK: Designer "{}"'.format(content.get('designer', ''))) return True
def fix(self, check=False): val = self.font['OS/2'].fsType fontfile = os.path.basename(self.fontpath) if val == 0: from bakery_cli.bakery import Bakery logger.info('OK: {}'.format(fontfile)) return if check: logger.error('ER: {} {}: Change to 0'.format(fontfile, val)) else: logger.error('ER: {} {}: Fixed to 0'.format(fontfile, val)) self.font['OS/2'].fsType = 0 return True
def walk(self): l = len(self.upstream_path) exclude = [ 'build_info', ] for root, dirs, files in os_origin.walk(self.upstream_path, topdown=True): dirs[:] = [d for d in dirs if d not in exclude] for f in files: fullpath = op.join(root, f) if f[-4:].lower() == '.ttx': try: doc = defusedxml.lxml.parse(fullpath) el = doc.xpath('//ttFont[@sfntVersion]') if not el: continue except Exception as exc: msg = 'Failed to parse "{}". Error: {}' logger.error(msg.format(fullpath, exc)) continue self.TTX.append(fullpath[l:].strip('/')) if op.basename(f).lower() == 'metadata.json': self.METADATA.append(fullpath[l:].strip('/')) if f[-4:].lower() in ['.ttf', '.otf']: self.BIN.append(fullpath[l:].strip('/')) if f[-4:].lower() == '.sfd': self.SFD.append(fullpath[l:].strip('/')) if f[-4:].lower() in ['.txt', '.markdown', '.md', '.LICENSE']: self.TXT.append(fullpath[l:].strip('/')) if op.basename(f).lower() in UpstreamDirectory.ALL_LICENSES: self.LICENSE.append(fullpath[l:].strip('/')) for d in dirs: fullpath = op.join(root, d) if op.splitext(fullpath)[1].lower() == '.ufo': self.UFO.append(fullpath[l:].strip('/'))
def is_valid(self): has_errors = False regex = re.compile(r'-(.*?)Italic\.ttf') match = regex.search(self.fontpath) if match: ttfont = ttLib.TTFont(self.fontpath) f = '{:#010b}'.format(ttfont['head'].macStyle) if match.group(1) != 'Bold': if not bool(ttfont['head'].macStyle & 0b10): logger.error( 'ER: HEAD macStyle is {} should be 00000010'.format(f)) has_errors = True elif not bool(ttfont['head'].macStyle & 0b11): logger.error( 'ER: HEAD macStyle is {} should be 00000011'.format(f)) has_errors = True if ttfont['post'].italicAngle == 0: logger.error('ER: POST italicAngle is 0 should be -13') has_errors = True # Check NAME table contains correct names for Italic if ttfont['OS/2'].fsSelection & 0b1: logger.info('OK: OS/2 fsSelection') else: logger.error('ER: OS/2 fsSelection') for name in ttfont['name'].names: if name.nameID not in [2, 4, 6, 17]: continue string = name.toUnicode() if string.endswith('Italic'): logger.info('OK: NAME ID{}:\t{}'.format( name.nameID, string)) else: logger.error('ER: NAME ID{}:\t{}'.format( name.nameID, string)) else: pass
def walk(self): l = len(self.upstream_path) exclude = ['build_info', ] for root, dirs, files in os_origin.walk(self.upstream_path, topdown=True): dirs[:] = [d for d in dirs if d not in exclude] for f in files: fullpath = op.join(root, f) if f[-4:].lower() == '.ttx': try: doc = defusedxml.lxml.parse(fullpath) el = doc.xpath('//ttFont[@sfntVersion]') if not el: continue except Exception as exc: msg = 'Failed to parse "{}". Error: {}' logger.error(msg.format(fullpath, exc)) continue self.TTX.append(fullpath[l:].strip('/')) if op.basename(f).lower() == 'metadata.json': self.METADATA.append(fullpath[l:].strip('/')) if f[-4:].lower() in ['.ttf', '.otf']: self.BIN.append(fullpath[l:].strip('/')) if f[-4:].lower() == '.sfd': self.SFD.append(fullpath[l:].strip('/')) if f[-4:].lower() in ['.txt', '.markdown', '.md', '.LICENSE']: self.TXT.append(fullpath[l:].strip('/')) if op.basename(f).lower() in UpstreamDirectory.ALL_LICENSES: self.LICENSE.append(fullpath[l:].strip('/')) for d in dirs: fullpath = op.join(root, d) if op.splitext(fullpath)[1].lower() == '.ufo': self.UFO.append(fullpath[l:].strip('/'))
def is_valid(self): has_errors = False regex = re.compile(r'-(.*?)Italic\.ttf') match = regex.search(self.fontpath) if match: ttfont = ttLib.TTFont(self.fontpath) f = '{:#010b}'.format(ttfont['head'].macStyle) if match.group(1) != 'Bold': if not bool(ttfont['head'].macStyle & 0b10): logger.error('ER: HEAD macStyle is {} should be 00000010'.format(f)) has_errors = True elif not bool(ttfont['head'].macStyle & 0b11): logger.error('ER: HEAD macStyle is {} should be 00000011'.format(f)) has_errors = True if ttfont['post'].italicAngle == 0: logger.error('ER: POST italicAngle is 0 should be -13') has_errors = True # Check NAME table contains correct names for Italic if ttfont['OS/2'].fsSelection & 0b1: logger.info('OK: OS/2 fsSelection') else: logger.error('ER: OS/2 fsSelection') for name in ttfont['name'].names: if name.nameID not in [2, 4, 6, 17]: continue if name.isUnicode(): string = name.string.decode('utf-16-be') else: string = name.string if string.endswith('Italic'): logger.info('OK: NAME ID{}:\t{}'.format(name.nameID, string)) else: logger.error('ER: NAME ID{}:\t{}'.format(name.nameID, string)) else: pass
def fix(self): # Convert huge and complex fontTools to config python dict fontdata = fontTools_to_dict(self.font) fontdata = clean_name_values(fontdata) familyname = '' for rec in fontdata['names']: if rec['nameID'] == 1: familyname = rec['string'] break fontdata = fix_all_names(fontdata, familyname) logger.error('```') logger.error(os.path.basename(self.fontpath)) logger.error('') for field in fontdata['names']: self.getOrCreateNameRecord(field['nameID'], field['string']) logger.error('```') return True
def is_valid(self): content = {} with io.open(self.fontpath, 'r', encoding="utf-8") as fp: content = json.load(fp, object_pairs_hook=collections.OrderedDict) if len(content.get('designer', '').split()) > 4: logger.error('ER: Designer key is too long. Fix to "Multiple Designer"') return False if ' and ' in content.get('designer', ''): logger.error('ER: Several designers in designer key. Fix to "Multiple Designer"') return False if ',' in content.get('designer', ''): logger.error('ER: Several designers in designer key. Fix to "Multiple Designer"') return False if '.' in content.get('designer', ''): logger.error('ER: Several designers in designer key. Fix to "Multiple Designer"') return False logger.info(u'OK: Designer "{}"'.format(content.get('designer', ''))) return True
def run_bakery(path, verbose=False): # fontbakery-build supports passing arguments of directory or # concrete bakery.y[a]ml files. In case of passing directory # it looks at existing bakery.yml or bakery.yaml and runs on # first matched filepath # There can also be cases when directory does not contain any # bakery.y[a]ml or passed bakery.yml file does not exist. Then # fontbakery-build loads default configuration. bakery_yml_file = None sourcedir = path if os.path.isdir(path): for filename in ['bakery.yml', 'bakery.yaml']: if os.path.exists(os.path.join(path, filename)): bakery_yml_file = os.path.join(path, filename) break else: bakery_yml_file = path sourcedir = os.path.dirname(path) try: if bakery_yml_file: config = yaml.safe_load(open(bakery_yml_file, 'r')) else: raise IOError except IOError: bakery_yml_file = os.path.join(sourcedir, 'bakery.yml') config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) try: builddir = 'build' if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse('HEAD', short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get('TRAVIS_COMMIT', builddir) if 'process_files' not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config['process_files'] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) b = Bakery('', sourcedir, 'builds', builddir) b.addLoggingToFile() b.load_config(bakery_yml_file) b.run() if not ttfautohint_installed(): msg = ('Command line tool `ttfautohint` is required. Install it with' ' `apt-get install ttfautohint` or `brew install ttfautohint`') logger.error(msg) except: logger.error('BUILD FAILED') if verbose or config.get('verbose'): raise logger.error('Run with --verbose to get stacktrace info.') sys.exit(1)
def fix(self, check=False): retval = False fontfile = os.path.basename(self.fontpath) space = self.getGlyph(0x0020) nbsp = self.getGlyph(0x00A0) if space not in ["space", "uni0020"]: logger.error( 'ER: {}: Glyph 0x0020 is called "{}": Change to "space" or "uni0020"' .format(fontfile, space)) if nbsp not in ["nbsp", "uni00A0"]: logger.error( 'ER: {}: Glyph 0x00A0 is called "{}": Change to "nbsp" or "uni00A0"' .format(fontfile, nbsp)) isNbspAdded = isSpaceAdded = False if not nbsp: isNbspAdded = True try: nbsp = self.addGlyph(0x00A0, 'nbsp') except Exception as ex: logger.error('ER: {}'.format(ex)) return False if not space: isSpaceAdded = True try: space = self.addGlyph(0x0020, 'space') except Exception as ex: logger.error('ER: {}'.format(ex)) return False for g in [space, nbsp]: if self.glyphHasInk(g): if check: logger.error( 'ER: {}: Glyph "{}" has ink. Delete any contours or components' .format(fontfile, g)) else: logger.error( 'ER: {}: Glyph "{}" has ink. Fixed: Overwritten by an empty glyph' .format(fontfile, g)) #overwrite existing glyph with an empty one self.font['glyf'].glyphs[g] = ttLib.getTableModule( 'glyf').Glyph() retval = True spaceWidth = self.getWidth(space) nbspWidth = self.getWidth(nbsp) if spaceWidth != nbspWidth or nbspWidth < 0: self.setWidth(nbsp, min(nbspWidth, spaceWidth)) self.setWidth(space, min(nbspWidth, spaceWidth)) if isNbspAdded: if check: msg = 'ER: {} space {} nbsp None: Add nbsp with advanceWidth {}' else: msg = 'ER: {} space {} nbsp None: Added nbsp with advanceWidth {}' logger.error(msg.format(fontfile, spaceWidth, spaceWidth)) if isSpaceAdded: if check: msg = 'ER: {} space None nbsp {}: Add space with advanceWidth {}' else: msg = 'ER: {} space None nbsp {}: Added space with advanceWidth {}' logger.error(msg.format(fontfile, nbspWidth, nbspWidth)) if nbspWidth > spaceWidth and spaceWidth >= 0: if check: msg = 'ER: {} space {} nbsp {}: Change space advanceWidth to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed space advanceWidth to {}' logger.error( msg.format(fontfile, spaceWidth, nbspWidth, nbspWidth)) else: if check: msg = 'ER: {} space {} nbsp {}: Change nbsp advanceWidth to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed nbsp advanceWidth to {}' logger.error( msg.format(fontfile, spaceWidth, nbspWidth, spaceWidth)) return True logger.info('OK: {} space {} nbsp {}'.format(fontfile, spaceWidth, nbspWidth)) return retval
def convert(sourceFont, ttf, otf=None): try: font = fontforge.open(sourceFont) except: logger.error("Error: Could not open font (%s)" % sourceFont) return font.selection.all() # Remove overlap try: font.removeOverlap() except: logger.error("Error: Could not remove overlaps") if otf: try: font.generate(otf) logger.info("OK: Generated OpenType-CFF (%s)" % otf) except: logger.error("Error: Could not generate OpenType-CFF (%s)" % otf) # Convert curves to quadratic (TrueType) try: font.layers["Fore"].is_quadratic = True except: logger.error("Error: Could not convert to quadratic TrueType curves") return # Simplify try: font.simplify(1, ('setstarttoextremum', 'removesingletonpoints', 'mergelines')) except: logger.error("Error: Could not simplify") # Correct Directions try: font.correctDirection() except: logger.error("Error: Could not correct directions") # Generate with DSIG and OpenType tables try: flags = ('dummy-dsig', 'opentype') font.generate(ttf, flags=flags) logger.info("Success: Generated OpenType-TTF (%s)" % ttf) except: logger.error("Error: Could not generate OpenType-TTF (%s)" % ttf) return
def is_valid_italicAngle(self): ttfont = ttLib.TTFont(self.fontpath) if ttfont['post'].italicAngle == 0: logger.error('ER: POST italicAngle is 0 should be -13') return False return True
def logging_err(self, message): logger.error('Error: ' + message.strip())
def fix(self, check=False): space = self.getGlyph(0x0020) nbsp = self.getGlyph(0x00A0) isNbspAdded = isSpaceAdded = False if not nbsp: isNbspAdded = True try: nbsp = self.addGlyph(0x00A0, 'nbsp') except Exception as ex: logger.error('ER: {}'.format(ex)) return False if not space: isSpaceAdded = True try: space = self.addGlyph(0x0020, 'space') except Exception as ex: logger.error('ER: {}'.format(ex)) return False spaceWidth = self.getWidth(space) nbspWidth = self.getWidth(nbsp) fontfile = os.path.basename(self.fontpath) if spaceWidth != nbspWidth or nbspWidth < 0: self.setWidth(nbsp, max(nbspWidth, spaceWidth)) self.setWidth(space, max(nbspWidth, spaceWidth)) if isNbspAdded: if check: msg = 'ER: {} space {} nbsp N: Add nbsp' logger.error(msg.format(fontfile, spaceWidth)) else: msg = 'ER: {} space {} nbsp N: Added nbsp to {}' logger.error(msg.format(fontfile, spaceWidth, spaceWidth)) if isSpaceAdded: if check: msg = 'ER: {} space N nbsp {}: Add space' logger.error(msg.format(fontfile, nbspWidth)) else: msg = 'ER: {} space N nbsp {}: Added space {}' logger.error(msg.format(fontfile, nbspWidth, nbspWidth)) if nbspWidth < spaceWidth: if check: msg = 'ER: {} space {} nbsp {}: Change nbsp to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed nbsp to {}' logger.error(msg.format(fontfile, spaceWidth, nbspWidth, spaceWidth)) else: if check: msg = 'ER: {} space {} nbsp {}: Change space to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed space to {}' logger.error(msg.format(fontfile, spaceWidth, nbspWidth, nbspWidth)) return True logger.info('OK: {} space {} nbsp {}'.format(fontfile, spaceWidth, nbspWidth)) return
def run_bakery(path, verbose=False): # fontbakery-build supports passing arguments of directory or # concrete bakery.y[a]ml files. In case of passing directory # it looks at existing bakery.yml or bakery.yaml and runs on # first matched filepath # There can also be cases when directory does not contain any # bakery.y[a]ml or passed bakery.yml file does not exist. Then # fontbakery-build loads default configuration. bakery_yml_file = None sourcedir = path if os.path.isdir(path): for filename in ["bakery.yml", "bakery.yaml"]: if os.path.exists(os.path.join(path, filename)): bakery_yml_file = os.path.join(path, filename) break else: bakery_yml_file = path sourcedir = os.path.dirname(path) try: if bakery_yml_file: config = yaml.safe_load(open(bakery_yml_file, "r")) else: raise IOError except IOError: bakery_yml_file = os.path.join(sourcedir, "bakery.yml") config = yaml.safe_load(open(BAKERY_CONFIGURATION_DEFAULTS)) try: builddir = "build" if GITPYTHON_INSTALLED: try: repo = Repo(sourcedir) builddir = repo.git.rev_parse("HEAD", short=True) except git.exc.InvalidGitRepositoryError: pass builddir = os.environ.get("TRAVIS_COMMIT", builddir) if "process_files" not in config: directory = UpstreamDirectory(sourcedir) # normalize process_files path config["process_files"] = directory.get_fonts() create_bakery_config(bakery_yml_file, config) b = Bakery("", sourcedir, "builds", builddir) b.addLoggingToFile() b.load_config(bakery_yml_file) b.run() if not ttfautohint_installed(): msg = ( "Command line tool `ttfautohint` is required. Install it with" " `apt-get install ttfautohint` or `brew install ttfautohint`" ) logger.error(msg) except: if verbose or config.get("verbose"): raise sys.exit(1)
return errors for path in args.ttf_font: if not os.path.exists(path): continue font = TTFont(path) name = font['name'].getName(2, 3, 1) if not name: continue fontStyle = name.string.decode(name.getEncoding()) errors = validate(font, fontStyle) if errors: for error, function, arguments in errors: logger.error(error) if args.autofix and function: try: function(*arguments) except FixNotApplied as ex: logger.error('ER: Fix can not be applied. See details below') logger.error('\t{}'.format(ex.message)) continue font.save(path + '.fix')
def fix(self, check=False): retval = False fontfile = os.path.basename(self.fontpath) space = self.getGlyph(0x0020) nbsp = self.getGlyph(0x00A0) if space not in ["space", "uni0020"]: logger.error('ER: {}: Glyph 0x0020 is called "{}": Change to "space" or "uni0020"'.format(fontfile, space)) if nbsp not in ["nbsp", "uni00A0"]: logger.error('ER: {}: Glyph 0x00A0 is called "{}": Change to "nbsp" or "uni00A0"'.format(fontfile, nbsp)) isNbspAdded = isSpaceAdded = False if not nbsp: isNbspAdded = True try: nbsp = self.addGlyph(0x00A0, 'nbsp') except Exception as ex: logger.error('ER: {}'.format(ex)) return False if not space: isSpaceAdded = True try: space = self.addGlyph(0x0020, 'space') except Exception as ex: logger.error('ER: {}'.format(ex)) return False for g in [space, nbsp]: if self.glyphHasInk(g): if check: logger.error('ER: {}: Glyph "{}" has ink. Delete any contours or components'.format(fontfile, g)) else: logger.error('ER: {}: Glyph "{}" has ink. Fixed: Overwritten by an empty glyph'.format(fontfile, g)) #overwrite existing glyph with an empty one self.font['glyf'].glyphs[g] = ttLib.getTableModule('glyf').Glyph() retval = True spaceWidth = self.getWidth(space) nbspWidth = self.getWidth(nbsp) if spaceWidth != nbspWidth or nbspWidth < 0: self.setWidth(nbsp, min(nbspWidth, spaceWidth)) self.setWidth(space, min(nbspWidth, spaceWidth)) if isNbspAdded: if check: msg = 'ER: {} space {} nbsp None: Add nbsp with advanceWidth {}' else: msg = 'ER: {} space {} nbsp None: Added nbsp with advanceWidth {}' logger.error(msg.format(fontfile, spaceWidth, spaceWidth)) if isSpaceAdded: if check: msg = 'ER: {} space None nbsp {}: Add space with advanceWidth {}' else: msg = 'ER: {} space None nbsp {}: Added space with advanceWidth {}' logger.error(msg.format(fontfile, nbspWidth, nbspWidth)) if nbspWidth > spaceWidth and spaceWidth >= 0: if check: msg = 'ER: {} space {} nbsp {}: Change space advanceWidth to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed space advanceWidth to {}' logger.error(msg.format(fontfile, spaceWidth, nbspWidth, nbspWidth)) else: if check: msg = 'ER: {} space {} nbsp {}: Change nbsp advanceWidth to {}' else: msg = 'ER: {} space {} nbsp {}: Fixed nbsp advanceWidth to {}' logger.error(msg.format(fontfile, spaceWidth, nbspWidth, spaceWidth)) return True logger.info('OK: {} space {} nbsp {}'.format(fontfile, spaceWidth, nbspWidth)) return retval