コード例 #1
0
ファイル: optimize.py プロジェクト: davelab6/fontbakery-cli
    def run(self, filename, pipedata):
        if 'optimize' in pipedata and not pipedata['optimize']:
            return
        self.bakery.logging_raw('### Optimize TTF {}'.format(filename))
        # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py
        from fontTools.subset import Options, Subsetter, load_font, save_font

        options = Options()
        options.layout_features = "*"
        options.name_IDs = "*"
        options.hinting = True
        options.notdef_outline = True

        font = load_font(op.join(self.builddir, filename), options)
        subsetter = Subsetter(options=options)
        subsetter.populate(glyphs=font.getGlyphOrder())
        subsetter.subset(font)
        save_font(font, op.join(self.builddir, filename + '.opt'), options)

        newsize = op.getsize(op.join(self.builddir, filename + '.opt'))
        origsize = op.getsize(op.join(self.builddir, filename))

        # compare filesizes TODO print analysis of this :)
        comment = "# look at the size savings of that subset process"
        self.bakery.logging_cmd("ls -l '%s'* %s" % (filename, comment))

        statusmessage = "{0}.opt: {1} bytes\n{0}: {2} bytes\n"
        self.bakery.logging_raw(statusmessage.format(filename, newsize, origsize))

        # move ttx files to src
        shutil.move(op.join(self.builddir, filename + '.opt'),
                    op.join(self.builddir, filename),
                    log=self.bakery.logger)
コード例 #2
0
ファイル: pyftsubset.py プロジェクト: bitforks/fontbakery
    def execute_pyftsubset(self, pipedata, subsetname, name, glyphs="", args=""):
        from fontTools.subset import Subsetter, Options, load_font, save_font
        target_file = '{0}.{1}'.format(op.join(self.builddir, name)[:-4],
                                       subsetname)

        options = Options()
        cmd_options = ''
        if pipedata.get('pyftsubset'):
            cmd_options = pipedata['pyftsubset']
            options.parse_opts(pipedata['pyftsubset'].split())

        if pipedata.get('pyftsubset.%s' % subsetname):
            cmd_options = pipedata['pyftsubset.%s' % subsetname]
            options.parse_opts(pipedata['pyftsubset.%s' % subsetname].split())

        font = load_font(op.join(self.builddir, name), options)

        unicodes = re_range([int(g.replace('U+', ''), 16) for g in glyphs.split()])
        self.bakery.logging_cmd('pyftsubset --unicodes="{0}" {2} {1}'.format(unicodes, name,
                                                                             cmd_options))

        subsetter = Subsetter(options=options)
        subsetter.populate(unicodes=[int(g.replace('U+', ''), 16) for g in glyphs.split()])
        subsetter.subset(font)

        self.bakery.logging_cmd('mv {0}.subset {1}'.format(name, target_file))
        save_font(font, target_file, options)
コード例 #3
0
ファイル: make_subset.py プロジェクト: bitforks/TachyFont
def main(args):
  """Subset a font (useful for making small test fonts).

  Args:
    args: list, arguments the user typed.
  """
  parser = argparse.ArgumentParser()
  parser.add_argument('fontfile', help='Input font file')
  parser.add_argument('--subset_postfix', default='',
                      help='Postfix to the subset extension')
  parser.add_argument('--text', default='',
                      help='Text to include in the subset')
  parser.add_argument('--unicodes', default='',
                      help='Comma separated list of Unicode codepoints (hex) '
                      'to include in the subset; eg, "e7,0xe8,U+00e9"')
  parser.add_argument('--glyphs', default='',
                      help='Comma separated list of glyph IDs (decimal) to '
                      'include in the subset; eg, "1,27"')
  parser.add_argument('--hinting', default=False, action='store_true',
                      help='Enable hinting if specified, no hinting if not '
                      'present')

  cmd_args = parser.parse_args(args)

  options = Options()
  # Definitely want the .notdef glyph and outlines.
  options.notdef_glyph = True
  options.notdef_outline = True
  # Get the item. to keep in the subset.
  text = cmd_args.text
  unicodes_str = cmd_args.unicodes.lower().replace('0x', '').replace('u+', '')
  # TODO(bstell) replace this whole files by using the new subset.py code
  unicodes_input = [c for c in unicodes_str.split(',') if c]
  unicodes = []
  for c in unicodes_input:
    if '-' in c:
      uni_range = c.split('-')
      uni_range_expanded = range(int(uni_range[0], 16), int(uni_range[1], 16) + 1)
      unicodes.extend(uni_range_expanded)
    else:
      unicodes.append(int(c, 16))
  #unicodes = [int(c, 16) for c in unicodes_input_expanded]
  glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c]
  fontfile = cmd_args.fontfile
  options.hinting = cmd_args.hinting  # False => no hinting
  options.hinting = True  # hint stripping for CFF is currently broken

  dirname = os.path.dirname(fontfile)
  basename = os.path.basename(fontfile)
  filename, extension = os.path.splitext(basename)
  subset_postfix = cmd_args.subset_postfix
  output_file = dirname + '/' + filename + '_subset' + subset_postfix + extension
  print "output_file =", output_file
  font = load_font(fontfile, options, lazy=False)

  subsetter = Subsetter(options)
  subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs)
  subsetter.subset(font)
  save_font(font, output_file, options)
