Example #1
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.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)
Example #2
0
def makeKit(font_path):
  # put the result into a directory named file_name.kit
  dest_dir = os.path.splitext(font_path)[0] + '.kit'
  if os.path.isdir(dest_dir):
    print 'FAILURE: dest %s already exists' % dest_dir
    return False
  os.makedirs(dest_dir)
  print 'Making a kit for %s in %s' % (font_path, dest_dir)

  # crack open the font
  # equivalent  pyftsubset /tmp/Lobster-Regular.ttf --unicodes='*' --obfuscate_names
  options = subset.Options()
  with contextlib.closing(subset.load_font(font_path, options)) as font:
    unicodes = []
    for t in font['cmap'].tables:
      if t.isUnicode():
        unicodes.extend(t.cmap.keys())
    options.unicodes = unicodes

    # mangle 'name' so the font can't be installed
    options.obfuscate_names

    # apply our subsetting, most notably trashing 'name'
    subsetter = subset.Subsetter(options=options)
    subsetter.populate()

    # write [ot]tf, woff, and woff2 editions with 'name' mangled
    font_name_noext = os.path.splitext(os.path.basename(font_path))[0]
    font_ext = os.path.splitext(os.path.basename(font_path))[1]
    for fmt in [font_ext, '.woff', '.woff2']:
      dest_file = os.path.join(dest_dir, font_name_noext + fmt)
      options.flavor = None
      if fmt.startswith('.woff'):
        options.flavor = fmt[1:]
      print 'Writing %s' % dest_file
      with open(dest_file, 'wb') as f:
        subset.save_font(font, f, options)

    # write a sample somewhat (no early Android, IE) bulletproof css
    dest_file = os.path.join(dest_dir, 'bulletproof.css')
    os2 = font['OS/2']
    font_style = 'normal'
    if os2.fsSelection & 1:
      font_style = 'italic'
    with open(dest_file, 'w') as f:
      f.write("@font-face {\n")
      f.write("  font-family: '%s';\n" % font_name_noext)
      f.write("  font-style: %s;\n" % font_style)
      f.write("  font-weight: %d;\n" % os2.usWeightClass)
      f.write("  src:\n")
      f.write("    url('./%s.woff2') format('woff2'),\n" % font_name_noext)
      f.write("    url('./%s.woff') format('woff'),\n" % font_name_noext)
      if font_ext == '.otf':
        f.write("    url('./%s.otf') format('opentype')" % font_name_noext)
      else:
        f.write("    url('./%s.ttf') format('truetype')" % font_name_noext)
      f.write(";\n")
      f.write("}\n")

  return True
Example #3
0
 def woffCreate(self, text=None, No='2'):
     mergeTools = merge.Merger()
     woffFile = "static/font/" + No + ".woff"
     saveFilename = str(Path(self.cwd) / woffFile)
     filename = [self.fontFile, saveFilename]
     textLuck = []
     baseUrl = 'https://www.font.cn/preview/getFont?font_id=303&format=ttf&vers=v1.0&words='
     urlEnd = '&hex=1'
     if text is None:
         return
     collectText = {t for t in text}
     text = ''.join(collectText)
     for t in text:
         if t not in self.name:
             hexT = str(hex(ord(t)))
             textLuck.append(hexT[2:])
     # print(len(textLuck))
     if len(textLuck) != 0:
         mergeUrl = ','.join(textLuck)
         aimUrl = baseUrl + mergeUrl + urlEnd
         fontDown = self.downloadWoff(aimUrl, saveFilename)
         if fontDown != None:
             mergeFont = mergeTools.merge(filename)
             mergeFont.save(self.fontFile)
     # 拆分合并后的字体文件成2.woff
     options = subset.Options()
     fontMerge = subset.load_font(self.fontFile, options)
     subsetter = subset.Subsetter(options)
     subsetter.populate(text=text)
     subsetter.subset(fontMerge)
     options.flavor = 'woff'
     subset.save_font(fontMerge, saveFilename, options)
