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(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 logging_raw(self, message): if message.startswith('### ') or message.startswith('## '): message = '\n' * 3 + message.strip() if message.startswith('$ '): message = '\n' + message.strip().replace(os.getcwd() + os.path.sep, '') logger.info(message)
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 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 logging_task(self, message): if self.forcerun: self.incr_total_tasks() return prefix = "### (%s of %s) " % (self._counter, self.total_tasks) self.incr_task_counter() logger.info('\n\n\n' + (prefix + message.strip()).strip())
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 addSuccess(self, test): super(BakeryTestResult, self).addSuccess(test) if hasattr(test, 'operator'): logger.info(self._format_test_output(test, 'OK')) test_method = getattr(test, test._testMethodName) if hasattr(test_method, 'autofix'): # if testcase is marked as autofixed then add it to ff if hasattr(self.ff, 'append'): self.ff.append(test) elif hasattr(self.sl, 'append'): self.sl.append(test)
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 callmethod(self, methodname, test): import inspect pkg = '.'.join(methodname.split('.')[:-1]) mod = importlib.import_module(pkg) method = getattr(mod, methodname.split('.')[-1]) if not inspect.isclass(method): return method(test) klass_instance = method(test, test.operator.path) if hasattr(klass_instance, 'get_shell_command'): logger.info('$ {}'.format(klass_instance.get_shell_command())) if test.apply_fix: klass_instance.apply(override_origin=test.apply_fix)
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 fix(self): newfilename = self.validate() new_targetpath = os.path.join(os.path.dirname(self.fontpath), newfilename) shutil.move(self.fontpath, new_targetpath) from bakery_cli.logger import logger logger.info('$ mv {} {}'.format(self.fontpath, os.path.basename(new_targetpath))) self.testcase.operator.path = new_targetpath from bakery_cli.utils import ProcessedFile f = ProcessedFile() f.filepath = newfilename self.save_after_fix = False return True
def fix(self, check=True): if check: if not self.is_valid(): return False return True if self.is_valid(): logger.info(u'OK: Designer "{}"'.format(content.get('designer', ''))) return True from bakery_cli.scripts.genmetadata import striplines content = {} with io.open(self.fontpath, 'r', encoding="utf-8") as fp: content = json.load(fp, object_pairs_hook=collections.OrderedDict) content['designer'] = 'Multiple Designers' with io.open(self.fontpath, 'w', encoding='utf-8') as f: contents = json.dumps(content, indent=2, ensure_ascii=False) f.write(striplines(contents)) 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 fix(self, check=True): if check: if not self.is_valid(): return False return True if self.is_valid(): logger.info(u'OK: Designer "{}"'.format(content.get( 'designer', ''))) return True from bakery_cli.scripts.genmetadata import striplines content = {} with io.open(self.fontpath, 'r', encoding="utf-8") as fp: content = json.load(fp, object_pairs_hook=collections.OrderedDict) content['designer'] = 'Multiple Designers' with io.open(self.fontpath, 'w', encoding='utf-8') as f: contents = json.dumps(content, indent=2, ensure_ascii=False) f.write(striplines(contents)) return True
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 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, 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 logging_cmd(self, message): logger.info('\n$ ' + message.strip().replace(os.getcwd() + os.path.sep, ''))
def validate(font, fontStyle): errors = [] f = '{:#09b}'.format(font['head'].macStyle) if fontStyle.endswith('Italic'): if not fontStyleIsBold(fontStyle): if not bool(font['head'].macStyle & 0b10): errors.append(('ER: HEAD macStyle is {} should be 00000010'.format(f), setMacStyle, [font, font['head'].macStyle | 0b10])) elif not bool(font['head'].macStyle & 0b11): errors.append(('ER: HEAD macStyle is {} should be 00000011'.format(f), setMacStyle, [font, font['head'].macStyle | 0b11])) else: if not fontStyleIsBold(fontStyle): if bool(font['head'].macStyle & 0b10): newvalue = font['head'].macStyle | 0b1111111111111100 errors.append(('ER: HEAD macStyle is {} should be {:#09b}'.format(f, newvalue), setMacStyle, [font, newvalue])) elif bool(font['head'].macStyle & 0b01): newvalue = font['head'].macStyle | 0b1111111111111101 errors.append(('ER: HEAD macStyle is {} should be {:#09b}'.format(f, newvalue), setMacStyle, [font, newvalue])) if font['post'].italicAngle != 0 and not fontStyle.endswith('Italic'): errors.append(('ER: POST italicAngle is {} should be 0'.format(font['post'].italicAngle), italicAngle, [font, 0])) if font['post'].italicAngle == 0 and fontStyle.endswith('Italic'): newvalue = getSuggestedItalicAngle(font) errors.append(('ER: POST italicAngle is 0 should be {}'.format(newvalue), italicAngle, [font, newvalue])) # Check NAME table contains correct names for Italic if fontStyle.endswith('Italic'): if not fontStyleIsBold(fontStyle): if font['OS/2'].fsSelection & 0b000001: logger.info('OK: OS/2 fsSelection') else: newvalue = font['OS/2'].fsSelection | 0b1 msg = 'ER: OS/2 fsSelection is {:#06b} should be {:#06b}' errors.append((msg.format(font['OS/2'].fsSelection, newvalue), setFsSelection, [font, newvalue])) else: if font['OS/2'].fsSelection & 0b100001: logger.info('OK: OS/2 fsSelection') else: newvalue = font['OS/2'].fsSelection | 0b100001 msg = 'ER: OS/2 fsSelection is {:#06b} should be {:#06b}' errors.append((msg.format(font['OS/2'].fsSelection, newvalue), setFsSelection, [font, newvalue])) elif fontStyleIsBold(fontStyle): if font['OS/2'].fsSelection & 0b100000: logger.info('OK: OS/2 fsSelection') else: newvalue = font['OS/2'].fsSelection | 0b100000 msg = 'ER: OS/2 fsSelection is {:#06b} should be {:#06b}' errors.append((msg.format(font['OS/2'].fsSelection, newvalue), setFsSelection, [font, newvalue])) for name in font['name'].names: if name.nameID not in [2, 4, 6, 17]: continue string = name.string.decode(name.getEncoding()) if fontStyle.endswith('Italic'): if string.endswith('Italic'): logger.info('OK: NAME ID{}:\t{}'.format(name.nameID, string)) else: errors.append(('ER: NAME ID{}:\t{}'.format(name.nameID, string), setValidNames, [font, fontStyleIsBold(fontStyle)])) elif fontStyleIsBold(fontStyle): if fontStyleIsBold(string): logger.info('OK: NAME ID{}:\t{}'.format(name.nameID, string)) else: errors.append(('ER: NAME ID{}:\t{}'.format(name.nameID, string), setValidNames, [font, fontStyleIsBold(fontStyle)])) return errors
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