diff --git a/python/afdko/checkoutlinesufo.py b/python/afdko/checkoutlinesufo.py index 98269977a..4f7e2b078 100644 --- a/python/afdko/checkoutlinesufo.py +++ b/python/afdko/checkoutlinesufo.py @@ -4,47 +4,54 @@ Tool that performs outline quality checks and can remove path overlaps. """ -__version__ = '2.3.2' +__version__ = '2.4.0' import argparse from functools import cmp_to_key import os import re -import shutil -import subprocess +from shutil import copy2 import sys import textwrap import booleanOperations.booleanGlyph import defcon from fontPens.digestPointPen import DigestPointPen -from fontTools import ufoLib +from fontTools.ufoLib import UFOWriter, UFOLibError from afdko import ufotools -from afdko.ufotools import kProcessedGlyphsLayer as PROCD_GLYPHS_LAYER -from afdko.ufotools import kProcessedGlyphsLayerName as PROCD_GLYPHS_LAYER_NAME +from afdko.ufotools import ( + kProcessedGlyphsLayer as PROCD_GLYPHS_LAYER, + kProcessedGlyphsLayerName as PROCD_GLYPHS_LAYER_NAME, +) +from afdko.fdkutils import ( + get_temp_file_path, + get_temp_dir_path, + get_font_format, + run_shell_command, +) -class FocusOptionParseError(KeyError): - pass +UFO_FONT_TYPE = 1 +TYPE1_FONT_TYPE = 2 +CFF_FONT_TYPE = 3 +OTF_FONT_TYPE = 4 -class FocusFontError(KeyError): +class FocusOptionParseError(Exception): pass -UNKNOWN_FONT_TYPE = 0 -UFO_FONT_TYPE = 1 -TYPE1_FONT_TYPE = 2 -CFF_FONT_TYPE = 3 -OPENTYPE_CFF_FONT_TYPE = 4 +class FocusFontError(Exception): + pass class FontFile(object): - def __init__(self, font_path): + def __init__(self, font_path, font_format): self.font_path = font_path + self.font_format = font_format self.temp_ufo_path = None - self.font_type = UNKNOWN_FONT_TYPE + self.font_type = None self.defcon_font = None self.use_hash_map = False self.ufo_font_hash_data = None @@ -52,58 +59,41 @@ def __init__(self, font_path): def open(self, use_hash_map): font_path = self.font_path - try: + + if self.font_format == 'UFO': + self.font_type = UFO_FONT_TYPE ufotools.validateLayers(font_path) self.defcon_font = defcon.Font(font_path) self.ufo_format = self.defcon_font.ufoFormatVersion if self.ufo_format < 2: self.ufo_format = 2 - self.font_type = UFO_FONT_TYPE self.use_hash_map = use_hash_map self.ufo_font_hash_data = ufotools.UFOFontData( font_path, self.use_hash_map, programName=ufotools.kCheckOutlineName) self.ufo_font_hash_data.readHashMap() - except ufoLib.UFOLibError as e: - if (not os.path.isdir(font_path)) \ - and "metainfo.plist is missing" in e.message: - # It was a file, but not a UFO font. - # Try converting to UFO font, and try again. - print("converting to temp UFO font...") - self.temp_ufo_path = temp_path = font_path + ".temp.ufo" - if os.path.exists(temp_path): - shutil.rmtree(temp_path) - cmd = "tx -ufo \"%s\" \"%s\"" % (font_path, temp_path) - subprocess.call( - cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - if os.path.exists(temp_path): - try: - self.defcon_font = defcon.Font(temp_path) - except ufoLib.UFOLibError: - return - # It must be a font file! - self.temp_ufo_path = temp_path - # figure out font type. - try: - ff = open(font_path, "rb") - data = ff.read(10) - ff.close() - except (IOError, OSError): - return - if data[:4] == "OTTO": # it is an OTF font. - self.font_type = OPENTYPE_CFF_FONT_TYPE - elif (data[0] == '\1') and (data[1] == '\0'): # CFF file - self.font_type = CFF_FONT_TYPE - elif "%" in data: - self.font_type = TYPE1_FONT_TYPE - else: - print('Font type is unknown: ' - 'will not be able to save changes') + else: + print("Converting to temp UFO font...") + self.temp_ufo_path = temp_path = get_temp_dir_path('font.ufo') + + if not run_shell_command([ + 'tx', '-ufo', font_path, temp_path]): + raise FocusFontError( + 'Failed to convert input font to UFO.') + + try: + self.defcon_font = defcon.Font(temp_path) + except UFOLibError: + raise + + if self.font_format == 'OTF': + self.font_type = OTF_FONT_TYPE + elif self.font_format == 'CFF': + self.font_type = CFF_FONT_TYPE else: - raise e + self.font_type = TYPE1_FONT_TYPE + return self.defcon_font def close(self): @@ -128,7 +118,7 @@ def save(self): only the processed layer. This hack is only performed if the original UFO is format 2. """ - writer = ufoLib.UFOWriter( + writer = UFOWriter( self.defcon_font.path, formatVersion=self.ufo_format) writer.layerContents[ PROCD_GLYPHS_LAYER_NAME] = PROCD_GLYPHS_LAYER @@ -156,39 +146,27 @@ def save(self): ufotools.regenerate_glyph_hashes(self.ufo_font_hash_data) # Write the hash data, if it has changed. self.ufo_font_hash_data.close() + elif self.font_type == TYPE1_FONT_TYPE: - cmd = "tx -t1 \"%s\" \"%s\"" % (self.temp_ufo_path, self.font_path) - subprocess.Popen( - cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - elif self.font_type == CFF_FONT_TYPE: - cmd = "tx -cff +b -std \"%s\" \"%s\"" % ( - self.temp_ufo_path, self.font_path) - subprocess.call( - cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - elif self.font_type == OPENTYPE_CFF_FONT_TYPE: - temp_cff_path = self.temp_ufo_path + ".cff" - cmd = "tx -cff +b -std \"%s\" \"%s\"" % ( - self.temp_ufo_path, temp_cff_path) - subprocess.call( - cmd, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - cmd = "sfntedit -a \"CFF \"=\"%s\" \"%s\"" % ( - temp_cff_path, self.font_path) - subprocess.call( - cmd, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if os.path.exists(temp_cff_path): - os.remove(temp_cff_path) + if not run_shell_command([ + 'tx', '-t1', self.temp_ufo_path, self.font_path]): + raise FocusFontError('Failed to convert UFO font to Type 1.') + else: - print("Font type is unknown: cannot save changes") + temp_cff_path = get_temp_file_path() + if not run_shell_command([ + 'tx', '-cff', '+S', '+b', '-std', + self.temp_ufo_path, temp_cff_path], suppress_output=True): + raise FocusFontError('Failed to convert UFO font to CFF.') + + if self.font_type == CFF_FONT_TYPE: + copy2(temp_cff_path, self.font_path) - if (self.temp_ufo_path is not None) \ - and os.path.exists(self.temp_ufo_path): - shutil.rmtree(self.temp_ufo_path) + else: # OTF_FONT_TYPE + if not run_shell_command([ + 'sfntedit', '-a', + f'CFF={temp_cff_path}', self.font_path]): + raise FocusFontError('Failed to add CFF table to OTF.') def check_skip_glyph(self, glyph_name, do_all): skip = False @@ -264,10 +242,17 @@ def _split_lines(self, arg, width): return [item for sublist in arg_rows for item in sublist] + [''] +def _validate_path(path_str): + valid_path = os.path.abspath(os.path.realpath(path_str)) + if not os.path.exists(valid_path): + raise argparse.ArgumentTypeError( + f"{path_str} is not a valid path.") + return valid_path + + def get_options(args): parser = argparse.ArgumentParser( formatter_class=InlineHelpFormatter, - prog='checkOutlinesUFO', description=__doc__ ) parser.add_argument( @@ -281,22 +266,6 @@ def get_options(args): action='store_true', help='run in quiet mode' ) - parser.add_argument( - '-d', - '--decimal', - action='store_true', - help='do NOT round point coordinates to integer' - ) - parser.add_argument( - '-e', - '--error-correction-mode', - action='store_true', - help='correct reported problems\n' - 'Adds a layer to the UFO containing a modified version ' - 'of the glyphs. The outlines in the default layer are left ' - "untouched. The modified layer's name is: '%s'" % - PROCD_GLYPHS_LAYER, - ) parser.add_argument( '--no-overlap-checks', action='store_true', @@ -317,9 +286,9 @@ def get_options(args): type=int, default=25, help='minimum area for a tiny outline\n' - 'Default is 25 square units. Subpaths with a bounding box less ' - 'than this will be reported, and deleted if the -e option is ' - 'used.' + 'Default is %(default)s square units. Subpaths with a bounding ' + 'box less than this will be reported, and deleted if the -e ' + 'option is used.' ) parser.add_argument( '--tolerance', @@ -327,18 +296,11 @@ def get_options(args): type=int, default=0, help='maximum allowed deviation from a straight line\n' - 'Default is 0 design space units. This is used to test ' - 'whether the control points of a curve define a flat ' + 'Default is %(default)s design space units. This is used to ' + 'test whether the control points of a curve define a flat ' 'curve, and whether colinear line segments define a ' 'straight line.' ) - parser.add_argument( - '-w', - '--write-to-default-layer', - action='store_true', - help="write modified glyphs to default layer instead of '%s'" % - PROCD_GLYPHS_LAYER - ) parser.add_argument( '-g', '--glyph-list', @@ -353,7 +315,8 @@ def get_options(args): parser.add_argument( '-f', '--glyph-file', - metavar='FILE_NAME', + metavar='FILE_PATH', + type=_validate_path, help='specify a file containing a list of glyphs to check\n' 'Check only specific glyphs listed in a file. The ' 'file must contain a comma-delimited list of glyph ' @@ -361,6 +324,30 @@ def get_options(args): 'characters are permitted between glyph names and commas.' ) parser.add_argument( + '-d', + '--decimal', + action='store_true', + help='do NOT round point coordinates to integer' + ) + parser.add_argument( + '-e', + '--error-correction-mode', + action='store_true', + help='correct reported problems\n' + 'The original font is modified. If the font is UFO, the outlines ' + 'in the default layer are left untouched and a layer containing ' + 'the modified version of the glyphs is added. The new layer is ' + f"named: '{PROCD_GLYPHS_LAYER}'" + ) + ufo_opts = parser.add_argument_group('UFO-only options') + ufo_opts.add_argument( + '-w', + '--write-to-default-layer', + action='store_true', + help='write the modified glyphs to the default layer instead of ' + f"'{PROCD_GLYPHS_LAYER}'" + ) + ufo_opts.add_argument( '--clear-hash-map', action='store_true', help='delete the hashes file\n' @@ -372,7 +359,7 @@ def get_options(args): 'tool will skip processing glyphs that were not ' 'modified since the last time the font was checked).' ) - parser.add_argument( + ufo_opts.add_argument( '--all', action='store_true', help='force all glyphs to be processed\n' @@ -380,29 +367,28 @@ def get_options(args): 'glyphs, even if they have already been processed.' ) parser.add_argument( - 'ufo_file', - metavar='UFO_FILE', - help='UFO font file' + 'font_path', + metavar='FONT_PATH', + type=_validate_path, + help='Path to UFO, OTF, CFF, or Type 1 font' ) parsed_args = parser.parse_args(args) + font_format = get_font_format(parsed_args.font_path) + if font_format not in ('UFO', 'OTF', 'CFF', 'PFA', 'PFB', 'PFC'): + parser.error('Font format is not supported.') + options = COOptions() - options.write_to_default_layer = parsed_args.write_to_default_layer if parsed_args.glyph_list: options.glyph_list += parse_glyph_list_arg(parsed_args.glyph_list) if parsed_args.glyph_file: - try: - gf = open(parsed_args.glyph_file, "rt") - glyph_string = gf.read() - glyph_string = glyph_string.strip() - gf.close() - except (IOError, OSError): - raise FocusOptionParseError( - "Option Error: could not open glyph list file <%s>." % - parsed_args.glyph_file) + gf = open(parsed_args.glyph_file) + glyph_string = gf.read() + glyph_string = glyph_string.strip() + gf.close() options.glyph_list += parse_glyph_list_arg(glyph_string) if parsed_args.no_overlap_checks: @@ -410,6 +396,8 @@ def get_options(args): if parsed_args.no_basic_checks: options.test_list.remove(do_cleanup) + options.font_path = parsed_args.font_path + options.font_format = font_format options.allow_changes = parsed_args.error_correction_mode options.quiet_mode = parsed_args.quiet_mode options.min_area = parsed_args.min_area @@ -417,7 +405,7 @@ def get_options(args): options.allow_decimal_coords = parsed_args.decimal options.check_all = parsed_args.all options.clear_hash_map = parsed_args.clear_hash_map - options.file_path = parsed_args.ufo_file + options.write_to_default_layer = parsed_args.write_to_default_layer return options @@ -977,16 +965,19 @@ def restore_contour_order(fixed_glyph, original_contours): def run(args=None): options = get_options(args) - font_path = os.path.abspath(options.file_path) - font_file = FontFile(font_path) - defcon_font = font_file.open(options.allow_changes) + font_path = options.font_path + font_format = options.font_format + font_file = FontFile(font_path, font_format) + use_hash_map = True if font_format == 'UFO' else False + defcon_font = font_file.open(use_hash_map) + # We allow use of a hash map to skip glyphs only if fixing glyphs if options.clear_hash_map: font_file.clear_hash_map() return if defcon_font is None: - print("Could not open file: %s." % font_path) + print("Could not open file: %s." % font_path) return if not options.glyph_list: @@ -995,17 +986,15 @@ def run(args=None): if not defcon_font.glyphOrder: raise FocusFontError( "Error: public.glyphOrder is empty or missing " - "from lib.plist file of %s" % - os.path.abspath(options.file_path)) + "from lib.plist file of %s" % font_path) else: glyph_list = filter_glyph_list( - options.glyph_list, defcon_font.glyphOrder, options.file_path) + options.glyph_list, defcon_font.glyphOrder, font_path) if not glyph_list: raise FocusFontError( - "Error: selected glyph list is empty for font <%s>." % - options.file_path) + "Error: selected glyph list is empty for font <%s>." % font_path) - if not options.write_to_default_layer: + if (font_format == 'UFO') and (not options.write_to_default_layer): try: processed_layer = defcon_font.layers[PROCD_GLYPHS_LAYER_NAME] except KeyError: @@ -1013,10 +1002,12 @@ def run(args=None): else: processed_layer = None font_file.save_to_default_layer = True + font_changed = False last_had_msg = False seen_glyph_count = 0 processed_glyph_count = 0 + for glyph_name in sorted(glyph_list): changed = False seen_glyph_count += 1 @@ -1062,7 +1053,7 @@ def run(args=None): if changed and options.allow_changes: font_changed = True original_contours = list(defcon_glyph) - if options.write_to_default_layer: + if font_file.save_to_default_layer: fixed_glyph = defcon_glyph fixed_glyph.clearContours() else: diff --git a/tests/checkoutlinesufo_data/expected_output/font.cff b/tests/checkoutlinesufo_data/expected_output/font.cff new file mode 100644 index 000000000..58836391a Binary files /dev/null and b/tests/checkoutlinesufo_data/expected_output/font.cff differ diff --git a/tests/checkoutlinesufo_data/expected_output/font.pfa b/tests/checkoutlinesufo_data/expected_output/font.pfa new file mode 100644 index 000000000..d9681bb9a --- /dev/null +++ b/tests/checkoutlinesufo_data/expected_output/font.pfa @@ -0,0 +1,245 @@ +%!FontType1-1.1: SourceSerifVariable-Roman 1.0 +%ADOt1write: (1.0.35) +%%BeginResource: font SourceSerifVariable-Roman +12 dict dup begin +/FontType 1 def +/FontName /SourceSerifVariable-Roman def +/FontInfo 8 dict dup begin +/version (1.0) def +/Notice (Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.) def +/Copyright (Copyright 2014 - 2017 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'.) def +/FamilyName (Source Serif Variable) def +/UnderlinePosition -75 def +end def +/PaintType 0 def +/FontMatrix [0.001 0 0 0.001 0 0] def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +def +/FontBBox {-180 -250 760 877} def +end +currentfile eexec BAB431EA06BB0A1031E1AA11919E714AC69FC6474167B6 +17C8FF56014245E5C1290C81D5312994CDC47B07C655DDD53C35D530C44F953F +804152D5ED7591016D3D1968FC4A9F629E0ADB83B91691E8A0AB81C098760580 +FFC73B03E534FD00C1C69885C6151AADEBF9BBF7284F088A81EB0A35C6F197CE +C173C8CE58E9B3FBEBF7D4954598D173E5896A4660789E367C49FBC7A3E20F42 +A52347EA49B9300561B0DBD43AE4C18A4352A96750397CC86B07D147617C9FC2 +CBEB239674A1C806A4B57AB3D602A0664613ED79E33294E73814153E93706FCE +632D700C2D012AFDF2EE270805A693D04B2966DBB5132247BC670553F1F440E0 +BE7AF56B0D60529A62DAE94A5DA509A20234E1B959D2C528EA6B8A76CDDCD235 +606435EDBCB1251380DEA9104B793E222DAC1F19D0C8EB360A79C2E589EDF79D +609E1137448AB94108C9D312582C3AF9C8863EB03AB1ADAD279B346DD2025242 +CDAF87FC1E6AB5F0F7FACDEB22D9E0F97AB76C2EB25239079FAE888BB595EECD +FDDBB1AA1EA2CE0BEBF8A3BFB54ECEC78A76E09D8EB15110F6D82C830F65A3B0 +EA89029777F5BEA946CC9F2AF2262EACC508A50C21A92EAA9AA24E5DE41B0611 +D54825A40186281552673EB1BE0A8FE147F131CCC68D0FE9FF9F4DE26C03FA92 +A5E5E4D0756520B4D16C7D09459017946C9E20B6F5F5195CD986DADC7FA87BE0 +ACA7AFEE98328992D98D33BDC7C03818FE8C78BC8853A063AD914B0F180D7B21 +DB1004D50C0645485BBC028DA3758BE4FDAAAEF6B4C210E0F2CE2D9CBBB44A5A +ABA5C2236D252B8BF25641EC945F5F19861F2CBD28EBFF3F67AF1BB695C1D609 +B749FED04CF95C4134ED71B27BE3FFC2F1D4EA46BE98A735506D138A08BFF4C1 +C6E849201D3071DB5C6B7C2FC7F615869D83F7660F34A586836BCF577F30167D +2B05F250724CC59F4C3848AFF4CEE792F3967B9B613E90413583EBEA6B9D453F +3C8CA390CF145D7939D9FEB961C797F341B176F851ED5BCA016BD81254B43F15 +04E16701D9B23DBD46C6CE2C68B223F1EDEB6BDAA2E9ACDE3B30DCEEEB59B50E +7D63C776BACE8F8488995CC1D43CC2A8CDE80E20E3E51D3F1431281B6D9F401C +5BC85D7A354BFFC19D40B10987945D278448FB8E49AE58B26B9DEC2EF6ED859B +76CCFD13B96518C390D029C255299B2C950FB6E4CB0C349334A7F0E20A50A34D +C78D26CE7E7826EBD7EA84381CE52E64E13948D004710AD07EDF9D82D564C253 +1C6283F6B807648A987D6156173592C2EECFD0CD416DB067B876EBD3F4FE77D1 +0C38A2858D3EE5B4D6F26C6BEE149ABBF647DC13D4CAA089CD19C75D4389765E +D5B7433F31AE05AEBEFFD37B570D2C422D65374144A1AFAD32FF89F9AA1AFBA7 +CF18E6CB8B898CE95085A9B0492DF87C5876FD71837D54AB0695EDE266C61AA9 +51A42A03D462158222006899C09009C3C786C67D2A6086C3974221FCBE930EA9 +0897FEF40AA9A9C26F63276B4813502C0660C11A8FEF7CCEA998E8D0A036EE55 +BCAAEE6DE3D8B21E38D9F4B7C7F0689737E5D3BC2A1D2859B638A27C8B793831 +99BDCEBD0598EE80435AB368C6FF6A32BF4C04B6A251D25681B6238C23A6B659 +57CAC130D08F1D79977E1F1B7A87329EDD8A97333820F03B41A479B54684F0B6 +9B77F7F9B68C96EA0D6F11EC79F814BB6F38CB3D3646744AA22122C7C62FBB3E +44D969BC59A26B04B92CC6F8528A7D414FEA1C4D3C418350334C91EC2522D483 +74DB5D10636177A0595C89D98E88B3CBA7995B189305AD24F7196403E4C05E3B +BEE1031CBFAB96EBDA3E76EBBE463554C926A5AB1F6BC8F03A343DF4F40CDEB7 +539330E7D7F1F906CB683B60E42510268BCFAC999B241AC50EA8BE5F38F5EC58 +3A97AFD526840FAF56BE49B8168070DEACBCD9982EB95663ED924A7735B43673 +5A7B280AEAC5C13FE396AED1F819D6B77657C60B9CC0E84363763CD3FF4EA8BE +6B842AE009214A11155D03AA182749AC3F7AF5294A62AFD79FABB7C31BADC081 +0B1B7567B0F1EF568D80B1169E31FFB413CDF712F85594C1ABB4E705CBE4A358 +047BC6DB875891366C3FAB05EB7070A78B1D5496BC505B476A3CDD88D8491FF4 +E4A6C40B839644F56A0B158864DA09CE51F40E1F35B676DD6287729EC282411E +20E7B38278B43FA5628B28B3706631FB06BA2E1158CE956375AA642BB32BBFC2 +558F1AB596D9542F041A559F5DC2497E565082FB5969F55B2E7D12ACF01B00DB +32597EF7A734CA67E107AF162EE36FDA71EBF8DF2FABA2CDBEFD4052563700A8 +61B07A999CA2725C4D26C4788ACB797BE5C2FFDD71E4433D5A06EE92F90C9113 +6521578C5FD6B086F250DB0410CB38C4BCCD469BEA616843CBB29102027EC7DA +46FC6D909866D2B116B188763EE6965F36877BBC04549A2BAE7500B05B68471A +C8107C7E339289EC201B23381705758388826A638B2A5438D770CB6958724351 +AFEE7FAB21C2C40FEC25050C674C1A7CE7682B4F5F47B93C56522EE33BA46475 +CED2D07D2A6EDD37D98D8470BFB8736C55942B0A2FB9D4C2D844F5A7863C91F0 +4DB37A9EADEFDFAEB9FAB38FB371DA62E98E5A42925504897FBDA64F1E285BE9 +1D5E24376EC297E9DBDF524867C187549FFDD867BEAF60FB42969EAB5E5A5E49 +914604FB4ED62D84B12872C9F9FDC59404A60FAFCD83C68B5536DC9D45955908 +942DC1C8F44E5ADBD5EDCB45336761542934297F53D9CF44092B10DD82ECD098 +6ADFDE5F6E988B2874EEE52B186BEFD0EC186283929A32572636C06D303D17B9 +BFBB43787B424FA7D6E01C08D3EBE4742A8432A7B190657A717EC6FEF2DB9535 +4BE533E9F04305AE833314D9C7000DA9CF77C52D9DE48300A7E78A30C525DC3D +2D4DDA929BF48AB7C15F16A0507C32F30F473083148BF58EA434C4ABB5935B43 +0E18FD9DB3581B7B3DA83461222AD064B5E42D4E4A9C3B1EE4AC9BFFF0E7C830 +60579442760B231697D7BDFEE94762F47BA2BA6E0B012264888C854910064917 +9A7CB4D4DC6221142341D3A3060C0E4907A0E5F147A2282993F7936E8E18AB3C +73C8DA5EEF1A77F7ABF02F9D74F63303FAFEDA96A9633247AA7D78FD1F1D27C4 +0B394DE38AD560A6A86B0FE4797F3A9CCDC6D730A0B9C6465DDCDF516E9D6762 +741C817E515863D854A5218FC8BCD287BC31A5FB5E1EB5A77DC1562BC99ED671 +B070DA06B004A09CE591C59F3CE2A4068BC8275CE0DDFCC381E24CF9D66366A0 +FA6055AEB9EE28D291E91E7E0E68DBA2F91BAED3F6F0ECE4EFD9D493C7FAD3C0 +F8D215E9EC8D6933F97DCC66C4C5C0B479672231DFA8A59031F3791DF5EF8BE5 +C954240AFD3925923E64DCFC06CC673D6C12C2013F007091D49B51336979F8E4 +29F856204109405C1E4BF058A555C3057E2A70D9C574F3D5A7BCED2EBD4C6A6D +04166D6A0C36707A0496001CE1B835780C71CBF286F40ABE9E56D741725AF367 +EAAA1767B8B0875981E0E8F410554125812361BA10AB587E09A6FA410722143B +5E8EFCD439E0AEA5D38D262D17D7624ED9979630C999778D6650F7709CD29F9E +1B01C5466E283325001046913248F04D5AED25014A529074B14E5CB5B96F6ED4 +5045C53CB7CF86CDB1B3F4E6EECC7A52EBED69B1F94ED9134A326C7AF17E258E +97C2854854F2103C86BEF46702906B985EF402C722962676B9DCD4237EC4512D +B84364D6FAA716B4C8C7478E71DB4CB6EE41F6FA5665957F8F599C34010A8662 +49E0CCF4BE78893530ABC8E3C0E9E3A6DB6257D02023C3B386DADB3FD5235989 +F906642C20D20180F593CC4F654BDA1E6F34D6CCDDC6E6DFCF968E9EACEDE05A +E571B07CFBB90AE925ECEF6292F32FFE2E7AD4494862041BAD286B0F3DA563A4 +A57D40F53560933E05286270BA6228505FEF18F4EE1E0254776473BEA57C5D13 +73A80B2026172AF1FD0866B1EE788EA31DA40A637CDC9BB420125263C2449D0E +FEC40BD39111D769E337390B346745058B3F83F0B8051B7DA5C1DAA7767120EF +B8971AB77E081E8B4D6E5C681A7C2A878E533BBCBF1F2D0E7C15173489230419 +227DC19A1D2274BB41B86ED18A92FF71C4068E79C8ADA0C6553352BBC27A5995 +571A1C8F4BAEDC5CC5A18CD955AA15CEE7FDCD812D28251ED0FABE8EDD32893B +6E7C7E6F3F4B5EA827347E16AF0DEC9A4D2D81C61175A1F16A879C5FFB559A02 +A7DE8AFB9442D46602D2CD20E4764813EC01C5D48A25156C2ABA255894C841F8 +BB603E3E9725AD75B4B00AE09AC0DD22C928B3D03850FD8154BCD04FDBA92308 +43D8519C9D0441304C015295527D96B62387CC9E2AA7502C2DD04AA26D26B3AB +91CDF40EADD38F44669778CCA0FCD53BCBD5FF3DBC73F65CCFC3D588ECED116F +758D3BE232A7F0C461E64A269A376589B13C461772A9AE77F980B94DDBC6DF40 +C39AC1FDA5773C221535CC23467A2C83E28D39B8B08A1159C7A64E9489B197AD +6DEF29E91BD165E426F2341855268B349F63C0A0D9305ECE17770630291A97B8 +2A9B90E328A02EE77AD92F7AF00D754E6648A2DCAD59C5F9B976902FE4713B8B +809032CA7436F86F2AF889E26611FC74726598C27F17EA4D4D12BED9A807D49C +269E53ADE4D87014F00BBD6AEEF318FAA1144DEDE820D3986C10D933589BFB48 +7E5E4C7130F12F9C21E195D613B670E4277A7E680C2F0EEC8876A14CC2B28755 +7CC995A174E240C42035EB939D6DAFD2812F32F04021206401B577E0C0C50AFD +6157BB5D07FA39268C0D780DEB22BEC207BB23AA519E440C107CC2D031B9BEC0 +AB7935D7EA5A28179C6704EFE5D017AEBDAC357BE4B9844BA03343D807974761 +734028D8638AC7D626C98037AE29A21749B2C9A555C61DF8C26A4BAA50568FC5 +2DEDE07FE5944EE3BF6C5D4003E3A2353D0A69017340FEC001077BA2BE26701D +F3FDF81CB83A6A55F9C790BE0C5FCE0046C5DB6D258CFDCE2997956AB29041DC +3B86E3393DBA91BB92262E0EFF6EA4968C72AB7ECA3EBA8B72DEEA9173CC470D +A0AF10D3276ADD37483FC03D87B53DFC9DA01376264A24E720C6D6D2FFC17995 +5E23CE755828E9B49FC3214F9769C24733565CFC5B847816499F511320B4F875 +9B96AAC1739472C8A6FF0016B6AD0E6616393B7A5B6F9ED84F8E31360A192E64 +AB2B7AAB72F657A420E67976490525C840BA2794AA027AA0822D46FD1F417F3D +F8E9CD571204074F85D8B1092E3C823D0648464415D67E4B68917F2223289F44 +205B3C26329157433CD012A51475C8F54A18B09223422D578110C1C41A3B8F8D +CD7E12AEE857BFA21D25FD2DDDB3F601BB91767A148EA6F6578001DF46CABB95 +AF9684A22DFBF9361935A2C0D1F28AF9DF6185BB34E128B633C35DD09A354DA1 +7470421CA09FF1802F46E6AFC8CE7B0DC2204814EF78DE71C9BCAAF2B9FFB949 +086617149E98DAA313C808D6E3F2BDE0721680DF74384DAC97F808A0B0113E40 +68610BB612841ED93CA2EF2E099DBED36E5316B82BAAF31E28FCFE365AFF81E8 +2F52C79D2904581C68FA9AEECFF66FCE4A26080A11EE5DE6EE443F3823DF3D1C +7D7EBAA81BE28FA72172868832156152749C485F3C657DE751DE2B0A23EF76AA +9385E5F72B659576E8BF03207A4C4A664E2CB201387EFE1D10293F05BB004B8F +B2173DB03E4FAC916E44F45E2F9818CC7F39E63BDED0B40C4A5E6692CCC6B5B6 +71F4CD11B6C4879179F128DC1D58B49AB60479CAB56AC9F0C00E6FEE27844BBF +19470533AA0EB61A84C113FAC4888FCD1EFD4F6238A5DF3CFA74ED1685B2D9B8 +C536EB4A5A9A9C74877DCB0C376CEFB9DCDE230644CDDDF219D5D93A3080E8D1 +451F146369BBBC2ACAD33A77AD3FABCFAABB2A98B64B6A0DD989519B293D08C0 +8ABB6BE084BDFD3EEFFA099EBE224C255158B989A9CE553C4536E5FBD028D0BC +DD42193623F3494A2605F06D0AFA645A1890A4B30C77BB0CC762A6784BA5CED2 +49FDBF7B1C71E28C9750270D9DE57230D73E8FB6AA00145594DD62F077165E6B +C6F7F66241AA41EAF60797547AA7FA8B44DED778F960218A9EE31F8802E66E35 +49D1EC63D97745916B33DE04F2BA25D598C0A7238FB063B2AB45666804EED7FA +2474505881F5EC51C3FAB852F98304081F1583F092D9BAE2E5215F10E8D73BB0 +B6C6C63FA990D24BCF49B6516953F6ADE64FCA687C75B7F60E7A4C773FBA06DB +17371AB82F40B26D789BD50363714CC9A90E0361B360785432EE73A87162AEBF +0DC2331CE0A827189B6462075C03F117A8A4C0D164A7AFEF99CE19838DA44989 +E48CCFD4B115AED4985E21071CEDADD6783253F7B39CF2648AD73E85889CCCBA +0AB4D3EDE2261A1A596385D9E4B80E88AB36E7573529D6150A4E5657A2D4F6C5 +D7CA25BB31368D6E2BE5AFA3D1BDA6E177E9F0F88F070EAEDF7F7D064FC1C01C +328BF38AAA84EE514D1D98CFF577D909DE3701A9BD218B1AD827DE50CE603702 +FD08EA002D30E418E4711BF376DFBB68372E7E0550FD6DE9DA7208BE92B18A1F +F703C6160B78B4B7DFB241A6A1D5634E6EB13A03CDE4E1CB0EB1116F9C78732D +C4E0A08A82FB4B1161587D09336975A5AA913029ED4FB9C71D56E1E6EEF1F677 +8FDA76B269CBF46677B291B29F80160FB785D81048AF64B0264EC9E3BCD05780 +001A6BFB5428B85CCC9F826873A7125D3F37D5B6F2BDB235376A7C2EB015AA85 +8642CBD8ADDCB48DCE57619B2231C1AF527EA026249BBB6A0AA3E7E19A8DBB21 +BE221B8E9176EE8014847A76C506E907C1775A2A09040AA235195880A63364E7 +B2E4F935544355033975DA23F6BD3D837F6A7BD7B219D85156C04DD24EFFB10D +F8A6A73708C3872068603428A93C83D0969BB0D0861788EF0B183970E155F00E +AEEEA95D63E436AEFD258EC6F2800CAE7D2F47C394492520374F00413BC8ACA8 +FFECFDCDE8D385D6E9BEE1731D730E4C171F13FEBFFEFB05E6384F02E91C6C6B +7CFE6BC2F3CB960330964E498A5D302AE1C6C8BA3C2E5B491CF5662FD3CCE1C5 +2D426C80155493B04C20B63F342A989CA30D4FAD9CB3DC1D65AB901FDFF56A34 +7FAA5848E45361F044149554C50DBFE367EAAE7361BB2A16CC01A43738043ECE +0BDBF7A95E98ABE3A651DAE1619AF83878C7FF4088033E598C94522A99DFF3DF +3C7F72E7936EDC434DBA91F9A08F49310D533D034491A5955882DEB094DE5225 +4ED5B2E8B6F083CD50B0B36AD0E7C54BCE4AC9C171F8EAB322D24ADE3C1FD117 +785A71401708340D4D2F16A2722A7ACE703691F3882DD22FA383499F8AE624EB +865F2D162CB853BABA91CC61E79F4B062B216C62F1D79B77AEC8F04EC59BE033 +9644C1448868C9B6858D324DBF86D77F7FF334BC4E4AA64E7CA5EF00B7220D57 +FB85B920C55D52B14962C676E17336596BD6CE6C73E7C20BA261309013FB4361 +9B284AE79500CFB327BD2CE8738BE69227D0E2C7E40723DE9F226D165205651B +E4C8AB9AFDF09B8E7EAEFE43EBB783E44472106134451901C01953D7EA8DD20F +92927DEA3C1A43E050EBEE78EFE09B73C4BED5B66696B959A15B6EF0AD1B6AE7 +0FF083B9686209B775A6AE50DC5CB21F07330CCEEC65FA8D21F6DBBB8ADF6A69 +ED00E049FE33E61D20575946352686441DA6C615AF808E6D782D05FD26ED5BE4 +343AE49F753DBE90A51EDF8C4B3F033718285E32634166C2072FD79FA23C93D0 +3CEA4C9781D45AFF8FEB2C9FFD101431C96DFD3D128A76B7779E5DFAEC75787E +2764D1D3A175DB49516C5B8877D30AA98DDD732C85CC9663EADB2E9B5B8D0A69 +DE4DEEE6D8374779DB54DED8A450258D4DD0803AD4AD26A20376DD3E7AB6DE72 +A5F6DC382D6E4EFCA473674F22813591F1162E79C8F0C4D469051F6F2B054434 +A02D7F6AAD57450876BAC63FDD62B7D7A84A748D03E29259022FC1576EBD9C2E +B87BE9EB335EC4C6DE845EE7BB4C9CF878DB5C10DE964D7A19437C9018357310 +2000EB4A5103752E45E95C5A6169109F57E2DB835AD604A5799DBBAF26E66F59 +1F1ED076E4003B4542AB62557B32843954989D723B2356480635CBB353A5626E +26F4E2A63992D0CED4D6FF54080B8F29D4832DA70B619866FAF916307C8F0BA8 +2A1DB347B9F7C832B98B1661B7A8811052442288A9AAD1008B803DA2549FEFB4 +0B5D8EEAC9AB4BED76684530740E79EA67729A678C9CB81640C340DB784CED88 +BE7F6834A7479CE7299D4D1EB3048C941B931E893BFD36CD1A8854EA12081B3B +F4DA03D3C69BF98E737990F272E044DEE91C01C7AED85F57FA04F0892120F86E +853FBCCD3094BF03F65564CA6BD02E587DA7B0C3D10F45BAFD4A92E84464A0B1 +65BF43351409F02A321A2D384DA65CA38207D33FC47E442541838AE1FFA6BEB6 +11A8418D908AF9CC8867FE91DE6D05C4BBE3FA1B544C3859949042DA12898FD6 +CCADF15E2B73CE2561D87DCC902E9DE2F46D5C6529B9E9A614EEA649A4EC210B +9770DF99362DAD3F9D13CF7F1B95D94C10B428BE6A358D33C1382A42565A3A70 +69119FC3F8FF9156E07B123D2538C093FC7A25BA248F8CFC3AD1247469611F59 +7AA09C989E38A6C9AB32DC894D1FDFADD75A1C090D5D8D288D3AB1BF65DFB4C2 +2C1FA3E5E8EE48010E22A792DAFE2BCDA644E4846C68EE1059DA07DB5FEB42BE +199707ED698D54679E138A2ED5B59EF8C26686B6D65DCCF70421DF1C6081BC5D +908CD9984D048D074FDF05991AF7817E97DA89D1F8BA18CA21A4BBA9B68645B5 +727BF06C732199801559CAF58195BB6635ED99FAF31498AFFD2BB4FE24DF8C6C +A70268BAD8331A5C039CEF51F0C487B6733027405A0B629649FDB95FF92CC807 +4AC2C6C4D9A1F28D8F6FBE57884A6C12AD662DDA625C018720468A16FB806E64 +53201D4253A2A1C9414EA50ED0822CFB11DA0D27ACCB6400DEA837DA87BB9485 +DA849334BF932D0208FF712301D48F6DAA5333E852011BF7A1650CA25452803D +447DC132F02C3519DA337DE2086EF363BF6453AADA14DFA1FCE34D25ED440B91 +DC5F2902D839CCCA1D0194C04625467572A2C9CBD98FAB212490EE9089432DBF +9F06049F9B455C6776B72ED003BE63B02EBD3FD47B8EDB1CCCB30D60B3EBB9D1 +3EBDB2D6200421DB133C17DD19863A77D289AA88BFC7A6B46CC4244C4987A3C4 +625DFDAFCE556868854519F5A9ABE52FF6561E738C6800E9C204D792F6794E8D +C4479DEAA7DD9187503C8DF841BF9EC214DB001DD6BB365C9D79AC4556BB4CC6 +335702BAFB6771DEA5A41D4ACE05D721547A4ABC3FB4031F96993F4CCE44DF6B +7D5E4AF3C5EAB47CD17A0DC932FD01E8FF7304117C00286E9DECFA0E344003AC +EE86FE7407E4EB699DDD573761E4A1DBCAE1C8E5BFB9789D1D09E600A7301640 +DAEE015C68AF4B317530774027A16FA4DC02A8D50955FE596960B9938BC6B89B +A168A6652B7102E482CDA4C20E26567DC27D79E88C13DA0329C4B672097C7734 +83BBAA56970CEA505BD7780FA59632F54D0F2D47CA3E739A9654728109A84713 +6CF83554D55ECE28ECBEB6EAB34C8779CB96C2718390333452D6BA6EA2A4B005 +E8AE87FC4ABE21A20573C96A688033F88D5313006876DC67E9C0A58391DCFD7D +C37F5732EF29E41511C72C49778A63354170732932BD881704CDCD8D8A40D7D8 +60655C38B3B040966C6F418ECB04F48590D56CFAAD +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +%%EndResource +%%EOF diff --git a/tests/checkoutlinesufo_data/expected_output/font.ttx b/tests/checkoutlinesufo_data/expected_output/font.ttx new file mode 100644 index 000000000..db5055bd2 --- /dev/null +++ b/tests/checkoutlinesufo_data/expected_output/font.ttx @@ -0,0 +1,666 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -88 callsubr + -204 136 -146 202 70 -73 callsubr + return + + + -85 callsubr + -153 86 -96 137 94 64 46 83 27 hvcurveto + -72 callsubr + return + + + -76 callsubr + 73 -24 75 hhcurveto + 137 99 72 118 94 -51 58 -132 51 hvcurveto + -95 callsubr + return + + + 200 -13 rmoveto + 128 65 68 74 62 -38 47 -100 37 hvcurveto + -97 callsubr + rlineto + -25 49 43 -13 56 hhcurveto + return + + + rlineto + -81 callsubr + 6 return + + + rmoveto + 25 -19 42 37 40 36 40 37 rlinecurve + 27 25 5 15 13 vvcurveto + 25 -18 12 -19 -17 -17 -10 -30 -19 vhcurveto + -31 -47 -29 -47 -29 -47 rrcurveto + endchar + + + 0 87 2 86 -66 callsubr + -85 0 -86 -88 vvcurveto + endchar + + + rmoveto + -24 -19 101 -147 rlineto + 74 hlineto + 101 147 -24 19 -114 -104 rlineto + endchar + + + 0 86 2 85 -66 callsubr + return + + + 153 335 -65 callsubr + 141 59 -150 -150 -150 -59 -150 -141 -141 -60 150 150 vhcurveto + 201 350 rmoveto + -63 callsubr + -228 151 -122 155 -64 callsubr + 228 -151 122 -154 hvcurveto + return + + + -30 11 rlineto + -68 25 -25 17 45 vvcurveto + 42 32 27 62 24 24 -6 -12 22 vhcurveto + 16 -79 rlineto + 50 hlineto + 4 101 rlineto + 26 -43 -42 15 -54 hhcurveto + -111 -61 -67 -72 -68 48 -39 73 -27 hvcurveto + 46 -17 rlineto + 63 -23 30 -22 -43 vvcurveto + -46 -36 -31 -77 -31 -26 5 11 -24 vhcurveto + -20 86 rlineto + -53 hlineto + 4 -109 return + + + rmoveto + -50 vlineto + -86 0 -87 -2 -86 -78 callsubr + 70 vlineto + 86 -101 callsubr + + + -39 15 rlineto + -73 28 -41 37 63 vvcurveto + 75 59 39 78 31 25 -6 -14 26 vhcurveto + 21 -113 rlineto + 60 hlineto + 6 130 rlineto + 32 -43 -53 21 -67 hhcurveto + -127 -97 -70 -115 -102 70 -58 102 -40 hvcurveto + 38 -15 rlineto + 87 -34 39 -29 -65 vvcurveto + -78 -63 -44 -95 vhcurveto + return + + + 455 630 rmoveto + 105 -13 rlineto + -449 vlineto + -365 502 rlineto + -150 -40 hlineto + 71 -9 9 -12 rlineto + -557 vlineto + -85 -12 rlineto + -40 240 40 vlineto + -105 14 rlineto + 487 vlineto + 403 -548 rlineto + 32 624 hlineto + 85 13 rlineto + 40 -240 vlineto + return + + + 646 657 rmoveto + -42 33 -61 -75 rlineto + 46 -54 -67 24 -68 hhcurveto + -63 callsubr + -99 29 -80 45 -57 hvcurveto + -70 -86 42 -33 65 79 rlineto + -49 55 69 -25 71 hhcurveto + -64 callsubr + 103 -31 81 -47 58 hvcurveto + return + + + rmoveto + 29 -9 rlineto + 50 16 20 31 33 hhcurveto + 30 21 -28 -21 25 hvcurveto + -18 22 23 -16 37 hhcurveto + 62 29 51 77 13 hvcurveto + -29 9 rlineto + -50 -16 -19 -31 -34 hhcurveto + -30 -21 28 21 -25 hvcurveto + 18 -23 -22 16 -37 hhcurveto + -61 -31 -51 -77 -12 hvcurveto + endchar + + + 140 310 rmoveto + -88 0 -86 -67 callsubr + 81 0 84 104 vvcurveto + 330 hlineto + -94 0 -90 -75 callsubr + return + + + -65 callsubr + 63 47 -30 -47 32 vhcurveto + -315 -387 rlineto + -19 49 -9 57 58 vvcurveto + 401 hmoveto + -150 -59 -150 -141 -66 -48 33 50 -33 vhcurveto + 317 389 rlineto + 20 -51 10 -60 -61 vvcurveto + endchar + + + rmoveto + -88 0 -86 -75 callsubr + 88 -99 callsubr + -86 0 -87 -86 vvcurveto + endchar + + + 390 35 rmoveto + -139 -98 105 195 195 109 105 135 32 36 -8 -20 37 hvcurveto + 19 -115 rlineto + 61 hlineto + -4 145 rlineto + 39 -68 -71 9 -54 hhcurveto + -195 -142 -150 -200 return + + + -25 -83 callsubr + 8 61 rlineto + -36 hlineto + endchar + + + -117 -84 callsubr + return + + + 275 442 rmoveto + 17 19 -3 -6 20 hvcurveto + 19 -67 rlineto + -25 7 13 -20 33 hhcurveto + 25 15 12 25 5 hvcurveto + 74 -13 -74 55 -89 hhcurveto + -119 -110 -94 -157 return + + + rmoveto + 13 -2 12 -7 21 vhcurveto + 13 -14 -15 6 -14 hhcurveto + -25 -21 -18 -26 -25 20 -23 26 -11 hvcurveto + -22 -34 -29 -17 -40 -16 10 -28 rcurveline + 70 23 63 40 70 vvcurveto + return + + + -86 rmoveto + -81 callsubr + return + + + 711 -80 callsubr + + + 49 -7 23 -14 -31 vvcurveto + -28 -19 -18 -39 -18 -14 1 2 -16 vhcurveto + -7 -30 rlineto + -4 13 21 -5 28 hhcurveto + 73 38 34 46 48 -41 26 -50 8 hvcurveto + return + + + rmoveto + 18 -24 41 21 42 23 41 23 rlinecurve + 37 21 13 16 18 vvcurveto + 20 -17 13 -20 -17 -17 -10 -28 -28 vhcurveto + -31 -30 -31 -31 -31 -32 rrcurveto + endchar + + + rmoveto + -30 31 -31 31 -32 31 rrcurveto + 28 -28 -17 10 -17 hhcurveto + -20 -17 -13 -20 -18 13 -17 37 -20 hvcurveto + 40 -22 42 -23 42 -22 rrcurveto + endchar + + + vhcurveto + -93 -11 rlineto + -40 497 vlineto + 10 188 rlineto + -60 hlineto + -29 -138 rlineto + -231 hlineto + -2 85 0 84 71 vvcurveto + return + + + hlineto + 1 36 0 35 1 35 93 11 rcurveline + 40 -280 -40 vlineto + 93 -11 1 -35 0 -36 1 -35 rlinecurve + return + + + 231 35 rmoveto + -44 -29 8 18 -32 hvcurveto + -20 117 rlineto + -60 hlineto + -6 -140 rlineto + -29 46 return + + + -67 callsubr + 86 0 87 86 vvcurveto + 50 vlineto + return + + + 832 -69 callsubr + + + 69 14 34 63 hvcurveto + 4 145 rlineto + -61 hlineto + -19 -115 rlineto + -20 -41 -42 -8 -39 hhcurveto + return + + + -24 12 rlineto + -50 -31 -51 -28 -58 hhcurveto + -94 -67 69 126 128 62 69 87 hvcurveto + return + + + vmoveto + -68 callsubr + + + rmoveto + -68 callsubr + + + rmoveto + -23 -20 100 -117 rlineto + 74 hlineto + 100 117 -23 20 -114 -85 rlineto + endchar + + + 37 29 26 37 37 -29 26 -37 -37 -29 -26 -37 -37 29 -26 37 hvcurveto + endchar + + + -2 -85 vhcurveto + -93 -11 rlineto + -40 280 40 vlineto + -93 11 rlineto + -2 return + + + vhcurveto + 93 11 rlineto + 40 -280 -40 vlineto + 93 -11 rlineto + 2 return + + + rmoveto + 150 60 150 141 return + + + 155 150 131 219 return + + + -154 -152 -131 -219 return + + + + + + -40 endchar + + + 123 5 hmoveto + 230 40 hlineto + -99 13 57 166 rlineto + 234 hlineto + 56 -167 -100 -12 rlineto + -40 270 40 vlineto + -77 9 -213 626 rlineto + -60 hlineto + -214 -622 -84 -13 rlineto + 203 225 rmoveto + 102 300 101 -300 rlineto + endchar + + + 88 140 310 rmoveto + -88 0 -86 -2 -85 vhcurveto + -93 -11 rlineto + -40 274 vlineto + 189 75 92 91 80 -55 66 -132 19 hvcurveto + 109 23 45 64 71 vvcurveto + 96 -80 68 -137 vhcurveto + -288 -40 hlineto + 93 -11 rlineto + 2 -86 0 -87 -86 vvcurveto + 147 -37 rmoveto + 140 61 -47 -93 -88 -63 -49 -130 hvcurveto + -63 hlineto + -2 85 0 87 105 vvcurveto + 74 301 rmoveto + 103 53 -32 -92 -92 -53 -39 -123 hvcurveto + -54 hlineto + 80 0 89 2 86 vhcurveto + endchar + + + 92 -107 callsubr + endchar + + + 92 -107 callsubr + -70 -82 callsubr + + + 92 -107 callsubr + -118 -74 callsubr + + + 92 -88 callsubr + -192 121 -141 183 -16 hvcurveto + -11 -72 -103 callsubr + 43 rlineto + 68 -73 callsubr + endchar + + + 92 -107 callsubr + -19 703 -70 callsubr + + + 250 -91 callsubr + 88 -99 callsubr + -82 0 -82 -89 vvcurveto + -330 hlineto + 82 -99 callsubr + -86 0 -87 -86 vvcurveto + endchar + + + 250 -91 callsubr + 36 0 36 35 vvcurveto + 110 46 -110 -77 callsubr + -330 -77 callsubr + -110 -46 110 hlineto + -36 0 -35 -36 vvcurveto + 90 6 rmoveto + 30 0 36 35 vvcurveto + 330 hlineto + -31 0 -32 -38 vvcurveto + endchar + + + -170 140 310 -89 callsubr + + + -170 201 855 rmoveto + -32 hlineto + -130 -113 19 -24 127 73 127 -73 19 24 rlineto + -191 -432 -89 callsubr + + + 57 140 360 -96 callsubr + + + 57 364 -86 callsubr + -224 477 -96 callsubr + + + 57 468 311 rmoveto + 39 31 32 38 39 -31 32 -39 -39 -31 -32 -39 -38 31 -32 39 hvcurveto + -328 49 rmoveto + -50 vlineto + -86 0 -87 -2 -86 vhcurveto + -93 -11 rlineto + -40 497 vlineto + 10 188 rlineto + -60 hlineto + -29 -138 rlineto + -231 hlineto + -2 85 0 84 71 vvcurveto + 70 vlineto + 86 -101 callsubr + + + 57 140 360 rmoveto + -42 vlineto + -95 -54 rlineto + -55 vlineto + 95 54 rlineto + -71 0 -71 -2 -70 -78 callsubr + 19 vlineto + 166 95 rlineto + 56 vlineto + -166 -95 rlineto + 81 -101 callsubr + + + 195 -94 callsubr + -10 -777 -84 callsubr + endchar + + + 195 -94 callsubr + -52 76 -79 callsubr + + + 167 -98 callsubr + endchar + + + 167 -93 callsubr + -428 -242 -90 callsubr + + + 167 -93 callsubr + -377 183 rmoveto + 300 61 -300 hlineto + -51 -486 -90 callsubr + + + 167 -98 callsubr + -180 50 -92 callsubr + + + -30 -105 callsubr + endchar + + + -30 -105 callsubr + -12 -82 callsubr + + + -30 -105 callsubr + -75 -74 callsubr + + + -30 -76 callsubr + 72 -24 74 hhcurveto + -10 -71 -103 callsubr + 45 rlineto + 119 11 82 69 108 vvcurveto + 94 -51 58 -132 51 vhcurveto + -95 callsubr + endchar + + + -58 576 -102 callsubr + + + -66 746 -80 callsubr + + + -54 -106 callsubr + endchar + + + -54 -106 callsubr + -40 134 -102 callsubr + + + -114 737 -100 callsubr + + + -114 867 -69 callsubr + + + -54 -106 callsubr + -116 295 -100 callsubr + + + -54 -85 callsubr + -146 79 -95 127 -7 hvcurveto + -11 -74 -103 callsubr + 46 84 6 57 45 25 77 rlinecurve + -72 callsubr + endchar + + + -54 -106 callsubr + -3 148 -70 callsubr + + + -87 callsubr + + + -87 callsubr + + + 61 -86 callsubr + endchar + + + 590 -71 callsubr + + + 738 -71 callsubr + + + -305 119 418 rmoveto + -3 -6 6 -273 4 -3 rlineto + 30 hlineto + 4 4 6 272 -3 6 rlineto + -22 -425 rmoveto + 33 32 -33 32 -33 -32 rlineto + endchar + + + -344 110 62 rmoveto + 3 6 -6 273 -4 3 rlineto + -30 hlineto + -4 -4 -6 -272 3 -6 rlineto + 22 425 rmoveto + -33 -32 33 -32 33 32 rlineto + endchar + + + 66 746 -79 callsubr + + + -150 760 rmoveto + 300 61 -300 hlineto + endchar + + + -107 -104 callsubr + endchar + + + -107 -104 callsubr + -12 589 -102 callsubr + + + -107 -104 callsubr + -88 750 -100 callsubr + + + -107 183 -83 callsubr + 6 46 rlineto + 108 10 55 63 68 vvcurveto + 62 -38 47 -100 37 vhcurveto + -97 callsubr + 47 -24 42 -13 53 -1 rlinecurve + endchar + + + -180 735 -92 callsubr + + + + + + + + + + diff --git a/tests/checkoutlinesufo_data/input/font.cff b/tests/checkoutlinesufo_data/input/font.cff new file mode 100644 index 000000000..f7bcf6fb3 Binary files /dev/null and b/tests/checkoutlinesufo_data/input/font.cff differ diff --git a/tests/checkoutlinesufo_data/input/font.otf b/tests/checkoutlinesufo_data/input/font.otf new file mode 100644 index 000000000..fe5021204 Binary files /dev/null and b/tests/checkoutlinesufo_data/input/font.otf differ diff --git a/tests/checkoutlinesufo_data/input/font.pfa b/tests/checkoutlinesufo_data/input/font.pfa new file mode 100644 index 000000000..b0f6fbf8c --- /dev/null +++ b/tests/checkoutlinesufo_data/input/font.pfa @@ -0,0 +1,255 @@ +%!FontType1-1.1: SourceSerifVariable-Roman 1.0 +%ADOt1write: (1.0.35) +%%BeginResource: font SourceSerifVariable-Roman +12 dict dup begin +/FontType 1 def +/FontName /SourceSerifVariable-Roman def +/FontInfo 8 dict dup begin +/version (1.0) def +/Notice (Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.) def +/Copyright (Copyright 2014 - 2017 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'.) def +/FamilyName (Source Serif Variable) def +/UnderlinePosition -75 def +end def +/PaintType 0 def +/FontMatrix [0.001 0 0 0.001 0 0] def +/Encoding 256 array +0 1 255 {1 index exch /.notdef put} for +def +/FontBBox {-180 -250 760 877} def +end +currentfile eexec BAB431EA06BB0A1031E1AA11919E714AC69FC6474167B6 +17C8FF56014245E5C1290C81D5312994CDC47B07C655DDD53C35D530C44F953F +804152D5ED7591016D3D1968FC4A9F629E0ADB83B91691E8A0AB81C098760580 +FFC73B03E534FD00C1C69885C6151AADEBF9BBF7284F088A81EB0A35C6F197CE +C173C8CE58E9B3FBEBF7D4954598D173E5896A4660789E367C49FBC7A3E20F42 +A52347EA49B9300561B0DBD43AE4C18A4352A96750397CC86B07D147617C9FC2 +CBEB239674A1C806A4B57AB3D602A0664613ED79E33294E73814153E93706FCE +632D700C2D012AFDF2EE270805A693D04B2966DBB5132247BC670553F1F440E0 +BE7AF56B0D60529A62DAE94A5DA509A20234E1B959D2C528EA6B8A76CDDCD235 +606435EDBCB1251380DEA9104B793E222DAC1F19D0C8EB360A79C2E589EDF79D +609E1137448AB94108C9D312582C3AF9C8863EB03AB1ADAD279B346DD2025242 +CDAF87FC1E6AB5F0F7FACDEB22D9E0F97AB76C2EB253E26774FC345FB5ABC046 +2FFF3193AE0743669384ECE499217C6C429699008BC100223B28D6C24AC6AD2E +B4922C8AD522EFA630C9A99C6888DC794772556E5783298868F101F2BB0F710A +85E5F1CDF3BE9FFD226137196101862AC14E6025636828EE5254EB7E66F7E381 +BCCC1F141176448E7ED6A4FF02480A28BF3013D2444723B76A72ACDBE2E054C5 +F99A9863A0E3CC0E80B6F07DF1BAE9CDA97DD86A554B22C500F4A2315D605574 +487E5A34B89BB2D0380F0E2398A62A41A226501BE841941236EA845700E4EE1B +2781C7393D7159678E3D97463376BEB5DE3CB865CAA11F0D00042CEABC070539 +C3FFDB5BC9D1F365FE0705A6CE3A68618D88909C9D393BA015794DDB1E4413FC +98B175412E739C870709A4FD3521786F64205B23CE68B9BC38EF3E5B880C072C +81114DE2B2D27CAD1437E33AFA75F1F4FA72CEE1ABE245A69757597812BF1E82 +6FDA3331C892F1BED014EAB96CBF8E09E01D7C4877B2E1AD9366701966B7850C +0F64779F1A946629CE456906CDB0A9FD219E823D1575D04F9AAED59F70879030 +4B3BC9788FE2799B10D5305F34F7579AA4B4158B926A05F91B51CE5FE90027C7 +9E230067967A1A32702BFD2F70E8FEBC99A576DDD10C20B0E29BF9BF3264F928 +E3EA8544E47A13E3EE3335C0E5F4172491E78AEE043AD539BBB6F81F2408DC74 +C9AA8E62155668BF1FF08BE01B70E26167458D90B00D04F96F278F6741388BF7 +312027519A8E7735B3582CB4513E0258955F022985F4C713BBB736733D8E85A5 +3057E251BC550893023A32817856E13CD8E7A14A640DD0F7179A8FDE9E8CD7E6 +974C27C7D0EEA1AAE82F7D4623DE59BA28A828DE5292B6EDF01646A0A7A54F1D +6188AC158BBFA4AA0ACC859746D707BABCF4E9EA2CB9D34ED484CED3315F0706 +7B2BF2B236125C9557DAA97085CEC59AD97EAA519A39ECD0F24DCA078196EEF1 +A23F49894D3352143DF09D842BA6F6113EB4A2E8ED824300090C66F25B899FF1 +E849A606CAF2E130535822E3F3B8C20959C93A7391D561DD7C527D565CD53160 +7133AE966F1CE321C150B7A6C437BC2076C2DD541D85ADD51119927DEDE05705 +CC2295F6FD8819347F99E71C5A4CA5FA2E51CDF1A11F62CC7D8FAFCE42142C08 +AEFDE0F93B9EA41A044B0D108BF0670E7E6678D3D800D7675D50CD2B3A2F902A +4AA287ADAE6B7AB070D441E716FD3C815B09150E001AECE4D1902A9568448F20 +AD7EAA4E3EBE14457BFAB4403F2E3FB41D5712608A9656AC95B6AFC5EA5F22F1 +7A83C6BF7C57BE01488743892B93C660083E213096D641550010D5377EED0DB0 +51DD2D581DF257DF30170238C854423FF23758FBCB6CFA97A940A7B7B24A5D27 +9128399E2F65CA2B80F789828A8CF0E359A66286FE7EA1DBD59398C5F214CB50 +20B0E16B2326E162E0869F7DC46561E863910878F72161095EA37D9606D13C78 +EA142B69549D6C7428F8371FD45E51687F5D0CB948F5A9DCD1C8EABBCEE490E7 +216E07C9A0EBB184BC04562F6CC49D4C39B62202987F68F7308A6514ECBD1457 +5545D0B85B9213ADECEF993B5E9FF12E52D4DC48848104272FE23AFCB4D3259A +6E7F63F4EA1883ACC43F454C7DDCD98799B7DFC5FFFD25543B90C66E377F4058 +499DEF207BCF6B0439D5B87489AAA4E4EB53C7DF219673035D873F790AE6797F +82A192A4B8DAE17A86A10604D02C55F9C78A4700C1C19D43948B203F045BB795 +202F096E36B5907453FE26A35FF6A9A91A39530EFA615B58F63B87FF85B5E7A7 +A44EF62A7E4EC606306D666CDC2962222D77B2C3FDF33F8E30256F375CACFF3D +A0EBA3FA539C3199D327C2D0928BF1DAC722AA301891829829EFE4604614A497 +92B3383EA6FD5A3C9D9D54CB0C1C0389960EA1076FA3964CACB9E1D28971D8E8 +2762374C8D82EB6B74DF1DA6F9437F521B74CAAC39FFF7C697D9CFE69B369B93 +6DBA5A48ED032F5ACDC5D6D0DFBE2D577093B5C48D7967B593C92C8B4EFDEA6C +F29105F1A3A945F7D5AEC4D4D9A881E2A065204658620D56A91A4EE2601C3BBB +54BFE6369530C1BEFA0D290B2637BC32CD460C7B2E7A1B0CC43BC1A13985585C +B4E199ED29A55253D1FE249412DB3C2BD409E192E494B274CA16615119B92987 +E47FAFA239C3D417B9F218BAF93B46F05BD62CE10653A33CA282AD60977ADB6E +4B5554E24BEC3266FEB5B8ADD35F477870D6045CDBA4059E79ACAFFEFC322EA5 +26B76FD56DFF3194C17077C7B13EC07565480B8D78D4FBE858BD1C5F795BE764 +F8B10D3258FF066E898DDE26F66BDA7C5605ECAE30AA8DB318FCE1018434FFA5 +404AB4FC1501222A751BAE64127879919E1039B317D26F97C1546F75E56201DD +00E476600F540945B6E9297BBF9F5E5E5A87ADFFEB458D8EAFC60F40D973FC4C +BE574E4982458AEB97E5C9CC8296389C12C2AEE1A8D252EA79AABE3DD8FDF071 +63C211F89F5DFF3BC950F4726D9BAC261E58C46A631D41DC2118AB48224A4869 +386375F9BE75678FD9158C5BB8016CE13FB80287C261703D45D1CAE048D11C39 +642E9850FAF3F5C719AF8C0BF36FF454EDCB33F50AF7C4B26595DDA7CA19CF65 +F1DD452C8D00CFCFC4FB7D87894DCBCA9BB61651182A26221BDE2B8020C692E8 +83F0E4584CD6B0E5624A434DBAC0CFE656BE2C199B13E0DDE839205798781D15 +A984603D3D66BA48B1BCD1F9962661DA0738099E5D2550F35BD58AAAE98BBCC7 +D4FF52D3CF699B0F59B252CF25935AD96FE524F48A053FD8AC76CDA5E60081D9 +6654F349DC265E89CD20E401C25D6C828E99D6F0A6E6ABD6EEEE4A484F32284D +BB0A2706B0CECE12D80ADEE499F46A464D7F43853CE56AC751A88A3D307E1BDC +AB2F2F04128BBC05E9B78EEDEE7CA4855B23F0E13582513284E34F3F1D5460FA +0A0C5607FA0F1EF9BCF8A2F116333374C883DE7CAFE2736E372FE954B99A9BE3 +9B4A4857090B1142EA0409482BE65FE0CB7E911AF9E67B0D646A2800D0C4468C +235892CCD17269F58045697A6A59C9640576B49904983AAB79D3B7DA6CF8CCDE +9739F665A995A7B954B9FE1EA832AE10257873ADD1DED32A9EDFF31374F47202 +827132841B40A779948ACE16DBF3CD8C6473E93AD48D4B886BD1501D6C9AED36 +B7B52F96393657B7C88F4D3F811976477D841EE8A84B7A53FC2A1F1EA9961114 +DF37C7B0113CB8372DF14DBB1EEA0EBE7B34DA89A9BD23DA7E0D807211580F25 +D1EC4E11F8F2590B83B2F36DE33BC8F6CE6CE92858A9F57D39B705E8BA147C97 +936A610D963877AD4578024303BAF245A2C891EC975D6CD0E1AEF32608077D35 +33BA8E1B87850F474729E749E7354AA140971AB0D81D7ABF5F137A8AB62A0B23 +E9A0085776FE787FC5068A0027CCE8DA87FDE7FDAF48EB31647B4D0683BE6A5A +B27E90D5AFB4772C7CE74E3F9402109BA195B9EEE8FC5F677FAD5B12C8E66062 +787C2D077520B0E3EB4B479F8EFE984FA5D9BF8C7956509676C3EB61BA0183A1 +F4C9E1973A52E971DA227D41867CC8DFF2E592CDB3D8B6DE38182E45EAD093F0 +FF393B4B041465FFCFA27E7E8A89FF0D07CB020EC43649213509DC95526FAE71 +83C33B0790EC8FF9C17331CCC5FF7660FE3513C978EB652632EC6294F9995F91 +FBB383EAB08D644D18B4656C67F80726FA80BCF3232905B9179774D9B224739C +52C55414E6B0DF21A9DF2313B23E88717952284D833BA6E7B8B4C1EC2F646E50 +37472ADCDA045FCE14AA45FB7F11831B5D9318C840EC6012EC286840F9B6ADAC +32F1E759042A199F8B2DC6DB2FFD8C8A87EC4E959CF81C556F65D79A63F5544B +E8A5237002D67F063B4294E81E4303ECFB1905C302DBE1A6D1846D731BAF1A3D +E4CFCF7FD7D37FBDFF93DAC8FFB803F9C15967C8E1F5E82A8E6090C1EA4C4DC7 +5845071EEC79AF1F04C74C02D17421F5533B76B5E04F68D016FD407C9B99D412 +22A0899BFCCE2011A94B2703006A8A0233D235A77DAD7BD09E0720947D701C2A +F480CD2130079C096966E7416E9B4A21D46E5125C332A6126A568AC7E83A5361 +0AD36819654C9B99CC528AEB509408C08681550743C4607756CF904C9545EAAE +F26541D08C5D940052978FF7D4F859D9EE83B50C59955CCBEEEF40F1A509F7D6 +1257677EF9FF4C8F02B1C9BD350B593EC1F8E6B5A9E3543BC4E8C028AB93D257 +A30D1048AC984FECFC3CEE45737B69E5BB53D02793E2F23F444492D3EFB94373 +0FEBF69209B2878284447E11692D5C07DCCCBFDE95EF862054290773E146F4B6 +C36FDE598002D4F614DD3AF0C736A90D7C75DF634D0255294F9119FEA8CB4C95 +C0A6A4E84E342F2C7EF38EF85BF498A814050D9E3EFCDC724606657B63C88EED +90FAF415D44F685F0223BD90E2EA45106C606843644F1D0F85C88899293321AC +F7CBE932BE7E8F8C5F9AFC48E56FDC54C4EE50A6933480908F80A46C20378068 +89F7FE5F47670C913F68B08618D34940CE8A7CB3C5AC66BCD58EF859DD03E6A3 +7B6166F06492D140DCBDF062B0CC6646E17A3860AC5BE73DF313DE45BAE8E473 +87FFF3A55B3BCC161181322B2C8679E89FB600AFECC448668452E62102EC3146 +E817C3271F3CBBFFF8639DA3578663F6ACF799A6E19694FB55D7B3509912F4D2 +F13C134CEE34D783CBE4DAA970A7C4F53EBECD089BB677A11956C4EF68D306F8 +2D54A4A4EB94BC58FB4D86909D275A224267C173143464E1440D93E8D258846B +EB1DFBA3A19FACCEBA6961DADD4A92F6D625F59E1B5BB4ADB9BA580DB3C48082 +F2D4DB8A41ED074360A06BCEDE74DE35C4A1C7A6997AE031EE762A8F5CA0DE48 +7DA31C8A9A94A025F0F14962CC436E8E340C263A4ED735DE03B67352F1A011D0 +1C68BF934F5C0F82608A43942ED5AE7EA97ACCFE0B679D1D0DC429B4D748DD86 +612E66AD8B788ABFE975D724935B5D1FD247D1E6E75632D3B2AEC38955FD55E7 +A9189C611719F59B99C290E8F4EBBA14F43C7A63A07FE959BB69A9CA6EE9C1E0 +C1EC7A6E732762A18EDA8FD691979F55A663A8B96DA87BE77E9B2206F9B03643 +5FB1B62AD96849C176CA9BD21B26E286E0B0F433DFBBF062A93E3B2672A4FB7E +37D95678C1E50B9A2FC0B99C1B461EFB6DD6EF01DA415709E8FCFFA8ABBE19B6 +0651D38667D603BF649CE13E788D4769E4EFB96ECF0357FEAC3987CF8572C897 +160F2FB80AA873C560FEB5518C8B6EDA36445B7F33666E4B4B2E0E27A34B7EF8 +5D512DB3BDF51372F2F2621A89949AB6BE9410B83AD30C7E257C93354F50F50B +AFDB862CBE6F0F29FC42BE104A2775C17D4CE4F3390C697FF789FDC21012C19B +9A7E20166D129971FA3A458BD35B0BDBE7FC38C5CEF22F08896A08332F60A9F3 +54B30B85DCE4961C4DE5F8BF62C66B961985A7FB82CE944BD8F3B9F8ABE18E15 +6E3E5F6CFF911910B38B683FF0A3D5219DC9F0F7CED30137C010277607F37927 +B9EA5455F1F83774EC9248CE4FC34A84727DE5F3F1944EA2EDCF3CAA7152B2E6 +B6EE8E0F68FBC01EDBB6249F744B995ED9721D51D4A5C3249105EEA1E19C82C9 +AD34124FD11AD7414EB8306811BA973D6D6B9C1BBA5F25084B3BFC2666F1221E +DBEA099E7EA7D696652AC71780A4E0B27E6CF2BDFC0E671CE5B4167F47964826 +9C90F6670C054CBDD251F8215B9E80ED9087C0D80BEE7BFA199EF743B15E7A21 +E0F2F17CECEA7EED0248FF61AD55D1538D6B0F6ABB07CB1EBB65E2442085A484 +3AD0BC418752496AA4C3A34E29EB752E22083C97838BF763BB96EBB9AB75753D +ACC8DECA75406DBF3258CC312E631A3B7E7A7C1F81A9CFE85ACC62418ACC8659 +EAD467EBBE9DCBAA1C809C82AB26831BE75C8ACB2430294BF7A1A631E0C2184A +45B88ADAAE1AFD6D876E6278499BAE4DDF64CDAC3EF7FF50C49390777F644BE2 +2DED3E5DB38A751A159E8473B74338B6202B50B3FE1925A5F50FE15E4EF96FD3 +319C5EC8F9F1BA5F5CB87D7CA798CD55305C0062EF2B847C4B46D0CBD5D40AE3 +7F6D4DF014DAF530312477E9F43AAF6A420A5F8200732A56142614D70AF97EDA +00B68DEC22DF36D14330EB31A54C5EBDD811924197FBCFF67A4CFC78D3A304BB +9BC1CED8C62B16F7C24FB638A34002309820612EC7BB2BFB5C54B40A1106988F +92B035C4194C4E960B9980978D136BB255998F3E2EC740AAB4FF1DAD289C951E +0B563D29C16C3705697D8936ED7E6B708B48F4802529160D45393808A2EF4AC5 +8D9FF141DF1F1C18B83597144AC63E7F896BDEEDCA0869828488BDD772DDF8A8 +E2BFF2AA4CE3F5A1BD39545B30CF7CF54734B6A6C426E0FE62AD3F7952F7A234 +60B38DB70F85F0A0A5589A9EB6391519D4877AC9084FB970EA4138A53D4BDC22 +CD77DE64A5522560990441A90C17543E3C5EE3233D18C875997FEB19BD4651D1 +D7644611A69053AE931F060297F059EF347CA97CF7A221A9BF77F522F45D199E +0B4DE268CE97037C5E6B59D8B04281D98BECF462D3FD41863F0620758B847DD5 +2C8B7723BF487D074D85C0C34DCA78296C126A99ED2E3CB7AB82A9690F0D8CB0 +E7EE381CC289087473C621BA217F6451206843FDD0C4B886A37C3F4B4DEE02CE +51F834064A0E9554DFCE2781765B6ED33B5B55191AB891CCBDC1BA2F103A9E5D +5E57626065E1EED3E18EE508C1F12AC03328E8D8BC89A8B0F91DA935D9F5A6F1 +4F9BF981D3DA905FB38329809E1AC5F4A54C113390483EBE10610720873F24BF +B282FA836EE5CA10FDD6861FECD247DA33B323CF727E1A470877B50533F47482 +A3CF50587F60CF6020FD65CE4A1D1B236EF0BA3836B2C83FA658A8205F524DA9 +56BEAA723FC1435439FD65CF3CBCFC8670E5A06F1F71A9741A6BE8ED8E62BBE9 +2AE3347FE1C11A24BEFE4B89AAE958AF7821DAE220C8EC49939F02D288865F3F +ADBFDADCBB08DE79DD16BF6487639E93BBFF7B584A06870BCCD8A43330C91D02 +74B4B942B7AAB1F0B4EB463E50EF4289FE45A971DC7B79100950B86390C30DD9 +72922238D412596CA3076DB1C76CBD2E4208FC8A8CB17195A371F6710521A828 +833935BC4FC5C51E65CC3F505653FFADC205A5260694227390B439DA2E05C624 +21B202321BC0890AEE7C8080DE041F0525E36A22B48FCAC7944508CA4962C476 +82ADB8FD87D3EAB5281CEBC2557469F6487923E8CBF90866F25E94EF6D95C267 +EC14B439A386AACC858228AFE5B529B6CCF6B3F29C7F92BBFE61286741842169 +9F3B4E03A81393FB7CEA429FC635C130B6B5302C4B13E6A84C983C00DF109380 +B13493D7A6CAA4DFE966907863FA5BAA878D5BAF99C232E15861E700312DD4DF +EF867B670FDC942ACFA5FDC3C7D3009F7C6A388EF78F9C80FE9332541B04F95B +4C160AEA8D5CB122E9975F91BEC5637EE369B6C92000D4CD12733E0B2E5372F5 +34742401E373DC24E555AC43F53ECA65F28BB50244FFED1B03ECB0DAC38ADE3B +FBAEF49FDE44CE302AE834CEA23C3BA71B8D4BAC021F33FC8030331D3DBE08DA +756ABA8FC53E5CB97844E3C3AD2CC26D0A08424ACDBA48E51DFC1B681B821DE1 +654C88DEE341953F264738666711E435B637292010E1D88EC6792095D7C25B03 +85EE73F97535EC27E45B3C5627AE94F3F36348FA21D0256C91DDE2CFECD45845 +AC00713C4302D8CB4EC6670C53A0CCF93DC5AB05800BA7AD662F7AD4CF570C32 +72D005BD6D95AF46D3E09A24AFE29669525DF51902444B5BB1CA522A09472955 +E9A5082E62931CF8C5DDF87D5EDB87EECE36A04B1C36305CE21B77A1299D6C3E +AB6304A5917962A93447DC538FD9C8B60DDB8053457DE1B9335C5F621F623A1B +C696DD3F73539052E62426C32C8261633BF77CDC1B774785D1BF65254EF70FD0 +4028434CFE87CC24DFBBB40E90A63EDA29D69709585864DB61B08F56A6750D4B +635B3F08F151BA098A946DCCB0E83A9366A00200B93B69668D7F5C3AD3F60DB7 +D501D4C325AF570044CA87B1582EDF03B85A348C6EEA9C7D84735DFB59CED340 +339F5DA29481E3E41D1E929B63C61B91BCB5997ECF2E34F3E3DECFE916F4C40B +0F6D99E938DA02732366E5B511BE2B05A159D105740AD178C4C2A28657CCC700 +B7549434AACE1248C1E79E1180096113E05BC6C8B6A80CE68F9C2AE30D82AABB +FD17BCABB44C03224DFAD1EAE823CC08F09B6B85B37F30D434BE980BED7D7781 +A7DF3B937781FF9DFC7C8D44C3CC9B3A999CD894C8AA273827674C6E7B196E8B +48101EFC0EF9FDE13643D2708184603555C8C1F31FA19052163AB4447F643D11 +2418E11EB6D7AFC54043B47FE7281ECA7CC22D7A97DD0ACEC5D49C8FCCC1713B +26AEBB9F9BC8EE2C033D8DAD02C41C026B76C577E8E3BB23A6C41EB83A74B425 +D83D61959F989E4550E14D65A4B770215EE71F5BC2213204087A182B2C00F701 +B3E6275B0852F95762EA373D6322139A6CBD9301B187F5B05B1E4AAB158078CC +5BB55322408962EB269B1586FD830F48D609D967E4BEA07F4D19A51A776D485F +EE3611CCD946F1ABB10077166BE54653EDAB1079E4DA2024FE45C93A0B44970C +3959102928CD9071D4828107526BC9AB1D5DA21FF780210797F4BD65DE30A250 +E137F78CC53A73728C7AAC8E9DEA9C60DF495B3D32DD70F7614B76E4B2D7A7A8 +33FE418A814CD7068F345BFF0B23D3B91022AF7949A1F48BBCFA06E4C39363CB +C34C215F7E70F1422F8BF9B2C725A660F7ED35BABAF5F5FCD4B7CDC515401A00 +BDAA71AFC754A9CC4E3A01D4C0F78D310CA0BF139EC969B3E9DCE9118BAACF58 +88E9E9355C77693DA34007CC9B7B8C17A83E640EFDD401EC1848EB6EF4040476 +DA53E4C0F4506A0BC9965A9E1F7E78993AF4CBD1E3EC1E5E76705F2FB140A459 +F59A36F57A282952176F211B518F2EA193B2BF33745CFCF9CD46FC02B238DE65 +A3E019D7C39D98973BDA1AB7C26008A014F941B6E38956B4F49EB1E2367860FC +B1A0205E6849C317A550795261153FDB5FD69FBBDD80043C2669D20A1701D979 +3E0F28BAB68BBD5834780187895E87D6E0481F94023A56333C0413A2EFA3B53B +AD6CF6D129A70036A3867229835393BA8CD26A3DF16B79417CE9862F66E99F20 +03E28A9C9FFFD5CE4BB40804A3761330CD2262D3D3E6791295388943ED0F5535 +24DC271402AD8C98A854BC257E4224B216AF8B664F267F2866F031086D069D2F +264EE973FA3028A72A21CCB44DF3EABB6A66177A1FA779512FB1B7DEB4B7D991 +612143CFB088B4BCE701923E42A2DB42DCAD3CFEA51F895308C019F9BD1011E9 +ACEDF223C88E75BA67BF7EFE68E550DB1E22806A7CE7150C8472CAF5717974DD +1908AECF94C6AFDF35BC9385A3E081ACD3DDDC46F471F99531C8E588EA4B1DF8 +28FC832DC72298A47BB152EC8B912E7EE9590EDE56E54019DFCE4E2EB11F01B8 +B538F5AF3712EE72D04FBE63328306616A8125756A92279B6CB6B3A641B2A4A3 +9B2C3B6869EACF713B146C2075A29A6A1A422DC1224056C86017EC61BD7BA132 +C9D951BC5B8B6B305CC37D72ADFF44CC039DED8BAB7EF5F19D1EB0CFC15077E1 +7CAAB6BFBE385847069A83536C1D18881523A59A45BCA12C3C50B135A9E5F8D7 +AFB81C80243655D9706A4E93A617C75A644A4C23A6A49DDF3277681B6BDF +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +cleartomark +%%EndResource +%%EOF diff --git a/tests/checkoutlinesufo_test.py b/tests/checkoutlinesufo_test.py index 8aeebc037..65866aa10 100644 --- a/tests/checkoutlinesufo_test.py +++ b/tests/checkoutlinesufo_test.py @@ -1,15 +1,19 @@ import pytest -from shutil import copytree +from shutil import copy2, copytree from booleanOperations.booleanGlyph import BooleanGlyph from defcon import Glyph from afdko.checkoutlinesufo import remove_tiny_sub_paths -from afdko.fdkutils import get_temp_dir_path +from afdko.fdkutils import ( + get_temp_file_path, + get_temp_dir_path, +) from test_utils import ( get_input_path, get_expected_path, + generate_ttx_dump, ) from runner import main as runner from differ import main as differ @@ -17,9 +21,6 @@ TOOL = 'checkoutlinesufo' CMD = ['-t', TOOL] -UFO2_NAME = 'ufo2.ufo' -UFO3_NAME = 'ufo3.ufo' - # ----- # Tests @@ -53,15 +54,36 @@ def test_remove_tiny_sub_paths_small_contour(): 'Start point: ((1, 1)).'] -@pytest.mark.parametrize('ufo_filename', [UFO2_NAME, UFO3_NAME]) +@pytest.mark.parametrize('ufo_filename', ['ufo2.ufo', 'ufo3.ufo']) @pytest.mark.parametrize('args, expct_label', [ (['e', 'w', 'q'], 'dflt-layer.ufo'), (['e', 'q'], 'proc-layer.ufo'), ]) -def test_remove_overlap(args, ufo_filename, expct_label): +def test_remove_overlap_ufo(args, ufo_filename, expct_label): actual_path = get_temp_dir_path(ufo_filename) copytree(get_input_path(ufo_filename), actual_path) runner(CMD + ['-f', actual_path, '-o'] + args) expct_filename = f'{ufo_filename[:-4]}-{expct_label}' expected_path = get_expected_path(expct_filename) assert differ([expected_path, actual_path]) + + +@pytest.mark.parametrize('filename, diffmode', [ + ('font.pfa', []), + ('font.cff', ['-m', 'bin']), +]) +def test_remove_overlap_type1_cff(filename, diffmode): + actual_path = get_temp_file_path() + copy2(get_input_path(filename), actual_path) + runner(CMD + ['-f', actual_path, '-o', 'e', 'q']) + expected_path = get_expected_path(filename) + assert differ([expected_path, actual_path] + diffmode) + + +def test_remove_overlap_otf(): + actual_path = get_temp_file_path() + copy2(get_input_path('font.otf'), actual_path) + runner(CMD + ['-f', actual_path, '-o', 'e', 'q']) + actual_ttx = generate_ttx_dump(actual_path, ['CFF ']) + expected_ttx = get_expected_path('font.ttx') + assert differ([expected_ttx, actual_ttx, '-s', '