コード例 #4
0
ファイル: cleaner.py プロジェクト: davelab6/incremental-fonts
 def clean(self):
   """Remove glyphs that should have outlines but do not.
   """
   rcmap = reverse_cmap(self.font)
   names = set(rcmap.keys())
   names.difference_update(self._invalid_glyphs(names, rcmap))
   subsetter = Subsetter(options=self.options)
   subsetter.populate(glyphs=names)
   subsetter.subset(self.font)
コード例 #5
0
ファイル: subset.py プロジェクト: ruuda/blog
def subset(fontfile, outfile_basename, glyphs):
    options = Options()

    # Fonttools has this feature that if you enable 'dlig', it will also give
    # you glyphs that you did not ask for, but if you do not enable 'dlig',
    # then discretionary ligatures do not render properly.
    # See https://github.com/behdad/fonttools/issues/43.
    # As a workaround, only enable 'dlig' if there are glyphs for discretionary
    # ligatures.
    # TODO: This should be fixed, consider upgrading.
    # https://github.com/fonttools/fonttools/commit/022536212be4cf022a2cb9a286fec8be1931d19b.
    dligs = set(glyphs).intersection(['c_b', 'c_h', 'c_k', 'c_p', 'ct', 'g_i',
                                      'q_u', 's_b', 's_h', 's_k', 's_p', 'st'])
    if len(dligs) > 0:
        options.layout_features.append('dlig')
    else:
        # Due to a bug in Fonttools, options are actually global, so the
        # remnants of the previous instance are visible here.
        # See https://github.com/behdad/fonttools/issues/413.
        if 'dlig' in options.layout_features:
            options.layout_features.remove('dlig')

    # Same for small caps, it needs to be enabled explicitly. Luckily, only the
    # glyphs in the list get included, no extra ones.
    if any(g.endswith('.smcp') for g in glyphs):
        options.layout_features.append('smcp')
        options.layout_features.append('c2sc')
    else:
        if 'smcp' in options.layout_features:
            options.layout_features.remove('smcp')
        if 'c2sc' in options.layout_features:
            options.layout_features.remove('c2sc')

    # Fonts that went through the FontForge roundtrip will have subroutinized
    # programs in the CFF table. This presumably reduces file size for full
    # fonts, but on subsetted fonts it hurts file size and compressability, so
    # desubroutinize.
    options.desubroutinize = True

    font = load_font(fontfile, options)

    subsetter = Subsetter(options = options)
    subsetter.populate(glyphs = glyphs)
    subsetter.subset(font)

    prune_cmaps(font)

    options.flavor = "woff"
    save_font(font, outfile_basename + ".woff", options)

    options.flavor = "woff2"
    save_font(font, outfile_basename + ".woff2", options)

    font.close()
コード例 #6
0
ファイル: closure_taker.py プロジェクト: ahmet-celik/TachyFon
 def closure(self):
   """Takes closure of glyphs specified by glyph_names and glyph_codes.
   """
   options = Options()
   options.notdef_glyph = False
   subsetter = Subsetter(options=options)
   subsetter.populate(glyphs=self.glyph_names, unicodes=self.glyph_codes)
   subsetter._closure_glyphs(self.font)
   gids = sorted(self.glyph_name_to_id[gg] for gg in subsetter.glyphs_all
                 if gg != '.notdef')
   return gids
コード例 #7
0
ファイル: subset.py プロジェクト: ruuda/blog
def subset(fontfile, outfile_basename, glyphs):
    options = Options()

    # Fonttools has this "feature" that if you enable 'dlig', it will also give
    # you glyphs that you did not ask for, but if you do not enable 'dlig',
    # then discretionary ligatures do not render properly.
    # See https://github.com/behdad/fonttools/issues/43.
    # As a workaround, only enable 'dlig' if there are glyphs for discretionary
    # ligatures.
    dligs = set(glyphs).intersection(["c_b", "c_h", "c_k", "c_p", "ct", "g_i", "q_u", "s_b", "s_h", "s_k", "s_p", "st"])
    if len(dligs) > 0:
        options.layout_features.append("dlig")
    else:
        # Due to a bug in Fonttools, options are actually global, so the
        # remnants of the previous instance are visible here.
        # See https://github.com/behdad/fonttools/issues/413.
        if "dlig" in options.layout_features:
            options.layout_features.remove("dlig")

    # Same for small caps, it needs to be enabled explicitly. Luckily, only the
    # glyphs in the list get included, no extra ones.
    if any(g.endswith(".smcp") for g in glyphs):
        options.layout_features.append("smcp")
        options.layout_features.append("c2sc")
    else:
        if "smcp" in options.layout_features:
            options.layout_features.remove("smcp")
        if "c2sc" in options.layout_features:
            options.layout_features.remove("c2sc")

    # Fonts that went through the FontForge roundtrip will have subroutinized
    # programs in the CFF table. This presumably reduces file size for full
    # fonts, but on subsetted fonts it hurts file size and compressability, so
    # desubroutinize.
    options.desubroutinize = True

    font = load_font(fontfile, options)

    subsetter = Subsetter(options=options)
    subsetter.populate(glyphs=glyphs)
    subsetter.subset(font)

    prune_cmaps(font)

    options.flavor = "woff"
    save_font(font, outfile_basename + ".woff", options)

    options.flavor = "woff2"
    save_font(font, outfile_basename + ".woff2", options)

    font.close()