Example #4
0
def get_glyphs_subset(fontfile, characters):
    """
    Subset a TTF font

    Reads the named fontfile and restricts the font to the characters.
    Returns a serialization of the subset font as file-like object.

    Parameters
    ----------
    symbol : str
        Path to the font file
    characters : str
        Continuous set of characters to include in subset
    """

    options = subset.Options(glyph_names=True, recommended_glyphs=True)

    # prevent subsetting FontForge Timestamp and other tables
    options.drop_tables += ['FFTM', 'PfEd', 'BDF']

    # if fontfile is a ttc, specify font number
    if fontfile.endswith(".ttc"):
        options.font_number = 0

    with subset.load_font(fontfile, options) as font:
        subsetter = subset.Subsetter(options=options)
        subsetter.populate(text=characters)
        subsetter.subset(font)
        fh = BytesIO()
        font.save(fh, reorderTables=False)
        return fh
Example #5
0
    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)
Example #6
0
def subset_font_cmap(srcname,
                     dstname,
                     exclude=None,
                     include=None,
                     bump_version=True):

    opt = _DEFAULT_OPTIONS

    font = subset.load_font(srcname, opt)
    target_charset = set(font_data.get_cmap(font).keys())

    if include is not None:
        target_charset &= include
    if exclude is not None:
        target_charset -= exclude

    subsetter = subset.Subsetter(options=opt)
    subsetter.populate(unicodes=target_charset)
    subsetter.subset(font)

    if bump_version:
        # assume version string has 'uh' if unhinted, else hinted.
        revision, version_string = swat_license.get_bumped_version(font)
        font['head'].fontRevision = revision
        font_data.set_name_record(font, _VERSION_ID, version_string)

    subset.save_font(font, dstname, opt)
Example #7
0
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()
Example #8
0
    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)
Example #9
0
    def subset_otf_from_ufo(self, otf_path, ufo):
        """Subset a font using export flags set by glyphsLib."""

        keep_glyphs = set(ufo.lib.get(GLYPHS_PREFIX + 'Keep Glyphs', []))

        include = []
        for old_name, new_name in zip(
                ufo.lib[PUBLIC_PREFIX + 'glyphOrder'],
                TTFont(otf_path).getGlyphOrder()):
            glyph = ufo[old_name]
            if ((keep_glyphs and old_name not in keep_glyphs) or
                not glyph.lib.get(GLYPHS_PREFIX + 'Glyphs.Export', True)):
                continue
            include.append(new_name)

        # copied from nototools.subset
        opt = subset.Options()
        opt.name_IDs = ['*']
        opt.name_legacy = True
        opt.name_languages = ['*']
        opt.layout_features = ['*']
        opt.notdef_outline = True
        opt.recalc_bounds = True
        opt.recalc_timestamp = True
        opt.canonical_order = True

        opt.glyph_names = True

        font = subset.load_font(otf_path, opt, lazy=False)
        subsetter = subset.Subsetter(options=opt)
        subsetter.populate(glyphs=include)
        subsetter.subset(font)
        subset.save_font(font, otf_path, opt)
Example #10
0
    def subset_otf_from_ufo(self, otf_path, ufo):
        """Subset a font using export flags set by glyphsLib."""

        keep_glyphs = set(ufo.lib.get(GLYPHS_PREFIX + 'Keep Glyphs', []))

        include = []
        for old_name, new_name in zip(
                ufo.lib[PUBLIC_PREFIX + 'glyphOrder'],
                TTFont(otf_path).getGlyphOrder()):
            glyph = ufo[old_name]
            if ((keep_glyphs and old_name not in keep_glyphs) or
                not glyph.lib.get(GLYPHS_PREFIX + 'Glyphs.Export', True)):
                continue
            include.append(new_name)

        # copied from nototools.subset
        opt = subset.Options()
        opt.name_IDs = ['*']
        opt.name_legacy = True
        opt.name_languages = ['*']
        opt.layout_features = ['*']
        opt.notdef_outline = True
        opt.recalc_bounds = True
        opt.recalc_timestamp = True
        opt.canonical_order = True

        opt.glyph_names = True

        font = subset.load_font(otf_path, opt, lazy=False)
        subsetter = subset.Subsetter(options=opt)
        subsetter.populate(glyphs=include)
        subsetter.subset(font)
        subset.save_font(font, otf_path, opt)
Example #11
0
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)
Example #12
0
def subset_ttf_font(filepath: str) -> dict:
    options = subset.Options()
    font = subset.load_font(f'{filepath}.ttf', options)
    options.flavor = 'woff'
    subset.save_font(font, f'{filepath}.woff', options)
    options.flavor = 'woff2'
    subset.save_font(font, f'{filepath}.woff2', options)
    return {'woff': f'{filepath}.woff', 'woff2': f'{filepath}.woff2'}
