ufoLib-2.1.1/0000755000000000000000000000000013174130416011431 5ustar rootrootufoLib-2.1.1/setup.py0000755000000000000000000001257613174123546013167 0ustar rootroot#! /usr/bin/env python import sys from setuptools import setup, find_packages, Command from distutils import log class bump_version(Command): description = "increment the package version and commit the changes" user_options = [ ("major", None, "bump the first digit, for incompatible API changes"), ("minor", None, "bump the second digit, for new backward-compatible features"), ("patch", None, "bump the third digit, for bug fixes (default)"), ] def initialize_options(self): self.minor = False self.major = False self.patch = False def finalize_options(self): part = None for attr in ("major", "minor", "patch"): if getattr(self, attr, False): if part is None: part = attr else: from distutils.errors import DistutilsOptionError raise DistutilsOptionError( "version part options are mutually exclusive") self.part = part or "patch" def bumpversion(self, part, **kwargs): """ Run bumpversion.main() with the specified arguments. """ import bumpversion args = ['--verbose'] if self.verbose > 1 else [] for k, v in kwargs.items(): k = "--{}".format(k.replace("_", "-")) is_bool = isinstance(v, bool) and v is True args.extend([k] if is_bool else [k, str(v)]) args.append(part) log.debug( "$ bumpversion %s" % " ".join(a.replace(" ", "\\ ") for a in args)) bumpversion.main(args) def run(self): log.info("bumping '%s' version" % self.part) self.bumpversion(self.part) class release(bump_version): """Drop the developmental release '.devN' suffix from the package version, open the default text $EDITOR to write release notes, commit the changes and generate a git tag. Release notes can also be set with the -m/--message option, or by reading from standard input. """ description = "tag a new release" user_options = [ ("message=", 'm', "message containing the release notes"), ] def initialize_options(self): self.message = None def finalize_options(self): import re current_version = self.distribution.metadata.get_version() if not re.search(r"\.dev[0-9]+", current_version): from distutils.errors import DistutilsSetupError raise DistutilsSetupError( "current version (%s) has no '.devN' suffix.\n " "Run 'setup.py bump_version' with any of " "--major, --minor, --patch options" % current_version) message = self.message if message is None: if sys.stdin.isatty(): # stdin is interactive, use editor to write release notes message = self.edit_release_notes() else: # read release notes from stdin pipe message = sys.stdin.read() if not message.strip(): from distutils.errors import DistutilsSetupError raise DistutilsSetupError("release notes message is empty") self.message = "v{new_version}\n\n%s" % message @staticmethod def edit_release_notes(): """Use the default text $EDITOR to write release notes. If $EDITOR is not set, use 'nano'.""" from tempfile import mkstemp import os import shlex import subprocess text_editor = shlex.split(os.environ.get('EDITOR', 'nano')) fd, tmp = mkstemp(prefix='bumpversion-') try: os.close(fd) with open(tmp, 'w') as f: f.write("\n\n# Write release notes.\n" "# Lines starting with '#' will be ignored.") subprocess.check_call(text_editor + [tmp]) with open(tmp, 'r') as f: changes = "".join( l for l in f.readlines() if not l.startswith('#')) finally: os.remove(tmp) return changes def run(self): log.info("stripping developmental release suffix") # drop '.dev0' suffix, commit with given message and create git tag self.bumpversion("release", tag=True, message="Release {new_version}", tag_message=self.message) needs_pytest = {'pytest', 'test'}.intersection(sys.argv) pytest_runner = ['pytest_runner'] if needs_pytest else [] needs_wheel = {'bdist_wheel'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] needs_bump2version = {'release', 'bump_version'}.intersection(sys.argv) bump2version = ['bump2version'] if needs_bump2version else [] long_description = """\ ufoLib reads and writes Unified Font Object (UFO) files. UFO is a file format that stores fonts source files. http://unifiedfontobject.org """ setup_params = dict( name="ufoLib", version="2.1.1", description="A low-level UFO reader and writer.", author="Just van Rossum, Tal Leming, Erik van Blokland, others", author_email="info@robofab.com", maintainer="Just van Rossum, Tal Leming, Erik van Blokland", maintainer_email="info@robofab.com", url="https://github.com/unified-font-object/ufoLib", license="OpenSource, BSD-style", platforms=["Any"], long_description=long_description, package_dir={'': 'Lib'}, packages=find_packages('Lib'), include_package_data=True, setup_requires=pytest_runner + wheel + bump2version, tests_require=[ 'pytest>=3.0.2', ], install_requires=[ "fonttools>=3.1.2", ], cmdclass={ "release": release, "bump_version": bump_version, }, classifiers=[ "Development Status :: 4 - Beta", "Environment :: Console", "Environment :: Other Environment", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Multimedia :: Graphics", "Topic :: Multimedia :: Graphics :: Graphics Conversion", ], ) if __name__ == "__main__": setup(**setup_params) ufoLib-2.1.1/PKG-INFO0000644000000000000000000000176413174123604012537 0ustar rootrootMetadata-Version: 1.1 Name: ufoLib Version: 2.1.1 Summary: A low-level UFO reader and writer. Home-page: https://github.com/unified-font-object/ufoLib Author: Just van Rossum, Tal Leming, Erik van Blokland Author-email: info@robofab.com License: OpenSource, BSD-style Description-Content-Type: UNKNOWN Description: ufoLib reads and writes Unified Font Object (UFO) files. UFO is a file format that stores fonts source files. http://unifiedfontobject.org Platform: Any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Environment :: Other Environment Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Multimedia :: Graphics Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion ufoLib-2.1.1/MANIFEST.in0000644000000000000000000000033413174123546013175 0ustar rootrootinclude README.md notes.txt LICENSE.txt include Documentation/Makefile recursive-include Documentation *.py *.rst recursive-include Lib/ufoLib/test/testdata *.plist *.glif *.fea *.txt include requirements.txt tox.ini ufoLib-2.1.1/Documentation/0000755000000000000000000000000013174130416014242 5ustar rootrootufoLib-2.1.1/Documentation/conf.py0000644000000000000000000002212213174123546015546 0ustar rootroot# -*- coding: utf-8 -*- # # ufoLib documentation build configuration file, created by # sphinx-quickstart on Wed Oct 19 19:32:35 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'ufoLib' copyright = u'2011, Tal Leming, Erik van Blokland, Just van Rossum' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.3' # The full version, including alpha/beta/rc tags. release = '1.3.0a' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'ufoLib-doc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'ufoLib.tex', u'ufoLib Documentation', u'Tal Leming, Erik van Blokland, Just van Rossum', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'ufoLib', u'ufoLib Documentation', [u'Tal Leming, Erik van Blokland, Just van Rossum'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'ufoLib', u'ufoLib Documentation', u'Tal Leming, Erik van Blokland, Just van Rossum', 'ufoLib', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'ufoLib' epub_author = u'Tal Leming, Erik van Blokland, Just van Rossum' epub_publisher = u'Tal Leming, Erik van Blokland, Just van Rossum' epub_copyright = u'2011, Tal Leming, Erik van Blokland, Just van Rossum' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True ufoLib-2.1.1/Documentation/Makefile0000644000000000000000000001267413174123546015722 0ustar rootroot# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ufoLib.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ufoLib.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/ufoLib" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ufoLib" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ufoLib-2.1.1/Documentation/index.rst0000644000000000000000000000077413174123546016121 0ustar rootroot.. ufoLib documentation master file, created by sphinx-quickstart on Wed Oct 19 19:32:35 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. UFOLib ====== Tools for accessing, validating and making UFO and glif. .. toctree:: :maxdepth: 1 ufoLib/ufoLib ufoLib/glifLib ufoLib/pointPen ufoLib/filenames ufoLib/converters Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ufoLib-2.1.1/Documentation/ufoLib/0000755000000000000000000000000013174130416015462 5ustar rootrootufoLib-2.1.1/Documentation/ufoLib/ufoLib.rst0000644000000000000000000000015013174123546017436 0ustar rootroot.. highlight:: python ====== ufoLib ====== .. automodule:: ufoLib :inherited-members: :members: ufoLib-2.1.1/Documentation/ufoLib/pointPen.rst0000644000000000000000000000016713174123546020022 0ustar rootroot.. highlight:: python ======== pointPen ======== .. automodule:: ufoLib.pointPen :inherited-members: :members: ufoLib-2.1.1/Documentation/ufoLib/filenames.rst0000644000000000000000000000017313174123546020166 0ustar rootroot.. highlight:: python ========= filenames ========= .. automodule:: ufoLib.filenames :inherited-members: :members: ufoLib-2.1.1/Documentation/ufoLib/converters.rst0000644000000000000000000000017713174123546020421 0ustar rootroot.. highlight:: python ========== converters ========== .. automodule:: ufoLib.converters :inherited-members: :members: ufoLib-2.1.1/Documentation/ufoLib/glifLib.rst0000644000000000000000000000016313174123546017572 0ustar rootroot.. highlight:: python ======= glifLib ======= .. automodule:: ufoLib.glifLib :inherited-members: :members: ufoLib-2.1.1/tox.ini0000644000000000000000000000073613174123546012760 0ustar rootroot[tox] envlist = py27, py35 [testenv] basepython = # we use TOXPYTHON env variable to specify the location of Appveyor Python py27: {env:TOXPYTHON:python2.7} py34: {env:TOXPYTHON:python3.4} py35: {env:TOXPYTHON:python3.5} deps = pytest -rrequirements.txt commands = # run the test suite against the package installed inside tox env. # any extra positional arguments after `tox -- ...` are passed on to pytest pytest {posargs:--pyargs ufoLib} ufoLib-2.1.1/setup.cfg0000644000000000000000000000147613174123604013263 0ustar rootroot[bumpversion] current_version = 2.1.1 commit = True tag = False tag_name = v{new_version} parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{dev} {major}.{minor}.{patch} [bumpversion:part:release] optional_value = final values = dev final [bumpversion:part:dev] [bumpversion:file:Lib/ufoLib/__init__.py] search = __version__ = "{current_version}" replace = __version__ = "{new_version}" [bumpversion:file:setup.py] search = version="{current_version}" replace = version="{new_version}" [wheel] universal = 1 [sdist] formats = zip [aliases] test = pytest [metadata] license_file = LICENSE.txt [tool:pytest] minversion = 3.0.2 testpaths = Lib/ufoLib addopts = -v -r a --doctest-modules [egg_info] tag_build = tag_date = 0 ufoLib-2.1.1/LICENSE.txt0000644000000000000000000000327213174123546013266 0ustar rootrootufoLib License Agreement Copyright (c) 2005-2016, The RoboFab Developers: Erik van Blokland Tal Leming Just van Rossum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the The RoboFab Developers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Up to date info on ufoLib: https://github.com/unified-font-object/ufoLib This is the BSD license: http://www.opensource.org/licenses/BSD-3-Clause ufoLib-2.1.1/notes.txt0000644000000000000000000000730413174123546013334 0ustar rootrootufoLib ------ - UFOReader - need to add up-conversion for kerning and kerning groups - this needs to be done in three different methods: - readInfo - default suffixes can be set in __init__ - readGroups - maybe skim the kerning for referenced groups and rename on the fly. this creates a race condition of sorts in that he kerning could be modified after the groups have been read. there isn't any way around this. maybe the doc should say that for < version 3 kerning and groups should always be read at the same time. - readKerning - need to temporarily read the groups for conversion. see above. - readInfo - double check to make sure that no conversion is needed - add support for new attributes - add a method for getting the images path - add a method for getting the directory path - add a getLayerNames method - how will this handle indicating that a layer is the default? - getGlyphSet - add a layerName=None argument - UFOWriter - change default version - writeInfo - need down-convert function to filter out >= 3 attributes - add support for new attributes - writeKerning and writeGroups - down conversion of modified group names is going to be a problem when writing into an existing < 3 file. - getGlyphSet - add layerName=None argument - add the user name to file name conversion abstraction to glifLib - add an isDefaultLayer=True (or something) argument for when the default layer has a name - update layercontents.plist each time a glyph set is created or make the caller do it? - add a method for getting the images path - add a method for getting the directory path - change the directory mod time for each write - build version 3 attribute definitions - maybe do a dynamic copy of the version 2 data - update validation functions - are the in place version converters necessary? glifLib ------- X roll in server optimizations - make GlyphSet a new style class - GlyphSet - add a readLayerInfo(info) method - add a writeLayerInfo(info) method - writeGlyph - need to support guides - glyph object must have a list like object located at the guidelines attribute. the objects returned from that object must have a dict like behavior. API sketch: for guideline in glyph.guidelines: x = guideline["x"] y = guideline["y"] ... - need to support images - glyph object must have a dict like object located at the image attribute. API sketch: fileName = glyph.image["fileName"] xScale = glyph.image["xScale"] - readGlyph - need to support guides - glyph object must accept a list of dicts at the guidelines attribute. API sketch: glyph.guidelines = [ dict(x=100, y=100, angle=12.3, name="name", color="0,0,0,1", identified="identifier1"), dict(x=100, y=100, angle=12.3, name="name", color="0,0,0,1", identified="identifier2") ] - need to support images - glyph object must accept a dict at the image attribute. API sketch: glyph.image = dict(fileName="foo.png", xScale=1, yScale=1, ..., color="1,0,0,0.5") - GLIFPointPen - update to new API - writeGlyphToString needs to be adapted - add a formatVersion=3 argument - add a buildOutline_Format2 function - add the user name to file name conversion from the UFO spec - don't write "180.0" point pen protocol ------------------ - add an identifier=None to the beginPath method - add an identifier=None to the addComponent method - add an identifier=None to the addPoint method? - add **kwargs to beginPath and addComponent? ufoLib-2.1.1/README.md0000644000000000000000000000143113174123546012715 0ustar rootroot[![Build Status](https://api.travis-ci.org/unified-font-object/ufoLib.svg)](https://travis-ci.org/unified-font-object/ufoLib) [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/unified-font-object/ufoLib?svg=true)](https://ci.appveyor.com/project/adrientetar/ufolib) ![Python Versions](https://img.shields.io/badge/python-2.7%2C%203.4%2C%203.5-blue.svg) [![PyPI](https://img.shields.io/pypi/v/ufoLib.svg)](https://pypi.org/project/ufoLib/) ufoLib ------ A low-level [UFO] reader and writer. [UFO] is a human-readable, XML-based file format that stores font source files. [UFO]: http://unifiedfontobject.org/ UFO 4 ----- This is a branch in which we are **experimenting** with UFO 4 ideas/support. - Single file structure (99.9% sure this will be zip) - Other stuff.ufoLib-2.1.1/Lib/0000755000000000000000000000000013174130416012137 5ustar rootrootufoLib-2.1.1/Lib/ufoLib.egg-info/0000755000000000000000000000000013174130416015051 5ustar rootrootufoLib-2.1.1/Lib/ufoLib.egg-info/dependency_links.txt0000644000000000000000000000000113174123604021120 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib.egg-info/requires.txt0000644000000000000000000000002113174123604017443 0ustar rootrootfonttools>=3.1.2 ufoLib-2.1.1/Lib/ufoLib.egg-info/SOURCES.txt0000644000000000000000000000611513174123604016741 0ustar rootrootLICENSE.txt MANIFEST.in README.md notes.txt requirements.txt setup.cfg setup.py tox.ini Documentation/Makefile Documentation/conf.py Documentation/index.rst Documentation/ufoLib/converters.rst Documentation/ufoLib/filenames.rst Documentation/ufoLib/glifLib.rst Documentation/ufoLib/pointPen.rst Documentation/ufoLib/ufoLib.rst Lib/ufoLib/__init__.py Lib/ufoLib/converters.py Lib/ufoLib/filenames.py Lib/ufoLib/glifLib.py Lib/ufoLib/kerning.py Lib/ufoLib/plistFromETree.py Lib/ufoLib/plistlib.py Lib/ufoLib/pointPen.py Lib/ufoLib/validators.py Lib/ufoLib.egg-info/PKG-INFO Lib/ufoLib.egg-info/SOURCES.txt Lib/ufoLib.egg-info/dependency_links.txt Lib/ufoLib.egg-info/requires.txt Lib/ufoLib.egg-info/top_level.txt Lib/ufoLib/test/__init__.py Lib/ufoLib/test/testSupport.py Lib/ufoLib/test/test_GLIF1.py Lib/ufoLib/test/test_GLIF2.py Lib/ufoLib/test/test_UFO1.py Lib/ufoLib/test/test_UFO2.py Lib/ufoLib/test/test_UFO3.py Lib/ufoLib/test/test_UFOConversion.py Lib/ufoLib/test/test_filenames.py Lib/ufoLib/test/test_glifLib.py Lib/ufoLib/test/testdata/DemoFont.ufo/fontinfo.plist Lib/ufoLib/test/testdata/DemoFont.ufo/lib.plist Lib/ufoLib/test/testdata/DemoFont.ufo/metainfo.plist Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/A_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/B_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/F_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/F__A__B_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/G_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/O_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/R_.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/a.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/contents.plist Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/testglyph1.glif Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/testglyph1.reversed.glif Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/fontinfo.plist Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/groups.plist Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/kerning.plist Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/lib.plist Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/metainfo.plist Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/A_.glif Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/B_.glif Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/contents.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/features.fea Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/fontinfo.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/groups.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/kerning.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/lib.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/metainfo.plist Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/A_.glif Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/B_.glif Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/contents.plist Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/metainfo.plist Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.file.txt Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.txt Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txtufoLib-2.1.1/Lib/ufoLib.egg-info/PKG-INFO0000644000000000000000000000176413174123604016157 0ustar rootrootMetadata-Version: 1.1 Name: ufoLib Version: 2.1.1 Summary: A low-level UFO reader and writer. Home-page: https://github.com/unified-font-object/ufoLib Author: Just van Rossum, Tal Leming, Erik van Blokland Author-email: info@robofab.com License: OpenSource, BSD-style Description-Content-Type: UNKNOWN Description: ufoLib reads and writes Unified Font Object (UFO) files. UFO is a file format that stores fonts source files. http://unifiedfontobject.org Platform: Any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Environment :: Other Environment Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Multimedia :: Graphics Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion ufoLib-2.1.1/Lib/ufoLib.egg-info/top_level.txt0000644000000000000000000000000713174123604017601 0ustar rootrootufoLib ufoLib-2.1.1/Lib/ufoLib/0000755000000000000000000000000013174130416013357 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/validators.py0000644000000000000000000006274113174123546016121 0ustar rootroot"""Various low level data validators.""" import os import calendar from io import open try: from collections.abc import Mapping # python >= 3.3 except ImportError: from collections import Mapping # ------- # Python 2 or 3 # ------- try: basestring except NameError: basestring = str # ------- # Generic # ------- def isDictEnough(value): """ Some objects will likely come in that aren't dicts but are dict-ish enough. """ if isinstance(value, Mapping): return True attrs = ("keys", "values", "items") for attr in attrs: if not hasattr(value, attr): return False return True def genericTypeValidator(value, typ): """ Generic. (Added at version 2.) """ return isinstance(value, typ) def genericIntListValidator(values, validValues): """ Generic. (Added at version 2.) """ if not isinstance(values, (list, tuple)): return False valuesSet = set(values) validValuesSet = set(validValues) if len(valuesSet - validValuesSet) > 0: return False for value in values: if not isinstance(value, int): return False return True def genericNonNegativeIntValidator(value): """ Generic. (Added at version 3.) """ if not isinstance(value, int): return False if value < 0: return False return True def genericNonNegativeNumberValidator(value): """ Generic. (Added at version 3.) """ if not isinstance(value, (int, float)): return False if value < 0: return False return True def genericDictValidator(value, prototype): """ Generic. (Added at version 3.) """ # not a dict if not isinstance(value, Mapping): return False # missing required keys for key, (typ, required) in list(prototype.items()): if not required: continue if key not in value: return False # unknown keys for key in list(value.keys()): if key not in prototype: return False # incorrect types for key, v in list(value.items()): prototypeType, required = prototype[key] if v is None and not required: continue if not isinstance(v, prototypeType): return False return True # -------------- # fontinfo.plist # -------------- # Data Validators def fontInfoStyleMapStyleNameValidator(value): """ Version 2+. """ options = ["regular", "italic", "bold", "bold italic"] return value in options def fontInfoOpenTypeGaspRangeRecordsValidator(value): """ Version 3+. """ if not isinstance(value, list): return False if len(value) == 0: return True validBehaviors = [0, 1, 2, 3] dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True)) ppemOrder = [] for rangeRecord in value: if not genericDictValidator(rangeRecord, dictPrototype): return False ppem = rangeRecord["rangeMaxPPEM"] behavior = rangeRecord["rangeGaspBehavior"] ppemValidity = genericNonNegativeIntValidator(ppem) if not ppemValidity: return False behaviorValidity = genericIntListValidator(behavior, validBehaviors) if not behaviorValidity: return False ppemOrder.append(ppem) if ppemOrder != sorted(ppemOrder): return False return True def fontInfoOpenTypeHeadCreatedValidator(value): """ Version 2+. """ # format: 0000/00/00 00:00:00 if not isinstance(value, basestring): return False # basic formatting if not len(value) == 19: return False if value.count(" ") != 1: return False date, time = value.split(" ") if date.count("/") != 2: return False if time.count(":") != 2: return False # date year, month, day = date.split("/") if len(year) != 4: return False if len(month) != 2: return False if len(day) != 2: return False try: year = int(year) month = int(month) day = int(day) except ValueError: return False if month < 1 or month > 12: return False monthMaxDay = calendar.monthrange(year, month)[1] if day < 1 or day > monthMaxDay: return False # time hour, minute, second = time.split(":") if len(hour) != 2: return False if len(minute) != 2: return False if len(second) != 2: return False try: hour = int(hour) minute = int(minute) second = int(second) except ValueError: return False if hour < 0 or hour > 23: return False if minute < 0 or minute > 59: return False if second < 0 or second > 59: return False # fallback return True def fontInfoOpenTypeNameRecordsValidator(value): """ Version 3+. """ if not isinstance(value, list): return False dictPrototype = dict(nameID=(int, True), platformID=(int, True), encodingID=(int, True), languageID=(int, True), string=(basestring, True)) for nameRecord in value: if not genericDictValidator(nameRecord, dictPrototype): return False return True def fontInfoOpenTypeOS2WeightClassValidator(value): """ Version 2+. """ if not isinstance(value, int): return False if value < 0: return False return True def fontInfoOpenTypeOS2WidthClassValidator(value): """ Version 2+. """ if not isinstance(value, int): return False if value < 1: return False if value > 9: return False return True def fontInfoVersion2OpenTypeOS2PanoseValidator(values): """ Version 2. """ if not isinstance(values, (list, tuple)): return False if len(values) != 10: return False for value in values: if not isinstance(value, int): return False # XXX further validation? return True def fontInfoVersion3OpenTypeOS2PanoseValidator(values): """ Version 3+. """ if not isinstance(values, (list, tuple)): return False if len(values) != 10: return False for value in values: if not isinstance(value, int): return False if value < 0: return False # XXX further validation? return True def fontInfoOpenTypeOS2FamilyClassValidator(values): """ Version 2+. """ if not isinstance(values, (list, tuple)): return False if len(values) != 2: return False for value in values: if not isinstance(value, int): return False classID, subclassID = values if classID < 0 or classID > 14: return False if subclassID < 0 or subclassID > 15: return False return True def fontInfoPostscriptBluesValidator(values): """ Version 2+. """ if not isinstance(values, (list, tuple)): return False if len(values) > 14: return False if len(values) % 2: return False for value in values: if not isinstance(value, (int, float)): return False return True def fontInfoPostscriptOtherBluesValidator(values): """ Version 2+. """ if not isinstance(values, (list, tuple)): return False if len(values) > 10: return False if len(values) % 2: return False for value in values: if not isinstance(value, (int, float)): return False return True def fontInfoPostscriptStemsValidator(values): """ Version 2+. """ if not isinstance(values, (list, tuple)): return False if len(values) > 12: return False for value in values: if not isinstance(value, (int, float)): return False return True def fontInfoPostscriptWindowsCharacterSetValidator(value): """ Version 2+. """ validValues = list(range(1, 21)) if value not in validValues: return False return True def fontInfoWOFFMetadataUniqueIDValidator(value): """ Version 3+. """ dictPrototype = dict(id=(basestring, True)) if not genericDictValidator(value, dictPrototype): return False return True def fontInfoWOFFMetadataVendorValidator(value): """ Version 3+. """ dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)} if not genericDictValidator(value, dictPrototype): return False if "dir" in value and value.get("dir") not in ("ltr", "rtl"): return False return True def fontInfoWOFFMetadataCreditsValidator(value): """ Version 3+. """ dictPrototype = dict(credits=(list, True)) if not genericDictValidator(value, dictPrototype): return False if not len(value["credits"]): return False dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "role" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)} for credit in value["credits"]: if not genericDictValidator(credit, dictPrototype): return False if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"): return False return True def fontInfoWOFFMetadataDescriptionValidator(value): """ Version 3+. """ dictPrototype = dict(url=(basestring, False), text=(list, True)) if not genericDictValidator(value, dictPrototype): return False for text in value["text"]: if not fontInfoWOFFMetadataTextValue(text): return False return True def fontInfoWOFFMetadataLicenseValidator(value): """ Version 3+. """ dictPrototype = dict(url=(basestring, False), text=(list, False), id=(basestring, False)) if not genericDictValidator(value, dictPrototype): return False if "text" in value: for text in value["text"]: if not fontInfoWOFFMetadataTextValue(text): return False return True def fontInfoWOFFMetadataTrademarkValidator(value): """ Version 3+. """ dictPrototype = dict(text=(list, True)) if not genericDictValidator(value, dictPrototype): return False for text in value["text"]: if not fontInfoWOFFMetadataTextValue(text): return False return True def fontInfoWOFFMetadataCopyrightValidator(value): """ Version 3+. """ dictPrototype = dict(text=(list, True)) if not genericDictValidator(value, dictPrototype): return False for text in value["text"]: if not fontInfoWOFFMetadataTextValue(text): return False return True def fontInfoWOFFMetadataLicenseeValidator(value): """ Version 3+. """ dictPrototype = {"name" : (basestring, True), "dir" : (basestring, False), "class" : (basestring, False)} if not genericDictValidator(value, dictPrototype): return False if "dir" in value and value.get("dir") not in ("ltr", "rtl"): return False return True def fontInfoWOFFMetadataTextValue(value): """ Version 3+. """ dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)} if not genericDictValidator(value, dictPrototype): return False if "dir" in value and value.get("dir") not in ("ltr", "rtl"): return False return True def fontInfoWOFFMetadataExtensionsValidator(value): """ Version 3+. """ if not isinstance(value, list): return False if not value: return False for extension in value: if not fontInfoWOFFMetadataExtensionValidator(extension): return False return True def fontInfoWOFFMetadataExtensionValidator(value): """ Version 3+. """ dictPrototype = dict(names=(list, False), items=(list, True), id=(basestring, False)) if not genericDictValidator(value, dictPrototype): return False if "names" in value: for name in value["names"]: if not fontInfoWOFFMetadataExtensionNameValidator(name): return False for item in value["items"]: if not fontInfoWOFFMetadataExtensionItemValidator(item): return False return True def fontInfoWOFFMetadataExtensionItemValidator(value): """ Version 3+. """ dictPrototype = dict(id=(basestring, False), names=(list, True), values=(list, True)) if not genericDictValidator(value, dictPrototype): return False for name in value["names"]: if not fontInfoWOFFMetadataExtensionNameValidator(name): return False for val in value["values"]: if not fontInfoWOFFMetadataExtensionValueValidator(val): return False return True def fontInfoWOFFMetadataExtensionNameValidator(value): """ Version 3+. """ dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)} if not genericDictValidator(value, dictPrototype): return False if "dir" in value and value.get("dir") not in ("ltr", "rtl"): return False return True def fontInfoWOFFMetadataExtensionValueValidator(value): """ Version 3+. """ dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)} if not genericDictValidator(value, dictPrototype): return False if "dir" in value and value.get("dir") not in ("ltr", "rtl"): return False return True # ---------- # Guidelines # ---------- def guidelinesValidator(value, identifiers=None): """ Version 3+. """ if not isinstance(value, list): return False if identifiers is None: identifiers = set() for guide in value: if not guidelineValidator(guide): return False identifier = guide.get("identifier") if identifier is not None: if identifier in identifiers: return False identifiers.add(identifier) return True def guidelineValidator(value): """ Version 3+. """ dictPrototype = dict( x=((int, float), False), y=((int, float), False), angle=((int, float), False), name=(basestring, False), color=(basestring, False), identifier=(basestring, False) ) if not genericDictValidator(value, dictPrototype): return False x = value.get("x") y = value.get("y") angle = value.get("angle") # x or y must be present if x is None and y is None: return False # if x or y are None, angle must not be present if x is None or y is None: if angle is not None: return False # if x and y are defined, angle must be defined if x is not None and y is not None and angle is None: return False # angle must be between 0 and 360 if angle is not None: if angle < 0: return False if angle > 360: return False # identifier must be 1 or more characters identifier = value.get("identifier") if identifier is not None and not identifierValidator(identifier): return False # color must follow the proper format color = value.get("color") if color is not None and not colorValidator(color): return False return True # ------- # Anchors # ------- def anchorsValidator(value, identifiers=None): """ Version 3+. """ if not isinstance(value, list): return False if identifiers is None: identifiers = set() for anchor in value: if not anchorValidator(anchor): return False identifier = anchor.get("identifier") if identifier is not None: if identifier in identifiers: return False identifiers.add(identifier) return True def anchorValidator(value): """ Version 3+. """ dictPrototype = dict( x=((int, float), False), y=((int, float), False), name=(basestring, False), color=(basestring, False), identifier=(basestring, False) ) if not genericDictValidator(value, dictPrototype): return False x = value.get("x") y = value.get("y") # x and y must be present if x is None or y is None: return False # identifier must be 1 or more characters identifier = value.get("identifier") if identifier is not None and not identifierValidator(identifier): return False # color must follow the proper format color = value.get("color") if color is not None and not colorValidator(color): return False return True # ---------- # Identifier # ---------- def identifierValidator(value): """ Version 3+. >>> identifierValidator("a") True >>> identifierValidator("") False >>> identifierValidator("a" * 101) False """ validCharactersMin = 0x20 validCharactersMax = 0x7E if not isinstance(value, basestring): return False if not value: return False if len(value) > 100: return False for c in value: c = ord(c) if c < validCharactersMin or c > validCharactersMax: return False return True # ----- # Color # ----- def colorValidator(value): """ Version 3+. >>> colorValidator("0,0,0,0") True >>> colorValidator(".5,.5,.5,.5") True >>> colorValidator("0.5,0.5,0.5,0.5") True >>> colorValidator("1,1,1,1") True >>> colorValidator("2,0,0,0") False >>> colorValidator("0,2,0,0") False >>> colorValidator("0,0,2,0") False >>> colorValidator("0,0,0,2") False >>> colorValidator("1r,1,1,1") False >>> colorValidator("1,1g,1,1") False >>> colorValidator("1,1,1b,1") False >>> colorValidator("1,1,1,1a") False >>> colorValidator("1 1 1 1") False >>> colorValidator("1 1,1,1") False >>> colorValidator("1,1 1,1") False >>> colorValidator("1,1,1 1") False >>> colorValidator("1, 1, 1, 1") True """ if not isinstance(value, basestring): return False parts = value.split(",") if len(parts) != 4: return False for part in parts: part = part.strip() converted = False try: part = int(part) converted = True except ValueError: pass if not converted: try: part = float(part) converted = True except ValueError: pass if not converted: return False if part < 0: return False if part > 1: return False return True # ----- # image # ----- pngSignature = b"\x89PNG\r\n\x1a\n" def imageValidator(value): """ Version 3+. """ dictPrototype = dict( fileName=(basestring, True), xScale=((int, float), False), xyScale=((int, float), False), yxScale=((int, float), False), yScale=((int, float), False), xOffset=((int, float), False), yOffset=((int, float), False), color=(basestring, False) ) if not genericDictValidator(value, dictPrototype): return False # fileName must be one or more characters if not value["fileName"]: return False # color must follow the proper format color = value.get("color") if color is not None and not colorValidator(color): return False return True def pngValidator(path=None, data=None, fileObj=None): """ Version 3+. This checks the signature of the image data. """ assert path is not None or data is not None or fileObj is not None if path is not None: with open(path, "rb") as f: signature = f.read(8) elif data is not None: signature = data[:8] elif fileObj is not None: pos = fileObj.tell() signature = fileObj.read(8) fileObj.seek(pos) if signature != pngSignature: return False, "Image does not begin with the PNG signature." return True, None # ------------------- # layercontents.plist # ------------------- def layerContentsValidator(value, ufoPath): """ Check the validity of layercontents.plist. Version 3+. """ bogusFileMessage = "layercontents.plist in not in the correct format." # file isn't in the right format if not isinstance(value, list): return False, bogusFileMessage # work through each entry usedLayerNames = set() usedDirectories = set() contents = {} for entry in value: # layer entry in the incorrect format if not isinstance(entry, list): return False, bogusFileMessage if not len(entry) == 2: return False, bogusFileMessage for i in entry: if not isinstance(i, basestring): return False, bogusFileMessage layerName, directoryName = entry # check directory naming if directoryName != "glyphs": if not directoryName.startswith("glyphs."): return False, "Invalid directory name (%s) in layercontents.plist." % directoryName if len(layerName) == 0: return False, "Empty layer name in layercontents.plist." # directory doesn't exist p = os.path.join(ufoPath, directoryName) if not os.path.exists(p): return False, "A glyphset does not exist at %s." % directoryName # default layer name if layerName == "public.default" and directoryName != "glyphs": return False, "The name public.default is being used by a layer that is not the default." # check usage if layerName in usedLayerNames: return False, "The layer name %s is used by more than one layer." % layerName usedLayerNames.add(layerName) if directoryName in usedDirectories: return False, "The directory %s is used by more than one layer." % directoryName usedDirectories.add(directoryName) # store contents[layerName] = directoryName # missing default layer foundDefault = "glyphs" in list(contents.values()) if not foundDefault: return False, "The required default glyph set is not in the UFO." return True, None # ------------ # groups.plist # ------------ def groupsValidator(value): """ Check the validity of the groups. Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). >>> groups = {"A" : ["A", "A"], "A2" : ["A"]} >>> groupsValidator(groups) (True, None) >>> groups = {"" : ["A"]} >>> groupsValidator(groups) (False, 'A group has an empty name.') >>> groups = {"public.awesome" : ["A"]} >>> groupsValidator(groups) (True, None) >>> groups = {"public.kern1." : ["A"]} >>> groupsValidator(groups) (False, 'The group data contains a kerning group with an incomplete name.') >>> groups = {"public.kern2." : ["A"]} >>> groupsValidator(groups) (False, 'The group data contains a kerning group with an incomplete name.') >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]} >>> groupsValidator(groups) (True, None) >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]} >>> groupsValidator(groups) (False, 'The glyph "A" occurs in too many kerning groups.') """ bogusFormatMessage = "The group data is not in the correct format." if not isDictEnough(value): return False, bogusFormatMessage firstSideMapping = {} secondSideMapping = {} for groupName, glyphList in list(value.items()): if not isinstance(groupName, (basestring)): return False, bogusFormatMessage if not isinstance(glyphList, (list, tuple)): return False, bogusFormatMessage if not groupName: return False, "A group has an empty name." if groupName.startswith("public."): if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."): # unknown pubic.* name. silently skip. continue else: if len("public.kernN.") == len(groupName): return False, "The group data contains a kerning group with an incomplete name." if groupName.startswith("public.kern1."): d = firstSideMapping else: d = secondSideMapping for glyphName in glyphList: if not isinstance(glyphName, basestring): return False, "The group data %s contains an invalid member." % groupName if glyphName in d: return False, "The glyph \"%s\" occurs in too many kerning groups." % glyphName d[glyphName] = groupName return True, None # ------------- # kerning.plist # ------------- def kerningValidator(data): """ Check the validity of the kerning data structure. Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). >>> kerning = {"A" : {"B" : 100}} >>> kerningValidator(kerning) (True, None) >>> kerning = {"A" : ["B"]} >>> kerningValidator(kerning) (False, 'The kerning data is not in the correct format.') >>> kerning = {"A" : {"B" : "100"}} >>> kerningValidator(kerning) (False, 'The kerning data is not in the correct format.') """ bogusFormatMessage = "The kerning data is not in the correct format." if not isinstance(data, Mapping): return False, bogusFormatMessage for first, secondDict in list(data.items()): if not isinstance(first, basestring): return False, bogusFormatMessage elif not isinstance(secondDict, Mapping): return False, bogusFormatMessage for second, value in list(secondDict.items()): if not isinstance(second, basestring): return False, bogusFormatMessage elif not isinstance(value, (int, float)): return False, bogusFormatMessage return True, None # ------------- # lib.plist/lib # ------------- def fontLibValidator(value): """ Check the validity of the lib. Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). >>> lib = {"foo" : "bar"} >>> fontLibValidator(lib) (True, None) >>> lib = {"public.awesome" : "hello"} >>> fontLibValidator(lib) (True, None) >>> lib = {"public.glyphOrder" : ["A", "C", "B"]} >>> fontLibValidator(lib) (True, None) >>> lib = {"public.glyphOrder" : [u"A", u"C", u"B"]} >>> fontLibValidator(lib) (True, None) >>> lib = "hello" >>> fontLibValidator(lib) (False, 'The lib data is not in the correct format: expected a dictionary, found str') >>> lib = {1: "hello"} >>> fontLibValidator(lib) (False, 'The lib key is not properly formatted: expected basestring, found int: 1') >>> lib = {"public.glyphOrder" : "hello"} >>> fontLibValidator(lib) (False, 'public.glyphOrder is not properly formatted: expected list or tuple, found str') >>> lib = {"public.glyphOrder" : ["A", 1, "B"]} >>> fontLibValidator(lib) (False, 'public.glyphOrder is not properly formatted: expected basestring, found int') """ bogusFormatMessage = "The lib data is not in the correct format: %s" if not isDictEnough(value): reason = "expected a dictionary, found %s" % type(value).__name__ return False, bogusFormatMessage % reason for key, value in list(value.items()): if not isinstance(key, basestring): return False, ( "The lib key is not properly formatted: expected basestring, found %s: %r" % (type(key).__name__, key)) # public.glyphOrder if key == "public.glyphOrder": bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s" if not isinstance(value, (list, tuple)): reason = "expected list or tuple, found %s" % type(value).__name__ return False, bogusGlyphOrderMessage % reason for glyphName in value: if not isinstance(glyphName, basestring): reason = "expected basestring, found %s" % type(glyphName).__name__ return False, bogusGlyphOrderMessage % reason return True, None # -------- # GLIF lib # -------- def glyphLibValidator(value): """ Check the validity of the lib. Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). >>> lib = {"foo" : "bar"} >>> glyphLibValidator(lib) (True, None) >>> lib = {"public.awesome" : "hello"} >>> glyphLibValidator(lib) (True, None) >>> lib = {"public.markColor" : "1,0,0,0.5"} >>> glyphLibValidator(lib) (True, None) >>> lib = {"public.markColor" : 1} >>> glyphLibValidator(lib) (False, 'public.markColor is not properly formatted.') """ bogusFormatMessage = "The lib data is not in the correct format." if not isDictEnough(value): return False, bogusFormatMessage for key, value in list(value.items()): if not isinstance(key, basestring): return False, bogusFormatMessage # public.markColor if key == "public.markColor": if not colorValidator(value): return False, "public.markColor is not properly formatted." return True, None if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/Lib/ufoLib/kerning.py0000644000000000000000000000505413174123546015400 0ustar rootrootdef lookupKerningValue(pair, kerning, groups, fallback=0, glyphToFirstGroup=None, glyphToSecondGroup=None): """ Note: This expects kerning to be a flat dictionary of kerning pairs, not the nested structure used in kerning.plist. >>> groups = { ... "public.kern1.O" : ["O", "D", "Q"], ... "public.kern2.E" : ["E", "F"] ... } >>> kerning = { ... ("public.kern1.O", "public.kern2.E") : -100, ... ("public.kern1.O", "F") : -200, ... ("D", "F") : -300 ... } >>> lookupKerningValue(("D", "F"), kerning, groups) -300 >>> lookupKerningValue(("O", "F"), kerning, groups) -200 >>> lookupKerningValue(("O", "E"), kerning, groups) -100 >>> lookupKerningValue(("O", "O"), kerning, groups) 0 >>> lookupKerningValue(("E", "E"), kerning, groups) 0 >>> lookupKerningValue(("E", "O"), kerning, groups) 0 >>> lookupKerningValue(("X", "X"), kerning, groups) 0 >>> lookupKerningValue(("public.kern1.O", "public.kern2.E"), ... kerning, groups) -100 >>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups) -200 >>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups) -100 >>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups) 0 """ # quickly check to see if the pair is in the kerning dictionary if pair in kerning: return kerning[pair] # create glyph to group mapping if glyphToFirstGroup is not None: assert glyphToSecondGroup is not None if glyphToSecondGroup is not None: assert glyphToFirstGroup is not None if glyphToFirstGroup is None: glyphToFirstGroup = {} glyphToSecondGroup = {} for group, groupMembers in groups.items(): if group.startswith("public.kern1."): for glyph in groupMembers: glyphToFirstGroup[glyph] = group elif group.startswith("public.kern2."): for glyph in groupMembers: glyphToSecondGroup[glyph] = group # get group names and make sure first and second are glyph names first, second = pair firstGroup = secondGroup = None if first.startswith("public.kern1."): firstGroup = first first = None else: firstGroup = glyphToFirstGroup.get(first) if second.startswith("public.kern2."): secondGroup = second second = None else: secondGroup = glyphToSecondGroup.get(second) # make an ordered list of pairs to look up pairs = [ (first, second), (first, secondGroup), (firstGroup, second), (firstGroup, secondGroup) ] # look up the pairs and return any matches for pair in pairs: if pair in kerning: return kerning[pair] # use the fallback value return fallback if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/Lib/ufoLib/plistFromETree.py0000644000000000000000000000131113174123546016637 0ustar rootrootfrom ufoLib.plistlib import PlistParser __all__ = ["readPlistFromTree"] def readPlistFromTree(tree): """ Given an ElementTree Element *tree*, parse Plist data and return the root object. """ parser = PlistTreeParser() return parser.parseTree(tree) class PlistTreeParser(PlistParser): def parseTree(self, tree): self.parseElement(tree) return self.root def parseElement(self, element): self.handleBeginElement(element.tag, element.attrib) # if there are children, recurse for child in element: self.parseElement(child) # otherwise, parse the leaf's data if not len(element): # always pass str, not None self.handleData(element.text or "") self.handleEndElement(element.tag) ufoLib-2.1.1/Lib/ufoLib/converters.py0000644000000000000000000002427513174123546016143 0ustar rootroot""" Conversion functions. """ # adapted from the UFO spec def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups): # gather known kerning groups based on the prefixes firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups) # Make lists of groups referenced in kerning pairs. for first, seconds in list(kerning.items()): if first in groups: if not first.startswith("public.kern1."): firstReferencedGroups.add(first) for second in list(seconds.keys()): if second in groups: if not second.startswith("public.kern2."): secondReferencedGroups.add(second) # Create new names for these groups. firstRenamedGroups = {} for first in firstReferencedGroups: # Make a list of existing group names. existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys()) # Add the prefix to the name. newName = "public.kern1." + first # Make a unique group name. newName = makeUniqueGroupName(newName, existingGroupNames) # Store for use later. firstRenamedGroups[first] = newName secondRenamedGroups = {} for second in secondReferencedGroups: # Make a list of existing group names. existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys()) # Add the prefix to the name. newName = "public.kern2." + second # Make a unique group name. newName = makeUniqueGroupName(newName, existingGroupNames) # Store for use later. secondRenamedGroups[second] = newName # Populate the new group names into the kerning dictionary as needed. newKerning = {} for first, seconds in list(kerning.items()): first = firstRenamedGroups.get(first, first) newSeconds = {} for second, value in list(seconds.items()): second = secondRenamedGroups.get(second, second) newSeconds[second] = value newKerning[first] = newSeconds # Make copies of the referenced groups and store them # under the new names in the overall groups dictionary. allRenamedGroups = list(firstRenamedGroups.items()) allRenamedGroups += list(secondRenamedGroups.items()) for oldName, newName in allRenamedGroups: group = list(groups[oldName]) groups[newName] = group # Return the kerning and the groups. return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups) def findKnownKerningGroups(groups): """ This will find kerning groups with known prefixes. In some cases not all kerning groups will be referenced by the kerning pairs. The algorithm for locating groups in convertUFO1OrUFO2KerningToUFO3Kerning will miss these unreferenced groups. By scanning for known prefixes this function will catch all of the prefixed groups. These are the prefixes and sides that are handled: @MMK_L_ - side 1 @MMK_R_ - side 2 >>> testGroups = { ... "@MMK_L_1" : None, ... "@MMK_L_2" : None, ... "@MMK_L_3" : None, ... "@MMK_R_1" : None, ... "@MMK_R_2" : None, ... "@MMK_R_3" : None, ... "@MMK_l_1" : None, ... "@MMK_r_1" : None, ... "@MMK_X_1" : None, ... "foo" : None, ... } >>> first, second = findKnownKerningGroups(testGroups) >>> sorted(first) ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3'] >>> sorted(second) ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3'] """ knownFirstGroupPrefixes = [ "@MMK_L_" ] knownSecondGroupPrefixes = [ "@MMK_R_" ] firstGroups = set() secondGroups = set() for groupName in list(groups.keys()): for firstPrefix in knownFirstGroupPrefixes: if groupName.startswith(firstPrefix): firstGroups.add(groupName) break for secondPrefix in knownSecondGroupPrefixes: if groupName.startswith(secondPrefix): secondGroups.add(groupName) break return firstGroups, secondGroups def makeUniqueGroupName(name, groupNames, counter=0): # Add a number to the name if the counter is higher than zero. newName = name if counter > 0: newName = "%s%d" % (newName, counter) # If the new name is in the existing group names, recurse. if newName in groupNames: return makeUniqueGroupName(name, groupNames, counter + 1) # Otherwise send back the new name. return newName def test(): """ No known prefixes. >>> testKerning = { ... "A" : { ... "A" : 1, ... "B" : 2, ... "CGroup" : 3, ... "DGroup" : 4 ... }, ... "BGroup" : { ... "A" : 5, ... "B" : 6, ... "CGroup" : 7, ... "DGroup" : 8 ... }, ... "CGroup" : { ... "A" : 9, ... "B" : 10, ... "CGroup" : 11, ... "DGroup" : 12 ... }, ... } >>> testGroups = { ... "BGroup" : ["B"], ... "CGroup" : ["C"], ... "DGroup" : ["D"], ... } >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( ... testKerning, testGroups) >>> expected = { ... "A" : { ... "A": 1, ... "B": 2, ... "public.kern2.CGroup": 3, ... "public.kern2.DGroup": 4 ... }, ... "public.kern1.BGroup": { ... "A": 5, ... "B": 6, ... "public.kern2.CGroup": 7, ... "public.kern2.DGroup": 8 ... }, ... "public.kern1.CGroup": { ... "A": 9, ... "B": 10, ... "public.kern2.CGroup": 11, ... "public.kern2.DGroup": 12 ... } ... } >>> kerning == expected True >>> expected = { ... "BGroup": ["B"], ... "CGroup": ["C"], ... "DGroup": ["D"], ... "public.kern1.BGroup": ["B"], ... "public.kern1.CGroup": ["C"], ... "public.kern2.CGroup": ["C"], ... "public.kern2.DGroup": ["D"], ... } >>> groups == expected True Known prefixes. >>> testKerning = { ... "A" : { ... "A" : 1, ... "B" : 2, ... "@MMK_R_CGroup" : 3, ... "@MMK_R_DGroup" : 4 ... }, ... "@MMK_L_BGroup" : { ... "A" : 5, ... "B" : 6, ... "@MMK_R_CGroup" : 7, ... "@MMK_R_DGroup" : 8 ... }, ... "@MMK_L_CGroup" : { ... "A" : 9, ... "B" : 10, ... "@MMK_R_CGroup" : 11, ... "@MMK_R_DGroup" : 12 ... }, ... } >>> testGroups = { ... "@MMK_L_BGroup" : ["B"], ... "@MMK_L_CGroup" : ["C"], ... "@MMK_L_XGroup" : ["X"], ... "@MMK_R_CGroup" : ["C"], ... "@MMK_R_DGroup" : ["D"], ... "@MMK_R_XGroup" : ["X"], ... } >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( ... testKerning, testGroups) >>> expected = { ... "A" : { ... "A": 1, ... "B": 2, ... "public.kern2.@MMK_R_CGroup": 3, ... "public.kern2.@MMK_R_DGroup": 4 ... }, ... "public.kern1.@MMK_L_BGroup": { ... "A": 5, ... "B": 6, ... "public.kern2.@MMK_R_CGroup": 7, ... "public.kern2.@MMK_R_DGroup": 8 ... }, ... "public.kern1.@MMK_L_CGroup": { ... "A": 9, ... "B": 10, ... "public.kern2.@MMK_R_CGroup": 11, ... "public.kern2.@MMK_R_DGroup": 12 ... } ... } >>> kerning == expected True >>> expected = { ... "@MMK_L_BGroup": ["B"], ... "@MMK_L_CGroup": ["C"], ... "@MMK_L_XGroup": ["X"], ... "@MMK_R_CGroup": ["C"], ... "@MMK_R_DGroup": ["D"], ... "@MMK_R_XGroup": ["X"], ... "public.kern1.@MMK_L_BGroup": ["B"], ... "public.kern1.@MMK_L_CGroup": ["C"], ... "public.kern1.@MMK_L_XGroup": ["X"], ... "public.kern2.@MMK_R_CGroup": ["C"], ... "public.kern2.@MMK_R_DGroup": ["D"], ... "public.kern2.@MMK_R_XGroup": ["X"], ... } >>> groups == expected True >>> from .validators import kerningValidator >>> kerningValidator(kerning) (True, None) Mixture of known prefixes and groups without prefixes. >>> testKerning = { ... "A" : { ... "A" : 1, ... "B" : 2, ... "@MMK_R_CGroup" : 3, ... "DGroup" : 4 ... }, ... "BGroup" : { ... "A" : 5, ... "B" : 6, ... "@MMK_R_CGroup" : 7, ... "DGroup" : 8 ... }, ... "@MMK_L_CGroup" : { ... "A" : 9, ... "B" : 10, ... "@MMK_R_CGroup" : 11, ... "DGroup" : 12 ... }, ... } >>> testGroups = { ... "BGroup" : ["B"], ... "@MMK_L_CGroup" : ["C"], ... "@MMK_R_CGroup" : ["C"], ... "DGroup" : ["D"], ... } >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( ... testKerning, testGroups) >>> expected = { ... "A" : { ... "A": 1, ... "B": 2, ... "public.kern2.@MMK_R_CGroup": 3, ... "public.kern2.DGroup": 4 ... }, ... "public.kern1.BGroup": { ... "A": 5, ... "B": 6, ... "public.kern2.@MMK_R_CGroup": 7, ... "public.kern2.DGroup": 8 ... }, ... "public.kern1.@MMK_L_CGroup": { ... "A": 9, ... "B": 10, ... "public.kern2.@MMK_R_CGroup": 11, ... "public.kern2.DGroup": 12 ... } ... } >>> kerning == expected True >>> expected = { ... "BGroup": ["B"], ... "@MMK_L_CGroup": ["C"], ... "@MMK_R_CGroup": ["C"], ... "DGroup": ["D"], ... "public.kern1.BGroup": ["B"], ... "public.kern1.@MMK_L_CGroup": ["C"], ... "public.kern2.@MMK_R_CGroup": ["C"], ... "public.kern2.DGroup": ["D"], ... } >>> groups == expected True """ if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/Lib/ufoLib/plistlib.py0000644000000000000000000000315013174123546015560 0ustar rootroot""" A py23 shim to plistlib. Reimplements plistlib under py2 naming. """ from __future__ import absolute_import import sys try: from plistlib import ( load as readPlist, dump as writePlist, loads as readPlistFromString, dumps as writePlistToString) from plistlib import _PlistParser, _PlistWriter # Public API changed in Python 3.4 if sys.version_info >= (3, 4): class PlistWriter(_PlistWriter): def __init__(self, *args, **kwargs): if "indentLevel" in kwargs: kwargs["indent_level"] = kwargs["indentLevel"] del kwargs["indentLevel"] super().__init__(*args, **kwargs) def writeValue(self, *args, **kwargs): super().write_value(*args, **kwargs) def writeData(self, *args, **kwargs): super().write_data(*args, **kwargs) def writeDict(self, *args, **kwargs): super().write_dict(*args, **kwargs) def writeArray(self, *args, **kwargs): super().write_array(*args, **kwargs) class PlistParser(_PlistParser): def __init__(self): super().__init__(use_builtin_types=True, dict_type=dict) def parseElement(self, *args, **kwargs): super().parse_element(*args, **kwargs) def handleBeginElement(self, *args, **kwargs): super().handle_begin_element(*args, **kwargs) def handleData(self, *args, **kwargs): super().handle_data(*args, **kwargs) def handleEndElement(self, *args, **kwargs): super().handle_end_element(*args, **kwargs) else: PlistWriter = _PlistWriter PlistParser = _PlistParser except ImportError: from plistlib import readPlist, writePlist, readPlistFromString, writePlistToString from plistlib import PlistParser, PlistWriter ufoLib-2.1.1/Lib/ufoLib/pointPen.py0000644000000000000000000002774013174123546015545 0ustar rootroot""" ========= PointPens ========= Where **SegmentPens** have an intuitive approach to drawing (if you're familiar with postscript anyway), the **PointPen** is geared towards accessing all the data in the contours of the glyph. A PointsPen has a very simple interface, it just steps through all the points in a call from glyph.drawPoints(). This allows the caller to provide more data for each point. For instance, whether or not a point is smooth, and its name. """ from fontTools.pens.basePen import AbstractPen import math __all__ = ["AbstractPointPen", "BasePointToSegmentPen", "PointToSegmentPen", "SegmentToPointPen"] class AbstractPointPen(object): """ Baseclass for all PointPens. """ def beginPath(self, identifier=None, **kwargs): """Start a new sub path.""" raise NotImplementedError def endPath(self): """End the current sub path.""" raise NotImplementedError def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs): """Add a point to the current sub path.""" raise NotImplementedError def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs): """Add a sub glyph.""" raise NotImplementedError class BasePointToSegmentPen(AbstractPointPen): """ Base class for retrieving the outline in a segment-oriented way. The PointPen protocol is simple yet also a little tricky, so when you need an outline presented as segments but you have as points, do use this base implementation as it properly takes care of all the edge cases. """ def __init__(self): self.currentPath = None def beginPath(self, **kwargs): assert self.currentPath is None self.currentPath = [] def _flushContour(self, segments): """Override this method. It will be called for each non-empty sub path with a list of segments: the 'segments' argument. The segments list contains tuples of length 2: (segmentType, points) segmentType is one of "move", "line", "curve" or "qcurve". "move" may only occur as the first segment, and it signifies an OPEN path. A CLOSED path does NOT start with a "move", in fact it will not contain a "move" at ALL. The 'points' field in the 2-tuple is a list of point info tuples. The list has 1 or more items, a point tuple has four items: (point, smooth, name, kwargs) 'point' is an (x, y) coordinate pair. For a closed path, the initial moveTo point is defined as the last point of the last segment. The 'points' list of "move" and "line" segments always contains exactly one point tuple. """ raise NotImplementedError def endPath(self): assert self.currentPath is not None points = self.currentPath self.currentPath = None if not points: return if len(points) == 1: # Not much more we can do than output a single move segment. pt, segmentType, smooth, name, kwargs = points[0] segments = [("move", [(pt, smooth, name, kwargs)])] self._flushContour(segments) return segments = [] if points[0][1] == "move": # It's an open contour, insert a "move" segment for the first # point and remove that first point from the point list. pt, segmentType, smooth, name, kwargs = points[0] segments.append(("move", [(pt, smooth, name, kwargs)])) points.pop(0) else: # It's a closed contour. Locate the first on-curve point, and # rotate the point list so that it _ends_ with an on-curve # point. firstOnCurve = None for i in range(len(points)): segmentType = points[i][1] if segmentType is not None: firstOnCurve = i break if firstOnCurve is None: # Special case for quadratics: a contour with no on-curve # points. Add a "None" point. (See also the Pen protocol's # qCurveTo() method and fontTools.pens.basePen.py.) points.append((None, "qcurve", None, None, None)) else: points = points[firstOnCurve+1:] + points[:firstOnCurve+1] currentSegment = [] for pt, segmentType, smooth, name, kwargs in points: currentSegment.append((pt, smooth, name, kwargs)) if segmentType is None: continue segments.append((segmentType, currentSegment)) currentSegment = [] self._flushContour(segments) def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): self.currentPath.append((pt, segmentType, smooth, name, kwargs)) class PointToSegmentPen(BasePointToSegmentPen): """ Adapter class that converts the PointPen protocol to the (Segment)Pen protocol. """ def __init__(self, segmentPen, outputImpliedClosingLine=False): BasePointToSegmentPen.__init__(self) self.pen = segmentPen self.outputImpliedClosingLine = outputImpliedClosingLine def _flushContour(self, segments): assert len(segments) >= 1 pen = self.pen if segments[0][0] == "move": # It's an open path. closed = False points = segments[0][1] assert len(points) == 1, "illegal move segment point count: %d" % len(points) movePt, smooth, name, kwargs = points[0] del segments[0] else: # It's a closed path, do a moveTo to the last # point of the last segment. closed = True segmentType, points = segments[-1] movePt, smooth, name, kwargs = points[-1] if movePt is None: # quad special case: a contour with no on-curve points contains # one "qcurve" segment that ends with a point that's None. We # must not output a moveTo() in that case. pass else: pen.moveTo(movePt) outputImpliedClosingLine = self.outputImpliedClosingLine nSegments = len(segments) for i in range(nSegments): segmentType, points = segments[i] points = [pt for pt, smooth, name, kwargs in points] if segmentType == "line": assert len(points) == 1, "illegal line segment point count: %d" % len(points) pt = points[0] if i + 1 != nSegments or outputImpliedClosingLine or not closed: pen.lineTo(pt) elif segmentType == "curve": pen.curveTo(*points) elif segmentType == "qcurve": pen.qCurveTo(*points) else: assert 0, "illegal segmentType: %s" % segmentType if closed: pen.closePath() else: pen.endPath() def addComponent(self, glyphName, transform, **kwargs): self.pen.addComponent(glyphName, transform) class SegmentToPointPen(AbstractPen): """ Adapter class that converts the (Segment)Pen protocol to the PointPen protocol. """ def __init__(self, pointPen, guessSmooth=True): if guessSmooth: self.pen = GuessSmoothPointPen(pointPen) else: self.pen = pointPen self.contour = None def _flushContour(self): pen = self.pen pen.beginPath() for pt, segmentType in self.contour: pen.addPoint(pt, segmentType=segmentType) pen.endPath() def moveTo(self, pt): self.contour = [] self.contour.append((pt, "move")) def lineTo(self, pt): self.contour.append((pt, "line")) def curveTo(self, *pts): for pt in pts[:-1]: self.contour.append((pt, None)) self.contour.append((pts[-1], "curve")) def qCurveTo(self, *pts): if pts[-1] is None: self.contour = [] for pt in pts[:-1]: self.contour.append((pt, None)) if pts[-1] is not None: self.contour.append((pts[-1], "qcurve")) def closePath(self): if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]: self.contour[0] = self.contour[-1] del self.contour[-1] else: # There's an implied line at the end, replace "move" with "line" # for the first point pt, tp = self.contour[0] if tp == "move": self.contour[0] = pt, "line" self._flushContour() self.contour = None def endPath(self): self._flushContour() self.contour = None def addComponent(self, glyphName, transform): assert self.contour is None self.pen.addComponent(glyphName, transform) class GuessSmoothPointPen(AbstractPointPen): """ Filtering PointPen that tries to determine whether an on-curve point should be "smooth", ie. that it's a "tangent" point or a "curve" point. """ def __init__(self, outPen): self._outPen = outPen self._points = None def _flushContour(self): points = self._points nPoints = len(points) if not nPoints: return if points[0][1] == "move": # Open path. indices = range(1, nPoints - 1) elif nPoints > 1: # Closed path. To avoid having to mod the contour index, we # simply abuse Python's negative index feature, and start at -1 indices = range(-1, nPoints - 1) else: # closed path containing 1 point (!), ignore. indices = [] for i in indices: pt, segmentType, dummy, name, kwargs = points[i] if segmentType is None: continue prev = i - 1 next = i + 1 if points[prev][1] is not None and points[next][1] is not None: continue # At least one of our neighbors is an off-curve point pt = points[i][0] prevPt = points[prev][0] nextPt = points[next][0] if pt != prevPt and pt != nextPt: dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1] dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1] a1 = math.atan2(dx1, dy1) a2 = math.atan2(dx2, dy2) if abs(a1 - a2) < 0.05: points[i] = pt, segmentType, True, name, kwargs for pt, segmentType, smooth, name, kwargs in points: self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs) def beginPath(self): assert self._points is None self._points = [] self._outPen.beginPath() def endPath(self): self._flushContour() self._outPen.endPath() self._points = None def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): self._points.append((pt, segmentType, False, name, kwargs)) def addComponent(self, glyphName, transformation): assert self._points is None self._outPen.addComponent(glyphName, transformation) class ReverseContourPointPen(AbstractPointPen): """ This is a PointPen that passes outline data to another PointPen, but reversing the winding direction of all contours. Components are simply passed through unchanged. Closed contours are reversed in such a way that the first point remains the first point. """ def __init__(self, outputPointPen): self.pen = outputPointPen # a place to store the points for the current sub path self.currentContour = None def _flushContour(self): pen = self.pen contour = self.currentContour if not contour: pen.beginPath(identifier=self.currentContourIdentifier) pen.endPath() return closed = contour[0][1] != "move" if not closed: lastSegmentType = "move" else: # Remove the first point and insert it at the end. When # the list of points gets reversed, this point will then # again be at the start. In other words, the following # will hold: # for N in range(len(originalContour)): # originalContour[N] == reversedContour[-N] contour.append(contour.pop(0)) # Find the first on-curve point. firstOnCurve = None for i in range(len(contour)): if contour[i][1] is not None: firstOnCurve = i break if firstOnCurve is None: # There are no on-curve points, be basically have to # do nothing but contour.reverse(). lastSegmentType = None else: lastSegmentType = contour[firstOnCurve][1] contour.reverse() if not closed: # Open paths must start with a move, so we simply dump # all off-curve points leading up to the first on-curve. while contour[0][1] is None: contour.pop(0) pen.beginPath(identifier=self.currentContourIdentifier) for pt, nextSegmentType, smooth, name, kwargs in contour: if nextSegmentType is not None: segmentType = lastSegmentType lastSegmentType = nextSegmentType else: segmentType = None pen.addPoint(pt, segmentType=segmentType, smooth=smooth, name=name, **kwargs) pen.endPath() def beginPath(self, identifier=None, **kwargs): assert self.currentContour is None self.currentContour = [] self.currentContourIdentifier = identifier self.onCurve = [] def endPath(self): assert self.currentContour is not None self._flushContour() self.currentContour = None def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): self.currentContour.append((pt, segmentType, smooth, name, kwargs)) def addComponent(self, glyphName, transform, identifier=None, **kwargs): assert self.currentContour is None self.pen.addComponent(glyphName, transform, identifier=identifier, **kwargs) ufoLib-2.1.1/Lib/ufoLib/glifLib.py0000755000000000000000000014522113174123546015317 0ustar rootroot# -*- coding: utf-8 -*- """ glifLib.py -- Generic module for reading and writing the .glif format. More info about the .glif format (GLyphInterchangeFormat) can be found here: http://unifiedfontobject.org The main class in this module is GlyphSet. It manages a set of .glif files in a folder. It offers two ways to read glyph data, and one way to write glyph data. See the class doc string for details. """ from __future__ import unicode_literals import os from io import BytesIO, open from warnings import warn from fontTools.misc.py23 import tobytes, unicode from ufoLib.plistlib import PlistWriter, readPlist, writePlist from ufoLib.plistFromETree import readPlistFromTree from ufoLib.pointPen import AbstractPointPen, PointToSegmentPen from ufoLib.filenames import userNameToFileName from ufoLib.validators import isDictEnough, genericTypeValidator, colorValidator,\ guidelinesValidator, anchorsValidator, identifierValidator, imageValidator, glyphLibValidator try: basestring except NameError: basestring = str try: from xml.etree import cElementTree as ElementTree except ImportError: from xml.etree import ElementTree __all__ = [ "GlyphSet", "GlifLibError", "readGlyphFromString", "writeGlyphToString", "glyphNameToFileName" ] class GlifLibError(Exception): pass # --------- # Constants # --------- LAYERINFO_FILENAME = "layerinfo.plist" supportedUFOFormatVersions = [1, 2, 3] supportedGLIFFormatVersions = [1, 2] # ------------ # Simple Glyph # ------------ class Glyph(object): """ Minimal glyph object. It has no glyph attributes until either the draw() or the drawPoints() method has been called. """ def __init__(self, glyphName, glyphSet): self.glyphName = glyphName self.glyphSet = glyphSet def draw(self, pen): """ Draw this glyph onto a *FontTools* Pen. """ pointPen = PointToSegmentPen(pen) self.drawPoints(pointPen) def drawPoints(self, pointPen): """ Draw this glyph onto a PointPen. """ self.glyphSet.readGlyph(self.glyphName, self, pointPen) # --------- # Glyph Set # --------- class GlyphSet(object): """ GlyphSet manages a set of .glif files inside one directory. GlyphSet's constructor takes a path to an existing directory as it's first argument. Reading glyph data can either be done through the readGlyph() method, or by using GlyphSet's dictionary interface, where the keys are glyph names and the values are (very) simple glyph objects. To write a glyph to the glyph set, you use the writeGlyph() method. The simple glyph objects returned through the dict interface do not support writing, they are just a convenient way to get at the glyph data. """ glyphClass = Glyph def __init__(self, dirName, glyphNameToFileNameFunc=None, ufoFormatVersion=3): """ 'dirName' should be a path to an existing directory. The optional 'glyphNameToFileNameFunc' argument must be a callback function that takes two arguments: a glyph name and the GlyphSet instance. It should return a file name (including the .glif extension). The glyphNameToFileName function is called whenever a file name is created for a given glyph name. """ self.dirName = dirName if ufoFormatVersion not in supportedUFOFormatVersions: raise GlifLibError("Unsupported UFO format version: %s" % ufoFormatVersion) self.ufoFormatVersion = ufoFormatVersion if glyphNameToFileNameFunc is None: glyphNameToFileNameFunc = glyphNameToFileName self.glyphNameToFileName = glyphNameToFileNameFunc self.rebuildContents() self._reverseContents = None self._glifCache = {} def rebuildContents(self): """ Rebuild the contents dict by loading contents.plist. """ contentsPath = os.path.join(self.dirName, "contents.plist") if not os.path.exists(contentsPath): # missing, consider the glyphset empty. contents = {} else: contents = self._readPlist(contentsPath) # validate the contents invalidFormat = False if not isinstance(contents, dict): invalidFormat = True else: for name, fileName in list(contents.items()): if not isinstance(name, basestring): invalidFormat = True if not isinstance(fileName, basestring): invalidFormat = True elif not os.path.exists(os.path.join(self.dirName, fileName)): raise GlifLibError("contents.plist references a file that does not exist: %s" % fileName) if invalidFormat: raise GlifLibError("contents.plist is not properly formatted") self.contents = contents self._reverseContents = None def getReverseContents(self): """ Return a reversed dict of self.contents, mapping file names to glyph names. This is primarily an aid for custom glyph name to file name schemes that want to make sure they don't generate duplicate file names. The file names are converted to lowercase so we can reliably check for duplicates that only differ in case, which is important for case-insensitive file systems. """ if self._reverseContents is None: d = {} for k, v in self.contents.items(): d[v.lower()] = k self._reverseContents = d return self._reverseContents def writeContents(self): """ Write the contents.plist file out to disk. Call this method when you're done writing glyphs. """ contentsPath = os.path.join(self.dirName, "contents.plist") with open(contentsPath, "wb") as f: writePlist(self.contents, f) # layer info def readLayerInfo(self, info): path = os.path.join(self.dirName, LAYERINFO_FILENAME) if not os.path.exists(path): return infoDict = self._readPlist(path) if not isinstance(infoDict, dict): raise GlifLibError("layerinfo.plist is not properly formatted.") infoDict = validateLayerInfoVersion3Data(infoDict) # populate the object for attr, value in list(infoDict.items()): try: setattr(info, attr, value) except AttributeError: raise GlifLibError("The supplied layer info object does not support setting a necessary attribute (%s)." % attr) def writeLayerInfo(self, info): if self.ufoFormatVersion < 3: raise GlifLibError("layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersion) # gather data infoData = {} for attr in list(layerInfoVersion3ValueData.keys()): if hasattr(info, attr): try: value = getattr(info, attr) except AttributeError: raise GlifLibError("The supplied info object does not support getting a necessary attribute (%s)." % attr) if value is None or (attr == 'lib' and not value): continue infoData[attr] = value # validate infoData = validateLayerInfoVersion3Data(infoData) # write file path = os.path.join(self.dirName, LAYERINFO_FILENAME) with open(path, "wb") as f: writePlist(infoData, f) # read caching def getGLIF(self, glyphName): """ Get the raw GLIF text for a given glyph name. This only works for GLIF files that are already on disk. This method is useful in situations when the raw XML needs to be read from a glyph set for a particular glyph before fully parsing it into an object structure via the readGlyph method. Internally, this method will load a GLIF the first time it is called and then cache it. The next time this method is called the GLIF will be pulled from the cache if the file's modification time has not changed since the GLIF was cached. For memory efficiency, the cached GLIF will be purged by various other methods such as readGlyph. """ needRead = False fileName = self.contents.get(glyphName) path = None if fileName is not None: path = os.path.join(self.dirName, fileName) if glyphName not in self._glifCache: needRead = True elif fileName is not None and os.path.getmtime(path) != self._glifCache[glyphName][1]: needRead = True if needRead: fileName = self.contents[glyphName] if not os.path.exists(path): raise KeyError(glyphName) with open(path, "rb") as f: text = f.read() self._glifCache[glyphName] = (text, os.path.getmtime(path)) return self._glifCache[glyphName][0] def getGLIFModificationTime(self, glyphName): """ Get the modification time (as reported by os.path.getmtime) of the GLIF with glyphName. """ self.getGLIF(glyphName) return self._glifCache[glyphName][1] def _purgeCachedGLIF(self, glyphName): if glyphName in self._glifCache: del self._glifCache[glyphName] # reading/writing API def readGlyph(self, glyphName, glyphObject=None, pointPen=None): """ Read a .glif file for 'glyphName' from the glyph set. The 'glyphObject' argument can be any kind of object (even None); the readGlyph() method will attempt to set the following attributes on it: "width" the advance with of the glyph "height" the advance height of the glyph "unicodes" a list of unicode values for this glyph "note" a string "lib" a dictionary containing custom data "image" a dictionary containing image data "guidelines" a list of guideline data dictionaries "anchors" a list of anchor data dictionaries All attributes are optional, in two ways: 1) An attribute *won't* be set if the .glif file doesn't contain data for it. 'glyphObject' will have to deal with default values itself. 2) If setting the attribute fails with an AttributeError (for example if the 'glyphObject' attribute is read- only), readGlyph() will not propagate that exception, but ignore that attribute. To retrieve outline information, you need to pass an object conforming to the PointPen protocol as the 'pointPen' argument. This argument may be None if you don't need the outline data. readGlyph() will raise KeyError if the glyph is not present in the glyph set. """ text = self.getGLIF(glyphName) self._purgeCachedGLIF(glyphName) tree = _glifTreeFromString(text) if self.ufoFormatVersion < 3: formatVersions = (1,) else: formatVersions = (1, 2) _readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions) def writeGlyph(self, glyphName, glyphObject=None, drawPointsFunc=None, formatVersion=None): """ Write a .glif file for 'glyphName' to the glyph set. The 'glyphObject' argument can be any kind of object (even None); the writeGlyph() method will attempt to get the following attributes from it: "width" the advance with of the glyph "height" the advance height of the glyph "unicodes" a list of unicode values for this glyph "note" a string "lib" a dictionary containing custom data "image" a dictionary containing image data "guidelines" a list of guideline data dictionaries "anchors" a list of anchor data dictionaries All attributes are optional: if 'glyphObject' doesn't have the attribute, it will simply be skipped. To write outline data to the .glif file, writeGlyph() needs a function (any callable object actually) that will take one argument: an object that conforms to the PointPen protocol. The function will be called by writeGlyph(); it has to call the proper PointPen methods to transfer the outline to the .glif file. The GLIF format version will be chosen based on the ufoFormatVersion passed during the creation of this object. If a particular format version is desired, it can be passed with the formatVersion argument. """ if formatVersion is None: if self.ufoFormatVersion >= 3: formatVersion = 2 else: formatVersion = 1 else: if formatVersion not in supportedGLIFFormatVersions: raise GlifLibError("Unsupported GLIF format version: %s" % formatVersion) if formatVersion == 2 and self.ufoFormatVersion < 3: raise GlifLibError("Unsupported GLIF format version (%d) for UFO format version %d." % (formatVersion, self.ufoFormatVersion)) self._purgeCachedGLIF(glyphName) data = writeGlyphToString(glyphName, glyphObject, drawPointsFunc, formatVersion=formatVersion) fileName = self.contents.get(glyphName) if fileName is None: fileName = self.glyphNameToFileName(glyphName, self) self.contents[glyphName] = fileName if self._reverseContents is not None: self._reverseContents[fileName.lower()] = glyphName path = os.path.join(self.dirName, fileName) if os.path.exists(path): with open(path, "rb") as f: oldData = f.read() if data == oldData: return with open(path, "wb") as f: f.write(tobytes(data, encoding="utf-8")) def deleteGlyph(self, glyphName): """Permanently delete the glyph from the glyph set on disk. Will raise KeyError if the glyph is not present in the glyph set. """ self._purgeCachedGLIF(glyphName) fileName = self.contents[glyphName] os.remove(os.path.join(self.dirName, fileName)) if self._reverseContents is not None: del self._reverseContents[self.contents[glyphName].lower()] del self.contents[glyphName] # dict-like support def keys(self): return list(self.contents.keys()) def has_key(self, glyphName): return glyphName in self.contents __contains__ = has_key def __len__(self): return len(self.contents) def __getitem__(self, glyphName): if glyphName not in self.contents: raise KeyError(glyphName) return self.glyphClass(glyphName, self) # quickly fetch unicode values def getUnicodes(self, glyphNames=None): """ Return a dictionary that maps glyph names to lists containing the unicode value[s] for that glyph, if any. This parses the .glif files partially, so it is a lot faster than parsing all files completely. By default this checks all glyphs, but a subset can be passed with glyphNames. """ unicodes = {} if glyphNames is None: glyphNames = list(self.contents.keys()) for glyphName in glyphNames: text = self.getGLIF(glyphName) unicodes[glyphName] = _fetchUnicodes(text) return unicodes def getComponentReferences(self, glyphNames=None): """ Return a dictionary that maps glyph names to lists containing the base glyph name of components in the glyph. This parses the .glif files partially, so it is a lot faster than parsing all files completely. By default this checks all glyphs, but a subset can be passed with glyphNames. """ components = {} if glyphNames is None: glyphNames = list(self.contents.keys()) for glyphName in glyphNames: text = self.getGLIF(glyphName) components[glyphName] = _fetchComponentBases(text) return components def getImageReferences(self, glyphNames=None): """ Return a dictionary that maps glyph names to the file name of the image referenced by the glyph. This parses the .glif files partially, so it is a lot faster than parsing all files completely. By default this checks all glyphs, but a subset can be passed with glyphNames. """ images = {} if glyphNames is None: glyphNames = list(self.contents.keys()) for glyphName in glyphNames: text = self.getGLIF(glyphName) images[glyphName] = _fetchImageFileName(text) return images # internal methods def _readPlist(self, path): try: with open(path, "rb") as f: data = readPlist(f) return data except: raise GlifLibError("The file %s could not be read." % path) # ----------------------- # Glyph Name to File Name # ----------------------- def glyphNameToFileName(glyphName, glyphSet): """ Wrapper around the userNameToFileName function in filenames.py """ if glyphSet: existing = [name.lower() for name in list(glyphSet.contents.values())] else: existing = [] if not isinstance(glyphName, unicode): try: new = unicode(glyphName) glyphName = new except UnicodeDecodeError: pass return userNameToFileName(glyphName, existing=existing, suffix=".glif") # ----------------------- # GLIF To and From String # ----------------------- def readGlyphFromString(aString, glyphObject=None, pointPen=None, formatVersions=(1, 2)): """ Read .glif data from a string into a glyph object. The 'glyphObject' argument can be any kind of object (even None); the readGlyphFromString() method will attempt to set the following attributes on it: "width" the advance with of the glyph "height" the advance height of the glyph "unicodes" a list of unicode values for this glyph "note" a string "lib" a dictionary containing custom data "image" a dictionary containing image data "guidelines" a list of guideline data dictionaries "anchors" a list of anchor data dictionaries All attributes are optional, in two ways: 1) An attribute *won't* be set if the .glif file doesn't contain data for it. 'glyphObject' will have to deal with default values itself. 2) If setting the attribute fails with an AttributeError (for example if the 'glyphObject' attribute is read- only), readGlyphFromString() will not propagate that exception, but ignore that attribute. To retrieve outline information, you need to pass an object conforming to the PointPen protocol as the 'pointPen' argument. This argument may be None if you don't need the outline data. The formatVersions argument defined the GLIF format versions that are allowed to be read. """ tree = _glifTreeFromString(aString) _readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions) def writeGlyphToString(glyphName, glyphObject=None, drawPointsFunc=None, writer=None, formatVersion=2): """ Return .glif data for a glyph as a UTF-8 encoded string. The 'glyphObject' argument can be any kind of object (even None); the writeGlyphToString() method will attempt to get the following attributes from it: "width" the advance width of the glyph "height" the advance height of the glyph "unicodes" a list of unicode values for this glyph "note" a string "lib" a dictionary containing custom data "image" a dictionary containing image data "guidelines" a list of guideline data dictionaries "anchors" a list of anchor data dictionaries All attributes are optional: if 'glyphObject' doesn't have the attribute, it will simply be skipped. To write outline data to the .glif file, writeGlyphToString() needs a function (any callable object actually) that will take one argument: an object that conforms to the PointPen protocol. The function will be called by writeGlyphToString(); it has to call the proper PointPen methods to transfer the outline to the .glif file. The GLIF format version can be specified with the formatVersion argument. """ if writer is None: try: from fontTools.misc.xmlWriter import XMLWriter except ImportError: # try the other location from xmlWriter import XMLWriter aFile = BytesIO() writer = XMLWriter(aFile, encoding="UTF-8") else: aFile = None identifiers = set() # start if not isinstance(glyphName, basestring): raise GlifLibError("The glyph name is not properly formatted.") if len(glyphName) == 0: raise GlifLibError("The glyph name is empty.") writer.begintag("glyph", [("name", glyphName), ("format", formatVersion)]) writer.newline() # advance _writeAdvance(glyphObject, writer) # unicodes if getattr(glyphObject, "unicodes", None): _writeUnicodes(glyphObject, writer) # note if getattr(glyphObject, "note", None): _writeNote(glyphObject, writer) # image if formatVersion >= 2 and getattr(glyphObject, "image", None): _writeImage(glyphObject, writer) # guidelines if formatVersion >= 2 and getattr(glyphObject, "guidelines", None): _writeGuidelines(glyphObject, writer, identifiers) # anchors anchors = getattr(glyphObject, "anchors", None) if formatVersion >= 2 and anchors: _writeAnchors(glyphObject, writer, identifiers) # outline if drawPointsFunc is not None: writer.begintag("outline") writer.newline() pen = GLIFPointPen(writer, identifiers=identifiers) drawPointsFunc(pen) if formatVersion == 1 and anchors: _writeAnchorsFormat1(pen, anchors) writer.endtag("outline") writer.newline() # lib if getattr(glyphObject, "lib", None): _writeLib(glyphObject, writer) # end writer.endtag("glyph") writer.newline() # return the appropriate value if aFile is not None: return aFile.getvalue().decode("utf-8") else: return None def _writeAdvance(glyphObject, writer): width = getattr(glyphObject, "width", None) if width is not None: if not isinstance(width, (int, float)): raise GlifLibError("width attribute must be int or float") if width == 0: width = None height = getattr(glyphObject, "height", None) if height is not None: if not isinstance(height, (int, float)): raise GlifLibError("height attribute must be int or float") if height == 0: height = None if width is not None and height is not None: writer.simpletag("advance", width=repr(width), height=repr(height)) writer.newline() elif width is not None: writer.simpletag("advance", width=repr(width)) writer.newline() elif height is not None: writer.simpletag("advance", height=repr(height)) writer.newline() def _writeUnicodes(glyphObject, writer): unicodes = getattr(glyphObject, "unicodes", None) if isinstance(unicodes, int): unicodes = [unicodes] seen = set() for code in unicodes: if not isinstance(code, int): raise GlifLibError("unicode values must be int") if code in seen: continue seen.add(code) hexCode = "%04X" % code writer.simpletag("unicode", hex=hexCode) writer.newline() def _writeNote(glyphObject, writer): note = getattr(glyphObject, "note", None) if not isinstance(note, basestring): raise GlifLibError("note attribute must be str or unicode") note = note.encode("utf-8") writer.begintag("note") writer.newline() for line in note.splitlines(): writer.write(line.strip()) writer.newline() writer.endtag("note") writer.newline() def _writeImage(glyphObject, writer): image = getattr(glyphObject, "image", None) if not imageValidator(image): raise GlifLibError("image attribute must be a dict or dict-like object with the proper structure.") attrs = [ ("fileName", image["fileName"]) ] for attr, default in _transformationInfo: value = image.get(attr, default) if value != default: attrs.append((attr, repr(value))) color = image.get("color") if color is not None: attrs.append(("color", color)) writer.simpletag("image", attrs) writer.newline() def _writeGuidelines(glyphObject, writer, identifiers): guidelines = getattr(glyphObject, "guidelines", []) if not guidelinesValidator(guidelines): raise GlifLibError("guidelines attribute does not have the proper structure.") for guideline in guidelines: attrs = [] x = guideline.get("x") if x is not None: attrs.append(("x", repr(x))) y = guideline.get("y") if y is not None: attrs.append(("y", repr(y))) angle = guideline.get("angle") if angle is not None: attrs.append(("angle", repr(angle))) name = guideline.get("name") if name is not None: attrs.append(("name", name)) color = guideline.get("color") if color is not None: attrs.append(("color", color)) identifier = guideline.get("identifier") if identifier is not None: if identifier in identifiers: raise GlifLibError("identifier used more than once: %s" % identifier) attrs.append(("identifier", identifier)) identifiers.add(identifier) writer.simpletag("guideline", attrs) writer.newline() def _writeAnchorsFormat1(pen, anchors): if not anchorsValidator(anchors): raise GlifLibError("anchors attribute does not have the proper structure.") for anchor in anchors: attrs = [] x = anchor["x"] attrs.append(("x", repr(x))) y = anchor["y"] attrs.append(("y", repr(y))) name = anchor.get("name") if name is not None: attrs.append(("name", name)) pen.beginPath() pen.addPoint((x, y), segmentType="move", name=name) pen.endPath() def _writeAnchors(glyphObject, writer, identifiers): anchors = getattr(glyphObject, "anchors", []) if not anchorsValidator(anchors): raise GlifLibError("anchors attribute does not have the proper structure.") for anchor in anchors: attrs = [] x = anchor["x"] attrs.append(("x", repr(x))) y = anchor["y"] attrs.append(("y", repr(y))) name = anchor.get("name") if name is not None: attrs.append(("name", name)) color = anchor.get("color") if color is not None: attrs.append(("color", color)) identifier = anchor.get("identifier") if identifier is not None: if identifier in identifiers: raise GlifLibError("identifier used more than once: %s" % identifier) attrs.append(("identifier", identifier)) identifiers.add(identifier) writer.simpletag("anchor", attrs) writer.newline() def _writeLib(glyphObject, writer): lib = getattr(glyphObject, "lib", None) valid, message = glyphLibValidator(lib) if not valid: raise GlifLibError(message) if not isinstance(lib, dict): lib = dict(lib) writer.begintag("lib") writer.newline() plistWriter = PlistWriter(writer.file, indentLevel=writer.indentlevel, indent=writer.indentwhite, writeHeader=False) plistWriter.writeValue(lib) writer.endtag("lib") writer.newline() # ----------------------- # layerinfo.plist Support # ----------------------- layerInfoVersion3ValueData = { "color" : dict(type=basestring, valueValidator=colorValidator), "lib" : dict(type=dict, valueValidator=genericTypeValidator) } def validateLayerInfoVersion3ValueForAttribute(attr, value): """ This performs very basic validation of the value for attribute following the UFO 3 fontinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the value is of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ if attr not in layerInfoVersion3ValueData: return False dataValidationDict = layerInfoVersion3ValueData[attr] valueType = dataValidationDict.get("type") validator = dataValidationDict.get("valueValidator") valueOptions = dataValidationDict.get("valueOptions") # have specific options for the validator if valueOptions is not None: isValidValue = validator(value, valueOptions) # no specific options else: if validator == genericTypeValidator: isValidValue = validator(value, valueType) else: isValidValue = validator(value) return isValidValue def validateLayerInfoVersion3Data(infoData): """ This performs very basic validation of the value for infoData following the UFO 3 layerinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the values are of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ validInfoData = {} for attr, value in list(infoData.items()): if attr not in layerInfoVersion3ValueData: raise GlifLibError("Unknown attribute %s." % attr) isValidValue = validateLayerInfoVersion3ValueForAttribute(attr, value) if not isValidValue: raise GlifLibError("Invalid value for attribute %s (%s)." % (attr, repr(value))) else: validInfoData[attr] = value return validInfoData # ----------------- # GLIF Tree Support # ----------------- def _glifTreeFromFile(aFile): root = ElementTree.parse(aFile).getroot() if root.tag != "glyph": raise GlifLibError("The GLIF is not properly formatted.") if root.text and root.text.strip() != '': raise GlifLibError("Invalid GLIF structure.") return root def _glifTreeFromString(aString): root = ElementTree.fromstring(aString) if root.tag != "glyph": raise GlifLibError("The GLIF is not properly formatted.") if root.text and root.text.strip() != '': raise GlifLibError("Invalid GLIF structure.") return root def _readGlyphFromTree(tree, glyphObject=None, pointPen=None, formatVersions=(1, 2)): # check the format version formatVersion = tree.get("format") if formatVersion is None: raise GlifLibError("Unspecified format version in GLIF.") try: v = int(formatVersion) formatVersion = v except ValueError: pass if formatVersion not in formatVersions: raise GlifLibError("Forbidden GLIF format version: %s" % formatVersion) if formatVersion == 1: _readGlyphFromTreeFormat1(tree=tree, glyphObject=glyphObject, pointPen=pointPen) elif formatVersion == 2: _readGlyphFromTreeFormat2(tree=tree, glyphObject=glyphObject, pointPen=pointPen) else: raise GlifLibError("Unsupported GLIF format version: %s" % formatVersion) def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None): # get the name _readName(glyphObject, tree) # populate the sub elements unicodes = [] haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False for element in tree: if element.tag == "outline": if haveSeenOutline: raise GlifLibError("The outline element occurs more than once.") if element.attrib: raise GlifLibError("The outline element contains unknown attributes.") if element.text and element.text.strip() != '': raise GlifLibError("Invalid outline structure.") haveSeenOutline = True buildOutlineFormat1(glyphObject, pointPen, element) elif glyphObject is None: continue elif element.tag == "advance": if haveSeenAdvance: raise GlifLibError("The advance element occurs more than once.") haveSeenAdvance = True _readAdvance(glyphObject, element) elif element.tag == "unicode": try: v = element.get("hex") v = int(v, 16) if v not in unicodes: unicodes.append(v) except ValueError: raise GlifLibError("Illegal value for hex attribute of unicode element.") elif element.tag == "note": if haveSeenNote: raise GlifLibError("The note element occurs more than once.") haveSeenNote = True _readNote(glyphObject, element) elif element.tag == "lib": if haveSeenLib: raise GlifLibError("The lib element occurs more than once.") haveSeenLib = True _readLib(glyphObject, element) else: raise GlifLibError("Unknown element in GLIF: %s" % element) # set the collected unicodes if unicodes: _relaxedSetattr(glyphObject, "unicodes", unicodes) def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None): # get the name _readName(glyphObject, tree) # populate the sub elements unicodes = [] guidelines = [] anchors = [] haveSeenAdvance = haveSeenImage = haveSeenOutline = haveSeenLib = haveSeenNote = False identifiers = set() for element in tree: if element.tag == "outline": if haveSeenOutline: raise GlifLibError("The outline element occurs more than once.") if element.attrib: raise GlifLibError("The outline element contains unknown attributes.") if element.text and element.text.strip() != '': raise GlifLibError("Invalid outline structure.") haveSeenOutline = True if pointPen is not None: buildOutlineFormat2(glyphObject, pointPen, element, identifiers) elif glyphObject is None: continue elif element.tag == "advance": if haveSeenAdvance: raise GlifLibError("The advance element occurs more than once.") haveSeenAdvance = True _readAdvance(glyphObject, element) elif element.tag == "unicode": try: v = element.get("hex") v = int(v, 16) if v not in unicodes: unicodes.append(v) except ValueError: raise GlifLibError("Illegal value for hex attribute of unicode element.") elif element.tag == "guideline": if len(element): raise GlifLibError("Unknown children in guideline element.") for attr in ("x", "y", "angle"): if attr in element.attrib: element.attrib[attr] = _number(element.attrib[attr]) guidelines.append(element.attrib) elif element.tag == "anchor": if len(element): raise GlifLibError("Unknown children in anchor element.") for attr in ("x", "y"): if attr in element.attrib: element.attrib[attr] = _number(element.attrib[attr]) anchors.append(element.attrib) elif element.tag == "image": if haveSeenImage: raise GlifLibError("The image element occurs more than once.") if len(element): raise GlifLibError("Unknown children in image element.") haveSeenImage = True _readImage(glyphObject, element) elif element.tag == "note": if haveSeenNote: raise GlifLibError("The note element occurs more than once.") haveSeenNote = True _readNote(glyphObject, element) elif element.tag == "lib": if haveSeenLib: raise GlifLibError("The lib element occurs more than once.") haveSeenLib = True _readLib(glyphObject, element) else: raise GlifLibError("Unknown element in GLIF: %s" % element) # set the collected unicodes if unicodes: _relaxedSetattr(glyphObject, "unicodes", unicodes) # set the collected guidelines if guidelines: if not guidelinesValidator(guidelines, identifiers): raise GlifLibError("The guidelines are improperly formatted.") _relaxedSetattr(glyphObject, "guidelines", guidelines) # set the collected anchors if anchors: if not anchorsValidator(anchors, identifiers): raise GlifLibError("The anchors are improperly formatted.") _relaxedSetattr(glyphObject, "anchors", anchors) def _readName(glyphObject, root): glyphName = root.get("name") if not glyphName: raise GlifLibError("Empty glyph name in GLIF.") if glyphName and glyphObject is not None: _relaxedSetattr(glyphObject, "name", glyphName) def _readAdvance(glyphObject, advance): width = _number(advance.get("width", 0)) _relaxedSetattr(glyphObject, "width", width) height = _number(advance.get("height", 0)) _relaxedSetattr(glyphObject, "height", height) def _readNote(glyphObject, note): lines = note.text.split("\n") note = "\n".join(line.strip() for line in lines if line.strip()) _relaxedSetattr(glyphObject, "note", note) def _readLib(glyphObject, lib): assert len(lib) == 1 child = lib[0] plist = readPlistFromTree(child) valid, message = glyphLibValidator(plist) if not valid: raise GlifLibError(message) _relaxedSetattr(glyphObject, "lib", plist) def _readImage(glyphObject, image): imageData = image.attrib for attr, default in _transformationInfo: value = imageData.get(attr, default) imageData[attr] = _number(value) if not imageValidator(imageData): raise GlifLibError("The image element is not properly formatted.") _relaxedSetattr(glyphObject, "image", imageData) # ---------------- # GLIF to PointPen # ---------------- contourAttributesFormat2 = set(["identifier"]) componentAttributesFormat1 = set(["base", "xScale", "xyScale", "yxScale", "yScale", "xOffset", "yOffset"]) componentAttributesFormat2 = componentAttributesFormat1 | set(["identifier"]) pointAttributesFormat1 = set(["x", "y", "type", "smooth", "name"]) pointAttributesFormat2 = pointAttributesFormat1 | set(["identifier"]) pointSmoothOptions = set(("no", "yes")) pointTypeOptions = set(["move", "line", "offcurve", "curve", "qcurve"]) # format 1 def buildOutlineFormat1(glyphObject, pen, outline): anchors = [] for element in outline: if element.tag == "contour": if len(element) == 1: point = element[0] if point.tag == "point": anchor = _buildAnchorFormat1(point) if anchor is not None: anchors.append(anchor) continue if pen is not None: _buildOutlineContourFormat1(pen, element) elif element.tag == "component": if pen is not None: _buildOutlineComponentFormat1(pen, element) else: raise GlifLibError("Unknown element in outline element: %s" % element) if glyphObject is not None and anchors: if not anchorsValidator(anchors): raise GlifLibError("GLIF 1 anchors are not properly formatted.") _relaxedSetattr(glyphObject, "anchors", anchors) def _buildAnchorFormat1(point): if point.get("type") != "move": return None x = point.get("x") y = point.get("y") if x is None: raise GlifLibError("Required x attribute is missing in point element.") if y is None: raise GlifLibError("Required y attribute is missing in point element.") x = _number(x) y = _number(y) name = point.get("name") anchor = dict(x=x, y=y, name=name) return anchor def _buildOutlineContourFormat1(pen, contour): if contour.attrib: raise GlifLibError("Unknown attributes in contour element.") pen.beginPath() if len(contour): _validateAndMassagePointStructures(contour, pointAttributesFormat1, openContourOffCurveLeniency=True) _buildOutlinePointsFormat1(pen, contour) pen.endPath() def _buildOutlinePointsFormat1(pen, contour): for index, element in enumerate(contour): x = element.attrib["x"] y = element.attrib["y"] segmentType = element.attrib["segmentType"] smooth = element.attrib["smooth"] name = element.attrib["name"] pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name) def _buildOutlineComponentFormat1(pen, component): if len(component): raise GlifLibError("Unknown child elements of component element.") if set(component.attrib.keys()) - componentAttributesFormat1: raise GlifLibError("Unknown attributes in component element.") baseGlyphName = component.get("base") if baseGlyphName is None: raise GlifLibError("The base attribute is not defined in the component.") transformation = [] for attr, default in _transformationInfo: value = component.get(attr) if value is None: value = default else: value = _number(value) transformation.append(value) pen.addComponent(baseGlyphName, tuple(transformation)) # format 2 def buildOutlineFormat2(glyphObject, pen, outline, identifiers): for element in outline: if element.tag == "contour": _buildOutlineContourFormat2(pen, element, identifiers) elif element.tag == "component": _buildOutlineComponentFormat2(pen, element, identifiers) else: raise GlifLibError("Unknown element in outline element: %s" % element.tag) def _buildOutlineContourFormat2(pen, contour, identifiers): if set(contour.attrib.keys()) - contourAttributesFormat2: raise GlifLibError("Unknown attributes in contour element.") identifier = contour.get("identifier") if identifier is not None: if identifier in identifiers: raise GlifLibError("The identifier %s is used more than once." % identifier) if not identifierValidator(identifier): raise GlifLibError("The contour identifier %s is not valid." % identifier) identifiers.add(identifier) try: pen.beginPath(identifier=identifier) except TypeError: pen.beginPath() warn("The beginPath method needs an identifier kwarg. The contour's identifier value has been discarded.", DeprecationWarning) if len(contour): _validateAndMassagePointStructures(contour, pointAttributesFormat2) _buildOutlinePointsFormat2(pen, contour, identifiers) pen.endPath() def _buildOutlinePointsFormat2(pen, contour, identifiers): for index, element in enumerate(contour): x = element.attrib["x"] y = element.attrib["y"] segmentType = element.attrib["segmentType"] smooth = element.attrib["smooth"] name = element.attrib["name"] identifier = element.get("identifier") if identifier is not None: if identifier in identifiers: raise GlifLibError("The identifier %s is used more than once." % identifier) if not identifierValidator(identifier): raise GlifLibError("The identifier %s is not valid." % identifier) identifiers.add(identifier) try: pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name, identifier=identifier) except TypeError: pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name) warn("The addPoint method needs an identifier kwarg. The point's identifier value has been discarded.", DeprecationWarning) def _buildOutlineComponentFormat2(pen, component, identifiers): if len(component): raise GlifLibError("Unknown child elements of component element.") if set(component.attrib.keys()) - componentAttributesFormat2: raise GlifLibError("Unknown attributes in component element.") baseGlyphName = component.get("base") if baseGlyphName is None: raise GlifLibError("The base attribute is not defined in the component.") transformation = [] for attr, default in _transformationInfo: value = component.get(attr) if value is None: value = default else: value = _number(value) transformation.append(value) identifier = component.get("identifier") if identifier is not None: if identifier in identifiers: raise GlifLibError("The identifier %s is used more than once." % identifier) if not identifierValidator(identifier): raise GlifLibError("The identifier %s is not valid." % identifier) identifiers.add(identifier) try: pen.addComponent(baseGlyphName, tuple(transformation), identifier=identifier) except TypeError: pen.addComponent(baseGlyphName, tuple(transformation)) warn("The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.", DeprecationWarning) # all formats def _validateAndMassagePointStructures(contour, pointAttributes, openContourOffCurveLeniency=False): if not len(contour): return # store some data for later validation lastOnCurvePoint = None haveOffCurvePoint = False # validate and massage the individual point elements for index, element in enumerate(contour): # not if element.tag != "point": raise GlifLibError("Unknown child element (%s) of contour element." % element.tag) # unknown attributes unknownAttributes = [attr for attr in list(element.attrib.keys()) if attr not in pointAttributes] if unknownAttributes: raise GlifLibError("Unknown attributes in point element.") # search for unknown children if len(element): raise GlifLibError("Unknown child elements in point element.") # x and y are required x = element.get("x") y = element.get("y") if x is None: raise GlifLibError("Required x attribute is missing in point element.") if y is None: raise GlifLibError("Required y attribute is missing in point element.") element.attrib["x"] = _number(x) element.attrib["y"] = _number(y) # segment type pointType = element.attrib.pop("type", "offcurve") if pointType not in pointTypeOptions: raise GlifLibError("Unknown point type: %s" % pointType) if pointType == "offcurve": pointType = None element.attrib["segmentType"] = pointType if pointType is None: haveOffCurvePoint = True else: lastOnCurvePoint = index # move can only occur as the first point if pointType == "move" and index != 0: raise GlifLibError("A move point occurs after the first point in the contour.") # smooth is optional smooth = element.get("smooth", "no") if smooth is not None: if smooth not in pointSmoothOptions: raise GlifLibError("Unknown point smooth value: %s" % smooth) smooth = smooth == "yes" element.attrib["smooth"] = smooth # smooth can only be applied to curve and qcurve if smooth and pointType is None: raise GlifLibError("smooth attribute set in an offcurve point.") # name is optional if "name" not in element.attrib: element.attrib["name"] = None if openContourOffCurveLeniency: # remove offcurves that precede a move. this is technically illegal, # but we let it slide because there are fonts out there in the wild like this. if contour[0].attrib["segmentType"] == "move": for element in reversed(contour): if element.attrib["segmentType"] is None: contour.remove(element) else: break # validate the off-curves in the segments if haveOffCurvePoint and lastOnCurvePoint is not None: # we only care about how many offCurves there are before an onCurve # filter out the trailing offCurves offCurvesCount = len(contour) - 1 - lastOnCurvePoint stripedContour = contour[:-offCurvesCount] if offCurvesCount else contour for element in stripedContour: segmentType = element.attrib["segmentType"] if segmentType is None: offCurvesCount += 1 else: if offCurvesCount: # move and line can't be preceded by off-curves if segmentType == "move": # this will have been filtered out already raise GlifLibError("move can not have an offcurve.") elif segmentType == "line": raise GlifLibError("line can not have an offcurve.") elif segmentType == "curve": if offCurvesCount > 2: raise GlifLibError("Too many offcurves defined for curve.") elif segmentType == "qcurve": pass else: # unknown segment type. it'll be caught later. pass offCurvesCount = 0 # --------------------- # Misc Helper Functions # --------------------- def _relaxedSetattr(object, attr, value): try: setattr(object, attr, value) except AttributeError: pass def _number(s): """ Given a numeric string, return an integer or a float, whichever the string indicates. _number("1") will return the integer 1, _number("1.0") will return the float 1.0. >>> _number("1") 1 >>> _number("1.0") 1.0 >>> _number("a") # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... GlifLibError: Could not convert a to an int or float. """ try: n = int(s) return n except ValueError: pass try: n = float(s) return n except ValueError: raise GlifLibError("Could not convert %s to an int or float." % s) # -------------------- # Rapid Value Fetching # -------------------- # base class _DoneParsing(Exception): pass class _BaseParser(object): def __init__(self): self._elementStack = [] def parse(self, text): from xml.parsers.expat import ParserCreate parser = ParserCreate() parser.StartElementHandler = self.startElementHandler parser.EndElementHandler = self.endElementHandler parser.Parse(text) def startElementHandler(self, name, attrs): self._elementStack.append(name) def endElementHandler(self, name): other = self._elementStack.pop(-1) assert other == name # unicodes def _fetchUnicodes(glif): """ Get a list of unicodes listed in glif. """ parser = _FetchUnicodesParser() parser.parse(glif) return parser.unicodes class _FetchUnicodesParser(_BaseParser): def __init__(self): self.unicodes = [] super(_FetchUnicodesParser, self).__init__() def startElementHandler(self, name, attrs): if name == "unicode" and self._elementStack and self._elementStack[-1] == "glyph": value = attrs.get("hex") if value is not None: try: value = int(value, 16) if value not in self.unicodes: self.unicodes.append(value) except ValueError: pass super(_FetchUnicodesParser, self).startElementHandler(name, attrs) # image def _fetchImageFileName(glif): """ The image file name (if any) from glif. """ parser = _FetchImageFileNameParser() try: parser.parse(glif) except _DoneParsing: pass return parser.fileName class _FetchImageFileNameParser(_BaseParser): def __init__(self): self.fileName = None super(_FetchImageFileNameParser, self).__init__() def startElementHandler(self, name, attrs): if name == "image" and self._elementStack and self._elementStack[-1] == "glyph": self.fileName = attrs.get("fileName") raise _DoneParsing super(_FetchImageFileNameParser, self).startElementHandler(name, attrs) # component references def _fetchComponentBases(glif): """ Get a list of component base glyphs listed in glif. """ parser = _FetchComponentBasesParser() try: parser.parse(glif) except _DoneParsing: pass return list(parser.bases) class _FetchComponentBasesParser(_BaseParser): def __init__(self): self.bases = [] super(_FetchComponentBasesParser, self).__init__() def startElementHandler(self, name, attrs): if name == "component" and self._elementStack and self._elementStack[-1] == "outline": base = attrs.get("base") if base is not None: self.bases.append(base) super(_FetchComponentBasesParser, self).startElementHandler(name, attrs) def endElementHandler(self, name): if name == "outline": raise _DoneParsing super(_FetchComponentBasesParser, self).endElementHandler(name) # -------------- # GLIF Point Pen # -------------- _transformationInfo = [ # field name, default value ("xScale", 1), ("xyScale", 0), ("yxScale", 0), ("yScale", 1), ("xOffset", 0), ("yOffset", 0), ] class GLIFPointPen(AbstractPointPen): """ Helper class using the PointPen protocol to write the part of .glif files. """ def __init__(self, xmlWriter, formatVersion=2, identifiers=None): if identifiers is None: identifiers = set() self.formatVersion = formatVersion self.identifiers = identifiers self.writer = xmlWriter self.prevOffCurveCount = 0 self.prevPointTypes = [] def beginPath(self, identifier=None, **kwargs): attrs = [] if identifier is not None and self.formatVersion >= 2: if identifier in self.identifiers: raise GlifLibError("identifier used more than once: %s" % identifier) if not identifierValidator(identifier): raise GlifLibError("identifier not formatted properly: %s" % identifier) attrs.append(("identifier", identifier)) self.identifiers.add(identifier) self.writer.begintag("contour", attrs) self.writer.newline() self.prevOffCurveCount = 0 def endPath(self): if self.prevPointTypes and self.prevPointTypes[0] == "move": if self.prevPointTypes[-1] == "offcurve": raise GlifLibError("open contour has loose offcurve point") self.writer.endtag("contour") self.writer.newline() self.prevPointType = None self.prevOffCurveCount = 0 self.prevPointTypes = [] def addPoint(self, pt, segmentType=None, smooth=None, name=None, identifier=None, **kwargs): attrs = [] # coordinates if pt is not None: for coord in pt: if not isinstance(coord, (int, float)): raise GlifLibError("coordinates must be int or float") attrs.append(("x", repr(pt[0]))) attrs.append(("y", repr(pt[1]))) # segment type if segmentType == "offcurve": segmentType = None if segmentType == "move" and self.prevPointTypes: raise GlifLibError("move occurs after a point has already been added to the contour.") if segmentType in ("move", "line") and self.prevPointTypes and self.prevPointTypes[-1] == "offcurve": raise GlifLibError("offcurve occurs before %s point." % segmentType) if segmentType == "curve" and self.prevOffCurveCount > 2: raise GlifLibError("too many offcurve points before curve point.") if segmentType is not None: attrs.append(("type", segmentType)) else: segmentType = "offcurve" if segmentType == "offcurve": self.prevOffCurveCount += 1 else: self.prevOffCurveCount = 0 self.prevPointTypes.append(segmentType) # smooth if smooth: if segmentType == "offcurve": raise GlifLibError("can't set smooth in an offcurve point.") attrs.append(("smooth", "yes")) # name if name is not None: attrs.append(("name", name)) # identifier if identifier is not None and self.formatVersion >= 2: if identifier in self.identifiers: raise GlifLibError("identifier used more than once: %s" % identifier) if not identifierValidator(identifier): raise GlifLibError("identifier not formatted properly: %s" % identifier) attrs.append(("identifier", identifier)) self.identifiers.add(identifier) self.writer.simpletag("point", attrs) self.writer.newline() def addComponent(self, glyphName, transformation, identifier=None, **kwargs): attrs = [("base", glyphName)] for (attr, default), value in zip(_transformationInfo, transformation): if not isinstance(value, (int, float)): raise GlifLibError("transformation values must be int or float") if value != default: attrs.append((attr, repr(value))) if identifier is not None and self.formatVersion >= 2: if identifier in self.identifiers: raise GlifLibError("identifier used more than once: %s" % identifier) if not identifierValidator(identifier): raise GlifLibError("identifier not formatted properly: %s" % identifier) attrs.append(("identifier", identifier)) self.identifiers.add(identifier) self.writer.simpletag("component", attrs) self.writer.newline() if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/Lib/ufoLib/test/0000755000000000000000000000000013174130416014336 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/test_UFO3.py0000644000000000000000000053176213174123546016507 0ustar rootroot# -*- coding: utf-8 -*- from __future__ import unicode_literals import os import shutil import unittest import tempfile from io import open from ufoLib import UFOReader, UFOWriter, UFOLibError from ufoLib.glifLib import GlifLibError from ufoLib.plistlib import readPlist, writePlist from ufoLib.test.testSupport import fontInfoVersion3 class TestInfoObject(object): pass # -------------- # fontinfo.plist # -------------- class ReadFontInfoVersion3TestCase(unittest.TestCase): def setUp(self): self.dstDir = tempfile.mktemp() os.mkdir(self.dstDir) metaInfo = { "creator": "test", "formatVersion": 3 } path = os.path.join(self.dstDir, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) def tearDown(self): shutil.rmtree(self.dstDir) def _writeInfoToPlist(self, info): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "wb") as f: writePlist(info, f) def testRead(self): originalData = dict(fontInfoVersion3) self._writeInfoToPlist(originalData) infoObject = TestInfoObject() reader = UFOReader(self.dstDir) reader.readInfo(infoObject) readData = {} for attr in list(fontInfoVersion3.keys()): readData[attr] = getattr(infoObject, attr) self.assertEqual(originalData, readData) def testGenericRead(self): # familyName info = dict(fontInfoVersion3) info["familyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleName info = dict(fontInfoVersion3) info["styleName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleMapFamilyName info = dict(fontInfoVersion3) info["styleMapFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleMapStyleName ## not a string info = dict(fontInfoVersion3) info["styleMapStyleName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion3) info["styleMapStyleName"] = "REGULAR" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # versionMajor info = dict(fontInfoVersion3) info["versionMajor"] = "1" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # versionMinor info = dict(fontInfoVersion3) info["versionMinor"] = "0" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["versionMinor"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # copyright info = dict(fontInfoVersion3) info["copyright"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # trademark info = dict(fontInfoVersion3) info["trademark"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # unitsPerEm info = dict(fontInfoVersion3) info["unitsPerEm"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["unitsPerEm"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["unitsPerEm"] = -1.0 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # descender info = dict(fontInfoVersion3) info["descender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # xHeight info = dict(fontInfoVersion3) info["xHeight"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # capHeight info = dict(fontInfoVersion3) info["capHeight"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # ascender info = dict(fontInfoVersion3) info["ascender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # italicAngle info = dict(fontInfoVersion3) info["italicAngle"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testGaspRead(self): # not a list info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # empty list info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) # not a dict info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = ["abc"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # dict not properly formatted info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=0xFFFF, notTheRightKey=1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(notTheRightKey=1, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # not an int for ppem info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM="abc", rangeGaspBehavior=[0]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # not a list for behavior info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior="abc"), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # invalid behavior value info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[-1]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # not sorted info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=10, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # no 0xFFFF info = dict(fontInfoVersion3) info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=20, rangeGaspBehavior=[0])] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) def testHeadRead(self): # openTypeHeadCreated ## not a string info = dict(fontInfoVersion3) info["openTypeHeadCreated"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## invalid format info = dict(fontInfoVersion3) info["openTypeHeadCreated"] = "2000-Jan-01 00:00:00" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHeadLowestRecPPEM info = dict(fontInfoVersion3) info["openTypeHeadLowestRecPPEM"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeHeadLowestRecPPEM"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHeadFlags info = dict(fontInfoVersion3) info["openTypeHeadFlags"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testHheaRead(self): # openTypeHheaAscender info = dict(fontInfoVersion3) info["openTypeHheaAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaDescender info = dict(fontInfoVersion3) info["openTypeHheaDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaLineGap info = dict(fontInfoVersion3) info["openTypeHheaLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretSlopeRise info = dict(fontInfoVersion3) info["openTypeHheaCaretSlopeRise"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretSlopeRun info = dict(fontInfoVersion3) info["openTypeHheaCaretSlopeRun"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretOffset info = dict(fontInfoVersion3) info["openTypeHheaCaretOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testNameRead(self): # openTypeNameDesigner info = dict(fontInfoVersion3) info["openTypeNameDesigner"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameDesignerURL info = dict(fontInfoVersion3) info["openTypeNameDesignerURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameManufacturer info = dict(fontInfoVersion3) info["openTypeNameManufacturer"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameManufacturerURL info = dict(fontInfoVersion3) info["openTypeNameManufacturerURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameLicense info = dict(fontInfoVersion3) info["openTypeNameLicense"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameLicenseURL info = dict(fontInfoVersion3) info["openTypeNameLicenseURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameVersion info = dict(fontInfoVersion3) info["openTypeNameVersion"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameUniqueID info = dict(fontInfoVersion3) info["openTypeNameUniqueID"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameDescription info = dict(fontInfoVersion3) info["openTypeNameDescription"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNamePreferredFamilyName info = dict(fontInfoVersion3) info["openTypeNamePreferredFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNamePreferredSubfamilyName info = dict(fontInfoVersion3) info["openTypeNamePreferredSubfamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameCompatibleFullName info = dict(fontInfoVersion3) info["openTypeNameCompatibleFullName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameSampleText info = dict(fontInfoVersion3) info["openTypeNameSampleText"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameWWSFamilyName info = dict(fontInfoVersion3) info["openTypeNameWWSFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameWWSSubfamilyName info = dict(fontInfoVersion3) info["openTypeNameWWSSubfamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameRecords ## not a list info = dict(fontInfoVersion3) info["openTypeNameRecords"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## not a dict info = dict(fontInfoVersion3) info["openTypeNameRecords"] = ["abc"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## invalid dict structure info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [dict(foo="bar")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## incorrect keys info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.", foo="bar") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(platformID=1, encodingID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, encodingID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1) ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## invalid values info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID="1", platformID=1, encodingID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID="1", encodingID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID="1", languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, languageID="1", string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string=1) ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## duplicate info = dict(fontInfoVersion3) info["openTypeNameRecords"] = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."), dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.") ] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) def testOS2Read(self): # openTypeOS2WidthClass ## not an int info = dict(fontInfoVersion3) info["openTypeOS2WidthClass"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out or range info = dict(fontInfoVersion3) info["openTypeOS2WidthClass"] = 15 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WeightClass info = dict(fontInfoVersion3) ## not an int info["openTypeOS2WeightClass"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info["openTypeOS2WeightClass"] = -50 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Selection info = dict(fontInfoVersion3) info["openTypeOS2Selection"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2VendorID info = dict(fontInfoVersion3) info["openTypeOS2VendorID"] = 1234 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Panose ## not an int info = dict(fontInfoVersion3) info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## negative info = dict(fontInfoVersion3) info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, -9] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too few values info = dict(fontInfoVersion3) info["openTypeOS2Panose"] = [0, 1, 2, 3] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2FamilyClass ## not an int info = dict(fontInfoVersion3) info["openTypeOS2FamilyClass"] = [1, str(1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too few values info = dict(fontInfoVersion3) info["openTypeOS2FamilyClass"] = [1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["openTypeOS2FamilyClass"] = [1, 1, 1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion3) info["openTypeOS2FamilyClass"] = [1, 201] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2UnicodeRanges ## not an int info = dict(fontInfoVersion3) info["openTypeOS2UnicodeRanges"] = ["0"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion3) info["openTypeOS2UnicodeRanges"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2CodePageRanges ## not an int info = dict(fontInfoVersion3) info["openTypeOS2CodePageRanges"] = ["0"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion3) info["openTypeOS2CodePageRanges"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoAscender info = dict(fontInfoVersion3) info["openTypeOS2TypoAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoDescender info = dict(fontInfoVersion3) info["openTypeOS2TypoDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoLineGap info = dict(fontInfoVersion3) info["openTypeOS2TypoLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WinAscent info = dict(fontInfoVersion3) info["openTypeOS2WinAscent"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeOS2WinAscent"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WinDescent info = dict(fontInfoVersion3) info["openTypeOS2WinDescent"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) info = dict(fontInfoVersion3) info["openTypeOS2WinDescent"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Type ## not an int info = dict(fontInfoVersion3) info["openTypeOS2Type"] = ["1"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion3) info["openTypeOS2Type"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptXSize info = dict(fontInfoVersion3) info["openTypeOS2SubscriptXSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptYSize info = dict(fontInfoVersion3) info["openTypeOS2SubscriptYSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptXOffset info = dict(fontInfoVersion3) info["openTypeOS2SubscriptXOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptYOffset info = dict(fontInfoVersion3) info["openTypeOS2SubscriptYOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptXSize info = dict(fontInfoVersion3) info["openTypeOS2SuperscriptXSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptYSize info = dict(fontInfoVersion3) info["openTypeOS2SuperscriptYSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptXOffset info = dict(fontInfoVersion3) info["openTypeOS2SuperscriptXOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptYOffset info = dict(fontInfoVersion3) info["openTypeOS2SuperscriptYOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2StrikeoutSize info = dict(fontInfoVersion3) info["openTypeOS2StrikeoutSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2StrikeoutPosition info = dict(fontInfoVersion3) info["openTypeOS2StrikeoutPosition"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testVheaRead(self): # openTypeVheaVertTypoAscender info = dict(fontInfoVersion3) info["openTypeVheaVertTypoAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaVertTypoDescender info = dict(fontInfoVersion3) info["openTypeVheaVertTypoDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaVertTypoLineGap info = dict(fontInfoVersion3) info["openTypeVheaVertTypoLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretSlopeRise info = dict(fontInfoVersion3) info["openTypeVheaCaretSlopeRise"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretSlopeRun info = dict(fontInfoVersion3) info["openTypeVheaCaretSlopeRun"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretOffset info = dict(fontInfoVersion3) info["openTypeVheaCaretOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testFONDRead(self): # macintoshFONDFamilyID info = dict(fontInfoVersion3) info["macintoshFONDFamilyID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # macintoshFONDName info = dict(fontInfoVersion3) info["macintoshFONDName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testPostscriptRead(self): # postscriptFontName info = dict(fontInfoVersion3) info["postscriptFontName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptFullName info = dict(fontInfoVersion3) info["postscriptFullName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptSlantAngle info = dict(fontInfoVersion3) info["postscriptSlantAngle"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptUniqueID info = dict(fontInfoVersion3) info["postscriptUniqueID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptUnderlineThickness info = dict(fontInfoVersion3) info["postscriptUnderlineThickness"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptUnderlinePosition info = dict(fontInfoVersion3) info["postscriptUnderlinePosition"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptIsFixedPitch info = dict(fontInfoVersion3) info["postscriptIsFixedPitch"] = 2 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueValues ## not a list info = dict(fontInfoVersion3) info["postscriptBlueValues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion3) info["postscriptBlueValues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptBlueValues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptOtherBlues ## not a list info = dict(fontInfoVersion3) info["postscriptOtherBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion3) info["postscriptOtherBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptFamilyBlues ## not a list info = dict(fontInfoVersion3) info["postscriptFamilyBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion3) info["postscriptFamilyBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptFamilyBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptFamilyOtherBlues ## not a list info = dict(fontInfoVersion3) info["postscriptFamilyOtherBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion3) info["postscriptFamilyOtherBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptFamilyOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptStemSnapH ## not list info = dict(fontInfoVersion3) info["postscriptStemSnapH"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptStemSnapH"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptStemSnapV ## not list info = dict(fontInfoVersion3) info["postscriptStemSnapV"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion3) info["postscriptStemSnapV"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueFuzz info = dict(fontInfoVersion3) info["postscriptBlueFuzz"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueShift info = dict(fontInfoVersion3) info["postscriptBlueShift"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueScale info = dict(fontInfoVersion3) info["postscriptBlueScale"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptForceBold info = dict(fontInfoVersion3) info["postscriptForceBold"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptDefaultWidthX info = dict(fontInfoVersion3) info["postscriptDefaultWidthX"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptNominalWidthX info = dict(fontInfoVersion3) info["postscriptNominalWidthX"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptWeightName info = dict(fontInfoVersion3) info["postscriptWeightName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptDefaultCharacter info = dict(fontInfoVersion3) info["postscriptDefaultCharacter"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptWindowsCharacterSet info = dict(fontInfoVersion3) info["postscriptWindowsCharacterSet"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # macintoshFONDFamilyID info = dict(fontInfoVersion3) info["macintoshFONDFamilyID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # macintoshFONDName info = dict(fontInfoVersion3) info["macintoshFONDName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) def testWOFFRead(self): # woffMajorVersion info = dict(fontInfoVersion3) info["woffMajorVersion"] = 1.0 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["woffMajorVersion"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMinorVersion info = dict(fontInfoVersion3) info["woffMinorVersion"] = 1.0 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["woffMinorVersion"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataUniqueID ## none info = dict(fontInfoVersion3) del info["woffMetadataUniqueID"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## not a dict info = dict(fontInfoVersion3) info["woffMetadataUniqueID"] = 1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## unknown key info = dict(fontInfoVersion3) info["woffMetadataUniqueID"] = dict(id="foo", notTheRightKey=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no id info = dict(fontInfoVersion3) info["woffMetadataUniqueID"] = dict() self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## not a string for id info = dict(fontInfoVersion3) info["woffMetadataUniqueID"] = dict(id=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## empty string info = dict(fontInfoVersion3) info["woffMetadataUniqueID"] = dict(id="") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) # woffMetadataVendor ## no name info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(url="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## name not a string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name=1, url="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## name an empty string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="", url="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## no URL info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url empty string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url="") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## have dir info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="ltr") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="rtl") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## dir not a string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url="bar", dir=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="utd") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## have class info = dict(fontInfoVersion3) info["woffMetadataVendor"] = {"name" : "foo", "url" : "bar", "class" : "hello"} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = {"name" : "foo", "url" : "bar", "class" : 1} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class empty string info = dict(fontInfoVersion3) info["woffMetadataVendor"] = {"name" : "foo", "url" : "bar", "class" : ""} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) # woffMetadataCredits ## no credits attribute info = dict(fontInfoVersion3) info["woffMetadataCredits"] = {} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## unknown attribute info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo")], notTheRightKey=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## not a list info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits="abc") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no elements in credits info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## credit not a dict info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=["abc"]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## unknown key info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo", notTheRightKey=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no name info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(url="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## name not a string info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo", url=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## role not a string info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo", role=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not a string info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo", dir=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[dict(name="foo", dir="utd")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataCredits"] = dict(credits=[{"name" : "foo", "class" : 1}]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataDescription ## no url info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo")], url=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no text info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(url="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a list info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text="abc") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item not a dict info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=["abc"]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item unknown key info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo", notTheRightKey=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item missing text info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(language="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a string info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo", url=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## language not a string info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo", language=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[dict(text="foo", dir="utd")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataDescription"] = dict(text=[{"text" : "foo", "class" : 1}]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataLicense ## no url info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo")], url=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## id not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo")], id=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no text info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(url="foo") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## text not a list info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text="abc") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item not a dict info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=["abc"]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item unknown key info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo", notTheRightKey=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item missing text info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(language="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo", url=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## language not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo", language=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[dict(text="foo", dir="utd")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataLicense"] = dict(text=[{"text" : "foo", "class" : 1}]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataCopyright ## unknown attribute info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text="foo")], notTheRightKey=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no text info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict() self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a list info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text="abc") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item not a dict info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=["abc"]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item unknown key info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text="foo", notTheRightKey=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item missing text info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(language="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a string info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text="foo", url=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## language not a string info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text="foo", language=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[dict(text="foo", dir="utd")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataCopyright"] = dict(text=[{"text" : "foo", "class" : 1}]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataTrademark ## unknown attribute info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text="foo")], notTheRightKey=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## no text info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict() self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a list info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text="abc") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item not a dict info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=["abc"]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item unknown key info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text="foo", notTheRightKey=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text item missing text info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(language="foo")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## text not a string info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## url not a string info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text="foo", url=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## language not a string info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text="foo", language=1)]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[dict(text="foo", dir="utd")]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataTrademark"] = dict(text=[{"text" : "foo", "class" : 1}]) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # woffMetadataLicensee ## no name info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict() self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## unknown attribute info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict(name="foo", notTheRightKey=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## name not a string info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict(name=1) self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## dir options info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict(name="foo", dir="ltr") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict(name="foo", dir="rtl") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## dir not ltr or rtl info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = dict(name="foo", dir="utd") self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## have class info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = {"name" : "foo", "class" : "hello"} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) reader.readInfo(TestInfoObject()) ## class not a string info = dict(fontInfoVersion3) info["woffMetadataLicensee"] = {"name" : "foo", "class" : 1} self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) def testGuidelinesRead(self): # x ## not an int or float info = dict(fontInfoVersion3) info["guidelines"] = [dict(x="1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # y ## not an int or float info = dict(fontInfoVersion3) info["guidelines"] = [dict(y="1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # angle ## < 0 info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, y=0, angle=-1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## > 360 info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, y=0, angle=361)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # name ## not a string info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, name=1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # color ## not a string info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color=1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## not enough commas info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1 0, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1 0 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1 0 0 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## not enough parts info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color=", 0, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, , 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, 0, , 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, 0, 0, ")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color=", , , ")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## not a number in all positions info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="r, 1, 1, 1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, g, 1, 1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, 1, b, 1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, 1, 1, a")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many parts info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="1, 0, 0, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## < 0 in each position info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="-1, 0, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, -1, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, 0, -1, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, 0, 0, -1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## > 1 in each position info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="2, 0, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, 2, 0, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, 0, 2, 0")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, color="0, 0, 0, 2")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # identifier ## duplicate info = dict(fontInfoVersion3) info["guidelines"] = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) class WriteFontInfoVersion3TestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def tearDownUFO(self): if os.path.exists(self.dstDir): shutil.rmtree(self.dstDir) def makeInfoObject(self): infoObject = TestInfoObject() for attr, value in list(fontInfoVersion3.items()): setattr(infoObject, attr, value) return infoObject def readPlist(self): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "rb") as f: plist = readPlist(f) return plist def testWrite(self): infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) writtenData = self.readPlist() for attr, originalValue in list(fontInfoVersion3.items()): newValue = writtenData[attr] self.assertEqual(newValue, originalValue) self.tearDownUFO() def testGenericWrite(self): # familyName infoObject = self.makeInfoObject() infoObject.familyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # styleName infoObject = self.makeInfoObject() infoObject.styleName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # styleMapFamilyName infoObject = self.makeInfoObject() infoObject.styleMapFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # styleMapStyleName ## not a string infoObject = self.makeInfoObject() infoObject.styleMapStyleName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.styleMapStyleName = "REGULAR" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # versionMajor infoObject = self.makeInfoObject() infoObject.versionMajor = "1" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # versionMinor infoObject = self.makeInfoObject() infoObject.versionMinor = "0" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # copyright infoObject = self.makeInfoObject() infoObject.copyright = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # trademark infoObject = self.makeInfoObject() infoObject.trademark = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # unitsPerEm infoObject = self.makeInfoObject() infoObject.unitsPerEm = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # descender infoObject = self.makeInfoObject() infoObject.descender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # xHeight infoObject = self.makeInfoObject() infoObject.xHeight = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # capHeight infoObject = self.makeInfoObject() infoObject.capHeight = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # ascender infoObject = self.makeInfoObject() infoObject.ascender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # italicAngle infoObject = self.makeInfoObject() infoObject.italicAngle = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testGaspWrite(self): # not a list infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # empty list infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [] writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) self.tearDownUFO() # not a dict infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = ["abc"] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # dict not properly formatted infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=0xFFFF, notTheRightKey=1)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(notTheRightKey=1, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # not an int for ppem infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM="abc", rangeGaspBehavior=[0]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # not a list for behavior infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior="abc"), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # invalid behavior value infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[-1]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # not sorted infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=10, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # no 0xFFFF infoObject = self.makeInfoObject() infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=20, rangeGaspBehavior=[0])] writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) self.tearDownUFO() def testHeadWrite(self): # openTypeHeadCreated ## not a string infoObject = self.makeInfoObject() infoObject.openTypeHeadCreated = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## invalid format infoObject = self.makeInfoObject() infoObject.openTypeHeadCreated = "2000-Jan-01 00:00:00" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHeadLowestRecPPEM infoObject = self.makeInfoObject() infoObject.openTypeHeadLowestRecPPEM = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHeadFlags infoObject = self.makeInfoObject() infoObject.openTypeHeadFlags = [-1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testHheaWrite(self): # openTypeHheaAscender infoObject = self.makeInfoObject() infoObject.openTypeHheaAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHheaDescender infoObject = self.makeInfoObject() infoObject.openTypeHheaDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHheaLineGap infoObject = self.makeInfoObject() infoObject.openTypeHheaLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHheaCaretSlopeRise infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretSlopeRise = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHheaCaretSlopeRun infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretSlopeRun = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeHheaCaretOffset infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testNameWrite(self): # openTypeNameDesigner infoObject = self.makeInfoObject() infoObject.openTypeNameDesigner = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameDesignerURL infoObject = self.makeInfoObject() infoObject.openTypeNameDesignerURL = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameManufacturer infoObject = self.makeInfoObject() infoObject.openTypeNameManufacturer = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameManufacturerURL infoObject = self.makeInfoObject() infoObject.openTypeNameManufacturerURL = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameLicense infoObject = self.makeInfoObject() infoObject.openTypeNameLicense = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameLicenseURL infoObject = self.makeInfoObject() infoObject.openTypeNameLicenseURL = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameVersion infoObject = self.makeInfoObject() infoObject.openTypeNameVersion = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameUniqueID infoObject = self.makeInfoObject() infoObject.openTypeNameUniqueID = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameDescription infoObject = self.makeInfoObject() infoObject.openTypeNameDescription = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNamePreferredFamilyName infoObject = self.makeInfoObject() infoObject.openTypeNamePreferredFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNamePreferredSubfamilyName infoObject = self.makeInfoObject() infoObject.openTypeNamePreferredSubfamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameCompatibleFullName infoObject = self.makeInfoObject() infoObject.openTypeNameCompatibleFullName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameSampleText infoObject = self.makeInfoObject() infoObject.openTypeNameSampleText = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameWWSFamilyName infoObject = self.makeInfoObject() infoObject.openTypeNameWWSFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameWWSSubfamilyName infoObject = self.makeInfoObject() infoObject.openTypeNameWWSSubfamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeNameRecords ## not a list infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not a dict infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = ["abc"] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## invalid dict structure infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [dict(foo="bar")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## incorrect keys infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.", foo="bar") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(platformID=1, encodingID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, encodingID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1) ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## invalid values infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID="1", platformID=1, encodingID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID="1", encodingID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID="1", languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, languageID="1", string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string=1) ] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## duplicate infoObject = self.makeInfoObject() infoObject.openTypeNameRecords = [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."), dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.") ] writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) def testOS2Write(self): # openTypeOS2WidthClass ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out or range infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = 15 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2WeightClass ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2WeightClass = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2WeightClass = -50 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2Selection infoObject = self.makeInfoObject() infoObject.openTypeOS2Selection = [-1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2VendorID infoObject = self.makeInfoObject() infoObject.openTypeOS2VendorID = 1234 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2Panose ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too few values infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2FamilyClass ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [0, str(1)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too few values infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1, 1, 1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1, 20] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2UnicodeRanges ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2UnicodeRanges = ["0"] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2UnicodeRanges = [-1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2CodePageRanges ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2CodePageRanges = ["0"] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2CodePageRanges = [-1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2TypoAscender infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2TypoDescender infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2TypoLineGap infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2WinAscent infoObject = self.makeInfoObject() infoObject.openTypeOS2WinAscent = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeOS2WinAscent = -1 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2WinDescent infoObject = self.makeInfoObject() infoObject.openTypeOS2WinDescent = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.openTypeOS2WinDescent = -1 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2Type ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2Type = ["1"] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2Type = [-1] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SubscriptXSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptXSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SubscriptYSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptYSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SubscriptXOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptXOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SubscriptYOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptYOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SuperscriptXSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptXSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SuperscriptYSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptYSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SuperscriptXOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptXOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2SuperscriptYOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptYOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2StrikeoutSize infoObject = self.makeInfoObject() infoObject.openTypeOS2StrikeoutSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeOS2StrikeoutPosition infoObject = self.makeInfoObject() infoObject.openTypeOS2StrikeoutPosition = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testVheaWrite(self): # openTypeVheaVertTypoAscender infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeVheaVertTypoDescender infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeVheaVertTypoLineGap infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeVheaCaretSlopeRise infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretSlopeRise = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeVheaCaretSlopeRun infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretSlopeRun = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # openTypeVheaCaretOffset infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testFONDWrite(self): # macintoshFONDFamilyID infoObject = self.makeInfoObject() infoObject.macintoshFONDFamilyID = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # macintoshFONDName infoObject = self.makeInfoObject() infoObject.macintoshFONDName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testPostscriptWrite(self): # postscriptFontName infoObject = self.makeInfoObject() infoObject.postscriptFontName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptFullName infoObject = self.makeInfoObject() infoObject.postscriptFullName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptSlantAngle infoObject = self.makeInfoObject() infoObject.postscriptSlantAngle = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptUniqueID infoObject = self.makeInfoObject() infoObject.postscriptUniqueID = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptUnderlineThickness infoObject = self.makeInfoObject() infoObject.postscriptUnderlineThickness = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptUnderlinePosition infoObject = self.makeInfoObject() infoObject.postscriptUnderlinePosition = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptIsFixedPitch infoObject = self.makeInfoObject() infoObject.postscriptIsFixedPitch = 2 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptBlueValues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = [500] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptOtherBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptFamilyBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptFamilyOtherBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptStemSnapH ## not list infoObject = self.makeInfoObject() infoObject.postscriptStemSnapH = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptStemSnapH = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptStemSnapV ## not list infoObject = self.makeInfoObject() infoObject.postscriptStemSnapV = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many values infoObject = self.makeInfoObject() infoObject.postscriptStemSnapV = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptBlueFuzz infoObject = self.makeInfoObject() infoObject.postscriptBlueFuzz = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptBlueShift infoObject = self.makeInfoObject() infoObject.postscriptBlueShift = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptBlueScale infoObject = self.makeInfoObject() infoObject.postscriptBlueScale = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptForceBold infoObject = self.makeInfoObject() infoObject.postscriptForceBold = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptDefaultWidthX infoObject = self.makeInfoObject() infoObject.postscriptDefaultWidthX = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptNominalWidthX infoObject = self.makeInfoObject() infoObject.postscriptNominalWidthX = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptWeightName infoObject = self.makeInfoObject() infoObject.postscriptWeightName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptDefaultCharacter infoObject = self.makeInfoObject() infoObject.postscriptDefaultCharacter = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # postscriptWindowsCharacterSet infoObject = self.makeInfoObject() infoObject.postscriptWindowsCharacterSet = -1 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # macintoshFONDFamilyID infoObject = self.makeInfoObject() infoObject.macintoshFONDFamilyID = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # macintoshFONDName infoObject = self.makeInfoObject() infoObject.macintoshFONDName = 123 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testWOFFWrite(self): # woffMajorVersion infoObject = self.makeInfoObject() infoObject.woffMajorVersion = 1.0 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.woffMajorVersion = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMinorVersion infoObject = self.makeInfoObject() infoObject.woffMinorVersion = 1.0 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.woffMinorVersion = "abc" writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataUniqueID ## none infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = None writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## not a dict infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = 1 writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## unknown key infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = dict(id="foo", notTheRightKey=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no id infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = dict() writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not a string for id infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = dict(id=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## empty string infoObject = self.makeInfoObject() infoObject.woffMetadataUniqueID = dict(id="") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() # woffMetadataVendor ## no name infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(url="foo") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## name not a string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name=1, url="foo") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## name an empty string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="", url="foo") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## no URL infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url empty string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url="") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## have dir infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="ltr") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="rtl") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## dir not a string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="utd") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## have class infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = {"name" : "foo", "url" : "bar", "class" : "hello"} writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = {"name" : "foo", "url" : "bar", "class" : 1} writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class empty string infoObject = self.makeInfoObject() infoObject.woffMetadataVendor = {"name" : "foo", "url" : "bar", "class" : ""} writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() # woffMetadataCredits ## no credits attribute infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = {} writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## unknown attribute infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo")], notTheRightKey=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not a list infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits="abc") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no elements in credits infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## credit not a dict infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=["abc"]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## unknown key infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", notTheRightKey=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no name infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(url="foo")]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## name not a string infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", url=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## role not a string infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", role=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not a string infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", dir=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", dir="utd")]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() infoObject.woffMetadataCredits = dict(credits=[{"name" : "foo", "class" : 1}]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataDescription ## no url infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo")]) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo")], url=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no text infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(url="foo") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a list infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text="abc") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item not a dict infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=["abc"]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item unknown key infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo", notTheRightKey=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item missing text infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(language="foo")]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a string infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo", url=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## language not a string infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo", language=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[dict(text="foo", dir="utd")]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() infoObject.woffMetadataDescription = dict(text=[{"text" : "foo", "class" : 1}]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataLicense ## no url infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text=[dict(text="foo")]) writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text=[dict(text="foo")], url=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## id not a string infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text=[dict(text="foo")], id=1) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no text infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(url="foo") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(TestInfoObject()) self.tearDownUFO() ## text not a list infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text="abc") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item not a dict infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text=["abc"]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item unknown key infoObject = self.makeInfoObject() infoObject.woffMetadataLicense = dict(text=[dict(text="foo", notTheRightKey=1)]) writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item missing text infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[dict(language="foo")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[dict(text=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[dict(text="foo", url=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## language not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[dict(text="foo", language=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[dict(text="foo", dir="utd")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicense = dict(text=[{"text" : "foo", "class" : 1}]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataCopyright ## unknown attribute infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text="foo")], notTheRightKey=1) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no text infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict() self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a list infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text="abc") self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item not a dict infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=["abc"]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item unknown key infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", notTheRightKey=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item missing text infoObject = self.makeInfoObject() infoObject.woffMetadataCopyright = dict(text=[dict(language="foo")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", url=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## language not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", language=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", dir="utd")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataCopyright = dict(text=[{"text" : "foo", "class" : 1}]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataTrademark ## unknown attribute infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text="foo")], notTheRightKey=1) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## no text infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict() self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a list infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text="abc") self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item not a dict infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=["abc"]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item unknown key infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", notTheRightKey=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text item missing text infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(language="foo")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## text not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## url not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", url=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## language not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", language=1)]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", dir="utd")]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataTrademark = dict(text=[{"text" : "foo", "class" : 1}]) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # woffMetadataLicensee ## no name infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicensee = dict() self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## unknown attribute infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicensee = dict(name="foo", notTheRightKey=1) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## name not a string infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=3) infoObject.woffMetadataLicensee = dict(name=1) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## dir options infoObject = self.makeInfoObject() infoObject.woffMetadataLicensee = dict(name="foo", dir="ltr") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.woffMetadataLicensee = dict(name="foo", dir="rtl") writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) self.tearDownUFO() ## dir not ltr or rtl infoObject = self.makeInfoObject() infoObject.woffMetadataLicensee = dict(name="foo", dir="utd") writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## have class infoObject = self.makeInfoObject() infoObject.woffMetadataLicensee = {"name" : "foo", "class" : "hello"} writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeInfo(infoObject) self.tearDownUFO() ## class not a string infoObject = self.makeInfoObject() infoObject.woffMetadataLicensee = {"name" : "foo", "class" : 1} writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() def testGuidelinesWrite(self): # x ## not an int or float infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x="1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # y ## not an int or float infoObject = self.makeInfoObject() infoObject.guidelines = [dict(y="1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # angle ## < 0 infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, y=0, angle=-1)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## > 360 infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, y=0, angle=361)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # name ## not a string infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, name=1)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # color ## not a string infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color=1)] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not enough commas infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1 0, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1 0 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1 0 0 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not enough parts infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color=", 0, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, , 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, 0, , 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, 0, 0, ")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color=", , , ")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## not a number in all positions infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="r, 1, 1, 1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, g, 1, 1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, 1, b, 1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, 1, 1, a")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## too many parts infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="1, 0, 0, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## < 0 in each position infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="-1, 0, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, -1, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, 0, -1, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, 0, 0, -1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## > 1 in each position infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="2, 0, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, 2, 0, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, 0, 2, 0")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, color="0, 0, 0, 2")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # identifier ## duplicate infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## below min infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, identifier="\0x1F")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() ## above max infoObject = self.makeInfoObject() infoObject.guidelines = [dict(x=0, identifier="\0x7F")] writer = UFOWriter(self.dstDir, formatVersion=3) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) self.tearDownUFO() # ------ # layers # ------ class UFO3ReadLayersTestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeUFO(self, metaInfo=None, layerContents=None): self.clearUFO() if not os.path.exists(self.ufoPath): os.mkdir(self.ufoPath) # metainfo.plist if metaInfo is None: metaInfo = dict(creator="test", formatVersion=3) path = os.path.join(self.ufoPath, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) # layers if layerContents is None: layerContents = [ ("public.default", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2"), ] if layerContents: path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "wb") as f: writePlist(layerContents, f) else: layerContents = [("", "glyphs")] for name, directory in layerContents: glyphsPath = os.path.join(self.ufoPath, directory) os.mkdir(glyphsPath) contents = dict(a="a.glif") path = os.path.join(glyphsPath, "contents.plist") with open(path, "wb") as f: writePlist(contents, f) path = os.path.join(glyphsPath, "a.glif") with open(path, "w") as f: f.write(" ") def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) # valid def testValidRead(self): # UFO 1 self.makeUFO( metaInfo=dict(creator="test", formatVersion=1), layerContents=dict() ) reader = UFOReader(self.ufoPath) reader.getGlyphSet() # UFO 2 self.makeUFO( metaInfo=dict(creator="test", formatVersion=2), layerContents=dict() ) reader = UFOReader(self.ufoPath) reader.getGlyphSet() # UFO 3 self.makeUFO() reader = UFOReader(self.ufoPath) reader.getGlyphSet() # missing layer contents def testMissingLayerContents(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # layer contents invalid format def testInvalidLayerContentsFormat(self): # bogus self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) with open(path, "w") as f: f.write("test") reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # dict self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = { "public.default" : "glyphs", "layer 1" : "glyphs.layer 1", "layer 2" : "glyphs.layer 2", } with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # layer contents invalid name format def testInvalidLayerContentsNameFormat(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ (1, "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # layer contents invalid directory format def testInvalidLayerContentsDirectoryFormat(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", 1), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # directory listed in contents not on disk def testLayerContentsHasMissingDirectory(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.doesnotexist"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # # directory on disk not listed in contents # XXX should this raise an error? # # def testLayerContentsHasMissingDirectory(self): # self.makeUFO() # path = os.path.join(self.ufoPath, "layercontents.plist") # os.remove(path) # layerContents = [ # ("public.foregound", "glyphs"), # ("layer 1", "glyphs.layer 2") # ] # with open(path, "wb") as f: # writePlist(layerContents, f) # reader = UFOReader(self.ufoPath) # with self.assertRaises(UFOLibError): # reader.getGlyphSet() # no default layer on disk def testMissingDefaultLayer(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # duplicate layer name def testDuplicateLayerName(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 1", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # directory referenced by two layer names def testDuplicateLayerDirectory(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 1") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) self.assertRaises(UFOLibError, reader.getGlyphSet) # default without a name def testDefaultLayerNoName(self): # get the glyph set self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) reader.getGlyphSet() # default with a name def testDefaultLayerName(self): # get the name self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("custom name", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] expected = layerContents[0][0] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) result = reader.getDefaultLayerName() self.assertEqual(expected, result) # get the glyph set self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("custom name", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) reader.getGlyphSet(expected) # layer order def testLayerOrder(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] expected = [name for (name, directory) in layerContents] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) result = reader.getLayerNames() self.assertEqual(expected, result) self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("layer 1", "glyphs.layer 1"), ("public.foregound", "glyphs"), ("layer 2", "glyphs.layer 2") ] expected = [name for (name, directory) in layerContents] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) result = reader.getLayerNames() self.assertEqual(expected, result) self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("layer 2", "glyphs.layer 2"), ("layer 1", "glyphs.layer 1"), ("public.foregound", "glyphs") ] expected = [name for (name, directory) in layerContents] with open(path, "wb") as f: writePlist(layerContents, f) reader = UFOReader(self.ufoPath) result = reader.getLayerNames() self.assertEqual(expected, result) class UFO3WriteLayersTestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeUFO(self, metaInfo=None, layerContents=None): self.clearUFO() if not os.path.exists(self.ufoPath): os.mkdir(self.ufoPath) # metainfo.plist if metaInfo is None: metaInfo = dict(creator="test", formatVersion=3) path = os.path.join(self.ufoPath, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) # layers if layerContents is None: layerContents = [ ("public.default", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2"), ] if layerContents: path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "wb") as f: writePlist(layerContents, f) else: layerContents = [("", "glyphs")] for name, directory in layerContents: glyphsPath = os.path.join(self.ufoPath, directory) os.mkdir(glyphsPath) contents = dict(a="a.glif") path = os.path.join(glyphsPath, "contents.plist") with open(path, "wb") as f: writePlist(contents, f) path = os.path.join(glyphsPath, "a.glif") with open(path, "w") as f: f.write(" ") def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) # __init__: missing layer contents def testMissingLayerContents(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: layer contents invalid format def testInvalidLayerContentsFormat(self): # bogus self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) with open(path, "w") as f: f.write("test") self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # dict self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = { "public.default" : "glyphs", "layer 1" : "glyphs.layer 1", "layer 2" : "glyphs.layer 2", } with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: layer contents invalid name format def testInvalidLayerContentsNameFormat(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ (1, "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: layer contents invalid directory format def testInvalidLayerContentsDirectoryFormat(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", 1), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: directory listed in contents not on disk def testLayerContentsHasMissingDirectory(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.doesnotexist"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: no default layer on disk def testMissingDefaultLayer(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: duplicate layer name def testDuplicateLayerName(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 1", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: directory referenced by two layer names def testDuplicateLayerDirectory(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 1") ] with open(path, "wb") as f: writePlist(layerContents, f) self.assertRaises(UFOLibError, UFOWriter, self.ufoPath) # __init__: default without a name def testDefaultLayerNoName(self): # get the glyph set self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("public.foregound", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) writer = UFOWriter(self.ufoPath) # __init__: default with a name def testDefaultLayerName(self): self.makeUFO() path = os.path.join(self.ufoPath, "layercontents.plist") os.remove(path) layerContents = [ ("custom name", "glyphs"), ("layer 1", "glyphs.layer 1"), ("layer 2", "glyphs.layer 2") ] with open(path, "wb") as f: writePlist(layerContents, f) writer = UFOWriter(self.ufoPath) # __init__: up convert 1 > 3 def testUpConvert1To3(self): self.makeUFO( metaInfo=dict(creator="test", formatVersion=1), layerContents=dict() ) writer = UFOWriter(self.ufoPath) writer.writeLayerContents(["public.default"]) path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["public.default", "glyphs"]] self.assertEqual(expected, result) # __init__: up convert 2 > 3 def testUpConvert2To3(self): self.makeUFO( metaInfo=dict(creator="test", formatVersion=2), layerContents=dict() ) writer = UFOWriter(self.ufoPath) writer.writeLayerContents(["public.default"]) path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["public.default", "glyphs"]] self.assertEqual(expected, result) # __init__: down convert 3 > 1 def testDownConvert3To1(self): self.makeUFO() self.assertRaises(UFOLibError, UFOWriter, self.ufoPath, formatVersion=1) # __init__: down convert 3 > 2 def testDownConvert3To2(self): self.makeUFO() self.assertRaises(UFOLibError, UFOWriter, self.ufoPath, formatVersion=2) # get glyph sets def testGetGlyphSets(self): self.makeUFO() # hack contents.plist path = os.path.join(self.ufoPath, "glyphs.layer 1", "contents.plist") with open(path, "wb") as f: writePlist(dict(b="a.glif"), f) path = os.path.join(self.ufoPath, "glyphs.layer 2", "contents.plist") with open(path, "wb") as f: writePlist(dict(c="a.glif"), f) # now test writer = UFOWriter(self.ufoPath) # default expected = ["a"] result = list(writer.getGlyphSet().keys()) self.assertEqual(expected, result) # layer 1 expected = ["b"] result = list(writer.getGlyphSet("layer 1", defaultLayer=False).keys()) self.assertEqual(expected, result) # layer 2 expected = ["c"] result = list(writer.getGlyphSet("layer 2", defaultLayer=False).keys()) self.assertEqual(expected, result) # make a new font with two layers def testNewFontOneLayer(self): self.clearUFO() writer = UFOWriter(self.ufoPath) writer.getGlyphSet() writer.writeLayerContents(["public.default"]) # directory path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["public.default", "glyphs"]] self.assertEqual(expected, result) def testNewFontThreeLayers(self): self.clearUFO() writer = UFOWriter(self.ufoPath) writer.getGlyphSet("layer 1", defaultLayer=False) writer.getGlyphSet() writer.getGlyphSet("layer 2", defaultLayer=False) writer.writeLayerContents(["layer 1", "public.default", "layer 2"]) # directories path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["layer 1", "glyphs.layer 1"], ["public.default", "glyphs"], ["layer 2", "glyphs.layer 2"]] self.assertEqual(expected, result) # add a layer to an existing font def testAddLayerToExistingFont(self): self.makeUFO() writer = UFOWriter(self.ufoPath) writer.getGlyphSet("layer 3", defaultLayer=False) writer.writeLayerContents(["public.default", "layer 1", "layer 2", "layer 3"]) # directories path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 3") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [['public.default', 'glyphs'], ['layer 1', 'glyphs.layer 1'], ['layer 2', 'glyphs.layer 2'], ["layer 3", "glyphs.layer 3"]] self.assertEqual(expected, result) # rename valid name def testRenameLayer(self): self.makeUFO() writer = UFOWriter(self.ufoPath) writer.renameGlyphSet("layer 1", "layer 3") writer.writeLayerContents(["public.default", "layer 3", "layer 2"]) # directories path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(False, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 3") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [['public.default', 'glyphs'], ['layer 3', 'glyphs.layer 3'], ['layer 2', 'glyphs.layer 2']] self.assertEqual(expected, result) def testRenameLayerDefault(self): self.makeUFO() writer = UFOWriter(self.ufoPath) writer.renameGlyphSet("public.default", "layer xxx") writer.renameGlyphSet("layer 1", "layer 1", defaultLayer=True) writer.writeLayerContents(["layer xxx", "layer 1", "layer 2"]) path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(False, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer xxx") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [['layer xxx', 'glyphs.layer xxx'], ['layer 1', 'glyphs'], ['layer 2', 'glyphs.layer 2']] self.assertEqual(expected, result) # rename duplicate name def testRenameLayerDuplicateName(self): self.makeUFO() writer = UFOWriter(self.ufoPath) self.assertRaises(UFOLibError, writer.renameGlyphSet, "layer 1", "layer 2") # rename unknown layer def testRenameLayerDuplicateName(self): self.makeUFO() writer = UFOWriter(self.ufoPath) self.assertRaises(UFOLibError, writer.renameGlyphSet, "does not exist", "layer 2") # remove valid layer def testRemoveLayer(self): self.makeUFO() writer = UFOWriter(self.ufoPath) writer.deleteGlyphSet("layer 1") writer.writeLayerContents(["public.default", "layer 2"]) # directories path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(False, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["public.default", "glyphs"], ["layer 2", "glyphs.layer 2"]] self.assertEqual(expected, result) # remove default layer def testRemoveDefaultLayer(self): self.makeUFO() writer = UFOWriter(self.ufoPath) writer.deleteGlyphSet("public.default") # directories path = os.path.join(self.ufoPath, "glyphs") exists = os.path.exists(path) self.assertEqual(False, exists) path = os.path.join(self.ufoPath, "glyphs.layer 1") exists = os.path.exists(path) self.assertEqual(True, exists) path = os.path.join(self.ufoPath, "glyphs.layer 2") exists = os.path.exists(path) self.assertEqual(True, exists) # layer contents path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "rb") as f: result = readPlist(f) expected = [["layer 1", "glyphs.layer 1"], ["layer 2", "glyphs.layer 2"]] self.assertEqual(expected, result) # remove unknown layer def testRemoveDefaultLayer(self): self.makeUFO() writer = UFOWriter(self.ufoPath) self.assertRaises(UFOLibError, writer.deleteGlyphSet, "does not exist") # ----- # /data # ----- class UFO3ReadDataTestCase(unittest.TestCase): def getFontPath(self): testdata = os.path.join(os.path.dirname(__file__), "testdata") return os.path.join(testdata, "UFO3-Read Data.ufo") def testUFOReaderDataDirectoryListing(self): reader = UFOReader(self.getFontPath()) found = reader.getDataDirectoryListing() expected = [ 'org.unifiedfontobject.directory%(s)sbar%(s)slol.txt' % {'s': os.sep}, 'org.unifiedfontobject.directory%(s)sfoo.txt' % {'s': os.sep}, 'org.unifiedfontobject.file.txt' ] self.assertEqual(set(found), set(expected)) def testUFOReaderBytesFromPath(self): reader = UFOReader(self.getFontPath()) found = reader.readBytesFromPath("data/org.unifiedfontobject.file.txt") expected = b"file.txt" self.assertEqual(found, expected) found = reader.readBytesFromPath("data/org.unifiedfontobject.directory/bar/lol.txt") expected = b"lol.txt" self.assertEqual(found, expected) found = reader.readBytesFromPath("data/org.unifiedfontobject.doesNotExist") expected = None self.assertEqual(found, expected) def testUFOReaderReadFileFromPath(self): reader = UFOReader(self.getFontPath()) fileObject = reader.getReadFileForPath("data/org.unifiedfontobject.file.txt") self.assertNotEqual(fileObject, None) hasRead = hasattr(fileObject, "read") self.assertEqual(hasRead, True) fileObject.close() fileObject = reader.getReadFileForPath("data/org.unifiedfontobject.doesNotExist") self.assertEqual(fileObject, None) class UFO3WriteDataTestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def tearDownUFO(self): if os.path.exists(self.dstDir): shutil.rmtree(self.dstDir) def testUFOWriterWriteBytesToPath(self): # basic file path = "data/org.unifiedfontobject.writebytesbasicfile.txt" testBytes = b"test" writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeBytesToPath(path, testBytes) path = os.path.join(self.dstDir, path) self.assertEqual(os.path.exists(path), True) with open(path, "rb") as f: written = f.read() self.assertEqual(testBytes, written) self.tearDownUFO() # basic file with unicode text path = "data/org.unifiedfontobject.writebytesbasicunicodefile.txt" text = b"t\xeb\xdft" writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeBytesToPath(path, text) path = os.path.join(self.dstDir, path) self.assertEqual(os.path.exists(path), True) with open(path, "rb") as f: written = f.read() self.assertEqual(text, written) self.tearDownUFO() # basic directory path = "data/org.unifiedfontobject.writebytesdirectory/level1/level2/file.txt" testBytes = b"test" writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeBytesToPath(path, testBytes) path = os.path.join(self.dstDir, path) self.assertEqual(os.path.exists(path), True) with open(path, "rb") as f: written = f.read() self.assertEqual(testBytes, written) self.tearDownUFO() def testUFOWriterWriteFileToPath(self): # basic file path = "data/org.unifiedfontobject.getwritefile.txt" writer = UFOWriter(self.dstDir, formatVersion=3) fileObject = writer.getFileObjectForPath(path) self.assertNotEqual(fileObject, None) hasRead = hasattr(fileObject, "read") self.assertEqual(hasRead, True) fileObject.close() self.tearDownUFO() def testUFOWriterRemoveFile(self): path1 = "data/org.unifiedfontobject.removefile/level1/level2/file1.txt" path2 = "data/org.unifiedfontobject.removefile/level1/level2/file2.txt" path3 = "data/org.unifiedfontobject.removefile/level1/file3.txt" writer = UFOWriter(self.dstDir, formatVersion=3) writer.writeBytesToPath(path1, b"test") writer.writeBytesToPath(path2, b"test") writer.writeBytesToPath(path3, b"test") self.assertEqual(os.path.exists(os.path.join(self.dstDir, path1)), True) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), True) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True) writer.removeFileForPath(path1) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path1)), False) self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path1))), True) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), True) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True) writer.removeFileForPath(path2) self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path1))), False) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), False) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True) writer.removeFileForPath(path3) self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), False) self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path2))), False) self.assertEqual(os.path.exists(os.path.join(self.dstDir, "data/org.unifiedfontobject.removefile")), False) self.assertRaises(UFOLibError, writer.removeFileForPath, path="metainfo.plist") self.assertRaises(UFOLibError, writer.removeFileForPath, path="data/org.unifiedfontobject.doesNotExist.txt") self.tearDownUFO() def testUFOWriterCopy(self): sourceDir = self.dstDir.replace(".ufo", "") + "-copy source" + ".ufo" dataPath = "data/org.unifiedfontobject.copy/level1/level2/file1.txt" writer = UFOWriter(sourceDir, formatVersion=3) writer.writeBytesToPath(dataPath, b"test") # copy a file reader = UFOReader(sourceDir) writer = UFOWriter(self.dstDir, formatVersion=3) writer.copyFromReader(reader, dataPath, dataPath) path = os.path.join(self.dstDir, dataPath) self.assertEqual(os.path.exists(path), True) self.tearDownUFO() # copy a directory reader = UFOReader(sourceDir) writer = UFOWriter(self.dstDir, formatVersion=3) p = "data/org.unifiedfontobject.copy" writer.copyFromReader(reader, p, p) path = os.path.join(self.dstDir, dataPath) self.assertEqual(os.path.exists(path), True) self.tearDownUFO() # --------------- # layerinfo.plist # --------------- class TestLayerInfoObject(object): color = guidelines = lib = None class UFO3ReadLayerInfoTestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeUFO(self, formatVersion=3, layerInfo=None): self.clearUFO() if not os.path.exists(self.ufoPath): os.mkdir(self.ufoPath) # metainfo.plist metaInfo = dict(creator="test", formatVersion=formatVersion) path = os.path.join(self.ufoPath, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) # layercontents.plist layerContents = [("public.default", "glyphs")] path = os.path.join(self.ufoPath, "layercontents.plist") with open(path, "wb") as f: writePlist(layerContents, f) # glyphs glyphsPath = os.path.join(self.ufoPath, "glyphs") os.mkdir(glyphsPath) contents = dict(a="a.glif") path = os.path.join(glyphsPath, "contents.plist") with open(path, "wb") as f: writePlist(contents, f) path = os.path.join(glyphsPath, "a.glif") with open(path, "w") as f: f.write(" ") # layerinfo.plist if layerInfo is None: layerInfo = dict( color="0,0,0,1", lib={"foo" : "bar"} ) path = os.path.join(glyphsPath, "layerinfo.plist") with open(path, "wb") as f: writePlist(layerInfo, f) def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) def testValidLayerInfo(self): self.makeUFO() reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() info = TestLayerInfoObject() glyphSet.readLayerInfo(info) expectedColor = "0,0,0,1" self.assertEqual(expectedColor, info.color) expectedLib = {"foo": "bar"} self.assertEqual(expectedLib, info.lib) def testMissingLayerInfo(self): self.makeUFO() path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist") os.remove(path) # read reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() info = TestLayerInfoObject() glyphSet.readLayerInfo(info) self.assertEqual(None, info.color) self.assertEqual(None, info.guidelines) self.assertEqual(None, info.lib) def testBogusLayerInfo(self): self.makeUFO() path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist") os.remove(path) with open(path, "w") as f: f.write("test") # read reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() info = TestLayerInfoObject() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, info) def testInvalidFormatLayerInfo(self): self.makeUFO() path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist") info = [("color", "0,0,0,0")] with open(path, "wb") as f: writePlist(info, f) # read reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() info = TestLayerInfoObject() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, info) def testColor(self): ## not a string info = {} info["color"] = 1 self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## not enough commas info = {} info["color"] = "1 0, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1 0 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1 0 0 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## not enough parts info = {} info["color"] = ", 0, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, , 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, 0, , 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, 0, 0, " self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = ", , , " self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## not a number in all positions info = {} info["color"] = "r, 1, 1, 1" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, g, 1, 1" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, 1, b, 1" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "1, 1, 1, a" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## too many parts info = {} info["color"] = "1, 0, 0, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## < 0 in each position info = {} info["color"] = "-1, 0, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, -1, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, 0, -1, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, 0, 0, -1" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) ## > 1 in each position info = {} info["color"] = "2, 0, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, 2, 0, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, 0, 2, 0" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) info = {} info["color"] = "0, 0, 0, 2" self.makeUFO(layerInfo=info) reader = UFOReader(self.ufoPath) glyphSet = reader.getGlyphSet() self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject()) class UFO3WriteLayerInfoTestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeGlyphSet(self): self.clearUFO() writer = UFOWriter(self.ufoPath) return writer.getGlyphSet() def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) def testValidWrite(self): expected = dict( color="0,0,0,1", lib={"foo" : "bar"} ) info = TestLayerInfoObject() info.color = expected["color"] info.lib = expected["lib"] glyphSet = self.makeGlyphSet() glyphSet.writeLayerInfo(info) path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist") with open(path, "rb") as f: result = readPlist(f) self.assertEqual(expected, result) def testColor(self): ## not a string info = TestLayerInfoObject() info.color = 1 glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## not enough commas info = TestLayerInfoObject() info.color = "1 0, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1 0 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1 0 0 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## not enough parts info = TestLayerInfoObject() info.color = ", 0, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, , 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, 0, , 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, 0, 0, " glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = ", , , " glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## not a number in all positions info = TestLayerInfoObject() info.color = "r, 1, 1, 1" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, g, 1, 1" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, 1, b, 1" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "1, 1, 1, a" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## too many parts info = TestLayerInfoObject() info.color = "1, 0, 0, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## < 0 in each position info = TestLayerInfoObject() info.color = "-1, 0, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, -1, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, 0, -1, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, 0, 0, -1" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) ## > 1 in each position info = TestLayerInfoObject() info.color = "2, 0, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, 2, 0, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, 0, 2, 0" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) info = TestLayerInfoObject() info.color = "0, 0, 0, 2" glyphSet = self.makeGlyphSet() self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info) if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/test_GLIF2.py0000644000000000000000000023076413174123546016574 0ustar rootrootfrom __future__ import unicode_literals import unittest from ufoLib.glifLib import GlifLibError, readGlyphFromString, writeGlyphToString from ufoLib.test.testSupport import Glyph, stripText try: basestring except NameError: basestring = str # ---------- # Test Cases # ---------- class TestGLIF2(unittest.TestCase): def assertEqual(self, first, second, msg=None): if isinstance(first, basestring): first = stripText(first) if isinstance(second, basestring): second = stripText(second) return super(TestGLIF2, self).assertEqual(first, second, msg=msg) def pyToGLIF(self, py): py = stripText(py) glyph = Glyph() exec(py, {"glyph" : glyph, "pointPen" : glyph}) glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=2) glif = "\n".join(glif.splitlines()[1:]) return glif def glifToPy(self, glif): glif = stripText(glif) glif = "\n" + glif glyph = Glyph() readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph) return glyph.py() def testTopElement(self): # not glyph glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testName_legal(self): # legal glif = """ """ py = """ glyph.name = "a" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testName_empty(self): # empty glif = """ """ py = """ glyph.name = "" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testName_not_a_string(self): # not a string py = """ glyph.name = 1 """ self.assertRaises(GlifLibError, self.pyToGLIF, py) def testFormat_legal(self): # legal glif = """ """ py = """ glyph.name = "a" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testFormat_illegal_wrong_number(self): # wrong number glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testFormat_illegal_not_int(self): # not an int glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testBogusGlyphStructure_unknown_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testBogusGlyphStructure_content(self): # content glif = """ Hello World. """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testAdvance_legal_widht_and_height(self): # legal: width and height glif = """ """ py = """ glyph.name = "a" glyph.width = 100 glyph.height = 200 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_width_and_height_floats(self): # legal: width and height floats glif = """ """ py = """ glyph.name = "a" glyph.width = 100.1 glyph.height = 200.1 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_width(self): # legal: width glif = """ """ py = """ glyph.name = "a" glyph.width = 100 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_height(self): # legal: height glif = """ """ py = """ glyph.name = "a" glyph.height = 200 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_illegal_width(self): # illegal: not a number glif = """ """ py = """ glyph.name = "a" glyph.width = "a" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAdvance_illegal_height(self): glif = """ """ py = """ glyph.name = "a" glyph.height = "a" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testUnicodes_legal(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.unicodes = [97] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testUnicodes_legal_multiple(self): glif = """ """ py = """ glyph.name = "a" glyph.unicodes = [98, 99, 97] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testUnicodes_illegal(self): # illegal glif = """ """ py = """ glyph.name = "zzzzzz" glyph.unicodes = ["1.1"] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testNote(self): glif = """ hello """ py = """ glyph.name = "a" glyph.note = "hello" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testLib(self): glif = """ dict hello world float 2.5 int 1 list a b 1 2.5 string a """ py = """ glyph.name = "a" glyph.lib = {"dict" : {"hello" : "world"}, "float" : 2.5, "int" : 1, "list" : ["a", "b", 1, 2.5], "string" : "a"} """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testGuidelines_legal(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"x" : 1}, {"y" : 1}, {"angle" : 0, "x" : 1, "y" : 1}, {"angle" : 360, "x" : 1, "y" : 1}, {"angle" : 45.5, "x" : 1.1, "y" : 1.1}, {"name" : "a", "x" : 1}, {"color" : "1,1,1,1", "x" : 1}] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testGuidelines_illegal_x(self): # x not an int or float glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : 45, "x" : "a", "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_y(self): # y not an int or float glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : 45, "x" : 1, "y" : "a"}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_angle(self): # angle not an int or float glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : "a", "x" : 1, "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_x_missing(self): # x missing glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : 45, "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_y_missing(self): # y missing glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : 45, "x" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_angle_missing(self): # angle missing glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"x" : 1, "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testGuidelines_illegal_angle_out_of_range(self): # angle out of range glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : -1, "x" : "1", "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"angle" : 361, "x" : "1", "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAnchors_legal(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"color" : "1,0,0,1", "name" : "test", "x" : 1, "y" : 2}, {"x" : 1, "y" : 2}] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAnchors_illegal_x(self): # x not an int or float glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"x" : "a", "y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAnchors_illegal_y(self): # y not an int or float glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"x" : 1, "y" : "a"}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAnchors_illegal_x_missing(self): # x missing glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"y" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAnchors_illegal_y_missing(self): # y missing glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"x" : 1}] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testImage_legal(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.image = {"color" : "1,1,1,1", "fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6} """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testImage_legal_no_color_or_transformation(self): # legal: no color or transformation glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 0, "xScale" : 1, "xyScale" : 0, "yOffset" : 0, "yScale" : 1, "yxScale" : 0} """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testImage_illegal_no_file_name(self): # no file name glif = """ """ py = """ glyph.name = "a" glyph.image = {"color" : "1,1,1,1", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testImage_bogus_transformation(self): # bogus transformation glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : "a", "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : "a", "yOffset" : 4, "yScale" : 5, "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : "a"} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : "a", "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : "a", "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : "a", "yScale" : 5, "yxScale" : 6} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testImage_bogus_color(self): # bogus color glif = """ """ py = """ glyph.name = "a" glyph.image = {"color" : "1,1,1,x"} """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testOutline_unknown_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testOutline_content(self): # content glif = """ hello """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testComponent_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, 1, 4)]) """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testComponent_illegal_no_base(self): # no base glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testComponent_illegal_bogus_transformation(self): # bogus values in transformation glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", ("a", 3, 6, 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, "a", 6, 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, "a", 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, "a", 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, "a", 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, 1, "a")]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testContour_legal_one_contour(self): # legal: one contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testContour_legal_two_contours(self): # legal: two contours glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testContour_illegal_unkonwn_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testContourIdentifier(self): glif = """ """ py = """ glyph.name = "a" pointPen.beginPath(**{"identifier" : "foo"}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointCoordinates_legal_int(self): # legal: int glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointCoordinates_legal_float(self): # legal: float glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1.1, -2.2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointCoordinates_illegal_x(self): # illegal: x as string glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[("a", 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointCoordinates_illegal_y(self): # illegal: y as string glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, "a")], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeMove_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeMove_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : True}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeMove_illegal_not_at_start(self): # illegal: not at start glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeLine_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeLine_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeLine_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_no_off_curves(self): # legal: no off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_1_off_curve(self): # legal: 1 off-curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(50, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_illegal_3_off_curves(self): # illegal: 3 off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(35, 125)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointQCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_no_off_curves(self): # legal: no off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_one_off_curve(self): # legal: 1 off-curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(50, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_3_off_curves(self): # legal: 3 off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(35, 125)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testSpecialCaseQCurve_legal_no_on_curve(self): # contour with no on curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 0)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_illegal_before_move(self): # before move glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeOffCurve_illegal_before_line(self): # before line glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeOffCurve_illegal_smooth(self): # smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : True}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testOpenContourLooseOffCurves(self): glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) def testPointIdentifier(self): glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"identifier" : "1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testIdentifierConflict_legal_no_conflict(self): glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testIdentifierConflict_point_point(self): # point - point glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_point_contour(self): # point - contour glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "contour1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_point_component(self): # point - component glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "component1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_point_guideline(self): # point - guideline glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "guideline1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_point_anchor(self): # point - anchor glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "anchor1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_contour_contour(self): # contour - contour glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_contour_component(self): # contour - component glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "contour1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_contour_guideline(self): # contour - guideline glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "contour1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_contour_anchor(self): # contour - anchor glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "anchor1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_component_component(self): # component - component glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_component_guideline(self): # component - guideline glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "component1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_component_anchor(self): # component - anchor glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "anchor1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_guideline_guideline(self): # guideline - guideline glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline1", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_guideline_anchor(self): # guideline - anchor glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "anchor1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testIdentifierConflict_anchor_anchor(self): # anchor - anchor glif = """ """ py = """ glyph.name = "a" glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}] glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor1", "x" : 0, "y" : 0}] pointPen.beginPath(**{"identifier" : "contour1"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() pointPen.beginPath(**{"identifier" : "contour2"}) pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False}) pointPen.endPath() pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"}) pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"}) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/test_filenames.py0000644000000000000000000000740513174123546017726 0ustar rootrootfrom __future__ import unicode_literals import unittest from ufoLib.filenames import userNameToFileName, handleClash1, handleClash2 class TestFilenames(unittest.TestCase): def test_userNameToFileName(self): self.assertEqual(userNameToFileName("a"), "a") self.assertEqual(userNameToFileName("A"), "A_") self.assertEqual(userNameToFileName("AE"), "A_E_") self.assertEqual(userNameToFileName("Ae"), "A_e") self.assertEqual(userNameToFileName("ae"), "ae") self.assertEqual(userNameToFileName("aE"), "aE_") self.assertEqual(userNameToFileName("a.alt"), "a.alt") self.assertEqual(userNameToFileName("A.alt"), "A_.alt") self.assertEqual(userNameToFileName("A.Alt"), "A_.A_lt") self.assertEqual(userNameToFileName("A.aLt"), "A_.aL_t") self.assertEqual(userNameToFileName("A.alT"), "A_.alT_") self.assertEqual(userNameToFileName("T_H"), "T__H_") self.assertEqual(userNameToFileName("T_h"), "T__h") self.assertEqual(userNameToFileName("t_h"), "t_h") self.assertEqual(userNameToFileName("F_F_I"), "F__F__I_") self.assertEqual(userNameToFileName("f_f_i"), "f_f_i") self.assertEqual(userNameToFileName("Aacute_V.swash"), "A_acute_V_.swash") self.assertEqual(userNameToFileName(".notdef"), "_notdef") self.assertEqual(userNameToFileName("con"), "_con") self.assertEqual(userNameToFileName("CON"), "C_O_N_") self.assertEqual(userNameToFileName("con.alt"), "_con.alt") self.assertEqual(userNameToFileName("alt.con"), "alt._con") def test_userNameToFileName_ValueError(self): with self.assertRaises(ValueError): userNameToFileName(b"a") with self.assertRaises(ValueError): userNameToFileName({"a"}) with self.assertRaises(ValueError): userNameToFileName(("a",)) with self.assertRaises(ValueError): userNameToFileName(["a"]) with self.assertRaises(ValueError): userNameToFileName(["a"]) with self.assertRaises(ValueError): userNameToFileName(b"\xd8\x00") def test_handleClash1(self): prefix = ("0" * 5) + "." suffix = "." + ("0" * 10) existing = ["a" * 5] e = list(existing) self.assertEqual( handleClash1(userName="A" * 5, existing=e, prefix=prefix, suffix=suffix), '00000.AAAAA000000000000001.0000000000' ) e = list(existing) e.append(prefix + "aaaaa" + "1".zfill(15) + suffix) self.assertEqual( handleClash1(userName="A" * 5, existing=e, prefix=prefix, suffix=suffix), '00000.AAAAA000000000000002.0000000000' ) e = list(existing) e.append(prefix + "AAAAA" + "2".zfill(15) + suffix) self.assertEqual( handleClash1(userName="A" * 5, existing=e, prefix=prefix, suffix=suffix), '00000.AAAAA000000000000001.0000000000' ) def test_handleClash2(self): prefix = ("0" * 5) + "." suffix = "." + ("0" * 10) existing = [prefix + str(i) + suffix for i in range(100)] e = list(existing) self.assertEqual( handleClash2(existing=e, prefix=prefix, suffix=suffix), '00000.100.0000000000' ) e = list(existing) e.remove(prefix + "1" + suffix) self.assertEqual( handleClash2(existing=e, prefix=prefix, suffix=suffix), '00000.1.0000000000' ) e = list(existing) e.remove(prefix + "2" + suffix) self.assertEqual( handleClash2(existing=e, prefix=prefix, suffix=suffix), '00000.2.0000000000' ) ufoLib-2.1.1/Lib/ufoLib/test/test_GLIF1.py0000644000000000000000000010366013174123546016565 0ustar rootrootfrom __future__ import unicode_literals import unittest from ufoLib.glifLib import GlifLibError, readGlyphFromString, writeGlyphToString from ufoLib.test.testSupport import Glyph, stripText try: basestring except NameError: basestring = str # ---------- # Test Cases # ---------- class TestGLIF1(unittest.TestCase): def assertEqual(self, first, second, msg=None): if isinstance(first, basestring): first = stripText(first) if isinstance(second, basestring): second = stripText(second) return super(TestGLIF1, self).assertEqual(first, second, msg=msg) def pyToGLIF(self, py): py = stripText(py) glyph = Glyph() exec(py, {"glyph" : glyph, "pointPen" : glyph}) glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=1) glif = "\n".join(glif.splitlines()[1:]) return glif def glifToPy(self, glif): glif = stripText(glif) glif = "\n" + glif glyph = Glyph() readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph) return glyph.py() def testTopElement(self): # not glyph glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testName_legal(self): # legal glif = """ """ py = """ glyph.name = "a" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testName_empty(self): # empty glif = """ """ py = """ glyph.name = "" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testName_not_a_string(self): # not a string py = """ glyph.name = 1 """ self.assertRaises(GlifLibError, self.pyToGLIF, py) def testFormat_legal(self): # legal glif = """ """ py = """ glyph.name = "a" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testFormat_wrong_number(self): # wrong number glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testFormat_not_an_int(self): # not an int glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testBogusGlyphStructure_unknown_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testBogusGlyphStructure_content(self): # content glif = """ Hello World. """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testAdvance_legal_width_and_height(self): # legal: width and height glif = """ """ py = """ glyph.name = "a" glyph.width = 100 glyph.height = 200 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_width_and_height_floats(self): # legal: width and height floats glif = """ """ py = """ glyph.name = "a" glyph.width = 100.1 glyph.height = 200.1 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_width(self): # legal: width glif = """ """ py = """ glyph.name = "a" glyph.width = 100 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_legal_height(self): # legal: height glif = """ """ py = """ glyph.name = "a" glyph.height = 200 """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAdvance_illegal_width(self): # illegal: not a number glif = """ """ py = """ glyph.name = "a" glyph.width = "a" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAdvance_illegal_height(self): glif = """ """ py = """ glyph.name = "a" glyph.height = "a" """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testUnicodes_legal(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.unicodes = [97] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testUnicodes_legal_multiple(self): glif = """ """ py = """ glyph.name = "a" glyph.unicodes = [98, 99, 97] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testUnicodes_illegal(self): # illegal glif = """ """ py = """ glyph.name = "zzzzzz" glyph.unicodes = ["1.1"] """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testNote(self): glif = """ hello """ py = """ glyph.name = "a" glyph.note = "hello" """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testLib_legal(self): glif = """ dict hello world float 2.5 int 1 list a b 1 2.5 string a """ py = """ glyph.name = "a" glyph.lib = {"dict" : {"hello" : "world"}, "float" : 2.5, "int" : 1, "list" : ["a", "b", 1, 2.5], "string" : "a"} """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testOutline_unknown_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testOutline_content(self): # content glif = """ hello """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testComponent_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, 1, 4)]) """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testComponent_illegal_no_base(self): # no base glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testComponent_bogus_transformation(self): # bogus values in transformation glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", ("a", 3, 6, 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, "a", 6, 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, "a", 5, 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, "a", 1, 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, "a", 4)]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) glif = """ """ py = """ glyph.name = "a" pointPen.addComponent(*["x", (2, 3, 6, 5, 1, "a")]) """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testContour_legal_one_contour(self): # legal: one contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testContour_legal_two_contours(self): # legal: two contours glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testContour_illegal_unkonwn_element(self): # unknown element glif = """ """ self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointCoordinates_legal_int(self): # legal: int glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointCoordinates_legal_float(self): # legal: float glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1.1, -2.2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointCoordinates_illegal_x(self): # illegal: string glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[("a", 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointCoordinates_illegal_y(self): # legal: int glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, "a")], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeMove_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeMove_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : True}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeMove_illegal_not_at_start(self): # illegal: not at start glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeLine_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeLine_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "line", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeLine_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_no_off_curves(self): # legal: no off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_legal_1_off_curve(self): # legal: 1 off-curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(50, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeCurve_illegal_3_off_curves(self): # illegal: 3 off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(35, 125)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointQCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_smooth(self): # legal: smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : True}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_no_off_curves(self): # legal: no off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_one_off_curve(self): # legal: 1 off-curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(50, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointQCurve_legal_3_off_curves(self): # legal: 3 off-curves glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(35, 125)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testSpecialCaseQCurve(self): # contour with no on curve glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"smooth" : False}) pointPen.addPoint(*[(0, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 100)], **{"smooth" : False}) pointPen.addPoint(*[(100, 0)], **{"smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_legal(self): # legal glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_legal_start_of_contour(self): # legal: start of contour glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(65, 200)], **{"smooth" : False}) pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testPointTypeOffCurve_illegal_before_move(self): # before move glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeOffCurve_illegal_before_line(self): # before line glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : False}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "line", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testPointTypeOffCurve_illegal_smooth(self): # smooth=True glif = """ """ py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(0, 65)], **{"smooth" : True}) pointPen.addPoint(*[(0, 0)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) self.assertRaises(GlifLibError, self.glifToPy, glif) def testAnchor_legal_without_name(self): # legal glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"x" : 1, "y" : 2}] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testAnchor_legal_with_name(self): glif = """ """ py = """ glyph.name = "a" glyph.anchors = [{"name" : "test", "x" : 1, "y" : 2}] """ resultGlif = self.pyToGLIF(py) resultPy = self.glifToPy(glif) self.assertEqual(glif, resultGlif) self.assertEqual(py, resultPy) def testOpenContourLooseOffCurves_legal(self): # a piece of software was writing this kind of structure glif = """ """ expectedPy = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False}) pointPen.endPath() """ resultPy = self.glifToPy(glif) self.assertEqual(resultPy, expectedPy) def testOpenContourLooseOffCurves_illegal(self): py = """ glyph.name = "a" pointPen.beginPath() pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False}) pointPen.addPoint(*[(1, 2)], **{"smooth" : False}) pointPen.endPath() """ self.assertRaises(GlifLibError, self.pyToGLIF, py) if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/testSupport.py0000755000000000000000000006126113174123546017303 0ustar rootroot"""Miscellaneous helpers for our test suite.""" from __future__ import unicode_literals import sys import os import unittest try: basestring except NameError: basestring = str def getDemoFontPath(): """Return the path to Data/DemoFont.ufo/.""" testdata = os.path.join(os.path.dirname(__file__), "testdata") return os.path.join(testdata, "DemoFont.ufo") def getDemoFontGlyphSetPath(): """Return the path to Data/DemoFont.ufo/glyphs/.""" return os.path.join(getDemoFontPath(), "glyphs") def _gatherTestCasesFromCallerByMagic(): # UGLY magic: fetch TestClass subclasses from the globals of our # caller's caller. frame = sys._getframe(2) return _gatherTestCasesFromDict(frame.f_globals) def _gatherTestCasesFromDict(d): testCases = [] for ob in list(d.values()): if isinstance(ob, type) and issubclass(ob, unittest.TestCase): testCases.append(ob) return testCases def runTests(testCases=None, verbosity=1): """Run a series of tests.""" if testCases is None: testCases = _gatherTestCasesFromCallerByMagic() loader = unittest.TestLoader() suites = [] for testCase in testCases: suites.append(loader.loadTestsFromTestCase(testCase)) testRunner = unittest.TextTestRunner(verbosity=verbosity) testSuite = unittest.TestSuite(suites) testRunner.run(testSuite) # GLIF test tools class Glyph(object): def __init__(self): self.name = None self.width = None self.height = None self.unicodes = None self.note = None self.lib = None self.image = None self.guidelines = None self.anchors = None self.outline = [] def _writePointPenCommand(self, command, args, kwargs): args = _listToString(args) kwargs = _dictToString(kwargs) if args and kwargs: return "pointPen.%s(*%s, **%s)" % (command, args, kwargs) elif len(args): return "pointPen.%s(*%s)" % (command, args) elif len(kwargs): return "pointPen.%s(**%s)" % (command, kwargs) else: return "pointPen.%s()" % command def beginPath(self, **kwargs): self.outline.append(self._writePointPenCommand("beginPath", [], kwargs)) def endPath(self): self.outline.append(self._writePointPenCommand("endPath", [], {})) def addPoint(self, *args, **kwargs): self.outline.append(self._writePointPenCommand("addPoint", args, kwargs)) def addComponent(self, *args, **kwargs): self.outline.append(self._writePointPenCommand("addComponent", args, kwargs)) def drawPoints(self, pointPen): if self.outline: py = "\n".join(self.outline) exec(py, {"pointPen" : pointPen}) def py(self): text = [] if self.name is not None: text.append("glyph.name = \"%s\"" % self.name) if self.width: text.append("glyph.width = %r" % self.width) if self.height: text.append("glyph.height = %r" % self.height) if self.unicodes is not None: text.append("glyph.unicodes = [%s]" % ", ".join([str(i) for i in self.unicodes])) if self.note is not None: text.append("glyph.note = \"%s\"" % self.note) if self.lib is not None: text.append("glyph.lib = %s" % _dictToString(self.lib)) if self.image is not None: text.append("glyph.image = %s" % _dictToString(self.image)) if self.guidelines is not None: text.append("glyph.guidelines = %s" % _listToString(self.guidelines)) if self.anchors is not None: text.append("glyph.anchors = %s" % _listToString(self.anchors)) if self.outline: text += self.outline return "\n".join(text) def _dictToString(d): text = [] for key, value in sorted(d.items()): if value is None: continue key = "\"%s\"" % key if isinstance(value, dict): value = _dictToString(value) elif isinstance(value, list): value = _listToString(value) elif isinstance(value, tuple): value = _tupleToString(value) elif isinstance(value, (int, float)): value = repr(value) elif isinstance(value, basestring): value = "\"%s\"" % value text.append("%s : %s" % (key, value)) if not text: return "" return "{%s}" % ", ".join(text) def _listToString(l): text = [] for value in l: if isinstance(value, dict): value = _dictToString(value) elif isinstance(value, list): value = _listToString(value) elif isinstance(value, tuple): value = _tupleToString(value) elif isinstance(value, (int, float)): value = repr(value) elif isinstance(value, basestring): value = "\"%s\"" % value text.append(value) if not text: return "" return "[%s]" % ", ".join(text) def _tupleToString(t): text = [] for value in t: if isinstance(value, dict): value = _dictToString(value) elif isinstance(value, list): value = _listToString(value) elif isinstance(value, tuple): value = _tupleToString(value) elif isinstance(value, (int, float)): value = repr(value) elif isinstance(value, basestring): value = "\"%s\"" % value text.append(value) if not text: return "" return "(%s)" % ", ".join(text) def stripText(text): new = [] for line in text.strip().splitlines(): line = line.strip() if not line: continue new.append(line) return "\n".join(new) # font info values used by several tests fontInfoVersion1 = { "familyName" : "Some Font (Family Name)", "styleName" : "Regular (Style Name)", "fullName" : "Some Font-Regular (Postscript Full Name)", "fontName" : "SomeFont-Regular (Postscript Font Name)", "menuName" : "Some Font Regular (Style Map Family Name)", "fontStyle" : 64, "note" : "A note.", "versionMajor" : 1, "versionMinor" : 0, "year" : 2008, "copyright" : "Copyright Some Foundry.", "notice" : "Some Font by Some Designer for Some Foundry.", "trademark" : "Trademark Some Foundry", "license" : "License info for Some Foundry.", "licenseURL" : "http://somefoundry.com/license", "createdBy" : "Some Foundry", "designer" : "Some Designer", "designerURL" : "http://somedesigner.com", "vendorURL" : "http://somefoundry.com", "unitsPerEm" : 1000, "ascender" : 750, "descender" : -250, "capHeight" : 750, "xHeight" : 500, "defaultWidth" : 400, "slantAngle" : -12.5, "italicAngle" : -12.5, "widthName" : "Medium (normal)", "weightName" : "Medium", "weightValue" : 500, "fondName" : "SomeFont Regular (FOND Name)", "otFamilyName" : "Some Font (Preferred Family Name)", "otStyleName" : "Regular (Preferred Subfamily Name)", "otMacName" : "Some Font Regular (Compatible Full Name)", "msCharSet" : 0, "fondID" : 15000, "uniqueID" : 4000000, "ttVendor" : "SOME", "ttUniqueID" : "OpenType name Table Unique ID", "ttVersion" : "OpenType name Table Version", } fontInfoVersion2 = { "familyName" : "Some Font (Family Name)", "styleName" : "Regular (Style Name)", "styleMapFamilyName" : "Some Font Regular (Style Map Family Name)", "styleMapStyleName" : "regular", "versionMajor" : 1, "versionMinor" : 0, "year" : 2008, "copyright" : "Copyright Some Foundry.", "trademark" : "Trademark Some Foundry", "unitsPerEm" : 1000, "descender" : -250, "xHeight" : 500, "capHeight" : 750, "ascender" : 750, "italicAngle" : -12.5, "note" : "A note.", "openTypeHeadCreated" : "2000/01/01 00:00:00", "openTypeHeadLowestRecPPEM" : 10, "openTypeHeadFlags" : [0, 1], "openTypeHheaAscender" : 750, "openTypeHheaDescender" : -250, "openTypeHheaLineGap" : 200, "openTypeHheaCaretSlopeRise" : 1, "openTypeHheaCaretSlopeRun" : 0, "openTypeHheaCaretOffset" : 0, "openTypeNameDesigner" : "Some Designer", "openTypeNameDesignerURL" : "http://somedesigner.com", "openTypeNameManufacturer" : "Some Foundry", "openTypeNameManufacturerURL" : "http://somefoundry.com", "openTypeNameLicense" : "License info for Some Foundry.", "openTypeNameLicenseURL" : "http://somefoundry.com/license", "openTypeNameVersion" : "OpenType name Table Version", "openTypeNameUniqueID" : "OpenType name Table Unique ID", "openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.", "openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)", "openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)", "openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)", "openTypeNameSampleText" : "Sample Text for Some Font.", "openTypeNameWWSFamilyName" : "Some Font (WWS Family Name)", "openTypeNameWWSSubfamilyName" : "Regular (WWS Subfamily Name)", "openTypeOS2WidthClass" : 5, "openTypeOS2WeightClass" : 500, "openTypeOS2Selection" : [3], "openTypeOS2VendorID" : "SOME", "openTypeOS2Panose" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "openTypeOS2FamilyClass" : [1, 1], "openTypeOS2UnicodeRanges" : [0, 1], "openTypeOS2CodePageRanges" : [0, 1], "openTypeOS2TypoAscender" : 750, "openTypeOS2TypoDescender" : -250, "openTypeOS2TypoLineGap" : 200, "openTypeOS2WinAscent" : 750, "openTypeOS2WinDescent" : 250, "openTypeOS2Type" : [], "openTypeOS2SubscriptXSize" : 200, "openTypeOS2SubscriptYSize" : 400, "openTypeOS2SubscriptXOffset" : 0, "openTypeOS2SubscriptYOffset" : -100, "openTypeOS2SuperscriptXSize" : 200, "openTypeOS2SuperscriptYSize" : 400, "openTypeOS2SuperscriptXOffset" : 0, "openTypeOS2SuperscriptYOffset" : 200, "openTypeOS2StrikeoutSize" : 20, "openTypeOS2StrikeoutPosition" : 300, "openTypeVheaVertTypoAscender" : 750, "openTypeVheaVertTypoDescender" : -250, "openTypeVheaVertTypoLineGap" : 200, "openTypeVheaCaretSlopeRise" : 0, "openTypeVheaCaretSlopeRun" : 1, "openTypeVheaCaretOffset" : 0, "postscriptFontName" : "SomeFont-Regular (Postscript Font Name)", "postscriptFullName" : "Some Font-Regular (Postscript Full Name)", "postscriptSlantAngle" : -12.5, "postscriptUniqueID" : 4000000, "postscriptUnderlineThickness" : 20, "postscriptUnderlinePosition" : -200, "postscriptIsFixedPitch" : False, "postscriptBlueValues" : [500, 510], "postscriptOtherBlues" : [-250, -260], "postscriptFamilyBlues" : [500, 510], "postscriptFamilyOtherBlues" : [-250, -260], "postscriptStemSnapH" : [100, 120], "postscriptStemSnapV" : [80, 90], "postscriptBlueFuzz" : 1, "postscriptBlueShift" : 7, "postscriptBlueScale" : 0.039625, "postscriptForceBold" : True, "postscriptDefaultWidthX" : 400, "postscriptNominalWidthX" : 400, "postscriptWeightName" : "Medium", "postscriptDefaultCharacter" : ".notdef", "postscriptWindowsCharacterSet" : 1, "macintoshFONDFamilyID" : 15000, "macintoshFONDName" : "SomeFont Regular (FOND Name)", } fontInfoVersion3 = { "familyName" : "Some Font (Family Name)", "styleName" : "Regular (Style Name)", "styleMapFamilyName" : "Some Font Regular (Style Map Family Name)", "styleMapStyleName" : "regular", "versionMajor" : 1, "versionMinor" : 0, "year" : 2008, "copyright" : "Copyright Some Foundry.", "trademark" : "Trademark Some Foundry", "unitsPerEm" : 1000, "descender" : -250, "xHeight" : 500, "capHeight" : 750, "ascender" : 750, "italicAngle" : -12.5, "note" : "A note.", "openTypeGaspRangeRecords" : [ dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=20, rangeGaspBehavior=[1]), dict(rangeMaxPPEM=30, rangeGaspBehavior=[2]), dict(rangeMaxPPEM=40, rangeGaspBehavior=[3]), dict(rangeMaxPPEM=50, rangeGaspBehavior=[0, 1, 2, 3]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0]) ], "openTypeHeadCreated" : "2000/01/01 00:00:00", "openTypeHeadLowestRecPPEM" : 10, "openTypeHeadFlags" : [0, 1], "openTypeHheaAscender" : 750, "openTypeHheaDescender" : -250, "openTypeHheaLineGap" : 200, "openTypeHheaCaretSlopeRise" : 1, "openTypeHheaCaretSlopeRun" : 0, "openTypeHheaCaretOffset" : 0, "openTypeNameDesigner" : "Some Designer", "openTypeNameDesignerURL" : "http://somedesigner.com", "openTypeNameManufacturer" : "Some Foundry", "openTypeNameManufacturerURL" : "http://somefoundry.com", "openTypeNameLicense" : "License info for Some Foundry.", "openTypeNameLicenseURL" : "http://somefoundry.com/license", "openTypeNameVersion" : "OpenType name Table Version", "openTypeNameUniqueID" : "OpenType name Table Unique ID", "openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.", "openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)", "openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)", "openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)", "openTypeNameSampleText" : "Sample Text for Some Font.", "openTypeNameWWSFamilyName" : "Some Font (WWS Family Name)", "openTypeNameWWSSubfamilyName" : "Regular (WWS Subfamily Name)", "openTypeNameRecords" : [ dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."), dict(nameID=2, platformID=1, encodingID=1, languageID=1, string="Name Record.") ], "openTypeOS2WidthClass" : 5, "openTypeOS2WeightClass" : 500, "openTypeOS2Selection" : [3], "openTypeOS2VendorID" : "SOME", "openTypeOS2Panose" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "openTypeOS2FamilyClass" : [1, 1], "openTypeOS2UnicodeRanges" : [0, 1], "openTypeOS2CodePageRanges" : [0, 1], "openTypeOS2TypoAscender" : 750, "openTypeOS2TypoDescender" : -250, "openTypeOS2TypoLineGap" : 200, "openTypeOS2WinAscent" : 750, "openTypeOS2WinDescent" : 250, "openTypeOS2Type" : [], "openTypeOS2SubscriptXSize" : 200, "openTypeOS2SubscriptYSize" : 400, "openTypeOS2SubscriptXOffset" : 0, "openTypeOS2SubscriptYOffset" : -100, "openTypeOS2SuperscriptXSize" : 200, "openTypeOS2SuperscriptYSize" : 400, "openTypeOS2SuperscriptXOffset" : 0, "openTypeOS2SuperscriptYOffset" : 200, "openTypeOS2StrikeoutSize" : 20, "openTypeOS2StrikeoutPosition" : 300, "openTypeVheaVertTypoAscender" : 750, "openTypeVheaVertTypoDescender" : -250, "openTypeVheaVertTypoLineGap" : 200, "openTypeVheaCaretSlopeRise" : 0, "openTypeVheaCaretSlopeRun" : 1, "openTypeVheaCaretOffset" : 0, "postscriptFontName" : "SomeFont-Regular (Postscript Font Name)", "postscriptFullName" : "Some Font-Regular (Postscript Full Name)", "postscriptSlantAngle" : -12.5, "postscriptUniqueID" : 4000000, "postscriptUnderlineThickness" : 20, "postscriptUnderlinePosition" : -200, "postscriptIsFixedPitch" : False, "postscriptBlueValues" : [500, 510], "postscriptOtherBlues" : [-250, -260], "postscriptFamilyBlues" : [500, 510], "postscriptFamilyOtherBlues" : [-250, -260], "postscriptStemSnapH" : [100, 120], "postscriptStemSnapV" : [80, 90], "postscriptBlueFuzz" : 1, "postscriptBlueShift" : 7, "postscriptBlueScale" : 0.039625, "postscriptForceBold" : True, "postscriptDefaultWidthX" : 400, "postscriptNominalWidthX" : 400, "postscriptWeightName" : "Medium", "postscriptDefaultCharacter" : ".notdef", "postscriptWindowsCharacterSet" : 1, "macintoshFONDFamilyID" : 15000, "macintoshFONDName" : "SomeFont Regular (FOND Name)", "woffMajorVersion" : 1, "woffMinorVersion" : 0, "woffMetadataUniqueID" : dict(id="string"), "woffMetadataVendor" : dict(name="Some Foundry", url="http://somefoundry.com"), "woffMetadataCredits" : dict( credits=[ dict(name="Some Designer"), dict(name=""), dict(name="Some Designer", url="http://somedesigner.com"), dict(name="Some Designer", url=""), dict(name="Some Designer", role="Designer"), dict(name="Some Designer", role=""), dict(name="Some Designer", dir="ltr"), dict(name="rengiseD emoS", dir="rtl"), {"name" : "Some Designer", "class" : "hello"}, {"name" : "Some Designer", "class" : ""}, ] ), "woffMetadataDescription" : dict( url="http://somefoundry.com/foo/description", text=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "foo"}, {"text" : "foo", "class" : ""}, ] ), "woffMetadataLicense" : dict( url="http://somefoundry.com/foo/license", id="foo", text=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "foo"}, {"text" : "foo", "class" : ""}, ] ), "woffMetadataCopyright" : dict( text=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "foo"}, {"text" : "foo", "class" : ""}, ] ), "woffMetadataTrademark" : dict( text=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "foo"}, {"text" : "foo", "class" : ""}, ] ), "woffMetadataLicensee" : dict( name="Some Licensee" ), "woffMetadataExtensions" : [ dict( # everything names=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "hello"}, {"text" : "foo", "class" : ""}, ], items=[ # everything dict( id="foo", names=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "hello"}, {"text" : "foo", "class" : ""}, ], values=[ dict(text="foo"), dict(text=""), dict(text="foo", language="bar"), dict(text="foo", language=""), dict(text="foo", dir="ltr"), dict(text="foo", dir="rtl"), {"text" : "foo", "class" : "hello"}, {"text" : "foo", "class" : ""}, ] ), # no id dict( names=[ dict(text="foo") ], values=[ dict(text="foo") ] ) ] ), # no names dict( items=[ dict( id="foo", names=[ dict(text="foo") ], values=[ dict(text="foo") ] ) ] ), ], "guidelines" : [ # ints dict(x=100, y=200, angle=45), # floats dict(x=100.5, y=200.5, angle=45.5), # edges dict(x=0, y=0, angle=0), dict(x=0, y=0, angle=360), dict(x=0, y=0, angle=360.0), # no y dict(x=100), # no x dict(y=200), # name dict(x=100, y=200, angle=45, name="foo"), dict(x=100, y=200, angle=45, name=""), # identifier dict(x=100, y=200, angle=45, identifier="guide1"), dict(x=100, y=200, angle=45, identifier="guide2"), dict(x=100, y=200, angle=45, identifier="\x20"), dict(x=100, y=200, angle=45, identifier="\x7E"), # colors dict(x=100, y=200, angle=45, color="0,0,0,0"), dict(x=100, y=200, angle=45, color="1,0,0,0"), dict(x=100, y=200, angle=45, color="1,1,1,1"), dict(x=100, y=200, angle=45, color="0,1,0,0"), dict(x=100, y=200, angle=45, color="0,0,1,0"), dict(x=100, y=200, angle=45, color="0,0,0,1"), dict(x=100, y=200, angle=45, color="1, 0, 0, 0"), dict(x=100, y=200, angle=45, color="0, 1, 0, 0"), dict(x=100, y=200, angle=45, color="0, 0, 1, 0"), dict(x=100, y=200, angle=45, color="0, 0, 0, 1"), dict(x=100, y=200, angle=45, color=".5,0,0,0"), dict(x=100, y=200, angle=45, color="0,.5,0,0"), dict(x=100, y=200, angle=45, color="0,0,.5,0"), dict(x=100, y=200, angle=45, color="0,0,0,.5"), dict(x=100, y=200, angle=45, color=".5,1,1,1"), dict(x=100, y=200, angle=45, color="1,.5,1,1"), dict(x=100, y=200, angle=45, color="1,1,.5,1"), dict(x=100, y=200, angle=45, color="1,1,1,.5"), ], } expectedFontInfo1To2Conversion = { "familyName" : "Some Font (Family Name)", "styleMapFamilyName" : "Some Font Regular (Style Map Family Name)", "styleMapStyleName" : "regular", "styleName" : "Regular (Style Name)", "unitsPerEm" : 1000, "ascender" : 750, "capHeight" : 750, "xHeight" : 500, "descender" : -250, "italicAngle" : -12.5, "versionMajor" : 1, "versionMinor" : 0, "year" : 2008, "copyright" : "Copyright Some Foundry.", "trademark" : "Trademark Some Foundry", "note" : "A note.", "macintoshFONDFamilyID" : 15000, "macintoshFONDName" : "SomeFont Regular (FOND Name)", "openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)", "openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.", "openTypeNameDesigner" : "Some Designer", "openTypeNameDesignerURL" : "http://somedesigner.com", "openTypeNameLicense" : "License info for Some Foundry.", "openTypeNameLicenseURL" : "http://somefoundry.com/license", "openTypeNameManufacturer" : "Some Foundry", "openTypeNameManufacturerURL" : "http://somefoundry.com", "openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)", "openTypeNamePreferredSubfamilyName": "Regular (Preferred Subfamily Name)", "openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)", "openTypeNameUniqueID" : "OpenType name Table Unique ID", "openTypeNameVersion" : "OpenType name Table Version", "openTypeOS2VendorID" : "SOME", "openTypeOS2WeightClass" : 500, "openTypeOS2WidthClass" : 5, "postscriptDefaultWidthX" : 400, "postscriptFontName" : "SomeFont-Regular (Postscript Font Name)", "postscriptFullName" : "Some Font-Regular (Postscript Full Name)", "postscriptSlantAngle" : -12.5, "postscriptUniqueID" : 4000000, "postscriptWeightName" : "Medium", "postscriptWindowsCharacterSet" : 1 } expectedFontInfo2To1Conversion = { "familyName" : "Some Font (Family Name)", "menuName" : "Some Font Regular (Style Map Family Name)", "fontStyle" : 64, "styleName" : "Regular (Style Name)", "unitsPerEm" : 1000, "ascender" : 750, "capHeight" : 750, "xHeight" : 500, "descender" : -250, "italicAngle" : -12.5, "versionMajor" : 1, "versionMinor" : 0, "copyright" : "Copyright Some Foundry.", "trademark" : "Trademark Some Foundry", "note" : "A note.", "fondID" : 15000, "fondName" : "SomeFont Regular (FOND Name)", "fullName" : "Some Font Regular (Compatible Full Name)", "notice" : "Some Font by Some Designer for Some Foundry.", "designer" : "Some Designer", "designerURL" : "http://somedesigner.com", "license" : "License info for Some Foundry.", "licenseURL" : "http://somefoundry.com/license", "createdBy" : "Some Foundry", "vendorURL" : "http://somefoundry.com", "otFamilyName" : "Some Font (Preferred Family Name)", "otStyleName" : "Regular (Preferred Subfamily Name)", "otMacName" : "Some Font Regular (Compatible Full Name)", "ttUniqueID" : "OpenType name Table Unique ID", "ttVersion" : "OpenType name Table Version", "ttVendor" : "SOME", "weightValue" : 500, "widthName" : "Medium (normal)", "defaultWidth" : 400, "fontName" : "SomeFont-Regular (Postscript Font Name)", "fullName" : "Some Font-Regular (Postscript Full Name)", "slantAngle" : -12.5, "uniqueID" : 4000000, "weightName" : "Medium", "msCharSet" : 0, "year" : 2008 } ufoLib-2.1.1/Lib/ufoLib/test/test_UFOConversion.py0000644000000000000000000002424713174123546020465 0ustar rootroot# -*- coding: utf-8 -*- import os import shutil import unittest import tempfile from io import open from ufoLib import convertUFOFormatVersion1ToFormatVersion2, UFOReader, UFOWriter from ufoLib.plistlib import readPlist, writePlist from ufoLib.test.testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion # the format version 1 lib.plist contains some data # that these tests shouldn't be concerned about. removeFromFormatVersion1Lib = [ "org.robofab.opentype.classes", "org.robofab.opentype.features", "org.robofab.opentype.featureorder", "org.robofab.postScriptHintData" ] class ConversionFunctionsTestCase(unittest.TestCase): def tearDown(self): path = self.getFontPath("TestFont1 (UFO1) converted.ufo") if os.path.exists(path): shutil.rmtree(path) path = self.getFontPath("TestFont1 (UFO2) converted.ufo") if os.path.exists(path): shutil.rmtree(path) def getFontPath(self, fileName): testdata = os.path.join(os.path.dirname(__file__), "testdata") return os.path.join(testdata, fileName) def compareFileStructures(self, path1, path2, expectedInfoData, testFeatures): # result metainfoPath1 = os.path.join(path1, "metainfo.plist") fontinfoPath1 = os.path.join(path1, "fontinfo.plist") kerningPath1 = os.path.join(path1, "kerning.plist") groupsPath1 = os.path.join(path1, "groups.plist") libPath1 = os.path.join(path1, "lib.plist") featuresPath1 = os.path.join(path1, "features.plist") glyphsPath1 = os.path.join(path1, "glyphs") glyphsPath1_contents = os.path.join(glyphsPath1, "contents.plist") glyphsPath1_A = os.path.join(glyphsPath1, "A_.glif") glyphsPath1_B = os.path.join(glyphsPath1, "B_.glif") # expected result metainfoPath2 = os.path.join(path2, "metainfo.plist") fontinfoPath2 = os.path.join(path2, "fontinfo.plist") kerningPath2 = os.path.join(path2, "kerning.plist") groupsPath2 = os.path.join(path2, "groups.plist") libPath2 = os.path.join(path2, "lib.plist") featuresPath2 = os.path.join(path2, "features.plist") glyphsPath2 = os.path.join(path2, "glyphs") glyphsPath2_contents = os.path.join(glyphsPath2, "contents.plist") glyphsPath2_A = os.path.join(glyphsPath2, "A_.glif") glyphsPath2_B = os.path.join(glyphsPath2, "B_.glif") # look for existence self.assertEqual(os.path.exists(metainfoPath1), True) self.assertEqual(os.path.exists(fontinfoPath1), True) self.assertEqual(os.path.exists(kerningPath1), True) self.assertEqual(os.path.exists(groupsPath1), True) self.assertEqual(os.path.exists(libPath1), True) self.assertEqual(os.path.exists(glyphsPath1), True) self.assertEqual(os.path.exists(glyphsPath1_contents), True) self.assertEqual(os.path.exists(glyphsPath1_A), True) self.assertEqual(os.path.exists(glyphsPath1_B), True) if testFeatures: self.assertEqual(os.path.exists(featuresPath1), True) # look for aggrement with open(metainfoPath1, "rb") as f: data1 = readPlist(f) with open(metainfoPath2, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) with open(fontinfoPath1, "rb") as f: data1 = readPlist(f) self.assertEqual(sorted(data1.items()), sorted(expectedInfoData.items())) with open(kerningPath1, "rb") as f: data1 = readPlist(f) with open(kerningPath2, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) with open(groupsPath1, "rb") as f: data1 = readPlist(f) with open(groupsPath2, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) with open(libPath1, "rb") as f: data1 = readPlist(f) with open(libPath2, "rb") as f: data2 = readPlist(f) if "UFO1" in libPath1: for key in removeFromFormatVersion1Lib: if key in data1: del data1[key] if "UFO1" in libPath2: for key in removeFromFormatVersion1Lib: if key in data2: del data2[key] self.assertEqual(data1, data2) with open(glyphsPath1_contents, "rb") as f: data1 = readPlist(f) with open(glyphsPath2_contents, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) with open(glyphsPath1_A, "rb") as f: data1 = readPlist(f) with open(glyphsPath2_A, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) with open(glyphsPath1_B, "rb") as f: data1 = readPlist(f) with open(glyphsPath2_B, "rb") as f: data2 = readPlist(f) self.assertEqual(data1, data2) def test1To2(self): path1 = self.getFontPath("TestFont1 (UFO1).ufo") path2 = self.getFontPath("TestFont1 (UFO1) converted.ufo") path3 = self.getFontPath("TestFont1 (UFO2).ufo") convertUFOFormatVersion1ToFormatVersion2(path1, path2) self.compareFileStructures(path2, path3, expectedFontInfo1To2Conversion, False) # --------------------- # kerning up conversion # --------------------- class TestInfoObject(object): pass class KerningUpConversionTestCase(unittest.TestCase): expectedKerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2 } expectedGroups = { "BGroup": ["B"], "CGroup": ["C", "Ccedilla"], "DGroup": ["D"], "public.kern1.BGroup": ["B"], "public.kern1.CGroup": ["C", "Ccedilla"], "public.kern2.CGroup": ["C", "Ccedilla"], "public.kern2.DGroup": ["D"], "Not A Kerning Group" : ["A"] } def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.ufoPath = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeUFO(self, formatVersion): self.clearUFO() if not os.path.exists(self.ufoPath): os.mkdir(self.ufoPath) # metainfo.plist metaInfo = dict(creator="test", formatVersion=formatVersion) path = os.path.join(self.ufoPath, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) # kerning kerning = { "A" : { "A" : 1, "B" : 2, "CGroup" : 3, "DGroup" : 4 }, "BGroup" : { "A" : 5, "B" : 6, "CGroup" : 7, "DGroup" : 8 }, "CGroup" : { "A" : 9, "B" : 10, "CGroup" : 11, "DGroup" : 12 } } path = os.path.join(self.ufoPath, "kerning.plist") with open(path, "wb") as f: writePlist(kerning, f) # groups groups = { "BGroup" : ["B"], "CGroup" : ["C", "Ccedilla"], "DGroup" : ["D"], "Not A Kerning Group" : ["A"] } path = os.path.join(self.ufoPath, "groups.plist") with open(path, "wb") as f: writePlist(groups, f) # font info fontInfo = { "familyName" : "Test" } path = os.path.join(self.ufoPath, "fontinfo.plist") with open(path, "wb") as f: writePlist(fontInfo, f) def clearUFO(self): if os.path.exists(self.ufoPath): shutil.rmtree(self.ufoPath) def testUFO1(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info) def testUFO2(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info) class KerningDownConversionTestCase(unittest.TestCase): expectedKerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2 } groups = { "BGroup": ["B"], "CGroup": ["C"], "DGroup": ["D"], "public.kern1.BGroup": ["B"], "public.kern1.CGroup": ["C", "Ccedilla"], "public.kern2.CGroup": ["C", "Ccedilla"], "public.kern2.DGroup": ["D"], "Not A Kerning Group" : ["A"] } expectedWrittenGroups = { "BGroup": ["B"], "CGroup": ["C", "Ccedilla"], "DGroup": ["D"], "Not A Kerning Group" : ["A"] } kerning = { ("public.kern1.BGroup", "public.kern2.CGroup"): 7, ("public.kern1.BGroup", "public.kern2.DGroup"): 8, ("public.kern1.BGroup", "A"): 5, ("public.kern1.BGroup", "B"): 6, ("public.kern1.CGroup", "public.kern2.CGroup"): 11, ("public.kern1.CGroup", "public.kern2.DGroup"): 12, ("public.kern1.CGroup", "A"): 9, ("public.kern1.CGroup", "B"): 10, ("A", "public.kern2.CGroup"): 3, ("A", "public.kern2.DGroup"): 4, ("A", "A"): 1, ("A", "B"): 2 } expectedWrittenKerning = { "BGroup" : { "CGroup" : 7, "DGroup" : 8, "A" : 5, "B" : 6 }, "CGroup" : { "CGroup" : 11, "DGroup" : 12, "A" : 9, "B" : 10 }, "A" : { "CGroup" : 3, "DGroup" : 4, "A" : 1, "B" : 2 } } downConversionMapping = { "side1" : { "BGroup" : "public.kern1.BGroup", "CGroup" : "public.kern1.CGroup" }, "side2" : { "CGroup" : "public.kern2.CGroup", "DGroup" : "public.kern2.DGroup" } } def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def tearDownUFO(self): shutil.rmtree(self.dstDir) def testWrite(self): writer = UFOWriter(self.dstDir, formatVersion=2) writer.setKerningGroupConversionRenameMaps(self.downConversionMapping) writer.writeKerning(self.kerning) writer.writeGroups(self.groups) # test groups path = os.path.join(self.dstDir, "groups.plist") with open(path, "rb") as f: writtenGroups = readPlist(f) self.assertEqual(writtenGroups, self.expectedWrittenGroups) # test kerning path = os.path.join(self.dstDir, "kerning.plist") with open(path, "rb") as f: writtenKerning = readPlist(f) self.assertEqual(writtenKerning, self.expectedWrittenKerning) self.tearDownUFO() if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/test_glifLib.py0000644000000000000000000001156513174123546017335 0ustar rootrootimport os import tempfile import shutil import unittest from io import open from ufoLib.test.testSupport import getDemoFontGlyphSetPath from ufoLib.glifLib import GlyphSet, glyphNameToFileName GLYPHSETDIR = getDemoFontGlyphSetPath() class GlyphSetTests(unittest.TestCase): def setUp(self): self.dstDir = tempfile.mktemp() os.mkdir(self.dstDir) def tearDown(self): shutil.rmtree(self.dstDir) def testRoundTrip(self): import difflib srcDir = GLYPHSETDIR dstDir = self.dstDir src = GlyphSet(srcDir, ufoFormatVersion=2) dst = GlyphSet(dstDir, ufoFormatVersion=2) for glyphName in src.keys(): g = src[glyphName] g.drawPoints(None) # load attrs dst.writeGlyph(glyphName, g, g.drawPoints) # compare raw file data: for glyphName in sorted(src.keys()): fileName = src.contents[glyphName] with open(os.path.join(srcDir, fileName), "r") as f: org = f.read() with open(os.path.join(dstDir, fileName), "r") as f: new = f.read() added = [] removed = [] for line in difflib.unified_diff( org.split("\n"), new.split("\n")): if line.startswith("+ "): added.append(line[1:]) elif line.startswith("- "): removed.append(line[1:]) self.assertEqual( added, removed, "%s.glif file differs after round tripping" % glyphName) def testRebuildContents(self): gset = GlyphSet(GLYPHSETDIR) contents = gset.contents gset.rebuildContents() self.assertEqual(contents, gset.contents) def testReverseContents(self): gset = GlyphSet(GLYPHSETDIR) d = {} for k, v in gset.getReverseContents().items(): d[v] = k org = {} for k, v in gset.contents.items(): org[k] = v.lower() self.assertEqual(d, org) def testReverseContents2(self): src = GlyphSet(GLYPHSETDIR) dst = GlyphSet(self.dstDir) dstMap = dst.getReverseContents() self.assertEqual(dstMap, {}) for glyphName in src.keys(): g = src[glyphName] g.drawPoints(None) # load attrs dst.writeGlyph(glyphName, g, g.drawPoints) self.assertNotEqual(dstMap, {}) srcMap = dict(src.getReverseContents()) # copy self.assertEqual(dstMap, srcMap) del srcMap["a.glif"] dst.deleteGlyph("a") self.assertEqual(dstMap, srcMap) def testCustomFileNamingScheme(self): def myGlyphNameToFileName(glyphName, glyphSet): return "prefix" + glyphNameToFileName(glyphName, glyphSet) src = GlyphSet(GLYPHSETDIR) dst = GlyphSet(self.dstDir, myGlyphNameToFileName) for glyphName in src.keys(): g = src[glyphName] g.drawPoints(None) # load attrs dst.writeGlyph(glyphName, g, g.drawPoints) d = {} for k, v in src.contents.items(): d[k] = "prefix" + v self.assertEqual(d, dst.contents) def testGetUnicodes(self): src = GlyphSet(GLYPHSETDIR) unicodes = src.getUnicodes() for glyphName in src.keys(): g = src[glyphName] g.drawPoints(None) # load attrs if not hasattr(g, "unicodes"): self.assertEqual(unicodes[glyphName], []) else: self.assertEqual(g.unicodes, unicodes[glyphName]) class FileNameTests(unittest.TestCase): def testDefaultFileNameScheme(self): self.assertEqual(glyphNameToFileName("a", None), "a.glif") self.assertEqual(glyphNameToFileName("A", None), "A_.glif") self.assertEqual(glyphNameToFileName("Aring", None), "A_ring.glif") self.assertEqual(glyphNameToFileName("F_A_B", None), "F__A__B_.glif") self.assertEqual(glyphNameToFileName("A.alt", None), "A_.alt.glif") self.assertEqual(glyphNameToFileName("A.Alt", None), "A_.A_lt.glif") self.assertEqual(glyphNameToFileName(".notdef", None), "_notdef.glif") self.assertEqual(glyphNameToFileName("T_H", None), "T__H_.glif") self.assertEqual(glyphNameToFileName("T_h", None), "T__h.glif") self.assertEqual(glyphNameToFileName("t_h", None), "t_h.glif") self.assertEqual(glyphNameToFileName("F_F_I", None), "F__F__I_.glif") self.assertEqual(glyphNameToFileName("f_f_i", None), "f_f_i.glif") self.assertEqual(glyphNameToFileName("AE", None), "A_E_.glif") self.assertEqual(glyphNameToFileName("Ae", None), "A_e.glif") self.assertEqual(glyphNameToFileName("ae", None), "ae.glif") self.assertEqual(glyphNameToFileName("aE", None), "aE_.glif") self.assertEqual(glyphNameToFileName("a.alt", None), "a.alt.glif") self.assertEqual(glyphNameToFileName("A.aLt", None), "A_.aL_t.glif") self.assertEqual(glyphNameToFileName("A.alT", None), "A_.alT_.glif") self.assertEqual(glyphNameToFileName("Aacute_V.swash", None), "A_acute_V_.swash.glif") self.assertEqual(glyphNameToFileName(".notdef", None), "_notdef.glif") self.assertEqual(glyphNameToFileName("con", None), "_con.glif") self.assertEqual(glyphNameToFileName("CON", None), "C_O_N_.glif") self.assertEqual(glyphNameToFileName("con.alt", None), "_con.alt.glif") self.assertEqual(glyphNameToFileName("alt.con", None), "alt._con.glif") if __name__ == "__main__": from ufoLib.test.testSupport import runTests import sys if len(sys.argv) > 1 and os.path.isdir(sys.argv[-1]): GLYPHSETDIR = sys.argv.pop() runTests() ufoLib-2.1.1/Lib/ufoLib/test/test_UFO2.py0000644000000000000000000015705013174123546016500 0ustar rootroot# -*- coding: utf-8 -*- import os import shutil import unittest import tempfile from io import open from ufoLib import UFOReader, UFOWriter, UFOLibError from ufoLib.plistlib import readPlist, writePlist from ufoLib.test.testSupport import fontInfoVersion2 class TestInfoObject(object): pass class ReadFontInfoVersion2TestCase(unittest.TestCase): def setUp(self): self.dstDir = tempfile.mktemp() os.mkdir(self.dstDir) metaInfo = { "creator": "test", "formatVersion": 2 } path = os.path.join(self.dstDir, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) def tearDown(self): shutil.rmtree(self.dstDir) def _writeInfoToPlist(self, info): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "wb") as f: writePlist(info, f) def testRead(self): originalData = dict(fontInfoVersion2) self._writeInfoToPlist(originalData) infoObject = TestInfoObject() reader = UFOReader(self.dstDir) reader.readInfo(infoObject) readData = {} for attr in list(fontInfoVersion2.keys()): readData[attr] = getattr(infoObject, attr) self.assertEqual(originalData, readData) def testGenericRead(self): # familyName info = dict(fontInfoVersion2) info["familyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleName info = dict(fontInfoVersion2) info["styleName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleMapFamilyName info = dict(fontInfoVersion2) info["styleMapFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # styleMapStyleName ## not a string info = dict(fontInfoVersion2) info["styleMapStyleName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion2) info["styleMapStyleName"] = "REGULAR" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # versionMajor info = dict(fontInfoVersion2) info["versionMajor"] = "1" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # versionMinor info = dict(fontInfoVersion2) info["versionMinor"] = "0" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # copyright info = dict(fontInfoVersion2) info["copyright"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # trademark info = dict(fontInfoVersion2) info["trademark"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # unitsPerEm info = dict(fontInfoVersion2) info["unitsPerEm"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # descender info = dict(fontInfoVersion2) info["descender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # xHeight info = dict(fontInfoVersion2) info["xHeight"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # capHeight info = dict(fontInfoVersion2) info["capHeight"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # ascender info = dict(fontInfoVersion2) info["ascender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # italicAngle info = dict(fontInfoVersion2) info["italicAngle"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testHeadRead(self): # openTypeHeadCreated ## not a string info = dict(fontInfoVersion2) info["openTypeHeadCreated"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## invalid format info = dict(fontInfoVersion2) info["openTypeHeadCreated"] = "2000-Jan-01 00:00:00" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHeadLowestRecPPEM info = dict(fontInfoVersion2) info["openTypeHeadLowestRecPPEM"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHeadFlags info = dict(fontInfoVersion2) info["openTypeHeadFlags"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testHheaRead(self): # openTypeHheaAscender info = dict(fontInfoVersion2) info["openTypeHheaAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaDescender info = dict(fontInfoVersion2) info["openTypeHheaDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaLineGap info = dict(fontInfoVersion2) info["openTypeHheaLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretSlopeRise info = dict(fontInfoVersion2) info["openTypeHheaCaretSlopeRise"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretSlopeRun info = dict(fontInfoVersion2) info["openTypeHheaCaretSlopeRun"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeHheaCaretOffset info = dict(fontInfoVersion2) info["openTypeHheaCaretOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testNameRead(self): # openTypeNameDesigner info = dict(fontInfoVersion2) info["openTypeNameDesigner"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameDesignerURL info = dict(fontInfoVersion2) info["openTypeNameDesignerURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameManufacturer info = dict(fontInfoVersion2) info["openTypeNameManufacturer"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameManufacturerURL info = dict(fontInfoVersion2) info["openTypeNameManufacturerURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameLicense info = dict(fontInfoVersion2) info["openTypeNameLicense"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameLicenseURL info = dict(fontInfoVersion2) info["openTypeNameLicenseURL"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameVersion info = dict(fontInfoVersion2) info["openTypeNameVersion"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameUniqueID info = dict(fontInfoVersion2) info["openTypeNameUniqueID"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameDescription info = dict(fontInfoVersion2) info["openTypeNameDescription"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNamePreferredFamilyName info = dict(fontInfoVersion2) info["openTypeNamePreferredFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNamePreferredSubfamilyName info = dict(fontInfoVersion2) info["openTypeNamePreferredSubfamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameCompatibleFullName info = dict(fontInfoVersion2) info["openTypeNameCompatibleFullName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameSampleText info = dict(fontInfoVersion2) info["openTypeNameSampleText"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameWWSFamilyName info = dict(fontInfoVersion2) info["openTypeNameWWSFamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeNameWWSSubfamilyName info = dict(fontInfoVersion2) info["openTypeNameWWSSubfamilyName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testOS2Read(self): # openTypeOS2WidthClass ## not an int info = dict(fontInfoVersion2) info["openTypeOS2WidthClass"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out or range info = dict(fontInfoVersion2) info["openTypeOS2WidthClass"] = 15 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WeightClass info = dict(fontInfoVersion2) ## not an int info["openTypeOS2WeightClass"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info["openTypeOS2WeightClass"] = -50 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Selection info = dict(fontInfoVersion2) info["openTypeOS2Selection"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2VendorID info = dict(fontInfoVersion2) info["openTypeOS2VendorID"] = 1234 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Panose ## not an int info = dict(fontInfoVersion2) info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too few values info = dict(fontInfoVersion2) info["openTypeOS2Panose"] = [0, 1, 2, 3] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2FamilyClass ## not an int info = dict(fontInfoVersion2) info["openTypeOS2FamilyClass"] = [1, str(1)] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too few values info = dict(fontInfoVersion2) info["openTypeOS2FamilyClass"] = [1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["openTypeOS2FamilyClass"] = [1, 1, 1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion2) info["openTypeOS2FamilyClass"] = [1, 201] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2UnicodeRanges ## not an int info = dict(fontInfoVersion2) info["openTypeOS2UnicodeRanges"] = ["0"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion2) info["openTypeOS2UnicodeRanges"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2CodePageRanges ## not an int info = dict(fontInfoVersion2) info["openTypeOS2CodePageRanges"] = ["0"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion2) info["openTypeOS2CodePageRanges"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoAscender info = dict(fontInfoVersion2) info["openTypeOS2TypoAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoDescender info = dict(fontInfoVersion2) info["openTypeOS2TypoDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2TypoLineGap info = dict(fontInfoVersion2) info["openTypeOS2TypoLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WinAscent info = dict(fontInfoVersion2) info["openTypeOS2WinAscent"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2WinDescent info = dict(fontInfoVersion2) info["openTypeOS2WinDescent"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2Type ## not an int info = dict(fontInfoVersion2) info["openTypeOS2Type"] = ["1"] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) ## out of range info = dict(fontInfoVersion2) info["openTypeOS2Type"] = [-1] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptXSize info = dict(fontInfoVersion2) info["openTypeOS2SubscriptXSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptYSize info = dict(fontInfoVersion2) info["openTypeOS2SubscriptYSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptXOffset info = dict(fontInfoVersion2) info["openTypeOS2SubscriptXOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SubscriptYOffset info = dict(fontInfoVersion2) info["openTypeOS2SubscriptYOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptXSize info = dict(fontInfoVersion2) info["openTypeOS2SuperscriptXSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptYSize info = dict(fontInfoVersion2) info["openTypeOS2SuperscriptYSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptXOffset info = dict(fontInfoVersion2) info["openTypeOS2SuperscriptXOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2SuperscriptYOffset info = dict(fontInfoVersion2) info["openTypeOS2SuperscriptYOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2StrikeoutSize info = dict(fontInfoVersion2) info["openTypeOS2StrikeoutSize"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeOS2StrikeoutPosition info = dict(fontInfoVersion2) info["openTypeOS2StrikeoutPosition"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testVheaRead(self): # openTypeVheaVertTypoAscender info = dict(fontInfoVersion2) info["openTypeVheaVertTypoAscender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaVertTypoDescender info = dict(fontInfoVersion2) info["openTypeVheaVertTypoDescender"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaVertTypoLineGap info = dict(fontInfoVersion2) info["openTypeVheaVertTypoLineGap"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretSlopeRise info = dict(fontInfoVersion2) info["openTypeVheaCaretSlopeRise"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretSlopeRun info = dict(fontInfoVersion2) info["openTypeVheaCaretSlopeRun"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # openTypeVheaCaretOffset info = dict(fontInfoVersion2) info["openTypeVheaCaretOffset"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testFONDRead(self): # macintoshFONDFamilyID info = dict(fontInfoVersion2) info["macintoshFONDFamilyID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # macintoshFONDName info = dict(fontInfoVersion2) info["macintoshFONDName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) def testPostscriptRead(self): # postscriptFontName info = dict(fontInfoVersion2) info["postscriptFontName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptFullName info = dict(fontInfoVersion2) info["postscriptFullName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptSlantAngle info = dict(fontInfoVersion2) info["postscriptSlantAngle"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject()) # postscriptUniqueID info = dict(fontInfoVersion2) info["postscriptUniqueID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptUnderlineThickness info = dict(fontInfoVersion2) info["postscriptUnderlineThickness"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptUnderlinePosition info = dict(fontInfoVersion2) info["postscriptUnderlinePosition"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptIsFixedPitch info = dict(fontInfoVersion2) info["postscriptIsFixedPitch"] = 2 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueValues ## not a list info = dict(fontInfoVersion2) info["postscriptBlueValues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion2) info["postscriptBlueValues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptBlueValues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptOtherBlues ## not a list info = dict(fontInfoVersion2) info["postscriptOtherBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion2) info["postscriptOtherBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptFamilyBlues ## not a list info = dict(fontInfoVersion2) info["postscriptFamilyBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion2) info["postscriptFamilyBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptFamilyBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptFamilyOtherBlues ## not a list info = dict(fontInfoVersion2) info["postscriptFamilyOtherBlues"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## uneven value count info = dict(fontInfoVersion2) info["postscriptFamilyOtherBlues"] = [500] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptFamilyOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptStemSnapH ## not list info = dict(fontInfoVersion2) info["postscriptStemSnapH"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptStemSnapH"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptStemSnapV ## not list info = dict(fontInfoVersion2) info["postscriptStemSnapV"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) ## too many values info = dict(fontInfoVersion2) info["postscriptStemSnapV"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueFuzz info = dict(fontInfoVersion2) info["postscriptBlueFuzz"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueShift info = dict(fontInfoVersion2) info["postscriptBlueShift"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptBlueScale info = dict(fontInfoVersion2) info["postscriptBlueScale"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptForceBold info = dict(fontInfoVersion2) info["postscriptForceBold"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptDefaultWidthX info = dict(fontInfoVersion2) info["postscriptDefaultWidthX"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptNominalWidthX info = dict(fontInfoVersion2) info["postscriptNominalWidthX"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptWeightName info = dict(fontInfoVersion2) info["postscriptWeightName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptDefaultCharacter info = dict(fontInfoVersion2) info["postscriptDefaultCharacter"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # postscriptWindowsCharacterSet info = dict(fontInfoVersion2) info["postscriptWindowsCharacterSet"] = -1 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # macintoshFONDFamilyID info = dict(fontInfoVersion2) info["macintoshFONDFamilyID"] = "abc" self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) # macintoshFONDName info = dict(fontInfoVersion2) info["macintoshFONDName"] = 123 self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject()) class WriteFontInfoVersion2TestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeInfoObject(self): infoObject = TestInfoObject() for attr, value in list(fontInfoVersion2.items()): setattr(infoObject, attr, value) return infoObject def readPlist(self): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "rb") as f: plist = readPlist(f) return plist def testWrite(self): infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=2) writer.writeInfo(infoObject) writtenData = self.readPlist() for attr, originalValue in list(fontInfoVersion2.items()): newValue = writtenData[attr] self.assertEqual(newValue, originalValue) def testGenericWrite(self): # familyName infoObject = self.makeInfoObject() infoObject.familyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # styleName infoObject = self.makeInfoObject() infoObject.styleName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # styleMapFamilyName infoObject = self.makeInfoObject() infoObject.styleMapFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # styleMapStyleName ## not a string infoObject = self.makeInfoObject() infoObject.styleMapStyleName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject = self.makeInfoObject() infoObject.styleMapStyleName = "REGULAR" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # versionMajor infoObject = self.makeInfoObject() infoObject.versionMajor = "1" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # versionMinor infoObject = self.makeInfoObject() infoObject.versionMinor = "0" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # copyright infoObject = self.makeInfoObject() infoObject.copyright = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # trademark infoObject = self.makeInfoObject() infoObject.trademark = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # unitsPerEm infoObject = self.makeInfoObject() infoObject.unitsPerEm = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # descender infoObject = self.makeInfoObject() infoObject.descender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # xHeight infoObject = self.makeInfoObject() infoObject.xHeight = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # capHeight infoObject = self.makeInfoObject() infoObject.capHeight = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # ascender infoObject = self.makeInfoObject() infoObject.ascender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # italicAngle infoObject = self.makeInfoObject() infoObject.italicAngle = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testHeadWrite(self): # openTypeHeadCreated ## not a string infoObject = self.makeInfoObject() infoObject.openTypeHeadCreated = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## invalid format infoObject = self.makeInfoObject() infoObject.openTypeHeadCreated = "2000-Jan-01 00:00:00" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHeadLowestRecPPEM infoObject = self.makeInfoObject() infoObject.openTypeHeadLowestRecPPEM = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHeadFlags infoObject = self.makeInfoObject() infoObject.openTypeHeadFlags = [-1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testHheaWrite(self): # openTypeHheaAscender infoObject = self.makeInfoObject() infoObject.openTypeHheaAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHheaDescender infoObject = self.makeInfoObject() infoObject.openTypeHheaDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHheaLineGap infoObject = self.makeInfoObject() infoObject.openTypeHheaLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHheaCaretSlopeRise infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretSlopeRise = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHheaCaretSlopeRun infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretSlopeRun = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeHheaCaretOffset infoObject = self.makeInfoObject() infoObject.openTypeHheaCaretOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testNameWrite(self): # openTypeNameDesigner infoObject = self.makeInfoObject() infoObject.openTypeNameDesigner = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameDesignerURL infoObject = self.makeInfoObject() infoObject.openTypeNameDesignerURL = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameManufacturer infoObject = self.makeInfoObject() infoObject.openTypeNameManufacturer = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameManufacturerURL infoObject = self.makeInfoObject() infoObject.openTypeNameManufacturerURL = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameLicense infoObject = self.makeInfoObject() infoObject.openTypeNameLicense = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameLicenseURL infoObject = self.makeInfoObject() infoObject.openTypeNameLicenseURL = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameVersion infoObject = self.makeInfoObject() infoObject.openTypeNameVersion = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameUniqueID infoObject = self.makeInfoObject() infoObject.openTypeNameUniqueID = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameDescription infoObject = self.makeInfoObject() infoObject.openTypeNameDescription = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNamePreferredFamilyName infoObject = self.makeInfoObject() infoObject.openTypeNamePreferredFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNamePreferredSubfamilyName infoObject = self.makeInfoObject() infoObject.openTypeNamePreferredSubfamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameCompatibleFullName infoObject = self.makeInfoObject() infoObject.openTypeNameCompatibleFullName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameSampleText infoObject = self.makeInfoObject() infoObject.openTypeNameSampleText = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameWWSFamilyName infoObject = self.makeInfoObject() infoObject.openTypeNameWWSFamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeNameWWSSubfamilyName infoObject = self.makeInfoObject() infoObject.openTypeNameWWSSubfamilyName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testOS2Write(self): # openTypeOS2WidthClass ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out or range infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = 15 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2WeightClass infoObject = self.makeInfoObject() ## not an int infoObject.openTypeOS2WeightClass = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject.openTypeOS2WeightClass = -50 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2Selection infoObject = self.makeInfoObject() infoObject.openTypeOS2Selection = [-1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2VendorID infoObject = self.makeInfoObject() infoObject.openTypeOS2VendorID = 1234 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2Panose ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too few values infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2FamilyClass ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [0, str(1)] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too few values infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1, 1, 1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2FamilyClass = [1, 20] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2UnicodeRanges ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2UnicodeRanges = ["0"] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2UnicodeRanges = [-1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2CodePageRanges ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2CodePageRanges = ["0"] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2CodePageRanges = [-1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2TypoAscender infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2TypoDescender infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2TypoLineGap infoObject = self.makeInfoObject() infoObject.openTypeOS2TypoLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2WinAscent infoObject = self.makeInfoObject() infoObject.openTypeOS2WinAscent = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2WinDescent infoObject = self.makeInfoObject() infoObject.openTypeOS2WinDescent = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2Type ## not an int infoObject = self.makeInfoObject() infoObject.openTypeOS2Type = ["1"] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## out of range infoObject = self.makeInfoObject() infoObject.openTypeOS2Type = [-1] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SubscriptXSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptXSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SubscriptYSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptYSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SubscriptXOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptXOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SubscriptYOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SubscriptYOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SuperscriptXSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptXSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SuperscriptYSize infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptYSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SuperscriptXOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptXOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2SuperscriptYOffset infoObject = self.makeInfoObject() infoObject.openTypeOS2SuperscriptYOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2StrikeoutSize infoObject = self.makeInfoObject() infoObject.openTypeOS2StrikeoutSize = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeOS2StrikeoutPosition infoObject = self.makeInfoObject() infoObject.openTypeOS2StrikeoutPosition = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testVheaWrite(self): # openTypeVheaVertTypoAscender infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoAscender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeVheaVertTypoDescender infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoDescender = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeVheaVertTypoLineGap infoObject = self.makeInfoObject() infoObject.openTypeVheaVertTypoLineGap = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeVheaCaretSlopeRise infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretSlopeRise = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeVheaCaretSlopeRun infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretSlopeRun = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # openTypeVheaCaretOffset infoObject = self.makeInfoObject() infoObject.openTypeVheaCaretOffset = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testFONDWrite(self): # macintoshFONDFamilyID infoObject = self.makeInfoObject() infoObject.macintoshFONDFamilyID = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # macintoshFONDName infoObject = self.makeInfoObject() infoObject.macintoshFONDName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) def testPostscriptWrite(self): # postscriptFontName infoObject = self.makeInfoObject() infoObject.postscriptFontName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptFullName infoObject = self.makeInfoObject() infoObject.postscriptFullName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptSlantAngle infoObject = self.makeInfoObject() infoObject.postscriptSlantAngle = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptUniqueID infoObject = self.makeInfoObject() infoObject.postscriptUniqueID = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptUnderlineThickness infoObject = self.makeInfoObject() infoObject.postscriptUnderlineThickness = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptUnderlinePosition infoObject = self.makeInfoObject() infoObject.postscriptUnderlinePosition = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptIsFixedPitch infoObject = self.makeInfoObject() infoObject.postscriptIsFixedPitch = 2 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptBlueValues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = [500] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptBlueValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptOtherBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptFamilyBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptFamilyBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptFamilyOtherBlues ## not a list infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## uneven value count infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = [500] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptFamilyOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptStemSnapH ## not list infoObject = self.makeInfoObject() infoObject.postscriptStemSnapH = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptStemSnapH = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptStemSnapV ## not list infoObject = self.makeInfoObject() infoObject.postscriptStemSnapV = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) ## too many values infoObject = self.makeInfoObject() infoObject.postscriptStemSnapV = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160] writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptBlueFuzz infoObject = self.makeInfoObject() infoObject.postscriptBlueFuzz = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptBlueShift infoObject = self.makeInfoObject() infoObject.postscriptBlueShift = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptBlueScale infoObject = self.makeInfoObject() infoObject.postscriptBlueScale = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptForceBold infoObject = self.makeInfoObject() infoObject.postscriptForceBold = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptDefaultWidthX infoObject = self.makeInfoObject() infoObject.postscriptDefaultWidthX = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptNominalWidthX infoObject = self.makeInfoObject() infoObject.postscriptNominalWidthX = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptWeightName infoObject = self.makeInfoObject() infoObject.postscriptWeightName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptDefaultCharacter infoObject = self.makeInfoObject() infoObject.postscriptDefaultCharacter = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # postscriptWindowsCharacterSet infoObject = self.makeInfoObject() infoObject.postscriptWindowsCharacterSet = -1 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # macintoshFONDFamilyID infoObject = self.makeInfoObject() infoObject.macintoshFONDFamilyID = "abc" writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) # macintoshFONDName infoObject = self.makeInfoObject() infoObject.macintoshFONDName = 123 writer = UFOWriter(self.dstDir, formatVersion=2) self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject) if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/testdata/0000755000000000000000000000000013174130416016147 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/0000755000000000000000000000000013174130416021403 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/features.fea0000644000000000000000000000007413174123546023705 0ustar rootroot@myClass = [A B]; feature liga { sub A A by b; } liga; ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/kerning.plist0000644000000000000000000000052413174123546024124 0ustar rootroot A B 100 B A -200 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/fontinfo.plist0000644000000000000000000001522613174123546024316 0ustar rootroot ascender 750 capHeight 750 copyright Copyright Some Foundry. descender -250 familyName Some Font (Family Name) italicAngle -12.5 macintoshFONDFamilyID 15000 macintoshFONDName SomeFont Regular (FOND Name) note A note. openTypeHeadCreated 2000/01/01 00:00:00 openTypeHeadFlags 0 1 openTypeHeadLowestRecPPEM 10 openTypeHheaAscender 750 openTypeHheaCaretOffset 0 openTypeHheaCaretSlopeRise 1 openTypeHheaCaretSlopeRun 0 openTypeHheaDescender -250 openTypeHheaLineGap 200 openTypeNameCompatibleFullName Some Font Regular (Compatible Full Name) openTypeNameDescription Some Font by Some Designer for Some Foundry. openTypeNameDesigner Some Designer openTypeNameDesignerURL http://somedesigner.com openTypeNameLicense License info for Some Foundry. openTypeNameLicenseURL http://somefoundry.com/license openTypeNameManufacturer Some Foundry openTypeNameManufacturerURL http://somefoundry.com openTypeNamePreferredFamilyName Some Font (Preferred Family Name) openTypeNamePreferredSubfamilyName Regular (Preferred Subfamily Name) openTypeNameSampleText Sample Text for Some Font. openTypeNameUniqueID OpenType name Table Unique ID openTypeNameVersion OpenType name Table Version openTypeNameWWSFamilyName Some Font (WWS Family Name) openTypeNameWWSSubfamilyName Regular (WWS Subfamily Name) openTypeOS2CodePageRanges 0 1 openTypeOS2Panose 0 1 2 3 4 5 6 7 8 9 openTypeOS2FamilyClass 1 1 openTypeOS2Selection 3 openTypeOS2StrikeoutPosition 300 openTypeOS2StrikeoutSize 20 openTypeOS2SubscriptXOffset 0 openTypeOS2SubscriptXSize 200 openTypeOS2SubscriptYOffset -100 openTypeOS2SubscriptYSize 400 openTypeOS2SuperscriptXOffset 0 openTypeOS2SuperscriptXSize 200 openTypeOS2SuperscriptYOffset 200 openTypeOS2SuperscriptYSize 400 openTypeOS2Type openTypeOS2TypoAscender 750 openTypeOS2TypoDescender -250 openTypeOS2TypoLineGap 200 openTypeOS2UnicodeRanges 0 1 openTypeOS2VendorID SOME openTypeOS2WeightClass 500 openTypeOS2WidthClass 5 openTypeOS2WinAscent 750 openTypeOS2WinDescent -250 openTypeVheaCaretOffset 0 openTypeVheaCaretSlopeRise 0 openTypeVheaCaretSlopeRun 1 openTypeVheaVertTypoAscender 750 openTypeVheaVertTypoDescender -250 openTypeVheaVertTypoLineGap 200 postscriptBlueFuzz 1 postscriptBlueScale 0.039625 postscriptBlueShift 7 postscriptBlueValues 500 510 postscriptDefaultCharacter .notdef postscriptDefaultWidthX 400 postscriptFamilyBlues 500 510 postscriptFamilyOtherBlues -250 -260 postscriptFontName SomeFont-Regular (Postscript Font Name) postscriptForceBold postscriptFullName Some Font-Regular (Postscript Full Name) postscriptIsFixedPitch postscriptNominalWidthX 400 postscriptOtherBlues -250 -260 postscriptSlantAngle -12.5 postscriptStemSnapH 100 120 postscriptStemSnapV 80 90 postscriptUnderlinePosition -200 postscriptUnderlineThickness 20 postscriptUniqueID 4000000 postscriptWeightName Medium postscriptWindowsCharacterSet 1 styleMapFamilyName Some Font Regular (Style Map Family Name) styleMapStyleName regular styleName Regular (Style Name) trademark Trademark Some Foundry unitsPerEm 1000 versionMajor 1 versionMinor 0 xHeight 500 year 2008 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/0000755000000000000000000000000013174130416022711 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/B_.glif0000644000000000000000000000120613174123546024101 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/A_.glif0000644000000000000000000000053113174123546024100 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/glyphs/contents.plist0000644000000000000000000000042513174123546025632 0ustar rootroot A A_.glif B B_.glif ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/groups.plist0000644000000000000000000000052013174123546024002 0ustar rootroot group1 A group2 A B ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/metainfo.plist0000644000000000000000000000045613174123546024275 0ustar rootroot creator org.robofab.ufoLib formatVersion 2 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO2).ufo/lib.plist0000644000000000000000000000040713174123546023235 0ustar rootroot org.robofab.testFontLibData Foo Bar ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/0000755000000000000000000000000013174130416020452 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/fontinfo.plist0000644000000000000000000000232713174123546023363 0ustar rootroot ascender 750 capHeight 527 copyright TOKEN COPYRIGHT STRING. COPYRIGHT SAME AS PACKAGE. defaultWidth 500 descender -170 designer Various designerURL familyName UFODEMOFONT fontStyle 64 license LICENSE SAME AS PACKAGE. notice TOKEN DESCRIPTION styleName JUSTADEMO trademark NO TRADEMARKS ttVendor NONE ttVersion Version 1.000;PS development 5;hotconv 1.0.38 unitsPerEm 1000 vendorURL versionMajor 1 xHeight 456 year 2003 ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/0000755000000000000000000000000013174130416021760 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/B_.glif0000644000000000000000000000272513174123546023157 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/G_.glif0000644000000000000000000000275413174123546023166 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/F_.glif0000644000000000000000000000125613174123546023161 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/testglyph1.reversed.glif0000644000000000000000000000103413174123546026551 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/testglyph1.glif0000644000000000000000000000102313174123546024731 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/R_.glif0000644000000000000000000000225213174123546023172 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/O_.glif0000644000000000000000000000244313174123546023171 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/a.glif0000644000000000000000000000017513174123546023054 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/A_.glif0000644000000000000000000000230413174123546023147 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/contents.plist0000644000000000000000000000122213174123546024675 0ustar rootroot A A_.glif B B_.glif F F_.glif F_A_B F__A__B_.glif G G_.glif O O_.glif R R_.glif a a.glif testglyph1 testglyph1.glif testglyph1.reversed testglyph1.reversed.glif ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/glyphs/F__A__B_.glif0000644000000000000000000000053513174123546024157 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/metainfo.plist0000644000000000000000000000045613174123546023344 0ustar rootroot creator org.robofab.ufoLib formatVersion 1 ufoLib-2.1.1/Lib/ufoLib/test/testdata/DemoFont.ufo/lib.plist0000644000000000000000000000030513174123546022301 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/0000755000000000000000000000000013174130416021402 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/kerning.plist0000644000000000000000000000052413174123546024123 0ustar rootroot A B 100 B A -200 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/fontinfo.plist0000644000000000000000000000472313174123546024315 0ustar rootroot ascender 750 capHeight 750 copyright Copyright Some Foundry. createdBy Some Foundry defaultWidth 400 descender -250 designer Some Designer designerURL http://somedesigner.com familyName Some Font (Family Name) fondID 15000 fondName SomeFont Regular (FOND Name) fontName SomeFont-Regular (Postscript Font Name) fontStyle 64 fullName Some Font-Regular (Postscript Full Name) italicAngle -12.5 license License info for Some Foundry. licenseURL http://somefoundry.com/license menuName Some Font Regular (Style Map Family Name) msCharSet 0 note A note. notice Some Font by Some Designer for Some Foundry. otFamilyName Some Font (Preferred Family Name) otMacName Some Font Regular (Compatible Full Name) otStyleName Regular (Preferred Subfamily Name) slantAngle -12.5 styleName Regular (Style Name) trademark Trademark Some Foundry ttUniqueID OpenType name Table Unique ID ttVendor SOME ttVersion OpenType name Table Version uniqueID 4000000 unitsPerEm 1000 vendorURL http://somefoundry.com versionMajor 1 versionMinor 0 weightName Medium weightValue 500 widthName Medium (normal) xHeight 500 year 2008 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/0000755000000000000000000000000013174130416022710 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/B_.glif0000644000000000000000000000120613174123546024100 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/A_.glif0000644000000000000000000000053113174123546024077 0ustar rootroot ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/glyphs/contents.plist0000644000000000000000000000042513174123546025631 0ustar rootroot A A_.glif B B_.glif ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/groups.plist0000644000000000000000000000052013174123546024001 0ustar rootroot group1 A group2 A B ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/metainfo.plist0000644000000000000000000000045613174123546024274 0ustar rootroot creator org.robofab.ufoLib formatVersion 1 ufoLib-2.1.1/Lib/ufoLib/test/testdata/TestFont1 (UFO1).ufo/lib.plist0000644000000000000000000000271213174123546023235 0ustar rootroot org.robofab.opentype.classes @myClass = [A B]; org.robofab.opentype.featureorder liga org.robofab.opentype.features liga feature liga { sub A A by b; } liga; org.robofab.postScriptHintData blueFuzz 1 blueScale 0.039625 blueShift 7 blueValues 500 510 familyBlues 500 510 familyOtherBlues -260 -250 forceBold hStems 100 120 otherBlues -260 -250 vStems 80 90 org.robofab.testFontLibData Foo Bar ufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/0000755000000000000000000000000013174130416021156 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/0000755000000000000000000000000013174130416022067 5ustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.file.txt0000644000000000000000000000001013174123546030032 0ustar rootrootfile.txtufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/0000755000000000000000000000000013174130416030361 5ustar rootroot././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.txtufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.tx0000644000000000000000000000000713174123546031524 0ustar rootrootfoo.txtufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/0000755000000000000000000000000013174130416031125 5ustar rootroot././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txtufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lo0000644000000000000000000000000713174123546031465 0ustar rootrootlol.txtufoLib-2.1.1/Lib/ufoLib/test/testdata/UFO3-Read Data.ufo/metainfo.plist0000644000000000000000000000045613174123546024050 0ustar rootroot creator org.robofab.ufoLib formatVersion 3 ufoLib-2.1.1/Lib/ufoLib/test/test_UFO1.py0000644000000000000000000001015013174123546016464 0ustar rootroot# -*- coding: utf-8 -*- import os import shutil import unittest import tempfile from io import open from ufoLib import UFOReader, UFOWriter, UFOLibError from ufoLib.plistlib import readPlist, writePlist from ufoLib.test.testSupport import fontInfoVersion1, fontInfoVersion2 class TestInfoObject(object): pass class ReadFontInfoVersion1TestCase(unittest.TestCase): def setUp(self): self.dstDir = tempfile.mktemp() os.mkdir(self.dstDir) metaInfo = { "creator": "test", "formatVersion": 1 } path = os.path.join(self.dstDir, "metainfo.plist") with open(path, "wb") as f: writePlist(metaInfo, f) def tearDown(self): shutil.rmtree(self.dstDir) def _writeInfoToPlist(self, info): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "wb") as f: writePlist(info, f) def testRead(self): originalData = dict(fontInfoVersion1) self._writeInfoToPlist(originalData) infoObject = TestInfoObject() reader = UFOReader(self.dstDir) reader.readInfo(infoObject) for attr in dir(infoObject): if attr not in fontInfoVersion2: continue originalValue = fontInfoVersion2[attr] readValue = getattr(infoObject, attr) self.assertEqual(originalValue, readValue) def testFontStyleConversion(self): fontStyle1To2 = { 64 : "regular", 1 : "italic", 32 : "bold", 33 : "bold italic" } for old, new in list(fontStyle1To2.items()): info = dict(fontInfoVersion1) info["fontStyle"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.styleMapStyleName) def testWidthNameConversion(self): widthName1To2 = { "Ultra-condensed" : 1, "Extra-condensed" : 2, "Condensed" : 3, "Semi-condensed" : 4, "Medium (normal)" : 5, "Semi-expanded" : 6, "Expanded" : 7, "Extra-expanded" : 8, "Ultra-expanded" : 9 } for old, new in list(widthName1To2.items()): info = dict(fontInfoVersion1) info["widthName"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.openTypeOS2WidthClass) class WriteFontInfoVersion1TestCase(unittest.TestCase): def setUp(self): self.tempDir = tempfile.mktemp() os.mkdir(self.tempDir) self.dstDir = os.path.join(self.tempDir, "test.ufo") def tearDown(self): shutil.rmtree(self.tempDir) def makeInfoObject(self): infoObject = TestInfoObject() for attr, value in list(fontInfoVersion2.items()): setattr(infoObject, attr, value) return infoObject def readPlist(self): path = os.path.join(self.dstDir, "fontinfo.plist") with open(path, "rb") as f: plist = readPlist(f) return plist def testWrite(self): infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() for attr, originalValue in list(fontInfoVersion1.items()): newValue = writtenData[attr] self.assertEqual(newValue, originalValue) def testFontStyleConversion(self): fontStyle1To2 = { 64 : "regular", 1 : "italic", 32 : "bold", 33 : "bold italic" } for old, new in list(fontStyle1To2.items()): infoObject = self.makeInfoObject() infoObject.styleMapStyleName = new writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() self.assertEqual(writtenData["fontStyle"], old) def testWidthNameConversion(self): widthName1To2 = { "Ultra-condensed" : 1, "Extra-condensed" : 2, "Condensed" : 3, "Semi-condensed" : 4, "Medium (normal)" : 5, "Semi-expanded" : 6, "Expanded" : 7, "Extra-expanded" : 8, "Ultra-expanded" : 9 } for old, new in list(widthName1To2.items()): infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = new writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() self.assertEqual(writtenData["widthName"], old) if __name__ == "__main__": from ufoLib.test.testSupport import runTests runTests() ufoLib-2.1.1/Lib/ufoLib/test/__init__.py0000644000000000000000000000000013174123546016443 0ustar rootrootufoLib-2.1.1/Lib/ufoLib/__init__.py0000755000000000000000000020147713174123546015514 0ustar rootrootimport os import shutil from io import StringIO, BytesIO, open import codecs from copy import deepcopy from fontTools.misc.py23 import basestring, unicode from ufoLib.glifLib import GlyphSet from ufoLib.validators import * from ufoLib.filenames import userNameToFileName from ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning from ufoLib.plistlib import readPlist, writePlist """ A library for importing .ufo files and their descendants. Refer to http://unifiedfontobject.com for the UFO specification. The UFOReader and UFOWriter classes support versions 1, 2 and 3 of the specification. Sets that list the font info attribute names for the fontinfo.plist formats are available for external use. These are: fontInfoAttributesVersion1 fontInfoAttributesVersion2 fontInfoAttributesVersion3 A set listing the fontinfo.plist attributes that were deprecated in version 2 is available for external use: deprecatedFontInfoAttributesVersion2 Functions that do basic validation on values for fontinfo.plist are available for external use. These are validateFontInfoVersion2ValueForAttribute validateFontInfoVersion3ValueForAttribute Value conversion functions are available for converting fontinfo.plist values between the possible format versions. convertFontInfoValueForAttributeFromVersion1ToVersion2 convertFontInfoValueForAttributeFromVersion2ToVersion1 convertFontInfoValueForAttributeFromVersion2ToVersion3 convertFontInfoValueForAttributeFromVersion3ToVersion2 """ __all__ = [ "makeUFOPath" "UFOLibError", "UFOReader", "UFOWriter", "fontInfoAttributesVersion1", "fontInfoAttributesVersion2", "fontInfoAttributesVersion3", "deprecatedFontInfoAttributesVersion2", "validateFontInfoVersion2ValueForAttribute", "validateFontInfoVersion3ValueForAttribute", "convertFontInfoValueForAttributeFromVersion1ToVersion2", "convertFontInfoValueForAttributeFromVersion2ToVersion1" # deprecated "convertUFOFormatVersion1ToFormatVersion2" ] __version__ = "2.1.1" class UFOLibError(Exception): pass # ---------- # File Names # ---------- DEFAULT_GLYPHS_DIRNAME = "glyphs" DATA_DIRNAME = "data" IMAGES_DIRNAME = "images" METAINFO_FILENAME = "metainfo.plist" FONTINFO_FILENAME = "fontinfo.plist" LIB_FILENAME = "lib.plist" GROUPS_FILENAME = "groups.plist" KERNING_FILENAME = "kerning.plist" FEATURES_FILENAME = "features.fea" LAYERCONTENTS_FILENAME = "layercontents.plist" LAYERINFO_FILENAME = "layerinfo.plist" DEFAULT_LAYER_NAME = "public.default" supportedUFOFormatVersions = [1, 2, 3] # -------------- # Shared Methods # -------------- def _getPlist(self, fileName, default=None): """ Read a property list relative to the path argument of UFOReader. If the file is missing and default is None a UFOLibError will be raised otherwise default is returned. The errors that could be raised during the reading of a plist are unpredictable and/or too large to list, so, a blind try: except: is done. If an exception occurs, a UFOLibError will be raised. """ path = os.path.join(self._path, fileName) if not os.path.exists(path): if default is not None: return default else: raise UFOLibError("%s is missing in %s. This file is required" % (fileName, self._path)) try: with open(path, "rb") as f: return readPlist(f) except: raise UFOLibError("The file %s could not be read." % fileName) # ---------- # UFO Reader # ---------- class UFOReader(object): """Read the various components of the .ufo.""" def __init__(self, path): if not os.path.exists(path): raise UFOLibError("The specified UFO doesn't exist.") self._path = path self.readMetaInfo() self._upConvertedKerningData = None # properties def _get_formatVersion(self): return self._formatVersion formatVersion = property(_get_formatVersion, doc="The format version of the UFO. This is determined by reading metainfo.plist during __init__.") # up conversion def _upConvertKerning(self): """ Up convert kerning and groups in UFO 1 and 2. The data will be held internally until each bit of data has been retrieved. The conversion of both must be done at once, so the raw data is cached and an error is raised if one bit of data becomes obsolete before it is called. """ if self._upConvertedKerningData: testKerning = self._readKerning() if testKerning != self._upConvertedKerningData["originalKerning"]: raise UFOLibError("The data in kerning.plist has been modified since it was converted to UFO 3 format.") testGroups = self._readGroups() if testGroups != self._upConvertedKerningData["originalGroups"]: raise UFOLibError("The data in groups.plist has been modified since it was converted to UFO 3 format.") else: groups = self._readGroups() invalidFormatMessage = "groups.plist is not properly formatted." if not isinstance(groups, dict): raise UFOLibError(invalidFormatMessage) for groupName, glyphList in list(groups.items()): if not isinstance(groupName, basestring): raise UFOLibError(invalidFormatMessage) elif not isinstance(glyphList, list): raise UFOLibError(invalidFormatMessage) for glyphName in glyphList: if not isinstance(glyphName, basestring): raise UFOLibError(invalidFormatMessage) self._upConvertedKerningData = dict( kerning={}, originalKerning=self._readKerning(), groups={}, originalGroups=groups ) # convert kerning and groups kerning, groups, conversionMaps = convertUFO1OrUFO2KerningToUFO3Kerning( self._upConvertedKerningData["originalKerning"], deepcopy(self._upConvertedKerningData["originalGroups"]) ) # store self._upConvertedKerningData["kerning"] = kerning self._upConvertedKerningData["groups"] = groups self._upConvertedKerningData["groupRenameMaps"] = conversionMaps # support methods _checkForFile = staticmethod(os.path.exists) _getPlist = _getPlist def readBytesFromPath(self, path, encoding=None): """ Returns the bytes in the file at the given path. The path must be relative to the UFO path. Returns None if the file does not exist. An encoding may be passed if needed. """ fullPath = os.path.join(self._path, path) if not self._checkForFile(fullPath): return None if os.path.isdir(fullPath): raise UFOLibError("%s is a directory." % path) if encoding: f = open(fullPath, encoding=encoding) else: f = open(fullPath, "rb", encoding=encoding) data = f.read() f.close() return data def getReadFileForPath(self, path, encoding=None): """ Returns a file (or file-like) object for the file at the given path. The path must be relative to the UFO path. Returns None if the file does not exist. An encoding may be passed if needed. Note: The caller is responsible for closing the open file. """ fullPath = os.path.join(self._path, path) if not self._checkForFile(fullPath): return None if os.path.isdir(fullPath): raise UFOLibError("%s is a directory." % path) if encoding: f = open(fullPath, "rb", encoding=encoding) else: f = open(fullPath, "r") return f def getFileModificationTime(self, path): """ Returns the modification time (as reported by os.path.getmtime) for the file at the given path. The path must be relative to the UFO path. Returns None if the file does not exist. """ fullPath = os.path.join(self._path, path) if not self._checkForFile(fullPath): return None return os.path.getmtime(fullPath) # metainfo.plist def readMetaInfo(self): """ Read metainfo.plist. Only used for internal operations. """ # should there be a blind try/except with a UFOLibError # raised in except here (and elsewhere)? It would be nice to # provide external callers with a single exception to catch. data = self._getPlist(METAINFO_FILENAME) if not isinstance(data, dict): raise UFOLibError("metainfo.plist is not properly formatted.") formatVersion = data["formatVersion"] if not isinstance(formatVersion, int): metaplist_path = os.path.join(self._path, METAINFO_FILENAME) raise UFOLibError("formatVersion must be specified as an integer in " + metaplist_path) if formatVersion not in supportedUFOFormatVersions: raise UFOLibError("Unsupported UFO format (%d) in %s." % (formatVersion, self._path)) self._formatVersion = formatVersion # groups.plist def _readGroups(self): return self._getPlist(GROUPS_FILENAME, {}) def readGroups(self): """ Read groups.plist. Returns a dict. """ # handle up conversion if self._formatVersion < 3: self._upConvertKerning() groups = self._upConvertedKerningData["groups"] # normal else: groups = self._readGroups() valid, message = groupsValidator(groups) if not valid: raise UFOLibError(message) return groups def getKerningGroupConversionRenameMaps(self): """ Get maps defining the renaming that was done during any needed kerning group conversion. This method returns a dictionary of this form: { "side1" : {"old group name" : "new group name"}, "side2" : {"old group name" : "new group name"} } When no conversion has been performed, the side1 and side2 dictionaries will be empty. """ if self._formatVersion >= 3: return dict(side1={}, side2={}) # use the public group reader to force the load and # conversion of the data if it hasn't happened yet. self.readGroups() return self._upConvertedKerningData["groupRenameMaps"] # fontinfo.plist def _readInfo(self): data = self._getPlist(FONTINFO_FILENAME, {}) if not isinstance(data, dict): raise UFOLibError("fontinfo.plist is not properly formatted.") return data def readInfo(self, info): """ Read fontinfo.plist. It requires an object that allows setting attributes with names that follow the fontinfo.plist version 3 specification. This will write the attributes defined in the file into the object. """ infoDict = self._readInfo() infoDataToSet = {} # version 1 if self._formatVersion == 1: for attr in fontInfoAttributesVersion1: value = infoDict.get(attr) if value is not None: infoDataToSet[attr] = value infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet) infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet) # version 2 elif self._formatVersion == 2: for attr, dataValidationDict in list(fontInfoAttributesVersion2ValueData.items()): value = infoDict.get(attr) if value is None: continue infoDataToSet[attr] = value infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet) # version 3 elif self._formatVersion == 3: for attr, dataValidationDict in list(fontInfoAttributesVersion3ValueData.items()): value = infoDict.get(attr) if value is None: continue infoDataToSet[attr] = value # unsupported version else: raise NotImplementedError # validate data infoDataToSet = validateInfoVersion3Data(infoDataToSet) # populate the object for attr, value in list(infoDataToSet.items()): try: setattr(info, attr, value) except AttributeError: raise UFOLibError("The supplied info object does not support setting a necessary attribute (%s)." % attr) # kerning.plist def _readKerning(self): data = self._getPlist(KERNING_FILENAME, {}) return data def readKerning(self): """ Read kerning.plist. Returns a dict. """ # handle up conversion if self._formatVersion < 3: self._upConvertKerning() kerningNested = self._upConvertedKerningData["kerning"] # normal else: kerningNested = self._readKerning() valid, message = kerningValidator(kerningNested) if not valid: raise UFOLibError(message) # flatten kerning = {} for left in kerningNested: for right in kerningNested[left]: value = kerningNested[left][right] kerning[left, right] = value return kerning # lib.plist def readLib(self): """ Read lib.plist. Returns a dict. """ data = self._getPlist(LIB_FILENAME, {}) valid, message = fontLibValidator(data) if not valid: raise UFOLibError(message) return data # features.fea def readFeatures(self): """ Read features.fea. Returns a string. """ path = os.path.join(self._path, FEATURES_FILENAME) if not self._checkForFile(path): return "" with open(path, "r") as f: text = f.read() return text # glyph sets & layers def _readLayerContents(self): """ Rebuild the layer contents list by checking what glyphsets are available on disk. """ if self._formatVersion < 3: return [(DEFAULT_LAYER_NAME, DEFAULT_GLYPHS_DIRNAME)] # read the file on disk contents = self._getPlist(LAYERCONTENTS_FILENAME) valid, error = layerContentsValidator(contents, self._path) if not valid: raise UFOLibError(error) return contents def getLayerNames(self): """ Get the ordered layer names from layercontents.plist. """ layerContents = self._readLayerContents() layerNames = [layerName for layerName, directoryName in layerContents] return layerNames def getDefaultLayerName(self): """ Get the default layer name from layercontents.plist. """ layerContents = self._readLayerContents() for layerName, layerDirectory in layerContents: if layerDirectory == DEFAULT_GLYPHS_DIRNAME: return layerName # this will already have been raised during __init__ raise UFOLibError("The default layer is not defined in layercontents.plist.") def getGlyphSet(self, layerName=None): """ Return the GlyphSet associated with the glyphs directory mapped to layerName in the UFO. If layerName is not provided, the name retrieved with getDefaultLayerName will be used. """ if layerName is None: layerName = self.getDefaultLayerName() directory = None layerContents = self._readLayerContents() for storedLayerName, storedLayerDirectory in layerContents: if layerName == storedLayerName: directory = storedLayerDirectory break if directory is None: raise UFOLibError("No glyphs directory is mapped to \"%s\"." % layerName) glyphsPath = os.path.join(self._path, directory) return GlyphSet(glyphsPath, ufoFormatVersion=self._formatVersion) def getCharacterMapping(self, layerName=None): """ Return a dictionary that maps unicode values (ints) to lists of glyph names. """ glyphSet = self.getGlyphSet(layerName) allUnicodes = glyphSet.getUnicodes() cmap = {} for glyphName, unicodes in allUnicodes.items(): for code in unicodes: if code in cmap: cmap[code].append(glyphName) else: cmap[code] = [glyphName] return cmap # /data def getDataDirectoryListing(self, maxDepth=100): """ Returns a list of all files in the data directory. The returned paths will be relative to the UFO. This will not list directory names, only file names. Thus, empty directories will be skipped. The maxDepth argument sets the maximum number of sub-directories that are allowed. """ path = os.path.join(self._path, DATA_DIRNAME) if not self._checkForFile(path): return [] listing = self._getDirectoryListing(path, maxDepth=maxDepth) listing = [os.path.relpath(path, "data") for path in listing] return listing def _getDirectoryListing(self, path, depth=0, maxDepth=100): if depth > maxDepth: raise UFOLibError("Maximum recusion depth reached.") result = [] for fileName in os.listdir(path): p = os.path.join(path, fileName) if os.path.isdir(p): result += self._getDirectoryListing(p, depth=depth+1, maxDepth=maxDepth) else: p = os.path.relpath(p, self._path) result.append(p) return result def getImageDirectoryListing(self): """ Returns a list of all image file names in the images directory. Each of the images will have been verified to have the PNG signature. """ if self._formatVersion < 3: return [] path = os.path.join(self._path, IMAGES_DIRNAME) if not os.path.exists(path): return [] if not os.path.isdir(path): raise UFOLibError("The UFO contains an \"images\" file instead of a directory.") result = [] for fileName in os.listdir(path): p = os.path.join(path, fileName) if os.path.isdir(p): # silently skip this as version control # systems often have hidden directories continue valid, error = pngValidator(path=p) if valid: result.append(fileName) return result def readImage(self, fileName): """ Return image data for the file named fileName. """ if self._formatVersion < 3: raise UFOLibError("Reading images is not allowed in UFO %d." % self._formatVersion) data = self.readBytesFromPath(os.path.join(IMAGES_DIRNAME, fileName)) if data is None: raise UFOLibError("No image file named %s." % fileName) valid, error = pngValidator(data=data) if not valid: raise UFOLibError(error) return data # ---------- # UFO Writer # ---------- class UFOWriter(object): """Write the various components of the .ufo.""" def __init__(self, path, formatVersion=3, fileCreator="org.robofab.ufoLib"): if formatVersion not in supportedUFOFormatVersions: raise UFOLibError("Unsupported UFO format (%d)." % formatVersion) # establish some basic stuff self._path = path self._formatVersion = formatVersion self._fileCreator = fileCreator self._downConversionKerningData = None # if the file already exists, get the format version. # this will be needed for up and down conversion. previousFormatVersion = None if os.path.exists(path): metaInfo = self._getPlist(METAINFO_FILENAME) previousFormatVersion = metaInfo.get("formatVersion") try: previousFormatVersion = int(previousFormatVersion) except: raise UFOLibError("The existing metainfo.plist is not properly formatted.") if previousFormatVersion not in supportedUFOFormatVersions: raise UFOLibError("Unsupported UFO format (%d)." % formatVersion) # catch down conversion if previousFormatVersion is not None and previousFormatVersion > formatVersion: raise UFOLibError("The UFO located at this path is a higher version (%d) than the version (%d) that is trying to be written. This is not supported." % (previousFormatVersion, formatVersion)) # handle the layer contents self.layerContents = {} if previousFormatVersion is not None and previousFormatVersion >= 3: # already exists self._readLayerContents() else: # previous < 3 # imply the layer contents p = os.path.join(path, DEFAULT_GLYPHS_DIRNAME) if os.path.exists(p): self.layerContents = {DEFAULT_LAYER_NAME : DEFAULT_GLYPHS_DIRNAME} # write the new metainfo self._writeMetaInfo() # properties def _get_path(self): return self._path path = property(_get_path, doc="The path the UFO is being written to.") def _get_formatVersion(self): return self._formatVersion formatVersion = property(_get_formatVersion, doc="The format version of the UFO. This is set into metainfo.plist during __init__.") def _get_fileCreator(self): return self._fileCreator fileCreator = property(_get_fileCreator, doc="The file creator of the UFO. This is set into metainfo.plist during __init__.") # support methods _getPlist = _getPlist def _writePlist(self, fileName, data): """ Write a property list. The errors that could be raised during the writing of a plist are unpredictable and/or too large to list, so, a blind try: except: is done. If an exception occurs, a UFOLibError will be raised. """ self._makeDirectory() path = os.path.join(self._path, fileName) try: data = writePlistAtomically(data, path) except: raise UFOLibError("The data for the file %s could not be written because it is not properly formatted." % fileName) def _deleteFile(self, fileName): path = os.path.join(self._path, fileName) if os.path.exists(path): os.remove(path) def _makeDirectory(self, subDirectory=None): path = self._path if subDirectory: path = os.path.join(self._path, subDirectory) if not os.path.exists(path): os.makedirs(path) return path def _buildDirectoryTree(self, path): directory, fileName = os.path.split(path) directoryTree = [] while directory: directory, d = os.path.split(directory) directoryTree.append(d) directoryTree.reverse() built = "" for d in directoryTree: d = os.path.join(built, d) p = os.path.join(self._path, d) if not os.path.exists(p): os.mkdir(p) built = d def _removeFileForPath(self, path, raiseErrorIfMissing=False): originalPath = path path = os.path.join(self._path, path) if not os.path.exists(path): if raiseErrorIfMissing: raise UFOLibError("The file %s does not exist." % path) else: if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) # remove any directories that are now empty self._removeEmptyDirectoriesForPath(os.path.dirname(originalPath)) def _removeEmptyDirectoriesForPath(self, directory): absoluteDirectory = os.path.join(self._path, directory) if not os.path.exists(absoluteDirectory): return if not len(os.listdir(absoluteDirectory)): shutil.rmtree(absoluteDirectory) else: return directory = os.path.dirname(directory) if directory: self._removeEmptyDirectoriesForPath(directory) # file system interaction def writeBytesToPath(self, path, data, encoding=None): """ Write bytes to path. If needed, the directory tree for the given path will be built. The path must be relative to the UFO. An encoding may be passed if needed. """ fullPath = os.path.join(self._path, path) if os.path.exists(fullPath) and os.path.isdir(fullPath): raise UFOLibError("A directory exists at %s." % path) self._buildDirectoryTree(path) if encoding: data = StringIO(data).encode(encoding) writeDataFileAtomically(data, fullPath) def getFileObjectForPath(self, path, encoding=None): """ Creates a write mode file object at path. If needed, the directory tree for the given path will be built. The path must be relative to the UFO. An encoding may be passed if needed. Note: The caller is responsible for closing the open file. """ fullPath = os.path.join(self._path, path) if os.path.exists(fullPath) and os.path.isdir(fullPath): raise UFOLibError("A directory exists at %s." % path) self._buildDirectoryTree(path) return open(fullPath, "w", encoding=encoding) def removeFileForPath(self, path): """ Remove the file (or directory) at path. The path must be relative to the UFO. This is only allowed for files in the data and image directories. """ # make sure that only data or images is being changed d = path parts = [] while d: d, p = os.path.split(d) if p: parts.append(p) if parts[-1] not in ("images", "data"): raise UFOLibError("Removing \"%s\" is not legal." % path) # remove the file self._removeFileForPath(path, raiseErrorIfMissing=True) def copyFromReader(self, reader, sourcePath, destPath): """ Copy the sourcePath in the provided UFOReader to destPath in this writer. The paths must be relative. They may represent directories or paths. This uses the most memory efficient method possible for copying the data possible. """ if not isinstance(reader, UFOReader): raise UFOLibError("The reader must be an instance of UFOReader.") fullSourcePath = os.path.join(reader._path, sourcePath) if not reader._checkForFile(fullSourcePath): raise UFOLibError("No file named \"%s\" to copy from." % sourcePath) fullDestPath = os.path.join(self._path, destPath) if os.path.exists(fullDestPath): raise UFOLibError("A file named \"%s\" already exists." % sourcePath) self._buildDirectoryTree(destPath) if os.path.isdir(fullSourcePath): shutil.copytree(fullSourcePath, fullDestPath) else: shutil.copy(fullSourcePath, fullDestPath) # UFO mod time def setModificationTime(self): """ Set the UFO modification time to the current time. This is never called automatically. It is up to the caller to call this when finished working on the UFO. """ os.utime(self._path, None) # metainfo.plist def _writeMetaInfo(self): metaInfo = dict( creator=self._fileCreator, formatVersion=self._formatVersion ) self._writePlist(METAINFO_FILENAME, metaInfo) # groups.plist def setKerningGroupConversionRenameMaps(self, maps): """ Set maps defining the renaming that should be done when writing groups and kerning in UFO 1 and UFO 2. This will effectively undo the conversion done when UFOReader reads this data. The dictionary should have this form: { "side1" : {"group name to use when writing" : "group name in data"}, "side2" : {"group name to use when writing" : "group name in data"} } This is the same form returned by UFOReader's getKerningGroupConversionRenameMaps method. """ if self._formatVersion >= 3: return # XXX raise an error here # flip the dictionaries remap = {} for side in ("side1", "side2"): for writeName, dataName in list(maps[side].items()): remap[dataName] = writeName self._downConversionKerningData = dict(groupRenameMap=remap) def writeGroups(self, groups): """ Write groups.plist. This method requires a dict of glyph groups as an argument. """ # validate the data structure valid, message = groupsValidator(groups) if not valid: raise UFOLibError(message) # down convert if self._formatVersion < 3 and self._downConversionKerningData is not None: remap = self._downConversionKerningData["groupRenameMap"] remappedGroups = {} # there are some edge cases here that are ignored: # 1. if a group is being renamed to a name that # already exists, the existing group is always # overwritten. (this is why there are two loops # below.) there doesn't seem to be a logical # solution to groups mismatching and overwriting # with the specifiecd group seems like a better # solution than throwing an error. # 2. if side 1 and side 2 groups are being renamed # to the same group name there is no check to # ensure that the contents are identical. that # is left up to the caller. for name, contents in list(groups.items()): if name in remap: continue remappedGroups[name] = contents for name, contents in list(groups.items()): if name not in remap: continue name = remap[name] remappedGroups[name] = contents groups = remappedGroups # pack and write groupsNew = {} for key, value in list(groups.items()): groupsNew[key] = list(value) if groupsNew: self._writePlist(GROUPS_FILENAME, groupsNew) else: self._deleteFile(GROUPS_FILENAME) # fontinfo.plist def writeInfo(self, info): """ Write info.plist. This method requires an object that supports getting attributes that follow the fontinfo.plist version 2 specification. Attributes will be taken from the given object and written into the file. """ # gather version 3 data infoData = {} for attr in list(fontInfoAttributesVersion3ValueData.keys()): if hasattr(info, attr): try: value = getattr(info, attr) except AttributeError: raise UFOLibError("The supplied info object does not support getting a necessary attribute (%s)." % attr) if value is None: continue infoData[attr] = value # down convert data if necessary and validate if self._formatVersion == 3: infoData = validateInfoVersion3Data(infoData) elif self._formatVersion == 2: infoData = _convertFontInfoDataVersion3ToVersion2(infoData) infoData = validateInfoVersion2Data(infoData) elif self._formatVersion == 1: infoData = _convertFontInfoDataVersion3ToVersion2(infoData) infoData = validateInfoVersion2Data(infoData) infoData = _convertFontInfoDataVersion2ToVersion1(infoData) # write file self._writePlist(FONTINFO_FILENAME, infoData) # kerning.plist def writeKerning(self, kerning): """ Write kerning.plist. This method requires a dict of kerning pairs as an argument. This performs basic structural validation of the kerning, but it does not check for compliance with the spec in regards to conflicting pairs. The assumption is that the kerning data being passed is standards compliant. """ # validate the data structure invalidFormatMessage = "The kerning is not properly formatted." if not isDictEnough(kerning): raise UFOLibError(invalidFormatMessage) for pair, value in list(kerning.items()): if not isinstance(pair, (list, tuple)): raise UFOLibError(invalidFormatMessage) if not len(pair) == 2: raise UFOLibError(invalidFormatMessage) if not isinstance(pair[0], basestring): raise UFOLibError(invalidFormatMessage) if not isinstance(pair[1], basestring): raise UFOLibError(invalidFormatMessage) if not isinstance(value, (int, float)): raise UFOLibError(invalidFormatMessage) # down convert if self._formatVersion < 3 and self._downConversionKerningData is not None: remap = self._downConversionKerningData["groupRenameMap"] remappedKerning = {} for (side1, side2), value in list(kerning.items()): side1 = remap.get(side1, side1) side2 = remap.get(side2, side2) remappedKerning[side1, side2] = value kerning = remappedKerning # pack and write kerningDict = {} for left, right in list(kerning.keys()): value = kerning[left, right] if not left in kerningDict: kerningDict[left] = {} kerningDict[left][right] = value if kerningDict: self._writePlist(KERNING_FILENAME, kerningDict) else: self._deleteFile(KERNING_FILENAME) # lib.plist def writeLib(self, libDict): """ Write lib.plist. This method requires a lib dict as an argument. """ valid, message = fontLibValidator(libDict) if not valid: raise UFOLibError(message) if libDict: self._writePlist(LIB_FILENAME, libDict) else: self._deleteFile(LIB_FILENAME) # features.fea def writeFeatures(self, features): """ Write features.fea. This method requires a features string as an argument. """ if self._formatVersion == 1: raise UFOLibError("features.fea is not allowed in UFO Format Version 1.") if not isinstance(features, basestring): raise UFOLibError("The features are not text.") self._makeDirectory() path = os.path.join(self._path, FEATURES_FILENAME) writeFileAtomically(features, path) # glyph sets & layers def _readLayerContents(self): """ Rebuild the layer contents list by checking what glyph sets are available on disk. """ # read the file on disk raw = self._getPlist(LAYERCONTENTS_FILENAME) contents = {} valid, error = layerContentsValidator(raw, self._path) if not valid: raise UFOLibError(error) for entry in raw: layerName, directoryName = entry contents[layerName] = directoryName self.layerContents = contents def writeLayerContents(self, layerOrder=None): """ Write the layercontents.plist file. This method *must* be called after all glyph sets have been written. """ if self.formatVersion < 3: return if layerOrder is not None: newOrder = [] for layerName in layerOrder: if layerName is None: layerName = DEFAULT_LAYER_NAME newOrder.append(layerName) layerOrder = newOrder else: layerOrder = list(self.layerContents.keys()) if set(layerOrder) != set(self.layerContents.keys()): raise UFOLibError("The layer order contents does not match the glyph sets that have been created.") layerContents = [(layerName, self.layerContents[layerName]) for layerName in layerOrder] self._writePlist(LAYERCONTENTS_FILENAME, layerContents) def _findDirectoryForLayerName(self, layerName): foundDirectory = None for existingLayerName, directoryName in list(self.layerContents.items()): if layerName is None and directoryName == DEFAULT_GLYPHS_DIRNAME: foundDirectory = directoryName break elif existingLayerName == layerName: foundDirectory = directoryName break if not foundDirectory: raise UFOLibError("Could not locate a glyph set directory for the layer named %s." % layerName) return foundDirectory def getGlyphSet(self, layerName=None, defaultLayer=True, glyphNameToFileNameFunc=None): """ Return the GlyphSet object associated with the appropriate glyph directory in the .ufo. If layerName is None, the default glyph set will be used. The defaultLayer flag indictes that the layer should be saved into the default glyphs directory. """ # only default can be written in < 3 if self._formatVersion < 3 and (not defaultLayer or layerName is not None): raise UFOLibError("Only the default layer can be writen in UFO %d." % self.formatVersion) # locate a layer name when None has been given if layerName is None and defaultLayer: for existingLayerName, directory in list(self.layerContents.items()): if directory == DEFAULT_GLYPHS_DIRNAME: layerName = existingLayerName if layerName is None: layerName = DEFAULT_LAYER_NAME elif layerName is None and not defaultLayer: raise UFOLibError("A layer name must be provided for non-default layers.") # move along to format specific writing if self.formatVersion == 1: return self._getGlyphSetFormatVersion1(glyphNameToFileNameFunc=glyphNameToFileNameFunc) elif self.formatVersion == 2: return self._getGlyphSetFormatVersion2(glyphNameToFileNameFunc=glyphNameToFileNameFunc) elif self.formatVersion == 3: return self._getGlyphSetFormatVersion3(layerName=layerName, defaultLayer=defaultLayer, glyphNameToFileNameFunc=glyphNameToFileNameFunc) def _getGlyphSetFormatVersion1(self, glyphNameToFileNameFunc=None): glyphDir = self._makeDirectory(DEFAULT_GLYPHS_DIRNAME) return GlyphSet(glyphDir, glyphNameToFileNameFunc, ufoFormatVersion=1) def _getGlyphSetFormatVersion2(self, glyphNameToFileNameFunc=None): glyphDir = self._makeDirectory(DEFAULT_GLYPHS_DIRNAME) return GlyphSet(glyphDir, glyphNameToFileNameFunc, ufoFormatVersion=2) def _getGlyphSetFormatVersion3(self, layerName=None, defaultLayer=True, glyphNameToFileNameFunc=None): # if the default flag is on, make sure that the default in the file # matches the default being written. also make sure that this layer # name is not already linked to a non-default layer. if defaultLayer: for existingLayerName, directory in list(self.layerContents.items()): if directory == DEFAULT_GLYPHS_DIRNAME: if existingLayerName != layerName: raise UFOLibError("Another layer is already mapped to the default directory.") elif existingLayerName == layerName: raise UFOLibError("The layer name is already mapped to a non-default layer.") # get an existing directory name if layerName in self.layerContents: directory = self.layerContents[layerName] # get a new directory name else: if defaultLayer: directory = DEFAULT_GLYPHS_DIRNAME else: # not caching this could be slightly expensive, # but caching it will be cumbersome existing = [d.lower() for d in list(self.layerContents.values())] if not isinstance(layerName, unicode): try: layerName = unicode(layerName) except UnicodeDecodeError: raise UFOLibError("The specified layer name is not a Unicode string.") directory = userNameToFileName(layerName, existing=existing, prefix="glyphs.") # make the directory path = os.path.join(self._path, directory) if not os.path.exists(path): self._makeDirectory(subDirectory=directory) # store the mapping self.layerContents[layerName] = directory # load the glyph set return GlyphSet(path, glyphNameToFileNameFunc=glyphNameToFileNameFunc, ufoFormatVersion=3) def renameGlyphSet(self, layerName, newLayerName, defaultLayer=False): """ Rename a glyph set. Note: if a GlyphSet object has already been retrieved for layerName, it is up to the caller to inform that object that the directory it represents has changed. """ if self._formatVersion < 3: # ignore renaming glyph sets for UFO1 UFO2 # just write the data from the default layer return # the new and old names can be the same # as long as the default is being switched if layerName == newLayerName: # if the default is off and the layer is already not the default, skip if self.layerContents[layerName] != DEFAULT_GLYPHS_DIRNAME and not defaultLayer: return # if the default is on and the layer is already the default, skip if self.layerContents[layerName] == DEFAULT_GLYPHS_DIRNAME and defaultLayer: return else: # make sure the new layer name doesn't already exist if newLayerName is None: newLayerName = DEFAULT_LAYER_NAME if newLayerName in self.layerContents: raise UFOLibError("A layer named %s already exists." % newLayerName) # make sure the default layer doesn't already exist if defaultLayer and DEFAULT_GLYPHS_DIRNAME in list(self.layerContents.values()): raise UFOLibError("A default layer already exists.") # get the paths oldDirectory = self._findDirectoryForLayerName(layerName) if defaultLayer: newDirectory = DEFAULT_GLYPHS_DIRNAME else: existing = [name.lower() for name in list(self.layerContents.values())] newDirectory = userNameToFileName(newLayerName, existing=existing, prefix="glyphs.") # update the internal mapping del self.layerContents[layerName] self.layerContents[newLayerName] = newDirectory # do the file system copy oldDirectory = os.path.join(self._path, oldDirectory) newDirectory = os.path.join(self._path, newDirectory) shutil.move(oldDirectory, newDirectory) def deleteGlyphSet(self, layerName): """ Remove the glyph set matching layerName. """ if self._formatVersion < 3: # ignore deleting glyph sets for UFO1 UFO2 as there are no layers # just write the data from the default layer return foundDirectory = self._findDirectoryForLayerName(layerName) self._removeFileForPath(foundDirectory) del self.layerContents[layerName] # /images def writeImage(self, fileName, data): """ Write data to fileName in the images directory. The data must be a valid PNG. """ if self._formatVersion < 3: raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion) valid, error = pngValidator(data=data) if not valid: raise UFOLibError(error) path = os.path.join(IMAGES_DIRNAME, fileName) self.writeBytesToPath(path, data) def removeImage(self, fileName): """ Remove the file named fileName from the images directory. """ if self._formatVersion < 3: raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion) path = os.path.join(IMAGES_DIRNAME, fileName) self.removeFileForPath(path) def copyImageFromReader(self, reader, sourceFileName, destFileName): """ Copy the sourceFileName in the provided UFOReader to destFileName in this writer. This uses the most memory efficient method possible for copying the data possible. """ if self._formatVersion < 3: raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion) sourcePath = os.path.join("images", sourceFileName) destPath = os.path.join("images", destFileName) self.copyFromReader(reader, sourcePath, destPath) # ---------------- # Helper Functions # ---------------- def makeUFOPath(path): """ Return a .ufo pathname. >>> makeUFOPath("directory/something.ext") == ( ... os.path.join('directory', 'something.ufo')) True >>> makeUFOPath("directory/something.another.thing.ext") == ( ... os.path.join('directory', 'something.another.thing.ufo')) True """ dir, name = os.path.split(path) name = ".".join([".".join(name.split(".")[:-1]), "ufo"]) return os.path.join(dir, name) def writePlistAtomically(obj, path): """ Write a plist for "obj" to "path". Do this sort of atomically, making it harder to cause corrupt files, for example when writePlist encounters an error halfway during write. This also checks to see if text matches the text that is already in the file at path. If so, the file is not rewritten so that the modification date is preserved. """ f = BytesIO() writePlist(obj, f) data = f.getvalue() writeDataFileAtomically(data, path) def writeFileAtomically(text, path, encoding="utf-8"): """ Write text into a file at path. Do this sort of atomically making it harder to cause corrupt files. This also checks to see if text matches the text that is already in the file at path. If so, the file is not rewritten so that the modification date is preserved. An encoding may be passed if needed. """ if os.path.exists(path): with open(path, "r", encoding=encoding) as f: oldText = f.read() if text == oldText: return # if the text is empty, remove the existing file if not text: os.remove(path) if text: with open(path, "w", encoding=encoding) as f: f.write(text) def writeDataFileAtomically(data, path): """ Write data into a file at path. Do this sort of atomically making it harder to cause corrupt files. This also checks to see if data matches the data that is already in the file at path. If so, the file is not rewritten so that the modification date is preserved. """ assert isinstance(data, bytes) if os.path.exists(path): f = open(path, "rb") oldData = f.read() f.close() if data == oldData: return # if the data is empty, remove the existing file if not data: os.remove(path) if data: f = open(path, "wb") f.write(data) f.close() # --------------------------- # Format Conversion Functions # --------------------------- def convertUFOFormatVersion1ToFormatVersion2(inPath, outPath=None): """ Function for converting a version format 1 UFO to version format 2. inPath should be a path to a UFO. outPath is the path where the new UFO should be written. If outPath is not given, the inPath will be used and, therefore, the UFO will be converted in place. Otherwise, if outPath is specified, nothing must exist at that path. """ from warnings import warn warn("convertUFOFormatVersion1ToFormatVersion2 is deprecated.", DeprecationWarning) if outPath is None: outPath = inPath if inPath != outPath and os.path.exists(outPath): raise UFOLibError("A file already exists at %s." % outPath) # use a reader for loading most of the data reader = UFOReader(inPath) if reader.formatVersion == 2: raise UFOLibError("The UFO at %s is already format version 2." % inPath) groups = reader.readGroups() kerning = reader.readKerning() libData = reader.readLib() # read the info data manually and convert infoPath = os.path.join(inPath, FONTINFO_FILENAME) if not os.path.exists(infoPath): infoData = {} else: with open(infoPath, "rb") as f: infoData = readPlist(f) infoData = _convertFontInfoDataVersion1ToVersion2(infoData) # if the paths are the same, only need to change the # fontinfo and meta info files. infoPath = os.path.join(outPath, FONTINFO_FILENAME) if inPath == outPath: metaInfoPath = os.path.join(inPath, METAINFO_FILENAME) metaInfo = dict( creator="org.robofab.ufoLib", formatVersion=2 ) writePlistAtomically(metaInfo, metaInfoPath) writePlistAtomically(infoData, infoPath) # otherwise write everything. else: writer = UFOWriter(outPath, formatVersion=2) writer.writeGroups(groups) writer.writeKerning(kerning) writer.writeLib(libData) # write the info manually writePlistAtomically(infoData, infoPath) # copy the glyph tree inGlyphs = os.path.join(inPath, DEFAULT_GLYPHS_DIRNAME) outGlyphs = os.path.join(outPath, DEFAULT_GLYPHS_DIRNAME) if os.path.exists(inGlyphs): shutil.copytree(inGlyphs, outGlyphs) # ---------------------- # fontinfo.plist Support # ---------------------- # Version Validators # There is no version 1 validator and there shouldn't be. # The version 1 spec was very loose and there were numerous # cases of invalid values. def validateFontInfoVersion2ValueForAttribute(attr, value): """ This performs very basic validation of the value for attribute following the UFO 2 fontinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the value is of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ dataValidationDict = fontInfoAttributesVersion2ValueData[attr] valueType = dataValidationDict.get("type") validator = dataValidationDict.get("valueValidator") valueOptions = dataValidationDict.get("valueOptions") # have specific options for the validator if valueOptions is not None: isValidValue = validator(value, valueOptions) # no specific options else: if validator == genericTypeValidator: isValidValue = validator(value, valueType) else: isValidValue = validator(value) return isValidValue def validateInfoVersion2Data(infoData): """ This performs very basic validation of the value for infoData following the UFO 2 fontinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the values are of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ validInfoData = {} for attr, value in list(infoData.items()): isValidValue = validateFontInfoVersion2ValueForAttribute(attr, value) if not isValidValue: raise UFOLibError("Invalid value for attribute %s (%s)." % (attr, repr(value))) else: validInfoData[attr] = value return validInfoData def validateFontInfoVersion3ValueForAttribute(attr, value): """ This performs very basic validation of the value for attribute following the UFO 3 fontinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the value is of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ dataValidationDict = fontInfoAttributesVersion3ValueData[attr] valueType = dataValidationDict.get("type") validator = dataValidationDict.get("valueValidator") valueOptions = dataValidationDict.get("valueOptions") # have specific options for the validator if valueOptions is not None: isValidValue = validator(value, valueOptions) # no specific options else: if validator == genericTypeValidator: isValidValue = validator(value, valueType) else: isValidValue = validator(value) return isValidValue def validateInfoVersion3Data(infoData): """ This performs very basic validation of the value for infoData following the UFO 3 fontinfo.plist specification. The results of this should not be interpretted as *correct* for the font that they are part of. This merely indicates that the values are of the proper type and, where the specification defines a set range of possible values for an attribute, that the value is in the accepted range. """ validInfoData = {} for attr, value in list(infoData.items()): isValidValue = validateFontInfoVersion3ValueForAttribute(attr, value) if not isValidValue: raise UFOLibError("Invalid value for attribute %s (%s)." % (attr, repr(value))) else: validInfoData[attr] = value return validInfoData # Value Options fontInfoOpenTypeHeadFlagsOptions = list(range(0, 15)) fontInfoOpenTypeOS2SelectionOptions = [1, 2, 3, 4, 7, 8, 9] fontInfoOpenTypeOS2UnicodeRangesOptions = list(range(0, 128)) fontInfoOpenTypeOS2CodePageRangesOptions = list(range(0, 64)) fontInfoOpenTypeOS2TypeOptions = [0, 1, 2, 3, 8, 9] # Version Attribute Definitions # This defines the attributes, types and, in some # cases the possible values, that can exist is # fontinfo.plist. fontInfoAttributesVersion1 = set([ "familyName", "styleName", "fullName", "fontName", "menuName", "fontStyle", "note", "versionMajor", "versionMinor", "year", "copyright", "notice", "trademark", "license", "licenseURL", "createdBy", "designer", "designerURL", "vendorURL", "unitsPerEm", "ascender", "descender", "capHeight", "xHeight", "defaultWidth", "slantAngle", "italicAngle", "widthName", "weightName", "weightValue", "fondName", "otFamilyName", "otStyleName", "otMacName", "msCharSet", "fondID", "uniqueID", "ttVendor", "ttUniqueID", "ttVersion", ]) fontInfoAttributesVersion2ValueData = { "familyName" : dict(type=basestring), "styleName" : dict(type=basestring), "styleMapFamilyName" : dict(type=basestring), "styleMapStyleName" : dict(type=basestring, valueValidator=fontInfoStyleMapStyleNameValidator), "versionMajor" : dict(type=int), "versionMinor" : dict(type=int), "year" : dict(type=int), "copyright" : dict(type=basestring), "trademark" : dict(type=basestring), "unitsPerEm" : dict(type=(int, float)), "descender" : dict(type=(int, float)), "xHeight" : dict(type=(int, float)), "capHeight" : dict(type=(int, float)), "ascender" : dict(type=(int, float)), "italicAngle" : dict(type=(float, int)), "note" : dict(type=basestring), "openTypeHeadCreated" : dict(type=basestring, valueValidator=fontInfoOpenTypeHeadCreatedValidator), "openTypeHeadLowestRecPPEM" : dict(type=(int, float)), "openTypeHeadFlags" : dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeHeadFlagsOptions), "openTypeHheaAscender" : dict(type=(int, float)), "openTypeHheaDescender" : dict(type=(int, float)), "openTypeHheaLineGap" : dict(type=(int, float)), "openTypeHheaCaretSlopeRise" : dict(type=int), "openTypeHheaCaretSlopeRun" : dict(type=int), "openTypeHheaCaretOffset" : dict(type=(int, float)), "openTypeNameDesigner" : dict(type=basestring), "openTypeNameDesignerURL" : dict(type=basestring), "openTypeNameManufacturer" : dict(type=basestring), "openTypeNameManufacturerURL" : dict(type=basestring), "openTypeNameLicense" : dict(type=basestring), "openTypeNameLicenseURL" : dict(type=basestring), "openTypeNameVersion" : dict(type=basestring), "openTypeNameUniqueID" : dict(type=basestring), "openTypeNameDescription" : dict(type=basestring), "openTypeNamePreferredFamilyName" : dict(type=basestring), "openTypeNamePreferredSubfamilyName" : dict(type=basestring), "openTypeNameCompatibleFullName" : dict(type=basestring), "openTypeNameSampleText" : dict(type=basestring), "openTypeNameWWSFamilyName" : dict(type=basestring), "openTypeNameWWSSubfamilyName" : dict(type=basestring), "openTypeOS2WidthClass" : dict(type=int, valueValidator=fontInfoOpenTypeOS2WidthClassValidator), "openTypeOS2WeightClass" : dict(type=int, valueValidator=fontInfoOpenTypeOS2WeightClassValidator), "openTypeOS2Selection" : dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2SelectionOptions), "openTypeOS2VendorID" : dict(type=basestring), "openTypeOS2Panose" : dict(type="integerList", valueValidator=fontInfoVersion2OpenTypeOS2PanoseValidator), "openTypeOS2FamilyClass" : dict(type="integerList", valueValidator=fontInfoOpenTypeOS2FamilyClassValidator), "openTypeOS2UnicodeRanges" : dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2UnicodeRangesOptions), "openTypeOS2CodePageRanges" : dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2CodePageRangesOptions), "openTypeOS2TypoAscender" : dict(type=(int, float)), "openTypeOS2TypoDescender" : dict(type=(int, float)), "openTypeOS2TypoLineGap" : dict(type=(int, float)), "openTypeOS2WinAscent" : dict(type=(int, float)), "openTypeOS2WinDescent" : dict(type=(int, float)), "openTypeOS2Type" : dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2TypeOptions), "openTypeOS2SubscriptXSize" : dict(type=(int, float)), "openTypeOS2SubscriptYSize" : dict(type=(int, float)), "openTypeOS2SubscriptXOffset" : dict(type=(int, float)), "openTypeOS2SubscriptYOffset" : dict(type=(int, float)), "openTypeOS2SuperscriptXSize" : dict(type=(int, float)), "openTypeOS2SuperscriptYSize" : dict(type=(int, float)), "openTypeOS2SuperscriptXOffset" : dict(type=(int, float)), "openTypeOS2SuperscriptYOffset" : dict(type=(int, float)), "openTypeOS2StrikeoutSize" : dict(type=(int, float)), "openTypeOS2StrikeoutPosition" : dict(type=(int, float)), "openTypeVheaVertTypoAscender" : dict(type=(int, float)), "openTypeVheaVertTypoDescender" : dict(type=(int, float)), "openTypeVheaVertTypoLineGap" : dict(type=(int, float)), "openTypeVheaCaretSlopeRise" : dict(type=int), "openTypeVheaCaretSlopeRun" : dict(type=int), "openTypeVheaCaretOffset" : dict(type=(int, float)), "postscriptFontName" : dict(type=basestring), "postscriptFullName" : dict(type=basestring), "postscriptSlantAngle" : dict(type=(float, int)), "postscriptUniqueID" : dict(type=int), "postscriptUnderlineThickness" : dict(type=(int, float)), "postscriptUnderlinePosition" : dict(type=(int, float)), "postscriptIsFixedPitch" : dict(type=bool), "postscriptBlueValues" : dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator), "postscriptOtherBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator), "postscriptFamilyBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator), "postscriptFamilyOtherBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator), "postscriptStemSnapH" : dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator), "postscriptStemSnapV" : dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator), "postscriptBlueFuzz" : dict(type=(int, float)), "postscriptBlueShift" : dict(type=(int, float)), "postscriptBlueScale" : dict(type=(float, int)), "postscriptForceBold" : dict(type=bool), "postscriptDefaultWidthX" : dict(type=(int, float)), "postscriptNominalWidthX" : dict(type=(int, float)), "postscriptWeightName" : dict(type=basestring), "postscriptDefaultCharacter" : dict(type=basestring), "postscriptWindowsCharacterSet" : dict(type=int, valueValidator=fontInfoPostscriptWindowsCharacterSetValidator), "macintoshFONDFamilyID" : dict(type=int), "macintoshFONDName" : dict(type=basestring), } fontInfoAttributesVersion2 = set(fontInfoAttributesVersion2ValueData.keys()) fontInfoAttributesVersion3ValueData = deepcopy(fontInfoAttributesVersion2ValueData) fontInfoAttributesVersion3ValueData.update({ "versionMinor" : dict(type=int, valueValidator=genericNonNegativeIntValidator), "unitsPerEm" : dict(type=(int, float), valueValidator=genericNonNegativeNumberValidator), "openTypeHeadLowestRecPPEM" : dict(type=int, valueValidator=genericNonNegativeNumberValidator), "openTypeHheaAscender" : dict(type=int), "openTypeHheaDescender" : dict(type=int), "openTypeHheaLineGap" : dict(type=int), "openTypeHheaCaretOffset" : dict(type=int), "openTypeOS2Panose" : dict(type="integerList", valueValidator=fontInfoVersion3OpenTypeOS2PanoseValidator), "openTypeOS2TypoAscender" : dict(type=int), "openTypeOS2TypoDescender" : dict(type=int), "openTypeOS2TypoLineGap" : dict(type=int), "openTypeOS2WinAscent" : dict(type=int, valueValidator=genericNonNegativeNumberValidator), "openTypeOS2WinDescent" : dict(type=int, valueValidator=genericNonNegativeNumberValidator), "openTypeOS2SubscriptXSize" : dict(type=int), "openTypeOS2SubscriptYSize" : dict(type=int), "openTypeOS2SubscriptXOffset" : dict(type=int), "openTypeOS2SubscriptYOffset" : dict(type=int), "openTypeOS2SuperscriptXSize" : dict(type=int), "openTypeOS2SuperscriptYSize" : dict(type=int), "openTypeOS2SuperscriptXOffset" : dict(type=int), "openTypeOS2SuperscriptYOffset" : dict(type=int), "openTypeOS2StrikeoutSize" : dict(type=int), "openTypeOS2StrikeoutPosition" : dict(type=int), "openTypeGaspRangeRecords" : dict(type="dictList", valueValidator=fontInfoOpenTypeGaspRangeRecordsValidator), "openTypeNameRecords" : dict(type="dictList", valueValidator=fontInfoOpenTypeNameRecordsValidator), "openTypeVheaVertTypoAscender" : dict(type=int), "openTypeVheaVertTypoDescender" : dict(type=int), "openTypeVheaVertTypoLineGap" : dict(type=int), "openTypeVheaCaretOffset" : dict(type=int), "woffMajorVersion" : dict(type=int, valueValidator=genericNonNegativeIntValidator), "woffMinorVersion" : dict(type=int, valueValidator=genericNonNegativeIntValidator), "woffMetadataUniqueID" : dict(type=dict, valueValidator=fontInfoWOFFMetadataUniqueIDValidator), "woffMetadataVendor" : dict(type=dict, valueValidator=fontInfoWOFFMetadataVendorValidator), "woffMetadataCredits" : dict(type=dict, valueValidator=fontInfoWOFFMetadataCreditsValidator), "woffMetadataDescription" : dict(type=dict, valueValidator=fontInfoWOFFMetadataDescriptionValidator), "woffMetadataLicense" : dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseValidator), "woffMetadataCopyright" : dict(type=dict, valueValidator=fontInfoWOFFMetadataCopyrightValidator), "woffMetadataTrademark" : dict(type=dict, valueValidator=fontInfoWOFFMetadataTrademarkValidator), "woffMetadataLicensee" : dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseeValidator), "woffMetadataExtensions" : dict(type=list, valueValidator=fontInfoWOFFMetadataExtensionsValidator), "guidelines" : dict(type=list, valueValidator=guidelinesValidator) }) fontInfoAttributesVersion3 = set(fontInfoAttributesVersion3ValueData.keys()) # insert the type validator for all attrs that # have no defined validator. for attr, dataDict in list(fontInfoAttributesVersion2ValueData.items()): if "valueValidator" not in dataDict: dataDict["valueValidator"] = genericTypeValidator for attr, dataDict in list(fontInfoAttributesVersion3ValueData.items()): if "valueValidator" not in dataDict: dataDict["valueValidator"] = genericTypeValidator # Version Conversion Support # These are used from converting from version 1 # to version 2 or vice-versa. def _flipDict(d): flipped = {} for key, value in list(d.items()): flipped[value] = key return flipped fontInfoAttributesVersion1To2 = { "menuName" : "styleMapFamilyName", "designer" : "openTypeNameDesigner", "designerURL" : "openTypeNameDesignerURL", "createdBy" : "openTypeNameManufacturer", "vendorURL" : "openTypeNameManufacturerURL", "license" : "openTypeNameLicense", "licenseURL" : "openTypeNameLicenseURL", "ttVersion" : "openTypeNameVersion", "ttUniqueID" : "openTypeNameUniqueID", "notice" : "openTypeNameDescription", "otFamilyName" : "openTypeNamePreferredFamilyName", "otStyleName" : "openTypeNamePreferredSubfamilyName", "otMacName" : "openTypeNameCompatibleFullName", "weightName" : "postscriptWeightName", "weightValue" : "openTypeOS2WeightClass", "ttVendor" : "openTypeOS2VendorID", "uniqueID" : "postscriptUniqueID", "fontName" : "postscriptFontName", "fondID" : "macintoshFONDFamilyID", "fondName" : "macintoshFONDName", "defaultWidth" : "postscriptDefaultWidthX", "slantAngle" : "postscriptSlantAngle", "fullName" : "postscriptFullName", # require special value conversion "fontStyle" : "styleMapStyleName", "widthName" : "openTypeOS2WidthClass", "msCharSet" : "postscriptWindowsCharacterSet" } fontInfoAttributesVersion2To1 = _flipDict(fontInfoAttributesVersion1To2) deprecatedFontInfoAttributesVersion2 = set(fontInfoAttributesVersion1To2.keys()) _fontStyle1To2 = { 64 : "regular", 1 : "italic", 32 : "bold", 33 : "bold italic" } _fontStyle2To1 = _flipDict(_fontStyle1To2) # Some UFO 1 files have 0 _fontStyle1To2[0] = "regular" _widthName1To2 = { "Ultra-condensed" : 1, "Extra-condensed" : 2, "Condensed" : 3, "Semi-condensed" : 4, "Medium (normal)" : 5, "Semi-expanded" : 6, "Expanded" : 7, "Extra-expanded" : 8, "Ultra-expanded" : 9 } _widthName2To1 = _flipDict(_widthName1To2) # FontLab's default width value is "Normal". # Many format version 1 UFOs will have this. _widthName1To2["Normal"] = 5 # FontLab has an "All" width value. In UFO 1 # move this up to "Normal". _widthName1To2["All"] = 5 # "medium" appears in a lot of UFO 1 files. _widthName1To2["medium"] = 5 # "Medium" appears in a lot of UFO 1 files. _widthName1To2["Medium"] = 5 _msCharSet1To2 = { 0 : 1, 1 : 2, 2 : 3, 77 : 4, 128 : 5, 129 : 6, 130 : 7, 134 : 8, 136 : 9, 161 : 10, 162 : 11, 163 : 12, 177 : 13, 178 : 14, 186 : 15, 200 : 16, 204 : 17, 222 : 18, 238 : 19, 255 : 20 } _msCharSet2To1 = _flipDict(_msCharSet1To2) # 1 <-> 2 def convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value): """ Convert value from version 1 to version 2 format. Returns the new attribute name and the converted value. If the value is None, None will be returned for the new value. """ # convert floats to ints if possible if isinstance(value, float): if int(value) == value: value = int(value) if value is not None: if attr == "fontStyle": v = _fontStyle1To2.get(value) if v is None: raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), attr)) value = v elif attr == "widthName": v = _widthName1To2.get(value) if v is None: raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), attr)) value = v elif attr == "msCharSet": v = _msCharSet1To2.get(value) if v is None: raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), attr)) value = v attr = fontInfoAttributesVersion1To2.get(attr, attr) return attr, value def convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value): """ Convert value from version 2 to version 1 format. Returns the new attribute name and the converted value. If the value is None, None will be returned for the new value. """ if value is not None: if attr == "styleMapStyleName": value = _fontStyle2To1.get(value) elif attr == "openTypeOS2WidthClass": value = _widthName2To1.get(value) elif attr == "postscriptWindowsCharacterSet": value = _msCharSet2To1.get(value) attr = fontInfoAttributesVersion2To1.get(attr, attr) return attr, value def _convertFontInfoDataVersion1ToVersion2(data): converted = {} for attr, value in list(data.items()): # FontLab gives -1 for the weightValue # for fonts wil no defined value. Many # format version 1 UFOs will have this. if attr == "weightValue" and value == -1: continue newAttr, newValue = convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value) # skip if the attribute is not part of version 2 if newAttr not in fontInfoAttributesVersion2: continue # catch values that can't be converted if value is None: raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), newAttr)) # store converted[newAttr] = newValue return converted def _convertFontInfoDataVersion2ToVersion1(data): converted = {} for attr, value in list(data.items()): newAttr, newValue = convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value) # only take attributes that are registered for version 1 if newAttr not in fontInfoAttributesVersion1: continue # catch values that can't be converted if value is None: raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), newAttr)) # store converted[newAttr] = newValue return converted # 2 <-> 3 _ufo2To3NonNegativeInt = set(( "versionMinor", "openTypeHeadLowestRecPPEM", "openTypeOS2WinAscent", "openTypeOS2WinDescent" )) _ufo2To3NonNegativeIntOrFloat = set(( "unitsPerEm" )) _ufo2To3FloatToInt = set((( "openTypeHeadLowestRecPPEM", "openTypeHheaAscender", "openTypeHheaDescender", "openTypeHheaLineGap", "openTypeHheaCaretOffset", "openTypeOS2TypoAscender", "openTypeOS2TypoDescender", "openTypeOS2TypoLineGap", "openTypeOS2WinAscent", "openTypeOS2WinDescent", "openTypeOS2SubscriptXSize", "openTypeOS2SubscriptYSize", "openTypeOS2SubscriptXOffset", "openTypeOS2SubscriptYOffset", "openTypeOS2SuperscriptXSize", "openTypeOS2SuperscriptYSize", "openTypeOS2SuperscriptXOffset", "openTypeOS2SuperscriptYOffset", "openTypeOS2StrikeoutSize", "openTypeOS2StrikeoutPosition", "openTypeVheaVertTypoAscender", "openTypeVheaVertTypoDescender", "openTypeVheaVertTypoLineGap", "openTypeVheaCaretOffset" ))) def convertFontInfoValueForAttributeFromVersion2ToVersion3(attr, value): """ Convert value from version 2 to version 3 format. Returns the new attribute name and the converted value. If the value is None, None will be returned for the new value. """ if attr in _ufo2To3FloatToInt: try: v = int(round(value)) except (ValueError, TypeError): raise UFOLibError("Could not convert value for %s." % attr) if v != value: value = v if attr in _ufo2To3NonNegativeInt: try: v = int(abs(value)) except (ValueError, TypeError): raise UFOLibError("Could not convert value for %s." % attr) if v != value: value = v elif attr in _ufo2To3NonNegativeIntOrFloat: try: v = float(abs(value)) except (ValueError, TypeError): raise UFOLibError("Could not convert value for %s." % attr) if v == int(v): v = int(v) if v != value: value = v return attr, value def convertFontInfoValueForAttributeFromVersion3ToVersion2(attr, value): """ Convert value from version 3 to version 2 format. Returns the new attribute name and the converted value. If the value is None, None will be returned for the new value. """ return attr, value def _convertFontInfoDataVersion3ToVersion2(data): converted = {} for attr, value in list(data.items()): newAttr, newValue = convertFontInfoValueForAttributeFromVersion3ToVersion2(attr, value) if newAttr not in fontInfoAttributesVersion2: continue converted[newAttr] = newValue return converted def _convertFontInfoDataVersion2ToVersion3(data): converted = {} for attr, value in list(data.items()): attr, value = convertFontInfoValueForAttributeFromVersion2ToVersion3(attr, value) converted[attr] = value return converted if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/Lib/ufoLib/filenames.py0000644000000000000000000001365213174123546015711 0ustar rootroot""" User name to file name conversion. This was taken form the UFO 3 spec. """ from __future__ import unicode_literals from fontTools.misc.py23 import basestring, unicode illegalCharacters = "\" * + / : < > ? [ \ ] | \0".split(" ") illegalCharacters += [chr(i) for i in range(1, 32)] illegalCharacters += [chr(0x7F)] reservedFileNames = "CON PRN AUX CLOCK$ NUL A:-Z: COM1".lower().split(" ") reservedFileNames += "LPT1 LPT2 LPT3 COM2 COM3 COM4".lower().split(" ") maxFileNameLength = 255 class NameTranslationError(Exception): pass def userNameToFileName(userName, existing=[], prefix="", suffix=""): """ existing should be a case-insensitive list of all existing file names. >>> userNameToFileName("a") == "a" True >>> userNameToFileName("A") == "A_" True >>> userNameToFileName("AE") == "A_E_" True >>> userNameToFileName("Ae") == "A_e" True >>> userNameToFileName("ae") == "ae" True >>> userNameToFileName("aE") == "aE_" True >>> userNameToFileName("a.alt") == "a.alt" True >>> userNameToFileName("A.alt") == "A_.alt" True >>> userNameToFileName("A.Alt") == "A_.A_lt" True >>> userNameToFileName("A.aLt") == "A_.aL_t" True >>> userNameToFileName(u"A.alT") == "A_.alT_" True >>> userNameToFileName("T_H") == "T__H_" True >>> userNameToFileName("T_h") == "T__h" True >>> userNameToFileName("t_h") == "t_h" True >>> userNameToFileName("F_F_I") == "F__F__I_" True >>> userNameToFileName("f_f_i") == "f_f_i" True >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash" True >>> userNameToFileName(".notdef") == "_notdef" True >>> userNameToFileName("con") == "_con" True >>> userNameToFileName("CON") == "C_O_N_" True >>> userNameToFileName("con.alt") == "_con.alt" True >>> userNameToFileName("alt.con") == "alt._con" True """ # the incoming name must be a unicode string if not isinstance(userName, unicode): raise ValueError("The value for userName must be a unicode string.") # establish the prefix and suffix lengths prefixLength = len(prefix) suffixLength = len(suffix) # replace an initial period with an _ # if no prefix is to be added if not prefix and userName[0] == ".": userName = "_" + userName[1:] # filter the user name filteredUserName = [] for character in userName: # replace illegal characters with _ if character in illegalCharacters: character = "_" # add _ to all non-lower characters elif character != character.lower(): character += "_" filteredUserName.append(character) userName = "".join(filteredUserName) # clip to 255 sliceLength = maxFileNameLength - prefixLength - suffixLength userName = userName[:sliceLength] # test for illegal files names parts = [] for part in userName.split("."): if part.lower() in reservedFileNames: part = "_" + part parts.append(part) userName = ".".join(parts) # test for clash fullName = prefix + userName + suffix if fullName.lower() in existing: fullName = handleClash1(userName, existing, prefix, suffix) # finished return fullName def handleClash1(userName, existing=[], prefix="", suffix=""): """ existing should be a case-insensitive list of all existing file names. >>> prefix = ("0" * 5) + "." >>> suffix = "." + ("0" * 10) >>> existing = ["a" * 5] >>> e = list(existing) >>> handleClash1(userName="A" * 5, existing=e, ... prefix=prefix, suffix=suffix) == ( ... '00000.AAAAA000000000000001.0000000000') True >>> e = list(existing) >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix) >>> handleClash1(userName="A" * 5, existing=e, ... prefix=prefix, suffix=suffix) == ( ... '00000.AAAAA000000000000002.0000000000') True >>> e = list(existing) >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix) >>> handleClash1(userName="A" * 5, existing=e, ... prefix=prefix, suffix=suffix) == ( ... '00000.AAAAA000000000000001.0000000000') True """ # if the prefix length + user name length + suffix length + 15 is at # or past the maximum length, silce 15 characters off of the user name prefixLength = len(prefix) suffixLength = len(suffix) if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength: l = (prefixLength + len(userName) + suffixLength + 15) sliceLength = maxFileNameLength - l userName = userName[:sliceLength] finalName = None # try to add numbers to create a unique name counter = 1 while finalName is None: name = userName + str(counter).zfill(15) fullName = prefix + name + suffix if fullName.lower() not in existing: finalName = fullName break else: counter += 1 if counter >= 999999999999999: break # if there is a clash, go to the next fallback if finalName is None: finalName = handleClash2(existing, prefix, suffix) # finished return finalName def handleClash2(existing=[], prefix="", suffix=""): """ existing should be a case-insensitive list of all existing file names. >>> prefix = ("0" * 5) + "." >>> suffix = "." + ("0" * 10) >>> existing = [prefix + str(i) + suffix for i in range(100)] >>> e = list(existing) >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == ( ... '00000.100.0000000000') True >>> e = list(existing) >>> e.remove(prefix + "1" + suffix) >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == ( ... '00000.1.0000000000') True >>> e = list(existing) >>> e.remove(prefix + "2" + suffix) >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == ( ... '00000.2.0000000000') True """ # calculate the longest possible string maxLength = maxFileNameLength - len(prefix) - len(suffix) maxValue = int("9" * maxLength) # try to find a number finalName = None counter = 1 while finalName is None: fullName = prefix + str(counter) + suffix if fullName.lower() not in existing: finalName = fullName break else: counter += 1 if counter >= maxValue: break # raise an error if nothing has been found if finalName is None: raise NameTranslationError("No unique name could be found.") # finished return finalName if __name__ == "__main__": import doctest doctest.testmod() ufoLib-2.1.1/requirements.txt0000644000000000000000000000002213174123546014715 0ustar rootrootfonttools==3.13.1