コード例 #8
0
ファイル: optimize.py プロジェクト: bitforks/fontbakery
    def run(self, pipedata):
        if 'optimize' in pipedata and not pipedata['optimize']:
            return

        from bakery_cli.utils import ProcessedFile
        filename = ProcessedFile()

        self.bakery.logging_raw('### Optimize TTF {}'.format(filename))
        # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py
        from fontTools.subset import Options, Subsetter, load_font, save_font

        options = Options()
        options.layout_features = ["*"]
        options.name_IDs = ["*"]
        options.hinting = True
        options.legacy_kern = True
        options.notdef_outline = True
        options.no_subset_tables += ['DSIG']
        options.drop_tables = list(set(options._drop_tables_default) - set(['DSIG']))

        cmd_options = ('--glyphs=*'
                       ' --layout-features=*'
                       ' --name-IDs=*'
                       ' --hinting'
                       ' --legacy-kern --notdef-outline'
                       ' --no-subset-tables+=DSIG'
                       ' --drop-tables-=DSIG')

        font = load_font(op.join(self.builddir, filename), options)

        cmdline = 'pyftsubset {1} {0}'.format(cmd_options, op.join(self.builddir, filename))
        self.bakery.logging_cmd(cmdline)

        subsetter = Subsetter(options=options)
        subsetter.populate(glyphs=font.getGlyphOrder())
        subsetter.subset(font)
        save_font(font, op.join(self.builddir, filename + '.fix'), options)

        # compare filesizes TODO print analysis of this :)
        comment = "# look at the size savings of that subset process"
        self.bakery.logging_cmd(comment)
        run(u"ls -la {0} {0}.fix | awk '{{ print $5 \"\t\" $9 }}'".format(unicode(op.join(self.builddir, filename))))

        comment = "# copy back optimized ttf to original filename"
        self.bakery.logging_cmd(comment)
        shutil.move(op.join(self.builddir, filename + '.fix'),
                    op.join(self.builddir, filename))
コード例 #9
0
ファイル: cleaner.py プロジェクト: bitforks/TachyFont
 def clean(self, verbose):
   """Remove glyphs that should have outlines but do not.
   """
   if verbose:
     print('reverse_cmap')
   rcmap = reverse_cmap(self.font)
   names = set(rcmap.keys())
   if verbose:
     print('names.difference_update')
   names.difference_update(self._invalid_glyphs(names, rcmap))
   subsetter = Subsetter(options=self.options)
   if verbose:
     print('populate')
   subsetter.populate(glyphs=names)
   if verbose:
     print('subset')
   subsetter.subset(self.font)
コード例 #10
0
ファイル: make_subset.py プロジェクト: ahmet-celik/TachyFon
def main(args):
  """Subset a font (useful for making small test fonts).

  Args:
    args: list, arguments the user typed.
  """
  parser = argparse.ArgumentParser()
  parser.add_argument('fontfile', help='Input font file')
  parser.add_argument('--text', default='',
                      help='Text to include in the subset')
  parser.add_argument('--unicodes', default='',
                      help='Comma separated list of Unicode codepoints (hex) '
                      'to include in the subset; eg, "e7,0xe8,U+00e9"')
  parser.add_argument('--glyphs', default='',
                      help='Comma separated list of glyph IDs (decimal) to '
                      'include in the subset; eg, "1,27"')
  parser.add_argument('--hinting', default=False, action='store_true',
                      help='Enable hinting if specified, no hinting if not '
                      'present')

  cmd_args = parser.parse_args(args)

  options = Options()
  # Definitely want the .notdef glyph and outlines.
  options.notdef_glyph = True
  options.notdef_outline = True
  # Get the item. to keep in the subset.
  text = cmd_args.text
  unicodes_str = cmd_args.unicodes.lower().replace('0x', '').replace('u+', '')
  unicodes = [int(c, 16) for c in unicodes_str.split(',') if c]
  glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c]
  fontfile = cmd_args.fontfile
  options.hinting = cmd_args.hinting  # False => no hinting

  dirname = os.path.dirname(fontfile)
  basename = os.path.basename(fontfile)
  filename, extension = os.path.splitext(basename)
  output_file = dirname + '/' + filename + '_subset' + extension
  font = load_font(fontfile, options, lazy=False)

  subsetter = Subsetter(options)
  subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs)
  subsetter.subset(font)
  save_font(font, output_file, options)
コード例 #11
0
ファイル: fonttoolsstuff.py プロジェクト: yanone/ynlib
	def shrink(self, freezeFeatures = [], removeFeatures = [], glyphs = [], replaceNames = '', suffix = ''):

			# Freeze features
			from pyftfeatfreeze.pyftfeatfreeze import RemapByOTL
			class FreezeOptions(object):
				pass
			options = FreezeOptions()
			options.inpath = ''
			options.outpath = ''
			options.features = ','.join(freezeFeatures) # comma-separated list of OpenType feature tags, e.g. 'smcp,c2sc,onum'
			options.script = 'latn' # OpenType script tag, e.g. 'cyrl' (default: '%(default)s')
			options.lang = None # OpenType language tag, e.g. 'SRB ' (optional)
			options.zapnames = False # zap glyphnames from the font ('post' table version 3, .ttf only)
			options.rename = True if suffix else False # add a suffix to the font menu names (by default, the suffix will be constructed from the OpenType feature tags)
			options.usesuffix = suffix # use a custom suffix when -S is provided
			options.replacenames = replaceNames # search for strings in the font naming tables and replace them, format is 'search1/replace1,search2/replace2,...'
			options.info = True # update font version string
			options.report = False # report languages, scripts and features in font
			options.names = False # output names of remapped glyphs during processing
			options.verbose = True
			remapByOTL = RemapByOTL(options)
			remapByOTL.ttx = self.TTFont
			remapByOTL.remapByOTL()
			remapByOTL.renameFont()

			# Subset
			from fontTools.subset import Subsetter, Options