Example #13
0
 def __init__(self, fontfile, hinting, whitespace_list):
   self.fontfile = fontfile
   self.options = Options()
   self.options.hinting = hinting
   self.font = load_font(fontfile, self.options, lazy=False)
   # assert 'glyf' in self.font, 'only support TrueType (quadratic) fonts \
   #(eg, not CFF) at this time'
   self.whitespace_list = whitespace_list
Example #14
0
def subSetFont(ff, tt):
    options = subset.Options()  # dir(options)
    font = subset.load_font(ff, options)
    subsetter = subset.Subsetter(options)
    subsetter.populate(text=tt)
    subsetter.subset(font)
    # options.flavor = 'woff'
    subset.save_font(font, 'font.ttf', options)
    modFont()
Example #15
0
def subset_font(source_file,
                target_file,
                include=None,
                exclude=None,
                options=None):
    """Subsets a font file.

    Subsets a font file based on a specified character set. If only include is
    specified, only characters from that set would be included in the output
    font.  If only exclude is specified, all characters except those in that
    set will be included.  If neither is specified, the character set will
    remain the same, but inaccessible glyphs will be removed.

    Args:
      source_file: Input file name.
      target_file: Output file name
      include: The list of characters to include from the source font.
      exclude: The list of characters to exclude from the source font.
      options: A dictionary listing which options should be different from the
          default.

    Raises:
      NotImplementedError: Both include and exclude were specified.
    """
    opt = subset.Options()

    opt.name_IDs = ["*"]
    opt.name_legacy = True
    opt.name_languages = ["*"]
    opt.layout_features = ["*"]
    opt.notdef_outline = True
    opt.recalc_bounds = True
    opt.recalc_timestamp = True
    opt.canonical_order = True
    opt.drop_tables = ["+TTFA"]

    if options is not None:
        for name, value in options.items():
            setattr(opt, name, value)

    if include is not None:
        if exclude is not None:
            raise NotImplementedError(
                "Subset cannot include and exclude a set at the same time.")
        target_charset = include
    else:
        if exclude is None:
            exclude = []
        source_charset = coverage.character_set(source_file)
        target_charset = source_charset - set(exclude)

    font = subset.load_font(source_file, opt)
    subsetter = subset.Subsetter(options=opt)
    subsetter.populate(unicodes=target_charset)
    subsetter.subset(font)
    subset.save_font(font, target_file, opt)
Example #16
0
    def subset_otf_from_ufo(self, otf_path, ufo):
        """Subset a font using export flags set by glyphsLib.

        There are two more settings that can change export behavior:
        "Export Glyphs" and "Remove Glyphs", which are currently not supported
        for complexity reasons. See
        https://github.com/googlei18n/glyphsLib/issues/295.
        """
        from fontTools import subset

        # ufo2ft always inserts a ".notdef" glyph as the first glyph
        ufo_order = makeOfficialGlyphOrder(ufo)
        if ".notdef" not in ufo_order:
            ufo_order.insert(0, ".notdef")
        ot_order = TTFont(otf_path).getGlyphOrder()
        assert ot_order[0] == ".notdef"
        assert len(ufo_order) == len(ot_order)

        for key in (KEEP_GLYPHS_NEW_KEY, KEEP_GLYPHS_OLD_KEY):
            keep_glyphs_list = ufo.lib.get(key)
            if keep_glyphs_list is not None:
                keep_glyphs = set(keep_glyphs_list)
                break
        else:
            keep_glyphs = None

        include = []
        for source_name, binary_name in zip(ufo_order, ot_order):
            if keep_glyphs and source_name not in keep_glyphs:
                continue

            if source_name in ufo:
                exported = ufo[source_name].lib.get(GLYPH_EXPORT_KEY, True)
                if not exported:
                    continue

            include.append(binary_name)

        # copied from nototools.subset
        opt = subset.Options()
        opt.name_IDs = ['*']
        opt.name_legacy = True
        opt.name_languages = ['*']
        opt.layout_features = ['*']
        opt.notdef_outline = True
        opt.recalc_bounds = True
        opt.recalc_timestamp = True
        opt.canonical_order = True

        opt.glyph_names = True

        font = subset.load_font(otf_path, opt, lazy=False)
        subsetter = subset.Subsetter(options=opt)
        subsetter.populate(glyphs=include)
        subsetter.subset(font)
        subset.save_font(font, otf_path, opt)