#			features = list(set(self.features()) - (set(freezeFeatures) & set(removeFeatures)))
			features = list(set(self.features()) - set(removeFeatures))
#			print 'target features', features
			options = Options(layout_features = features, name_IDs = '*', glyph_names = True, name_legacy = True, name_languages = '*')
			subsetter = Subsetter(options = options)

			# populate with unicodes
			unicodes = []
			for t in self.TTFont['cmap'].tables:
				if t.isUnicode():
					unicodes.extend(list(t.cmap.keys()))
			subsetter.populate(unicodes = unicodes)
			subsetter.subset(self.TTFont)
コード例 #12
0
ファイル: subset.py プロジェクト: klaaz0r/fonttools-service
def subsetFont(base64, subset):
    # tmp file names
    tmpInputFontName = tmpFileName(".ttf")
    tmpOutputFontName = tmpFileName(".woff")

    # remove data header from base64
    fontbase64 = base64.split(",")[1]

    with open(tmpInputFontName, "wb") as f:
        fontinput = f.write(fontbase64.decode('base64'))
        f.close()

    # open the font with fontTools
    font = TTFont(tmpInputFontName)

    options = Options()
    options.desubroutinize = True

    # export the font as woff for web use
    options.with_zopfli = True
    options.flavor = "woff"

    subsetter = Subsetter(options=options)
    subsetter.populate(text=subset)
    subsetter.subset(font)

    save_font(font, tmpOutputFontName, options)

    subsettedFont = open(tmpOutputFontName, "rb").read().encode("base64")

    os.unlink(tmpOutputFontName)
    os.unlink(tmpInputFontName)

    return {'subset': subsettedFont}
コード例 #13
0
ファイル: main.py プロジェクト: Arahabica/font-slice
def generate_subset(unicode_range, flavor, font_file, output_dir):
    """
    Generate font subset.
    You can do the same with the following command.
    $ pyftsubset YOUR_FONT.otf \
    --unicodes=U+943a-943b \
    --layout-features='*' \
    --flavor=woff \
    --name-IDs='*' \
    --output-file=style/font-subsets/YOUR_FONT-subset-1.woff
    """
    args = ["--layout-features='*'", "--flavor=%s" % flavor]
    options = Options()
    options.parse_opts(args)
    subsetter = Subsetter(options)
    font = load_font(font_file, options)

    subsetter.populate(unicodes=parse_unicodes(unicode_range))
    subsetter.subset(font)

    font_path = Path(font_file)
    name = font_path.stem
    unicode_range_hash = _get_unicode_range_hash(unicode_range)
    outfile = "%s/%s/%s-subset-%s.%s" % (
        output_dir,
        FONT_DIR,
        name,
        unicode_range_hash,
        flavor,
    )
    save_font(font, outfile, options)
    font.close()
コード例 #14
0
 def generateWoff2(self, verbosity=0):
     woff2_list = []
     os.makedirs(self.assetdir, exist_ok=True)
     for subname, (subrange, unicodes) in self.urdict.items():
         if verbosity == 2: print("Processing", subname)
         subs = Subsetter()
         font = TTFont(self.fontfile)
         subs.populate(unicodes=unicodes)
         subs.subset(font)
         cmap = font.getBestCmap()
         glyphcount = len(font.getGlyphOrder()) - 1
         if cmap:
             outfile = os.path.join(self.assetdir,
                                    self.basename + "." + subname + ".woff2")
             font.flavor = 'woff2'
             font.save(outfile)
             woff2_list.append((outfile, subrange))
             if verbosity == 1:
                 print("Generated", outfile)
             elif verbosity == 2:
                 print("  Generated", outfile)
                 print("  Found", glyphcount, "glyphs for",
                       len(cmap), "out of", len(unicodes), "unicodes")
         else:
             if verbosity == 2:
                 print("  Found no glyphs for any of", len(unicodes), "unicodes")
         font.close()
     return woff2_list
コード例 #15
0
ファイル: pyftsubset.py プロジェクト: davelab6/fontbakery
    def execute_pyftsubset(self,
                           pipedata,
                           subsetname,
                           name,
                           glyphs="",
                           args=""):
        from fontTools.subset import Subsetter, Options, load_font, save_font
        target_file = '{0}.{1}'.format(
            op.join(self.builddir, name)[:-4], subsetname)

        options = Options()
        cmd_options = ''
        if pipedata.get('pyftsubset'):
            cmd_options = pipedata['pyftsubset']
            options.parse_opts(pipedata['pyftsubset'].split())

        if pipedata.get('pyftsubset.%s' % subsetname):
            cmd_options = pipedata['pyftsubset.%s' % subsetname]
            options.parse_opts(pipedata['pyftsubset.%s' % subsetname].split())

        font = load_font(op.join(self.builddir, name), options)

        unicodes = re_range(
            [int(g.replace('U+', ''), 16) for g in glyphs.split()])
        self.bakery.logging_cmd('pyftsubset --unicodes="{0}" {2} {1}'.format(
            unicodes, name, cmd_options))

        subsetter = Subsetter(options=options)
        subsetter.populate(
            unicodes=[int(g.replace('U+', ''), 16) for g in glyphs.split()])
        subsetter.subset(font)

        self.bakery.logging_cmd('mv {0}.subset {1}'.format(name, target_file))
        save_font(font, target_file, options)
コード例 #16
0
ファイル: fontstamp.py プロジェクト: marksteve/fontstamp
def subset_font(font, text, options):
    subsetter = Subsetter(options)
    subsetter.populate(text=text)
    subsetter.subset(font)
    with BytesIO() as outfile:
        save_font(font, outfile, options)
        outfile.seek(0)
        out = b64encode(outfile.read())
    return out.decode()
コード例 #17
0
ファイル: webfont.py プロジェクト: openfonts-jp/prepare_tool
    def __generateForWeight(self, weight: str, font: Font) -> None:
        package = self.__core.package
        base_dir = self.__core.directories.webfonts
        output_dir = base_dir.joinpath(f"./{package.version}/{weight}")
        font_path = self.__core.findFontfilePath(font)

        output_dir.mkdir(parents=True, exist_ok=True)
        metadata = self.__generateMetadata()

        fake = Faker()
        fake.seed(package.id)
        subset_fontname: str = fake.name()

        options = Options()
        options.font_number = font.number
        options.hinting = False
        options.desubroutinize = True
        options.drop_tables += [
            'FFTM', 'PfEd', 'TeX', 'BDF', 'cvt', 'fpgm', 'prep', 'gasp',
            'VORG', 'CBDT', 'CBLC', 'sbix'
        ]
        for ignored in ['rvrn', 'locl']:
            options.layout_features.remove(ignored)

        for unicodes_file in FILE_DIR.UNICODE_TEXT.glob('./**/*.txt'):
            idx = unicodes_file.stem
            unicodes: List[str] = []

            with open(unicodes_file, 'r') as unicode_read_io:
                for line in unicode_read_io.readlines():
                    unicodes.extend(parse_unicodes(line.split('#')[0]))

            with load_font(font_path, options) as ttfont:
                subsetter = Subsetter(options=options)
                subsetter.populate(unicodes=unicodes)
                subsetter.subset(ttfont)

                for record in ttfont['name'].names:
                    if record.nameID == NAME_ID.COPYRIGHT:
                        record.string = '\n'.join(package.copyrights)
                    elif record.nameID in FAMILY_RELATED_NAME_ID:
                        record.string = subset_fontname

                woff_file = output_dir.joinpath(f"{idx}.woff")
                with open(woff_file, 'wb') as woff_write_io:
                    options.flavor = 'woff'
                    ttfont.flavorData = WOFFFlavorData()
                    ttfont.flavorData.metaData = metadata
                    save_font(ttfont, woff_write_io, options)

                woff2_file = output_dir.joinpath(f"{idx}.woff2")
                with open(woff2_file, 'wb') as woff2_write_io:
                    options.flavor = 'woff2'
                    ttfont.flavorData = WOFF2FlavorData()
                    ttfont.flavorData.metaData = metadata
                    save_font(ttfont, woff2_write_io, options)
コード例 #18
0
 def closure(self):
     """Takes closure of glyphs specified by glyph_names and glyph_codes.
 """
     options = Options()
     options.notdef_glyph = False
     subsetter = Subsetter(options=options)
     subsetter.populate(glyphs=self.glyph_names, unicodes=self.glyph_codes)
     subsetter._closure_glyphs(self.font)
     gids = sorted(self.glyph_name_to_id[gg] for gg in subsetter.glyphs_all
                   if gg != '.notdef')
     return gids
コード例 #19
0
def subset(fontfile, outfile_basename, glyphs):
    options = Options()

    # Fonttools has this "feature" that if you enable 'dlig', it will also give
    # you glyphs that you did not ask for, but if you do not enable 'dlig',
    # then discretionary ligatures do not render properly.
    # See https://github.com/behdad/fonttools/issues/43.
    # As a workaround, only enable 'dlig' if there are glyphs for discretionary
    # ligatures.
    dligs = set(glyphs).intersection([
        'c_b', 'c_h', 'c_k', 'c_p', 'ct', 'g_i', 'q_u', 's_b', 's_h', 's_k',
        's_p', 'st'
    ])
    if len(dligs) > 0:
        options.layout_features.append('dlig')
    else:
        # Due to a bug in Fonttools, options are actually global, so the
        # remnants of the previous instance are visible here.
        # See https://github.com/behdad/fonttools/issues/413.
        if 'dlig' in options.layout_features:
            options.layout_features.remove('dlig')

    # Same for small caps, it needs to be enabled explicitly. Luckily, only the
    # glyphs in the list get included, no extra ones.
    if any(g.endswith('.smcp') for g in glyphs):
        options.layout_features.append('smcp')
        options.layout_features.append('c2sc')
    else:
        if 'smcp' in options.layout_features:
            options.layout_features.remove('smcp')
        if 'c2sc' in options.layout_features:
            options.layout_features.remove('c2sc')

    # Fonts that went through the FontForge roundtrip will have subroutinized
    # programs in the CFF table. This presumably reduces file size for full
    # fonts, but on subsetted fonts it hurts file size and compressability, so
    # desubroutinize.
    options.desubroutinize = True

    font = load_font(fontfile, options)

    subsetter = Subsetter(options=options)
    subsetter.populate(glyphs=glyphs)
    subsetter.subset(font)

    prune_cmaps(font)

    options.flavor = "woff"
    save_font(font, outfile_basename + ".woff", options)

    options.flavor = "woff2"
    save_font(font, outfile_basename + ".woff2", options)

    font.close()