Example #17
0
    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)
Example #18
0
 def __init__(self, fontfile, hinting, whitespace_and_ignorable_list):
     self.fontfile = fontfile
     self.options = Options()
     self.options.hinting = hinting
     # Want the .notdef glyph and outlines.
     self.options.notdef_glyph = True
     self.options.notdef_outline = True
     self.options.desubroutinize = True
     self.font = load_font(fontfile, self.options, lazy=False)
     self.whitespace_and_ignorable_list = whitespace_and_ignorable_list
Example #19
0
 def __init__(self, fontfile, hinting, whitespace_and_ignorable_list):
   self.fontfile = fontfile
   self.options = Options()
   self.options.hinting = hinting
   # Want the .notdef glyph and outlines.
   self.options.notdef_glyph = True
   self.options.notdef_outline = True
   self.options.decompress = True
   self.font = load_font(fontfile, self.options, lazy=False)
   self.whitespace_and_ignorable_list = whitespace_and_ignorable_list
Example #20
0
File: subset.py Project: 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()
Example #21
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()
Example #22
0
    def subset(self, font_bytes, codepoints):  # pylint: disable=no-self-use
        """Computes a subset of font_bytes to the given codepoints."""
        options = subset.Options()
        subsetter = subset.Subsetter(options=options)
        with io.BytesIO(font_bytes) as font_io, \
             subset.load_font(font_io, options) as font:
            subsetter.populate(unicodes=codepoints)
            subsetter.subset(font)

            with io.BytesIO() as output:
                subset.save_font(font, output, options)
                return output.getvalue()
Example #23
0
def prune_font(fontfile, out_dir):
    font = fonttools.load_font(fontfile, fonttools.Options())

    # Roundtripping a font trough FontForge adds a GDEF table. This table is not
    # present in the original version of Calluna, so remove it. It is present in
    # Inconsolata, but it does not appear to do any harm to remove it, apart
    # from reducing the file size.
    if 'GDEF' in font:
        del font['GDEF']

    font.save(os.path.join(out_dir, os.path.basename(fontfile)))
    font.close()
Example #24
0
def prune_font(fontfile, out_dir):
    font = fonttools.load_font(fontfile, fonttools.Options())

    # Roundtripping a font trough FontForge adds a GDEF table. This table is not
    # present in the original version of Calluna, so remove it. It is present in
    # Inconsolata, but it does not appear to do any harm to remove it, apart
    # from reducing the file size.
    if 'GDEF' in font:
        del font['GDEF']

    font.save(os.path.join(out_dir, os.path.basename(fontfile)))
    font.close()
Example #25
0
def subset_font(source_file, target_file,
                include=None, exclude=None, options=None):
    """Subsets a font file.

    Subsets a font file based on a specified character set. If only include is
    specified, only characters from that set would be included in the output
    font.  If only exclude is specified, all characters except those in that
    set will be included.  If neither is specified, the character set will
    remain the same, but inaccessible glyphs will be removed.

    Args:
      source_file: Input file name.
      target_file: Output file name
      include: The list of characters to include from the source font.
      exclude: The list of characters to exclude from the source font.
      options: A dictionary listing which options should be different from the
          default.

    Raises:
      NotImplementedError: Both include and exclude were specified.
    """
    opt = subset.Options()

    opt.name_IDs = ['*']
    opt.name_legacy = True
    opt.name_languages = ['*']
    opt.layout_features = ['*']
    opt.notdef_outline = True
    opt.recalc_bounds = True
    opt.recalc_timestamp = True
    opt.canonical_order = True
    opt.drop_tables = ['+TTFA']

    if options is not None:
        for name, value in options.iteritems():
            setattr(opt, name, value)

    if include is not None:
        if exclude is not None:
            raise NotImplementedError(
                'Subset cannot include and exclude a set at the same time.')
        target_charset = include
    else:
        if exclude is None:
            exclude = []
        source_charset = coverage.character_set(source_file)
        target_charset = source_charset - set(exclude)

    font = subset.load_font(source_file, opt)
    subsetter = subset.Subsetter(options=opt)
    subsetter.populate(unicodes=target_charset)
    subsetter.subset(font)
    subset.save_font(font, target_file, opt)