コード例 #20
0
ファイル: optimize.py プロジェクト: davelab6/fontbakery
    def run(self, pipedata):
        if 'optimize' in pipedata and not pipedata['optimize']:
            return

        from bakery_cli.utils import ProcessedFile
        filename = ProcessedFile()

        self.bakery.logging_raw('### Optimize TTF {}'.format(filename))
        # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py
        from fontTools.subset import Options, Subsetter, load_font, save_font

        options = Options()
        options.layout_features = ["*"]
        options.name_IDs = ["*"]
        options.hinting = True
        options.legacy_kern = True
        options.notdef_outline = True
        options.no_subset_tables += ['DSIG']
        options.drop_tables = list(
            set(options._drop_tables_default) - set(['DSIG']))

        cmd_options = ('--glyphs=*'
                       ' --layout-features=*'
                       ' --name-IDs=*'
                       ' --hinting'
                       ' --legacy-kern --notdef-outline'
                       ' --no-subset-tables+=DSIG'
                       ' --drop-tables-=DSIG')

        font = load_font(op.join(self.builddir, filename), options)

        cmdline = 'pyftsubset {1} {0}'.format(cmd_options,
                                              op.join(self.builddir, filename))
        self.bakery.logging_cmd(cmdline)

        subsetter = Subsetter(options=options)
        subsetter.populate(glyphs=font.getGlyphOrder())
        subsetter.subset(font)
        save_font(font, op.join(self.builddir, filename + '.fix'), options)

        # compare filesizes TODO print analysis of this :)
        comment = "# look at the size savings of that subset process"
        self.bakery.logging_cmd(comment)
        run(u"ls -la {0} {0}.fix | awk '{{ print $5 \"\t\" $9 }}'".format(
            unicode(op.join(self.builddir, filename))))

        comment = "# copy back optimized ttf to original filename"
        self.bakery.logging_cmd(comment)
        shutil.move(op.join(self.builddir, filename + '.fix'),
                    op.join(self.builddir, filename))
コード例 #21
0
def subset_font(basefile_path, buff, text):
    options = Options()
    options.name_IDs = []
    options.obfuscate_names = True
    options.flavor = 'woff'

    font = load_font(basefile_path, options)
    subsetter = Subsetter(options=options)
    subsetter.populate(text=text)
    subsetter.subset(font)
    save_font(font, buff, options)

    font.close()
    buff.seek(0)
    return
コード例 #22
0
    def customSubsetting(self, font, text):
        options = Options()
        options.layout_features = "*"  # keep all GSUB/GPOS features
        options.glyph_names = False  # keep post glyph names
        options.legacy_cmap = True  # keep non-Unicode cmaps
        options.name_legacy = True  # keep non-Unicode names
        options.name_IDs = ["*"]  # keep all nameIDs
        options.name_languages = ["*"]  # keep all name languages
        options.notdef_outline = True
        options.ignore_missing_glyphs = False
        options.recommended_glyphs = True
        options.prune_unicode_ranges = True
        subsetter = Subsetter(options=options)
        subsetter.populate(text=text, unicodes=[0, 13, 32])
        subsetter.subset(font)

        return font
コード例 #23
0
 def clean(self, verbose):
     """Remove glyphs that should have outlines but do not.
 """
     if verbose:
         print('reverse_cmap')
     rcmap = reverse_cmap(self.font)
     names = set(rcmap.keys())
     if verbose:
         print('names.difference_update')
     names.difference_update(self._invalid_glyphs(names, rcmap))
     subsetter = Subsetter(options=self.options)
     if verbose:
         print('populate')
     subsetter.populate(glyphs=names)
     if verbose:
         print('subset')
     subsetter.subset(self.font)
コード例 #24
0
    def _generate_font(self, filename, data):
        name = data['name-slug']
        self._append_log(_('Generating fonts for %s:' % data['name']),
                         bold=True)

        if self.ranges:
            for range, unicodes in self.ranges.items():
                font = TTFont(filename)
                subs = Subsetter()

                subs.populate(unicodes=parse_unicodes(unicodes))
                subs.subset(font)
                self._write_font(font, data, range=range)
                font.close()
                del font
        else:
            font = TTFont(filename)
            self._write_font(font, data)
            font.close()
コード例 #25
0
    def run(self, filename, pipedata):
        if 'optimize' in pipedata and not pipedata['optimize']:
            return

        self.bakery.logging_raw('### Optimize TTF {}'.format(filename))
        # copied from https://code.google.com/p/noto/source/browse/nototools/subset.py
        from fontTools.subset import Options, Subsetter, load_font, save_font

        options = Options()
        options.layout_features = ["*"]
        options.name_IDs = ["*"]
        options.hinting = True
        options.legacy_kern = True
        options.notdef_outline = True
        options.no_subset_tables += ['DSIG']
        options.drop_tables = list(
            set(options._drop_tables_default) - set(['DSIG']))

        font = load_font(op.join(self.builddir, filename), options)
        self.bakery.logging_raw('Before: {}'.format(font.keys()))

        self.bakery.logging_raw('{}'.format(options.__dict__))
        subsetter = Subsetter(options=options)
        subsetter.populate(glyphs=font.getGlyphOrder())
        subsetter.subset(font)
        save_font(font, op.join(self.builddir, filename + '.opt'), options)

        newsize = op.getsize(op.join(self.builddir, filename + '.opt'))
        origsize = op.getsize(op.join(self.builddir, filename))

        # compare filesizes TODO print analysis of this :)
        comment = "# look at the size savings of that subset process"
        self.bakery.logging_cmd("ls -l '%s'* %s" % (filename, comment))

        statusmessage = "{0}.opt: {1} bytes\n{0}: {2} bytes\n"
        self.bakery.logging_raw(
            statusmessage.format(filename, newsize, origsize))

        self.bakery.logging_raw('Now: {}'.format(font.keys()))
        # move ttx files to src
        shutil.move(op.join(self.builddir, filename + '.opt'),
                    op.join(self.builddir, filename),
                    log=self.bakery.logger)