Example #26
0
 def initOption(self, **kwargs):
     # 设置选项
     self.options = subset.Options()
     self.options.flavor = 'woff'
     for k, v in kwargs.items():
         if not hasattr(self.options, k):
             setattr(self.options, k, v)
     # 实例化字体
     self.font = subset.load_font(self.file_path, self.options)
     # 设置配置器
     self.subsetter = subset.Subsetter(self.options)
     self.subsetter.populate(text=self.text)
     self.subsetter.subset(self.font)
Example #27
0
File: subset.py Project: 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()
Example #28
0
    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))
Example #29
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
Example #30
0
    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))
Example #31
0
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)
Example #32
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)
Example #33
0
def _load_font(path):
    guess = mimetypes.guess_type(path)
    if guess[0] not in [
            "font/ttc",
            "font/ttf",
            "font/otf",
            "font/woff",
            "application/font-sfnt",
            "application/font-woff",
    ]:
        logging.error("Not a font file: {}".format(path))
        logging.error("Guessed mimetype: '{}'".format(guess[0]))
        logging.error("If this is a text file: do you have Git LFS installed?")
        sys.exit(1)
    try:
        return subset.load_font(path,
                                FONT_TOOLS_OPTIONS,
                                dontLoadGlyphNames=True)
    except FileNotFoundError as e:  # noqa F821
        logging.error("Could not load font: {}".format(str(e)))
        logging.error("You may need to run: `make i18n-download-source-fonts`")
        sys.exit(1)
def main(args):
    if not args.filepath:
        raise AttributeError('Please specify font filepath')
    if not os.path.exists(args.filepath):
        raise AttributeError('File: %s not found' % args.filepath)

    textfile_dir = '_posts'
    unichars = set()

    def walk_callback(args, dirname, fnames):
        for fname in fnames:
            unichars.update(get_unicodes(os.path.join(dirname, fname)))

    os.path.walk(textfile_dir, walk_callback, None)
    unicodes = [ord(c) for c in unichars]

    cjk_fontfile = args.filepath
    out_fontdir = 'assets/fonts'
    out_fontfile = makeOutputFileName(os.path.basename(args.filepath),
                                      outputDir=out_fontdir,
                                      extension='.woff',
                                      overWrite=True)

    options = subset.Options()
    dontLoadGlyphNames = not options.glyph_names
    font = subset.load_font(cjk_fontfile,
                            options,
                            dontLoadGlyphNames=dontLoadGlyphNames)
    subsetter = subset.Subsetter()
    subsetter.populate(glyphs=[], gids=[], unicodes=unicodes, text='')
    subsetter.subset(font)
    sfnt.USE_ZOPFLI = True
    font.flavor = 'woff'
    font.save(out_fontfile, reorderTables=False)

    print('Input font: % 7d bytes: %s' %
          (os.path.getsize(cjk_fontfile), cjk_fontfile))
    print('Subset font: % 7d bytes: %s' %
          (os.path.getsize(out_fontfile), out_fontfile))
Example #35
0
def stamp(svgfile, fontfile, text, font_family=None, xml_transform=None):
    options = Options(flavor="woff2")
    with load_font(fontfile, options) as font:
        font_family = font_family or font_name(font)
        font_data = subset_font(font, text, options)
    with open(svgfile) as f:
        xml = xmltodict.parse(f.read())
    # Embed font in root style tag
    style = xml["svg"].get("style", {"#text": ""})
    if type(style) == str:
        style = {"#text": style}
    style["#text"] += f"""
<![CDATA[
@font-face {{
  font-family: "{font_family}";
  src: url("data:font/woff2;base64,{font_data}");
}}
]]>
"""
    xml["svg"]["style"] = style
    if xml_transform:
        xml = xml_transform(xml)
    return unescape(xmltodict.unparse(xml))
Example #36
0
    def subset_otf_from_ufo(self, otf_path, ufo):
        """Subset a font using export flags set by glyphs2ufo."""

        font_lib_prefix = 'com.schriftgestaltung.'
        glyph_lib_prefix = font_lib_prefix + 'Glyphs.'

        keep_glyphs = set(ufo.lib.get(font_lib_prefix + 'Keep Glyphs', []))

        include = []
        glyph_order = ufo.lib['public.glyphOrder']
        for glyph_name in glyph_order:
            glyph = ufo[glyph_name]
            if ((keep_glyphs and glyph_name not in keep_glyphs) or
                not glyph.lib.get(glyph_lib_prefix + 'Export', True)):
                continue
            include.append(glyph_name)

        # copied from nototools.subset
        opt = subset.Options()
        opt.name_IDs = ['*']
        opt.name_legacy = True
        opt.name_languages = ['*']
        opt.layout_features = ['*']
        opt.notdef_outline = True
        opt.recalc_bounds = True
        opt.recalc_timestamp = True
        opt.canonical_order = True

        opt.glyph_names = ufo.lib.get(
            font_lib_prefix + "Don't use Production Names")

        font = subset.load_font(otf_path, opt, lazy=False)
        subsetter = subset.Subsetter(options=opt)
        subsetter.populate(glyphs=include)
        subsetter.subset(font)
        subset.save_font(font, otf_path, opt)
Example #37
0
def subset_font_cmap(
  srcname, dstname, exclude=None, include=None, bump_version=True):

  opt = _DEFAULT_OPTIONS

  font = subset.load_font(srcname, opt)
  target_charset = set(font_data.get_cmap(font).keys())

  if include is not None:
    target_charset &= include
  if exclude is not None:
    target_charset -= exclude

  subsetter = subset.Subsetter(options=opt)
  subsetter.populate(unicodes=target_charset)
  subsetter.subset(font)

  if bump_version:
    # assume version string has 'uh' if unhinted, else hinted.
    revision, version_string = swat_license.get_bumped_version(font)
    font['head'].fontRevision = revision
    font_data.set_name_record(font, _VERSION_ID, version_string)

  subset.save_font(font, dstname, opt)
Example #38
0
    def subset_otf_from_ufo(self, otf_path, ufo):
        """Subset a font using "Keep Glyphs" custom parameter and export flags as set
        by glyphsLib.

        "Export Glyphs" and "Remove Glyphs" are currently not supported:
        https://github.com/googlei18n/glyphsLib/issues/295.
        """
        from fontTools import subset

        # we must exclude from the final UFO glyphOrder all the glyphs that were not
        # exported to OTF because included in 'public.skipExportGlyphs'
        skip_export_glyphs = set(ufo.lib.get("public.skipExportGlyphs", ()))
        exported_glyphs = dict.fromkeys(g for g in ufo.keys()
                                        if g not in skip_export_glyphs)
        ufo_order = makeOfficialGlyphOrder(exported_glyphs,
                                           glyphOrder=ufo.glyphOrder)
        # ufo2ft always inserts a ".notdef" glyph as the first glyph
        if ".notdef" not in exported_glyphs:
            ufo_order.insert(0, ".notdef")
        ot_order = TTFont(otf_path).getGlyphOrder()
        assert ot_order[0] == ".notdef"
        assert len(ufo_order) == len(ot_order)

        for key in (KEEP_GLYPHS_NEW_KEY, KEEP_GLYPHS_OLD_KEY):
            keep_glyphs_list = ufo.lib.get(key)
            if keep_glyphs_list is not None:
                keep_glyphs = set(keep_glyphs_list)
                break
        else:
            keep_glyphs = None

        include = []
        for source_name, binary_name in zip(ufo_order, ot_order):
            if keep_glyphs and source_name not in keep_glyphs:
                continue

            if source_name in ufo:
                exported = ufo[source_name].lib.get(GLYPH_EXPORT_KEY, True)
                if not exported:
                    continue

            include.append(binary_name)

        # copied from nototools.subset
        opt = subset.Options()
        opt.name_IDs = ["*"]
        opt.name_legacy = True
        opt.name_languages = ["*"]
        opt.layout_features = ["*"]
        opt.notdef_outline = True
        opt.recalc_bounds = True
        opt.recalc_timestamp = True
        opt.canonical_order = True

        opt.glyph_names = True

        font = subset.load_font(otf_path, opt, lazy=False)
        subsetter = subset.Subsetter(options=opt)
        subsetter.populate(glyphs=include)
        subsetter.subset(font)
        subset.save_font(font, otf_path, opt)
Example #39
0
 def __init__(self, font_path, options=None, featurs=['*']):
     if not options:
         options = self.make_options(featurs)
     self.options = options
     self.font = load_font(font_path, self.options)
Example #40
0
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)
Example #41
0
#!/usr/bin/python
import sys

from fontTools import subset