コード例 #26
0
ファイル: tirobuild.py プロジェクト: TiroTypeworks/Clairvo
    def _subset(self, otf, fmt):
        from fontTools.subset import Options, Subsetter

        for name, subset in self.subsets.items():
            logger.info(f"Creating {name}.{fmt.value} subset")
            new = deepcopy(otf)
            options = Options()
            options.name_IDs = ["*"]
            options.name_legacy = True
            options.name_languages = ["*"]
            options.recommended_glyphs = True
            options.layout_features = ["*"]
            options.notdef_outline = True
            options.notdef_glyph = True
            options.glyph_names = True
            options.hinting = True
            options.legacy_kern = True
            options.symbol_cmap = True
            options.layout_closure = False
            options.prune_unicode_ranges = False
            options.passthrough_tables = False
            options.recalc_average_width = True
            options.ignore_missing_glyphs = True
            options.layout_scripts = subset["langsys"]

            options.drop_tables.remove("DSIG")
            options.no_subset_tables += ["DSIG"]

            subsetter = Subsetter(options=options)
            subsetter.populate(subset["glyphlist"])

            with TemporaryLogLevel(logging.WARNING):
                subsetter.subset(new)

            new = self._optimize(new, name, fmt)
            names = subset.get("names")
            if names:
                logger.info(
                    f"Adding name entries to {name}.{fmt.value} susbet")
                self._setnames(new, names)
            self._buildwoff(new, name, fmt)
            self._save(new, name, fmt)
コード例 #27
0
    def subsetter(self, font, subset):
        """ use the noto fonts glyphsnames
            to subset fonts with premade subsettings
        """
        options = Options()
        options.layout_features = "*"  # keep all GSUB/GPOS features
        # options.no_layout_closure = True # TESTING
        options.glyph_names = False  # keep post glyph names
        options.legacy_cmap = True  # keep non-Unicode cmaps
        options.name_legacy = True  # keep non-Unicode names
        options.name_IDs = ["*"]  # keep all nameIDs
        options.name_languages = ["*"]  # keep all name languages
        options.notdef_outline = False
        options.ignore_missing_glyphs = True
        options.prune_unicode_ranges = True
        options.recommended_glyphs = True
        subsetter = Subsetter(options=options)
        subsetter.populate(glyphs=subset)
        subsetter.subset(font)

        return font
コード例 #28
0
def subsetFont(fontPath, subset):
    tmpOutputFontName = os.path.dirname(
        os.path.abspath(__file__)) + "/tmp/" + str(uuid.uuid4()) + ".woff"

    font = TTFont(fontPath)

    options = Options()
    options.desubroutinize = True

    options.with_zopfli = True
    options.flavor = "woff"

    subsetter = Subsetter(options=options)
    subsetter.populate(text=subset)
    subsetter.subset(font)

    save_font(font, tmpOutputFontName, options)
    subsettedFont = 'data:;base64,' + open(tmpOutputFontName,
                                           "rb").read().encode("base64")

    cleanUp([tmpOutputFontName])

    print subsettedFont.replace('\n', '')
コード例 #29
0
ファイル: make_subset.py プロジェクト: sahwar/TachyFont
def main(args):
    """Subset a font (useful for making small test fonts).

  Args:
    args: list, arguments the user typed.
  """
    parser = argparse.ArgumentParser()
    parser.add_argument('fontfile', help='Input font file')
    parser.add_argument('--subset_postfix',
                        default='',
                        help='Postfix to the subset extension')
    parser.add_argument('--text',
                        default='',
                        help='Text to include in the subset')
    parser.add_argument(
        '--unicodes',
        default='',
        help='Comma separated list of Unicode codepoints (hex) '
        'to include in the subset; eg, "e7,0xe8,U+00e9"')
    parser.add_argument('--glyphs',
                        default='',
                        help='Comma separated list of glyph IDs (decimal) to '
                        'include in the subset; eg, "1,27"')
    parser.add_argument('--hinting',
                        default=False,
                        action='store_true',
                        help='Enable hinting if specified, no hinting if not '
                        'present')

    cmd_args = parser.parse_args(args)

    options = Options()
    # Definitely want the .notdef glyph and outlines.
    options.notdef_glyph = True
    options.notdef_outline = True
    # Get the item. to keep in the subset.
    text = cmd_args.text
    unicodes_str = cmd_args.unicodes.lower().replace('0x',
                                                     '').replace('u+', '')
    # TODO(bstell) replace this whole files by using the new subset.py code
    unicodes_input = [c for c in unicodes_str.split(',') if c]
    unicodes = []
    for c in unicodes_input:
        if '-' in c:
            uni_range = c.split('-')
            uni_range_expanded = range(int(uni_range[0], 16),
                                       int(uni_range[1], 16) + 1)
            unicodes.extend(uni_range_expanded)
        else:
            unicodes.append(int(c, 16))
    #unicodes = [int(c, 16) for c in unicodes_input_expanded]
    glyphs = [int(c) for c in cmd_args.glyphs.split(',') if c]
    fontfile = cmd_args.fontfile
    options.hinting = cmd_args.hinting  # False => no hinting
    options.hinting = True  # hint stripping for CFF is currently broken

    dirname = os.path.dirname(fontfile)
    basename = os.path.basename(fontfile)
    filename, extension = os.path.splitext(basename)
    subset_postfix = cmd_args.subset_postfix
    output_file = dirname + '/' + filename + '_subset' + subset_postfix + extension
    print "output_file =", output_file
    font = load_font(fontfile, options, lazy=False)

    subsetter = Subsetter(options)
    subsetter.populate(text=text, unicodes=unicodes, glyphs=glyphs)
    subsetter.subset(font)
    save_font(font, output_file, options)