# 测试使用
# sys.argv = ['字体拆分.py', '.\data\Funkster.ttf']
sys.argv = ['字体拆分.py', '.\data\HARLOWSI.TTF']

if len(sys.argv) < 2:
    print("Usage : 字体拆分.py in.ttx")
    sys.exit(1)

ttx_file = sys.argv[1]
del sys.argv

ops = subset.Options()
font = subset.load_font(ttx_file, ops)

sub = subset.Subsetter(ops)
sub.populate(text='Google')
sub.subset(font)

ops.flavor = 'woff'

subset.save_font(font, '.\data\sub.woff', ops)

font.close()

# sub.woff 在 windows 上利用 FontCreator 软件导出成ttf文件 即可使用
Example #42
0
from fontTools import subset

options = subset.Options() # dir(options)
font = subset.load_font('shuibo.ttf', options)
subsetter = subset.Subsetter(options)
subsetter.populate(text = 'Google')
subsetter.subset(font)
options.flavor = 'woff'
subset.save_font(font, 'font.woff', options)
Example #43
0
def makeKit(font_path):
    # put the result into a directory named file_name.kit
    dest_dir = os.path.splitext(font_path)[0] + '.kit'
    if os.path.isdir(dest_dir):
        print 'FAILURE: dest %s already exists' % dest_dir
        return False
    os.makedirs(dest_dir)
    print 'Making a kit for %s in %s' % (font_path, dest_dir)

    # crack open the font
    # equivalent  pyftsubset /tmp/Lobster-Regular.ttf --unicodes='*' --obfuscate_names
    options = subset.Options()
    with contextlib.closing(subset.load_font(font_path, options)) as font:
        unicodes = []
        for t in font['cmap'].tables:
            if t.isUnicode():
                unicodes.extend(t.cmap.keys())
        options.unicodes = unicodes

        # mangle 'name' so the font can't be installed
        options.obfuscate_names

        # apply our subsetting, most notably trashing 'name'
        subsetter = subset.Subsetter(options=options)
        subsetter.populate()

        # write [ot]tf, woff, and woff2 editions with 'name' mangled
        font_name_noext = os.path.splitext(os.path.basename(font_path))[0]
        font_ext = os.path.splitext(os.path.basename(font_path))[1]
        for fmt in [font_ext, '.woff', '.woff2']:
            dest_file = os.path.join(dest_dir, font_name_noext + fmt)
            options.flavor = None
            if fmt.startswith('.woff'):
                options.flavor = fmt[1:]
            print 'Writing %s' % dest_file
            with open(dest_file, 'wb') as f:
                subset.save_font(font, f, options)

        # write a sample somewhat (no early Android, IE) bulletproof css
        dest_file = os.path.join(dest_dir, 'bulletproof.css')
        os2 = font['OS/2']
        font_style = 'normal'
        if os2.fsSelection & 1:
            font_style = 'italic'
        with open(dest_file, 'w') as f:
            f.write("@font-face {\n")
            f.write("  font-family: '%s';\n" % font_name_noext)
            f.write("  font-style: %s;\n" % font_style)
            f.write("  font-weight: %d;\n" % os2.usWeightClass)
            f.write("  src:\n")
            f.write("    url('./%s.woff2') format('woff2'),\n" %
                    font_name_noext)
            f.write("    url('./%s.woff') format('woff'),\n" % font_name_noext)
            if font_ext == '.otf':
                f.write("    url('./%s.otf') format('opentype')" %
                        font_name_noext)
            else:
                f.write("    url('./%s.ttf') format('truetype')" %
                        font_name_noext)
            f.write(";\n")
            f.write("}\n")

    return True
Example #44
0
if len(sys.argv) != 5:
    print("Usage: " + sys.argv[0] +
          " font-file font-reader dimension-output woff2-output")
    exit()

fname = sys.argv[1]
freader = sys.argv[2]
doutput = sys.argv[3]
woutput = sys.argv[4]

options = ftss.Options()
options.flavor = 'woff2'

subsetter = ftss.Subsetter(options=options)

font = ftss.load_font(fname, options)

subsetter.populate(unicodes=glyphs)

subsetter.subset(font)

out = io.BytesIO()

ftss.save_font(font, out, options)

font.close()

# save the woff2 file
fw = open(woutput, "w")
fw.write("package serif\n")
fw.write("var Woff2Blob = \"")