コード例 #30
0
        # We round-trip through a buffer to workaround PIL not
        # resetting the dpi value.
        data = im.tobytes()
        im = Image.frombytes(im.mode, im.size, data)
        if dst.exists():
            dstIm = Image.open(dst)
            if data == dstIm.tobytes():
                # Don't save when the image data is the same, some
                # meta data may still have changed, making us do
                # unwanted commits.
                print("-- same image, skipping", dst)
                continue
        im.save(dst)

print("Subsetting fonts...")
for src in sorted(docsSourceFonts.glob("*.woff2")):
    dst = docsFonts / src.name
    font = TTFont(src)
    subsetter = Subsetter()
    unicodes = set(ord(c) for c in markdownSource)
    subsetter.populate(unicodes=unicodes)
    subsetter.subset(font)

    if dst.exists():
        existing = TTFont(dst, lazy=True)
        if sorted(font.getBestCmap()) == sorted(existing.getBestCmap()):
            print("-- same cmap, skipping", dst)
            continue
    font.flavor = "woff2"
    font.save(dst)
def subsetFonts(family,
                writingSystem,
                flavor=["ttf"],
                familyNewName=" ",
                jsonpath=" ",
                keepFea=True):
    print(familyNewName)
    # if len(flavor) == 0:
    flavor = ["ttf"]
    latinProCodePageRange = [0, 1, 4, 7, 8]
    cyrProCodePageRange = [2]
    greekProCodePageRange = [3]
    ASCII = [0, 1]
    SecureSet = [0]
    coreArabicCodePageRange = [0, 6]
    unicodePageRangeDict = {
        "Cyrillic": latinProCodePageRange,
        "CyrillicPro": latinProCodePageRange,
        "Greek": greekProCodePageRange,
        "Latin": latinProCodePageRange,
        "ASCII": ASCII,
        "SecureSet": SecureSet,
        "Core_Arabic": coreArabicCodePageRange
    }
    pageRangeToApply = []
    subsetFolder = ""
    for i in writingSystem:
        subsetFolder += i
    formats = ["ttf", "woff", "woff2"]
    toKeep = list()
    folder = getFolder(family)
    folderFonts = os.path.join(folder, "fonts")
    options = Options()
    options.layout_features = '*'  # keep all GSUB/GPOS features
    # options.legacy_kern = True  # keep kern table
    options.glyph_names = False  # keep post glyph names
    options.legacy_cmap = True  # keep non-Unicode cmaps
    options.symbol_cmap = True  # keep Symbol cmaps
    options.name_legacy = True  # keep non-Unicode names
    options.name_IDs = ['*']  # keep all nameIDs
    options.name_languages = ['*']  # keep all name languages
    options.notdef_outline = True  # keep outline of .notdef
    options.ignore_missing_glyphs = True
    options.prune_unicode_ranges = True
    keep = []
    if family in pan_european_fonts:
        jsonpath = "subsets/lgc_glyphset.json"
    elif family in arabic_fonts:
        jsonpath = "subsets/arabic_glyphset.json"
    keep, pageRangeToApply = readJsonStoredSubset(jsonpath, writingSystem)
    for i in flavor:
        if not os.path.exists(os.path.join(folderFonts, i.upper())):
            print(">> Make {} fonts.".format(family))
            designSpace2Instances(family, i, secureSet=False)
    for i in flavor:
        fontspath = [os.path.join(folderFonts, i.upper(), font) \
                    for font in os.listdir(folder + "/fonts/" + i.upper())]
        for f in fontspath:
            if f.split(".")[-1] in formats:
                newfont = TTFont(f)
                for namerecord in newfont['name'].names:
                    namerecord.string = namerecord.toUnicode()
                    if namerecord.nameID == 2:
                        WeightName = namerecord.string
                    if namerecord.nameID == 17:
                        WeightName = "".join(namerecord.string.split(" "))
                        # print(family, WeightName)
                subsetter = Subsetter(options=options)
                subsetter.populate(glyphs=keep)
                subsetter.subset(newfont)
                destination = os.path.join(folder, "fonts",
                                           subsetFolder + "_subset", "fonts")
                if not os.path.exists(os.path.join(destination, i.upper())):
                    os.makedirs(os.path.join(destination, i.upper()))
                subsetName = family + subsetFolder + "-" + WeightName + "." + i
                newfont.save(os.path.join(destination, i.upper(), subsetName))
    folder = family + "/fonts/" + subsetFolder + "_subset"
    if familyNewName != " ":
        renameFonts(folder, familyNewName, codePageRange=pageRangeToApply)
    else:
        familyNewName = family + subsetFolder
        renameFonts(folder, familyNewName, codePageRange=pageRangeToApply)
    shutil.rmtree(destination)
コード例 #32
0
 def subset(self, text):
     subsetter = Subsetter(self.options)
     subsetter.populate(text=text)
     subsetter.subset(self.font)