├── .codecov.yml ├── .coveragerc ├── .github └── workflows │ ├── publish-package.yml │ └── run-tests.yml ├── .gitignore ├── .prospector.yaml ├── .readthedocs.yaml ├── CONTRIBUTING.rst ├── LICENSE ├── Lib └── fontParts │ ├── __init__.py │ ├── base │ ├── __init__.py │ ├── anchor.py │ ├── bPoint.py │ ├── base.py │ ├── color.py │ ├── compatibility.py │ ├── component.py │ ├── contour.py │ ├── deprecated.py │ ├── errors.py │ ├── features.py │ ├── font.py │ ├── glyph.py │ ├── groups.py │ ├── guideline.py │ ├── image.py │ ├── info.py │ ├── kerning.py │ ├── layer.py │ ├── lib.py │ ├── normalizers.py │ ├── point.py │ └── segment.py │ ├── fontshell │ ├── __init__.py │ ├── anchor.py │ ├── bPoint.py │ ├── base.py │ ├── component.py │ ├── contour.py │ ├── features.py │ ├── font.py │ ├── glyph.py │ ├── groups.py │ ├── guideline.py │ ├── image.py │ ├── info.py │ ├── kerning.py │ ├── layer.py │ ├── lib.py │ ├── point.py │ ├── segment.py │ └── test.py │ ├── test │ ├── __init__.py │ ├── legacyPointPen.py │ ├── test_anchor.py │ ├── test_bPoint.py │ ├── test_color.py │ ├── test_component.py │ ├── test_contour.py │ ├── test_deprecated.py │ ├── test_features.py │ ├── test_font.py │ ├── test_fuzzyNumber.py │ ├── test_glyph.py │ ├── test_groups.py │ ├── test_guideline.py │ ├── test_image.py │ ├── test_info.py │ ├── test_kerning.py │ ├── test_layer.py │ ├── test_lib.py │ ├── test_normalizers.py │ ├── test_point.py │ ├── test_segment.py │ └── test_world.py │ ├── ui.py │ └── world.py ├── MANIFEST.in ├── NEWS.rst ├── README.rst ├── documentation ├── Makefile └── source │ ├── _static │ ├── fontparts-map.png │ ├── full_ltgray.png │ └── mono_ltgray.png │ ├── _themes │ └── fontPartsTheme │ │ ├── intro.html │ │ ├── layout.html │ │ ├── navbar.html │ │ ├── relations.html │ │ ├── search.html │ │ ├── searchbox.html │ │ ├── static │ │ ├── diagrams │ │ │ ├── fp-object-tree.svg │ │ │ └── object-tree.svg │ │ ├── fontparts.css │ │ ├── fontparts.css.map │ │ ├── fontparts.sass │ │ ├── fontparts_logo │ │ │ ├── favicon.png │ │ │ ├── favicon.svg │ │ │ ├── fp-monogram.svg │ │ │ ├── full_animated.svg │ │ │ ├── full_ltgray.png │ │ │ └── mono_ltgray.png │ │ ├── icons │ │ │ ├── github-icon.svg │ │ │ ├── icon-search.svg │ │ │ └── nav.svg │ │ ├── js │ │ │ ├── fontparts_theme.js │ │ │ └── readthedocs.js │ │ ├── sass2css.py │ │ └── sass_partials │ │ │ ├── animations.sass │ │ │ ├── content.sass │ │ │ ├── elements.sass │ │ │ ├── general.sass │ │ │ ├── navbar.sass │ │ │ ├── objects-index.sass │ │ │ ├── reset.sass │ │ │ ├── search.sass │ │ │ ├── sidebar.sass │ │ │ ├── syntax-highlighting.sass │ │ │ └── variables.sass │ │ ├── theme.conf │ │ └── versions.html │ ├── conf.py │ ├── contents.rst │ ├── development │ ├── documenting.rst │ ├── index.rst │ └── testing.rst │ ├── environments │ ├── index.rst │ ├── layers │ │ └── index.rst │ ├── objects │ │ ├── anchor.rst │ │ ├── bpoint.rst │ │ ├── component.rst │ │ ├── contour.rst │ │ ├── features.rst │ │ ├── font.rst │ │ ├── glyph.rst │ │ ├── groups.rst │ │ ├── guideline.rst │ │ ├── image.rst │ │ ├── index.rst │ │ ├── info.rst │ │ ├── kerning.rst │ │ ├── layer.rst │ │ ├── lib.rst │ │ ├── normalizers.rst │ │ ├── point.rst │ │ └── segment.rst │ └── testing │ │ └── index.rst │ ├── gettingstarted │ └── index.rst │ ├── index.rst │ └── objectref │ ├── fontpartsworld │ └── index.rst │ ├── index.rst │ ├── objects │ ├── anchor.rst │ ├── bpoint.rst │ ├── component.rst │ ├── contour.rst │ ├── features.rst │ ├── font.rst │ ├── glyph.rst │ ├── groups.rst │ ├── guideline.rst │ ├── image.rst │ ├── index.rst │ ├── info.rst │ ├── kerning.rst │ ├── layer.rst │ ├── lib.rst │ ├── point.rst │ └── segment.rst │ └── valuetypes │ └── index.rst ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── setup.cfg ├── setup.py └── tox.ini /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "diff, files" 3 | behavior: default 4 | require_changes: true 5 | coverage: 6 | status: 7 | project: off 8 | patch: off 9 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | # measure 'branch' coverage in addition to 'statement' coverage 3 | # See: http://coverage.readthedocs.io/en/coverage-4.5.1/branch.html 4 | branch = True 5 | 6 | # list of directories or packages to measure 7 | source = fontParts 8 | 9 | omit = 10 | */__init__.py 11 | */test/* 12 | */test.py 13 | 14 | 15 | [paths] 16 | source = 17 | Lib/fontParts 18 | .tox/*/lib/python*/site-packages/fontParts 19 | .tox/pypy*/site-packages/fontParts 20 | 21 | [report] 22 | # Regexes for lines to exclude from consideration 23 | exclude_lines = 24 | # keywords to use in inline comments to skip coverage 25 | pragma: no cover 26 | 27 | # don't complain if tests don't hit defensive assertion code 28 | (raise|except)(\s)?NotImplementedError 29 | 30 | # don't complain if non-runnable code isn't run 31 | if __name__ == .__main__.: 32 | 33 | # ignore source code that can’t be found 34 | ignore_errors = True 35 | 36 | # when running a summary report, show missing lines 37 | show_missing = True 38 | -------------------------------------------------------------------------------- /.github/workflows/publish-package.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish Python Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - '[0-9].*' 7 | 8 | jobs: 9 | create_release: 10 | name: Create GitHub Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Create release 14 | uses: actions/create-release@v1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | with: 18 | tag_name: ${{ github.ref }} 19 | release_name: ${{ github.ref }} 20 | draft: false 21 | prerelease: true 22 | 23 | deploy: 24 | 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v3.5.3 29 | - name: Set up Python 30 | uses: actions/setup-python@v4.7.0 31 | with: 32 | python-version: '3.x' 33 | - name: Install dependencies 34 | run: | 35 | python -m pip install --upgrade pip 36 | pip install setuptools wheel twine 37 | - name: Build and publish 38 | env: 39 | TWINE_USERNAME: __token__ 40 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 41 | run: | 42 | python setup.py sdist bdist_wheel 43 | twine upload dist/* 44 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | workflow_dispatch: 9 | inputs: 10 | reason: 11 | description: 'Reason for running workflow' 12 | required: true 13 | 14 | jobs: 15 | 16 | test: 17 | runs-on: ${{ matrix.platform }} 18 | if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')" 19 | strategy: 20 | matrix: 21 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] 22 | platform: [ubuntu-latest, macos-latest, windows-latest] 23 | exclude: # Only test on the oldest and latest supported stable Python on macOS and Windows. 24 | - platform: macos-latest 25 | python-version: 3.10 26 | - platform: windows-latest 27 | python-version: 3.10 28 | - platform: macos-latest 29 | python-version: 3.11 30 | - platform: windows-latest 31 | python-version: 3.11 32 | - platform: macos-latest 33 | python-version: 3.13 34 | - platform: windows-latest 35 | python-version: 3.13 36 | steps: 37 | - uses: actions/checkout@v3.5.3 38 | - name: Set up Python ${{ matrix.python-version }} 39 | uses: actions/setup-python@v4.7.0 40 | with: 41 | python-version: ${{ matrix.python-version }} 42 | - name: Install packages 43 | run: pip install tox coverage 44 | - name: Run Tox 45 | run: tox -e py-cov 46 | - name: Produce coverage files 47 | run: | 48 | coverage combine 49 | coverage xml 50 | - name: Upload coverage to Codecov 51 | uses: codecov/codecov-action@v4 52 | with: 53 | file: coverage.xml 54 | flags: unittests 55 | name: codecov-umbrella 56 | fail_ci_if_error: true 57 | token: ${{ secrets.CODECOV_TOKEN }} 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .tox 3 | .coverage 4 | .coverage.* 5 | .DS_Store 6 | *.egg-info 7 | __pycache__/ 8 | *.py[cod] 9 | *.pyc 10 | build/* 11 | dist/* 12 | .eggs/ 13 | .vscode 14 | *-venv/ 15 | 16 | documentation/build/* 17 | documentation/.sass-cache/* 18 | documentation/source/_themes/fontPartsTheme/static/.sass-cache/* 19 | 20 | doc/* 21 | .pytest_cache 22 | 23 | htmlcov 24 | 25 | # scm version 26 | Lib/fontParts/_version.py 27 | -------------------------------------------------------------------------------- /.prospector.yaml: -------------------------------------------------------------------------------- 1 | strictness: high 2 | 3 | pylint: 4 | run: false # since Codacy runs pylint in addition to prospector 5 | 6 | pep8: 7 | options: 8 | single-line-if-stmt: n 9 | disable: 10 | - N802 11 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3" 12 | 13 | sphinx: 14 | configuration: documentation/source/conf.py 15 | 16 | python: 17 | install: 18 | - requirements: requirements.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 The RoboFab Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lib/fontParts/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from ._version import __version__ 3 | except ImportError: 4 | try: 5 | from setuptools_scm import get_version 6 | __version__ = get_version() 7 | except ImportError: 8 | __version__ = 'unknown' 9 | -------------------------------------------------------------------------------- /Lib/fontParts/base/__init__.py: -------------------------------------------------------------------------------- 1 | from fontParts.base.errors import FontPartsError 2 | from fontParts.base.font import BaseFont 3 | from fontParts.base.info import BaseInfo 4 | from fontParts.base.groups import BaseGroups 5 | from fontParts.base.kerning import BaseKerning 6 | from fontParts.base.features import BaseFeatures 7 | from fontParts.base.lib import BaseLib 8 | from fontParts.base.layer import BaseLayer 9 | from fontParts.base.glyph import BaseGlyph 10 | from fontParts.base.contour import BaseContour 11 | from fontParts.base.point import BasePoint 12 | from fontParts.base.segment import BaseSegment 13 | from fontParts.base.bPoint import BaseBPoint 14 | from fontParts.base.component import BaseComponent 15 | from fontParts.base.anchor import BaseAnchor 16 | from fontParts.base.guideline import BaseGuideline 17 | from fontParts.base.image import BaseImage 18 | -------------------------------------------------------------------------------- /Lib/fontParts/base/color.py: -------------------------------------------------------------------------------- 1 | class Color(tuple): 2 | 3 | """ 4 | An color object. This follows the :ref:`type-color`. 5 | """ 6 | 7 | def _get_r(self): 8 | return self[0] 9 | 10 | r = property(_get_r, 11 | "The color's red component as :ref:`type-int-float`.") 12 | 13 | def _get_g(self): 14 | return self[1] 15 | 16 | g = property(_get_g, 17 | "The color's green component as :ref:`type-int-float`.") 18 | 19 | def _get_b(self): 20 | return self[2] 21 | 22 | b = property(_get_b, 23 | "The color's blue component as :ref:`type-int-float`.") 24 | 25 | def _get_a(self): 26 | return self[3] 27 | 28 | a = property(_get_a, 29 | "The color's alpha component as :ref:`type-int-float`.") 30 | -------------------------------------------------------------------------------- /Lib/fontParts/base/errors.py: -------------------------------------------------------------------------------- 1 | # ------------------- 2 | # Universal Exception 3 | # ------------------- 4 | 5 | 6 | class FontPartsError(Exception): 7 | pass 8 | -------------------------------------------------------------------------------- /Lib/fontParts/base/features.py: -------------------------------------------------------------------------------- 1 | from fontParts.base.base import BaseObject, dynamicProperty, reference 2 | from fontParts.base import normalizers 3 | from fontParts.base.deprecated import DeprecatedFeatures, RemovedFeatures 4 | 5 | 6 | class BaseFeatures(BaseObject, DeprecatedFeatures, RemovedFeatures): 7 | 8 | copyAttributes = ("text",) 9 | 10 | def _reprContents(self): 11 | contents = [] 12 | if self.font is not None: 13 | contents.append("for font") 14 | contents += self.font._reprContents() 15 | return contents 16 | 17 | # ------- 18 | # Parents 19 | # ------- 20 | 21 | # Font 22 | 23 | _font = None 24 | 25 | font = dynamicProperty("font", "The features' parent :class:`BaseFont`.") 26 | 27 | def _get_font(self): 28 | if self._font is None: 29 | return None 30 | return self._font() 31 | 32 | def _set_font(self, font): 33 | if self._font is not None and self._font() != font: 34 | raise AssertionError("font for features already set and is not same as font") 35 | if font is not None: 36 | font = reference(font) 37 | self._font = font 38 | 39 | # ---- 40 | # Text 41 | # ---- 42 | 43 | text = dynamicProperty( 44 | "base_text", 45 | """ 46 | The `.fea formated 47 | `_ 48 | text representing the features. 49 | It must be a :ref:`type-string`. 50 | """ 51 | ) 52 | 53 | def _get_base_text(self): 54 | value = self._get_text() 55 | if value is not None: 56 | value = normalizers.normalizeFeatureText(value) 57 | return value 58 | 59 | def _set_base_text(self, value): 60 | if value is not None: 61 | value = normalizers.normalizeFeatureText(value) 62 | self._set_text(value) 63 | 64 | def _get_text(self): 65 | """ 66 | This is the environment implementation of 67 | :attr:`BaseFeatures.text`. This must return a 68 | :ref:`type-string`. 69 | 70 | Subclasses must override this method. 71 | """ 72 | self.raiseNotImplementedError() 73 | 74 | def _set_text(self, value): 75 | """ 76 | This is the environment implementation of 77 | :attr:`BaseFeatures.text`. **value** will be 78 | a :ref:`type-string`. 79 | 80 | Subclasses must override this method. 81 | """ 82 | self.raiseNotImplementedError() 83 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/__init__.py: -------------------------------------------------------------------------------- 1 | from fontParts.base.errors import FontPartsError 2 | from fontParts.fontshell.font import RFont 3 | from fontParts.fontshell.info import RInfo 4 | from fontParts.fontshell.groups import RGroups 5 | from fontParts.fontshell.kerning import RKerning 6 | from fontParts.fontshell.features import RFeatures 7 | from fontParts.fontshell.lib import RLib 8 | from fontParts.fontshell.layer import RLayer 9 | from fontParts.fontshell.glyph import RGlyph 10 | from fontParts.fontshell.contour import RContour 11 | from fontParts.fontshell.point import RPoint 12 | from fontParts.fontshell.segment import RSegment 13 | from fontParts.fontshell.bPoint import RBPoint 14 | from fontParts.fontshell.component import RComponent 15 | from fontParts.fontshell.anchor import RAnchor 16 | from fontParts.fontshell.guideline import RGuideline 17 | from fontParts.fontshell.image import RImage 18 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/anchor.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseAnchor 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RAnchor(RBaseObject, BaseAnchor): 7 | 8 | wrapClass = defcon.Anchor 9 | 10 | def _init(self, wrap=None): 11 | if wrap is None: 12 | wrap = self.wrapClass() 13 | wrap.x = 0 14 | wrap.y = 0 15 | super(RAnchor, self)._init(wrap=wrap) 16 | 17 | # -------- 18 | # Position 19 | # -------- 20 | 21 | # x 22 | 23 | def _get_x(self): 24 | return self.naked().x 25 | 26 | def _set_x(self, value): 27 | self.naked().x = value 28 | 29 | # y 30 | 31 | def _get_y(self): 32 | return self.naked().y 33 | 34 | def _set_y(self, value): 35 | self.naked().y = value 36 | 37 | # -------------- 38 | # Identification 39 | # -------------- 40 | 41 | # identifier 42 | 43 | def _get_identifier(self): 44 | anchor = self.naked() 45 | return anchor.identifier 46 | 47 | def _getIdentifier(self): 48 | anchor = self.naked() 49 | return anchor.generateIdentifier() 50 | 51 | def _setIdentifier(self, value): 52 | self.naked().identifier = value 53 | 54 | # name 55 | 56 | def _get_name(self): 57 | return self.naked().name 58 | 59 | def _set_name(self, value): 60 | self.naked().name = value 61 | 62 | # color 63 | 64 | def _get_color(self): 65 | value = self.naked().color 66 | if value is not None: 67 | value = tuple(value) 68 | return value 69 | 70 | def _set_color(self, value): 71 | self.naked().color = value 72 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/bPoint.py: -------------------------------------------------------------------------------- 1 | from fontParts.base import BaseBPoint 2 | from fontParts.fontshell.base import RBaseObject 3 | 4 | 5 | class RBPoint(BaseBPoint, RBaseObject): 6 | pass 7 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/base.py: -------------------------------------------------------------------------------- 1 | class RBaseObject(object): 2 | 3 | wrapClass = None 4 | 5 | def _init(self, wrap=None): 6 | if wrap is None and self.wrapClass is not None: 7 | wrap = self.wrapClass() 8 | if wrap is not None: 9 | self._wrapped = wrap 10 | 11 | def changed(self): 12 | self.naked().dirty = True 13 | 14 | def naked(self): 15 | if hasattr(self, "_wrapped"): 16 | return self._wrapped 17 | return None 18 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/component.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseComponent 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RComponent(RBaseObject, BaseComponent): 7 | 8 | wrapClass = defcon.Component 9 | 10 | # ---------- 11 | # Attributes 12 | # ---------- 13 | 14 | # baseGlyph 15 | 16 | def _get_baseGlyph(self): 17 | return self.naked().baseGlyph 18 | 19 | def _set_baseGlyph(self, value): 20 | self.naked().baseGlyph = value 21 | 22 | # transformation 23 | 24 | def _get_transformation(self): 25 | return self.naked().transformation 26 | 27 | def _set_transformation(self, value): 28 | self.naked().transformation = value 29 | 30 | # -------------- 31 | # Identification 32 | # -------------- 33 | 34 | # index 35 | 36 | def _set_index(self, value): 37 | component = self.naked() 38 | glyph = component.glyph 39 | if value > glyph.components.index(component): 40 | value -= 1 41 | glyph.removeComponent(component) 42 | glyph.insertComponent(value, component) 43 | 44 | # identifier 45 | 46 | def _get_identifier(self): 47 | component = self.naked() 48 | return component.identifier 49 | 50 | def _getIdentifier(self): 51 | component = self.naked() 52 | return component.generateIdentifier() 53 | 54 | def _setIdentifier(self, value): 55 | self.naked().identifier = value 56 | 57 | # ------------- 58 | # Normalization 59 | # ------------- 60 | 61 | def _decompose(self): 62 | component = self.naked() 63 | glyph = component.glyph 64 | glyph.decomposeComponent(component) 65 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/contour.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseContour 3 | from fontParts.fontshell.base import RBaseObject 4 | from fontParts.fontshell.point import RPoint 5 | from fontParts.fontshell.segment import RSegment 6 | from fontParts.fontshell.bPoint import RBPoint 7 | 8 | 9 | class RContour(RBaseObject, BaseContour): 10 | 11 | wrapClass = defcon.Contour 12 | pointClass = RPoint 13 | segmentClass = RSegment 14 | bPointClass = RBPoint 15 | 16 | # -------------- 17 | # Identification 18 | # -------------- 19 | 20 | # index 21 | 22 | def _set_index(self, value): 23 | contour = self.naked() 24 | glyph = contour.glyph 25 | glyph.removeContour(contour) 26 | glyph.insertContour(value, contour) 27 | 28 | # identifier 29 | 30 | def _get_identifier(self): 31 | contour = self.naked() 32 | return contour.identifier 33 | 34 | def _getIdentifier(self): 35 | contour = self.naked() 36 | return contour.generateIdentifier() 37 | 38 | def _getIdentifierforPoint(self, point): 39 | contour = self.naked() 40 | point = point.naked() 41 | return contour.generateIdentifierForPoint(point) 42 | 43 | # ---- 44 | # Open 45 | # ---- 46 | 47 | def _get_open(self): 48 | return self.naked().open 49 | 50 | # ------ 51 | # Bounds 52 | # ------ 53 | 54 | def _get_bounds(self): 55 | return self.naked().bounds 56 | 57 | # ---- 58 | # Area 59 | # ---- 60 | 61 | def _get_area(self): 62 | return self.naked().area 63 | 64 | # --------- 65 | # Direction 66 | # --------- 67 | 68 | def _get_clockwise(self): 69 | return self.naked().clockwise 70 | 71 | def _reverseContour(self, **kwargs): 72 | self.naked().reverse() 73 | 74 | # ------------------------ 75 | # Point and Contour Inside 76 | # ------------------------ 77 | 78 | def _pointInside(self, point): 79 | return self.naked().pointInside(point) 80 | 81 | def _contourInside(self, otherContour): 82 | return self.naked().contourInside(otherContour.naked(), segmentLength=5) 83 | 84 | # ------ 85 | # Points 86 | # ------ 87 | 88 | def _lenPoints(self, **kwargs): 89 | return len(self.naked()) 90 | 91 | def _getPoint(self, index, **kwargs): 92 | contour = self.naked() 93 | point = contour[index] 94 | return self.pointClass(point) 95 | 96 | def _insertPoint(self, index, position, type=None, smooth=None, 97 | name=None, identifier=None, **kwargs): 98 | point = self.pointClass() 99 | point.x = position[0] 100 | point.y = position[1] 101 | point.type = type 102 | point.smooth = smooth 103 | point.name = name 104 | point = point.naked() 105 | point.identifier = identifier 106 | self.naked().insertPoint(index, point) 107 | 108 | def _removePoint(self, index, preserveCurve, **kwargs): 109 | contour = self.naked() 110 | point = contour[index] 111 | contour.removePoint(point) 112 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/features.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseFeatures 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RFeatures(RBaseObject, BaseFeatures): 7 | 8 | wrapClass = defcon.Features 9 | 10 | def _get_text(self): 11 | return self.naked().text 12 | 13 | def _set_text(self, value): 14 | self.naked().text = value 15 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/font.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | import os 3 | from fontParts.base import BaseFont 4 | from fontParts.fontshell.base import RBaseObject 5 | from fontParts.fontshell.info import RInfo 6 | from fontParts.fontshell.groups import RGroups 7 | from fontParts.fontshell.kerning import RKerning 8 | from fontParts.fontshell.features import RFeatures 9 | from fontParts.fontshell.lib import RLib 10 | from fontParts.fontshell.layer import RLayer 11 | from fontParts.fontshell.guideline import RGuideline 12 | 13 | 14 | class RFont(RBaseObject, BaseFont): 15 | 16 | wrapClass = defcon.Font 17 | infoClass = RInfo 18 | groupsClass = RGroups 19 | kerningClass = RKerning 20 | featuresClass = RFeatures 21 | libClass = RLib 22 | layerClass = RLayer 23 | guidelineClass = RGuideline 24 | 25 | # --------------- 26 | # File Operations 27 | # --------------- 28 | 29 | # Initialize 30 | 31 | def _init(self, pathOrObject=None, showInterface=True, **kwargs): 32 | if pathOrObject is None: 33 | font = self.wrapClass() 34 | elif isinstance(pathOrObject, str): 35 | font = self.wrapClass(pathOrObject) 36 | elif hasattr(pathOrObject, "__fspath__"): 37 | font = self.wrapClass(os.fspath(pathOrObject)) 38 | else: 39 | font = pathOrObject 40 | self._wrapped = font 41 | 42 | # path 43 | 44 | def _get_path(self, **kwargs): 45 | return self.naked().path 46 | 47 | # save 48 | 49 | def _save(self, path=None, showProgress=False, 50 | formatVersion=None, fileStructure=None, **kwargs): 51 | self.naked().save(path=path, formatVersion=formatVersion, structure=fileStructure) 52 | 53 | # close 54 | 55 | def _close(self, **kwargs): 56 | del self._wrapped 57 | 58 | # ----------- 59 | # Sub-Objects 60 | # ----------- 61 | 62 | # info 63 | 64 | def _get_info(self): 65 | return self.infoClass(wrap=self.naked().info) 66 | 67 | # groups 68 | 69 | def _get_groups(self): 70 | return self.groupsClass(wrap=self.naked().groups) 71 | 72 | # kerning 73 | 74 | def _get_kerning(self): 75 | return self.kerningClass(wrap=self.naked().kerning) 76 | 77 | # features 78 | 79 | def _get_features(self): 80 | return self.featuresClass(wrap=self.naked().features) 81 | 82 | # lib 83 | 84 | def _get_lib(self): 85 | return self.libClass(wrap=self.naked().lib) 86 | 87 | # tempLib 88 | 89 | def _get_tempLib(self): 90 | return self.libClass(wrap=self.naked().tempLib) 91 | 92 | # ------ 93 | # Layers 94 | # ------ 95 | 96 | def _get_layers(self, **kwargs): 97 | return [self.layerClass(wrap=layer) for layer in self.naked().layers] 98 | 99 | # order 100 | 101 | def _get_layerOrder(self, **kwargs): 102 | return self.naked().layers.layerOrder 103 | 104 | def _set_layerOrder(self, value, **kwargs): 105 | self.naked().layers.layerOrder = value 106 | 107 | # default layer 108 | 109 | def _get_defaultLayerName(self): 110 | return self.naked().layers.defaultLayer.name 111 | 112 | def _set_defaultLayerName(self, value, **kwargs): 113 | for layer in self.layers: 114 | if layer.name == value: 115 | break 116 | layer = layer.naked() 117 | self.naked().layers.defaultLayer = layer 118 | 119 | # new 120 | 121 | def _newLayer(self, name, color, **kwargs): 122 | layers = self.naked().layers 123 | layer = layers.newLayer(name) 124 | layer.color = color 125 | return self.layerClass(wrap=layer) 126 | 127 | # remove 128 | 129 | def _removeLayer(self, name, **kwargs): 130 | layers = self.naked().layers 131 | del layers[name] 132 | 133 | # ------ 134 | # Glyphs 135 | # ------ 136 | 137 | def _get_glyphOrder(self): 138 | return self.naked().glyphOrder 139 | 140 | def _set_glyphOrder(self, value): 141 | self.naked().glyphOrder = value 142 | 143 | # ---------- 144 | # Guidelines 145 | # ---------- 146 | 147 | def _lenGuidelines(self, **kwargs): 148 | return len(self.naked().guidelines) 149 | 150 | def _getGuideline(self, index, **kwargs): 151 | guideline = self.naked().guidelines[index] 152 | return self.guidelineClass(guideline) 153 | 154 | def _appendGuideline(self, position, angle, name=None, color=None, identifier=None, **kwargs): 155 | guideline = self.guidelineClass().naked() 156 | guideline.x = position[0] 157 | guideline.y = position[1] 158 | guideline.angle = angle 159 | guideline.name = name 160 | guideline.color = color 161 | guideline.identifier = identifier 162 | self.naked().appendGuideline(guideline) 163 | return self.guidelineClass(guideline) 164 | 165 | def _removeGuideline(self, index, **kwargs): 166 | guideline = self.naked().guidelines[index] 167 | self.naked().removeGuideline(guideline) 168 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/groups.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseGroups 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RGroups(RBaseObject, BaseGroups): 7 | 8 | wrapClass = defcon.Groups 9 | 10 | def _get_side1KerningGroups(self): 11 | return self.naked().getRepresentation("defcon.groups.kerningSide1Groups") 12 | 13 | def _get_side2KerningGroups(self): 14 | return self.naked().getRepresentation("defcon.groups.kerningSide2Groups") 15 | 16 | def _items(self): 17 | return self.naked().items() 18 | 19 | def _contains(self, key): 20 | return key in self.naked() 21 | 22 | def _setItem(self, key, value): 23 | self.naked()[key] = list(value) 24 | 25 | def _getItem(self, key): 26 | return self.naked()[key] 27 | 28 | def _delItem(self, key): 29 | del self.naked()[key] 30 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/guideline.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseGuideline 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RGuideline(RBaseObject, BaseGuideline): 7 | 8 | wrapClass = defcon.Guideline 9 | 10 | def _init(self, wrap=None): 11 | if wrap is None: 12 | wrap = self.wrapClass() 13 | wrap.x = 0 14 | wrap.y = 0 15 | wrap.angle = 0 16 | super(RGuideline, self)._init(wrap=wrap) 17 | 18 | # -------- 19 | # Position 20 | # -------- 21 | 22 | # x 23 | 24 | def _get_x(self): 25 | return self.naked().x 26 | 27 | def _set_x(self, value): 28 | self.naked().x = value 29 | 30 | # y 31 | 32 | def _get_y(self): 33 | return self.naked().y 34 | 35 | def _set_y(self, value): 36 | self.naked().y = value 37 | 38 | # angle 39 | 40 | def _get_angle(self): 41 | return self.naked().angle 42 | 43 | def _set_angle(self, value): 44 | self.naked().angle = value 45 | 46 | # -------------- 47 | # Identification 48 | # -------------- 49 | 50 | # identifier 51 | 52 | def _get_identifier(self): 53 | guideline = self.naked() 54 | return guideline.identifier 55 | 56 | def _getIdentifier(self): 57 | guideline = self.naked() 58 | return guideline.generateIdentifier() 59 | 60 | def _setIdentifier(self, value): 61 | self.naked().identifier = value 62 | 63 | # name 64 | 65 | def _get_name(self): 66 | return self.naked().name 67 | 68 | def _set_name(self, value): 69 | self.naked().name = value 70 | 71 | # color 72 | 73 | def _get_color(self): 74 | value = self.naked().color 75 | if value is not None: 76 | value = tuple(value) 77 | return value 78 | 79 | def _set_color(self, value): 80 | self.naked().color = value 81 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/image.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseImage, FontPartsError 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RImage(RBaseObject, BaseImage): 7 | 8 | wrapClass = defcon.Image 9 | _orphanData = None 10 | _orphanColor = None 11 | 12 | # ---------- 13 | # Attributes 14 | # ---------- 15 | 16 | # Transformation 17 | 18 | def _get_transformation(self): 19 | return self.naked().transformation 20 | 21 | def _set_transformation(self, value): 22 | self.naked().transformation = value 23 | 24 | # Color 25 | 26 | def _get_color(self): 27 | if self.font is None: 28 | return self._orphanColor 29 | value = self.naked().color 30 | if value is not None: 31 | value = tuple(value) 32 | return value 33 | 34 | def _set_color(self, value): 35 | if self.font is None: 36 | self._orphanColor = value 37 | else: 38 | self.naked().color = value 39 | 40 | # Data 41 | 42 | def _get_data(self): 43 | if self.font is None: 44 | return self._orphanData 45 | image = self.naked() 46 | images = self.font.naked().images 47 | fileName = image.fileName 48 | if fileName is None: 49 | return None 50 | if fileName not in images: 51 | return None 52 | return images[fileName] 53 | 54 | def _set_data(self, value): 55 | from fontTools.ufoLib.validators import pngValidator 56 | if not isinstance(value, bytes): 57 | raise FontPartsError("The image data provided is not valid.") 58 | if not pngValidator(data=value)[0]: 59 | raise FontPartsError("The image must be in PNG format.") 60 | if self.font is None: 61 | self._orphanData = value 62 | else: 63 | image = self.naked() 64 | images = image.font.images 65 | fileName = images.findDuplicateImage(value) 66 | if fileName is None: 67 | fileName = images.makeFileName("image.png") 68 | images[fileName] = value 69 | image.fileName = fileName 70 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/info.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseInfo 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RInfo(RBaseObject, BaseInfo): 7 | 8 | wrapClass = defcon.Info 9 | 10 | def _getAttr(self, attr): 11 | return getattr(self.naked(), attr) 12 | 13 | def _setAttr(self, attr, value): 14 | setattr(self.naked(), attr, value) 15 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/kerning.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseKerning 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RKerning(RBaseObject, BaseKerning): 7 | 8 | wrapClass = defcon.Kerning 9 | 10 | def _items(self): 11 | return self.naked().items() 12 | 13 | def _contains(self, key): 14 | return key in self.naked() 15 | 16 | def _setItem(self, key, value): 17 | self.naked()[key] = value 18 | 19 | def _getItem(self, key): 20 | return self.naked()[key] 21 | 22 | def _delItem(self, key): 23 | del self.naked()[key] 24 | 25 | def _find(self, pair, default=0): 26 | return self.naked().find(pair, default) 27 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/layer.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseLayer 3 | from fontParts.fontshell.base import RBaseObject 4 | from fontParts.fontshell.lib import RLib 5 | from fontParts.fontshell.glyph import RGlyph 6 | 7 | 8 | class RLayer(RBaseObject, BaseLayer): 9 | 10 | wrapClass = defcon.Layer 11 | libClass = RLib 12 | glyphClass = RGlyph 13 | 14 | # ----------- 15 | # Sub-Objects 16 | # ----------- 17 | 18 | # lib 19 | 20 | def _get_lib(self): 21 | return self.libClass(wrap=self.naked().lib) 22 | 23 | # tempLib 24 | 25 | def _get_tempLib(self): 26 | return self.libClass(wrap=self.naked().tempLib) 27 | 28 | # -------------- 29 | # Identification 30 | # -------------- 31 | 32 | # name 33 | 34 | def _get_name(self): 35 | return self.naked().name 36 | 37 | def _set_name(self, value, **kwargs): 38 | self.naked().name = value 39 | 40 | # color 41 | 42 | def _get_color(self): 43 | value = self.naked().color 44 | if value is not None: 45 | value = tuple(value) 46 | return value 47 | 48 | def _set_color(self, value, **kwargs): 49 | self.naked().color = value 50 | 51 | # ----------------- 52 | # Glyph Interaction 53 | # ----------------- 54 | 55 | def _getItem(self, name, **kwargs): 56 | layer = self.naked() 57 | glyph = layer[name] 58 | return self.glyphClass(glyph) 59 | 60 | def _keys(self, **kwargs): 61 | return self.naked().keys() 62 | 63 | def _newGlyph(self, name, **kwargs): 64 | layer = self.naked() 65 | layer.newGlyph(name) 66 | return self[name] 67 | 68 | def _removeGlyph(self, name, **kwargs): 69 | layer = self.naked() 70 | del layer[name] 71 | 72 | # ------- 73 | # mapping 74 | # ------- 75 | 76 | def _getReverseComponentMapping(self): 77 | return self.naked().componentReferences 78 | 79 | def _getCharacterMapping(self): 80 | return self.naked().unicodeData 81 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/lib.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BaseLib 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RLib(RBaseObject, BaseLib): 7 | 8 | wrapClass = defcon.Lib 9 | 10 | def _items(self): 11 | return self.naked().items() 12 | 13 | def _contains(self, key): 14 | return key in self.naked() 15 | 16 | def _setItem(self, key, value): 17 | self.naked()[key] = value 18 | 19 | def _getItem(self, key): 20 | return self.naked()[key] 21 | 22 | def _delItem(self, key): 23 | del self.naked()[key] 24 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/point.py: -------------------------------------------------------------------------------- 1 | import defcon 2 | from fontParts.base import BasePoint, FontPartsError 3 | from fontParts.fontshell.base import RBaseObject 4 | 5 | 6 | class RPoint(RBaseObject, BasePoint): 7 | 8 | wrapClass = defcon.Point 9 | 10 | def _init(self, wrap=None): 11 | if wrap is None: 12 | wrap = self.wrapClass((0, 0)) 13 | super(RPoint, self)._init(wrap=wrap) 14 | 15 | def _postChangeNotification(self): 16 | contour = self.contour 17 | if contour is None: 18 | return 19 | contour.naked().postNotification("Contour.PointsChanged") 20 | self.changed() 21 | 22 | def changed(self): 23 | self.contour.naked().dirty = True 24 | 25 | # ---------- 26 | # Attributes 27 | # ---------- 28 | 29 | # type 30 | 31 | def _get_type(self): 32 | value = self.naked().segmentType 33 | if value is None: 34 | value = "offcurve" 35 | return value 36 | 37 | def _set_type(self, value): 38 | if value == "offcurve": 39 | value = None 40 | self.naked().segmentType = value 41 | self._postChangeNotification() 42 | 43 | # smooth 44 | 45 | def _get_smooth(self): 46 | return self.naked().smooth 47 | 48 | def _set_smooth(self, value): 49 | self.naked().smooth = value 50 | self._postChangeNotification() 51 | 52 | # x 53 | 54 | def _get_x(self): 55 | return self.naked().x 56 | 57 | def _set_x(self, value): 58 | self.naked().x = value 59 | self._postChangeNotification() 60 | 61 | # y 62 | 63 | def _get_y(self): 64 | return self.naked().y 65 | 66 | def _set_y(self, value): 67 | self.naked().y = value 68 | self._postChangeNotification() 69 | 70 | # -------------- 71 | # Identification 72 | # -------------- 73 | 74 | # name 75 | 76 | def _get_name(self): 77 | return self.naked().name 78 | 79 | def _set_name(self, value): 80 | self.naked().name = value 81 | self._postChangeNotification() 82 | 83 | # identifier 84 | 85 | def _get_identifier(self): 86 | point = self.naked() 87 | return point.identifier 88 | 89 | def _getIdentifier(self): 90 | point = self.naked() 91 | value = point.identifier 92 | if value is not None: 93 | return value 94 | if self.contour is not None: 95 | contour = self.contour.naked() 96 | contour.generateIdentifierForPoint(point) 97 | value = point.identifier 98 | else: 99 | raise FontPartsError(("An identifier can not be generated " 100 | "for this point because it does not " 101 | "belong to a contour.")) 102 | return value 103 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/segment.py: -------------------------------------------------------------------------------- 1 | from fontParts.base import BaseSegment 2 | from fontParts.fontshell.base import RBaseObject 3 | 4 | 5 | class RSegment(BaseSegment, RBaseObject): 6 | pass 7 | -------------------------------------------------------------------------------- /Lib/fontParts/fontshell/test.py: -------------------------------------------------------------------------------- 1 | from fontParts.test import testEnvironment 2 | from fontParts.fontshell.font import RFont 3 | from fontParts.fontshell.info import RInfo 4 | from fontParts.fontshell.groups import RGroups 5 | from fontParts.fontshell.kerning import RKerning 6 | from fontParts.fontshell.features import RFeatures 7 | from fontParts.fontshell.layer import RLayer 8 | from fontParts.fontshell.glyph import RGlyph 9 | from fontParts.fontshell.contour import RContour 10 | from fontParts.fontshell.segment import RSegment 11 | from fontParts.fontshell.bPoint import RBPoint 12 | from fontParts.fontshell.point import RPoint 13 | from fontParts.fontshell.anchor import RAnchor 14 | from fontParts.fontshell.component import RComponent 15 | from fontParts.fontshell.image import RImage 16 | from fontParts.fontshell.lib import RLib 17 | from fontParts.fontshell.guideline import RGuideline 18 | 19 | 20 | # defcon does not have prebuilt support for 21 | # selection states, so we simulate selection 22 | # behavior with a small subclasses for testing 23 | # purposes only. 24 | 25 | def _get_selected(self): 26 | if isinstance(self, FSTestSegment): 27 | for point in self.points: 28 | if point.selected: 29 | return True 30 | return False 31 | elif isinstance(self, FSTestBPoint): 32 | point = self._point.naked() 33 | return point.name == "selected" 34 | elif isinstance(self, FSTestPoint): 35 | return self.name == "selected" 36 | else: 37 | if not hasattr(self.naked(), "_testSelected"): 38 | return False 39 | return self.naked()._testSelected 40 | 41 | 42 | def _set_selected(self, value): 43 | if isinstance(self, FSTestSegment): 44 | for point in self.points: 45 | point.selected = value 46 | elif isinstance(self, FSTestBPoint): 47 | point = self._point.naked() 48 | if value: 49 | point.name = "selected" 50 | else: 51 | point.name = None 52 | elif isinstance(self, FSTestPoint): 53 | if value: 54 | self.name = "selected" 55 | else: 56 | self.name = None 57 | else: 58 | self.naked()._testSelected = value 59 | 60 | 61 | class FSTestPoint(RPoint): 62 | 63 | _get_selected = _get_selected 64 | _set_selected = _set_selected 65 | 66 | 67 | class FSTestBPoint(RBPoint): 68 | 69 | _get_selected = _get_selected 70 | _set_selected = _set_selected 71 | 72 | 73 | class FSTestSegment(RSegment): 74 | 75 | _get_selected = _get_selected 76 | _set_selected = _set_selected 77 | 78 | 79 | class FSTestGuideline(RGuideline): 80 | 81 | _get_selected = _get_selected 82 | _set_selected = _set_selected 83 | 84 | 85 | class FSTestImage(RImage): 86 | 87 | _get_selected = _get_selected 88 | _set_selected = _set_selected 89 | 90 | 91 | class FSTestAnchor(RAnchor): 92 | 93 | _get_selected = _get_selected 94 | _set_selected = _set_selected 95 | 96 | 97 | class FSTestComponent(RComponent): 98 | 99 | _get_selected = _get_selected 100 | _set_selected = _set_selected 101 | 102 | 103 | class FSTestContour(RContour): 104 | 105 | segmentClass = FSTestSegment 106 | bPointClass = FSTestBPoint 107 | pointClass = FSTestPoint 108 | _get_selected = _get_selected 109 | _set_selected = _set_selected 110 | 111 | 112 | class FSTestGlyph(RGlyph): 113 | 114 | contourClass = FSTestContour 115 | componentClass = FSTestComponent 116 | anchorClass = FSTestAnchor 117 | guidelineClass = FSTestGuideline 118 | _get_selected = _get_selected 119 | _set_selected = _set_selected 120 | 121 | 122 | class FSTestLayer(RLayer): 123 | 124 | glyphClass = FSTestGlyph 125 | _get_selected = _get_selected 126 | _set_selected = _set_selected 127 | 128 | 129 | class FSTestFont(RFont): 130 | 131 | layerClass = FSTestLayer 132 | guidelineClass = FSTestGuideline 133 | _get_selected = _get_selected 134 | _set_selected = _set_selected 135 | 136 | 137 | classMapping = dict( 138 | font=FSTestFont, 139 | info=RInfo, 140 | groups=RGroups, 141 | kerning=RKerning, 142 | features=RFeatures, 143 | layer=FSTestLayer, 144 | glyph=FSTestGlyph, 145 | contour=FSTestContour, 146 | segment=FSTestSegment, 147 | bPoint=FSTestBPoint, 148 | point=FSTestPoint, 149 | anchor=FSTestAnchor, 150 | component=FSTestComponent, 151 | image=FSTestImage, 152 | lib=RLib, 153 | guideline=FSTestGuideline, 154 | ) 155 | 156 | 157 | def fontshellObjectGenerator(cls): 158 | unrequested = [] 159 | obj = classMapping[cls]() 160 | return obj, unrequested 161 | 162 | 163 | if __name__ == "__main__": 164 | import sys 165 | if {"-v", "--verbose"}.intersection(sys.argv): 166 | verbosity = 2 167 | else: 168 | verbosity = 1 169 | testEnvironment(fontshellObjectGenerator, verbosity=verbosity) 170 | -------------------------------------------------------------------------------- /Lib/fontParts/test/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | import unittest 4 | from fontParts.test import test_normalizers 5 | from fontParts.test import test_font 6 | from fontParts.test import test_info 7 | from fontParts.test import test_groups 8 | from fontParts.test import test_kerning 9 | from fontParts.test import test_features 10 | from fontParts.test import test_layer 11 | from fontParts.test import test_glyph 12 | from fontParts.test import test_contour 13 | from fontParts.test import test_segment 14 | from fontParts.test import test_bPoint 15 | from fontParts.test import test_point 16 | from fontParts.test import test_component 17 | from fontParts.test import test_anchor 18 | from fontParts.test import test_image 19 | from fontParts.test import test_lib 20 | from fontParts.test import test_guideline 21 | from fontParts.test import test_deprecated 22 | from fontParts.test import test_color 23 | from fontParts.test import test_world 24 | 25 | 26 | def testEnvironment(objectGenerator, inApp=False, verbosity=1, testNormalizers=True): 27 | modules = [ 28 | test_font, 29 | test_info, 30 | test_groups, 31 | test_kerning, 32 | test_features, 33 | test_layer, 34 | test_glyph, 35 | test_contour, 36 | test_segment, 37 | test_bPoint, 38 | test_point, 39 | test_component, 40 | test_anchor, 41 | test_image, 42 | test_lib, 43 | test_guideline, 44 | test_deprecated, 45 | test_color, 46 | test_world 47 | ] 48 | if testNormalizers: 49 | modules.append(test_normalizers) 50 | 51 | globalSuite = unittest.TestSuite() 52 | loader = unittest.TestLoader() 53 | for module in modules: 54 | suite = loader.loadTestsFromModule(module) 55 | _setObjectGenerator(suite, objectGenerator) 56 | globalSuite.addTest(suite) 57 | runner = unittest.TextTestRunner(verbosity=verbosity) 58 | succes = runner.run(globalSuite).wasSuccessful() 59 | if not inApp: 60 | sys.exit(not succes) 61 | else: 62 | return succes # pragma: no cover 63 | 64 | 65 | def _setObjectGenerator(suite, objectGenerator): 66 | for i in suite: 67 | if isinstance(i, unittest.TestSuite): 68 | _setObjectGenerator(i, objectGenerator) 69 | else: 70 | i.objectGenerator = objectGenerator 71 | -------------------------------------------------------------------------------- /Lib/fontParts/test/legacyPointPen.py: -------------------------------------------------------------------------------- 1 | from fontPens.recordingPointPen import RecordingPointPen 2 | 3 | 4 | class LegacyPointPen(RecordingPointPen): 5 | 6 | """ 7 | A point pen that accepts only the original 8 | arguments in the various methods. 9 | """ 10 | 11 | def beginPath(self): 12 | super(LegacyPointPen, self).beginPath() 13 | 14 | def endPath(self): 15 | super(LegacyPointPen, self).endPath() 16 | 17 | def addPoint(self, pt, segmentType=None, smooth=False, name=None): 18 | super(LegacyPointPen, self).addPoint(pt, 19 | segmentType=segmentType, 20 | smooth=smooth, name=name) 21 | 22 | def addComponent(self, baseGlyphName, transformation): 23 | super(LegacyPointPen, self).addComponent(baseGlyphName, transformation) 24 | -------------------------------------------------------------------------------- /Lib/fontParts/test/test_color.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from fontParts.base.color import Color 3 | 4 | 5 | class TestComponent(unittest.TestCase): 6 | 7 | def test_color_r(self): 8 | color = Color((1.0, 0, 0, 0)) 9 | self.assertEqual(color.r, 1.0) 10 | 11 | def test_color_g(self): 12 | color = Color((0, 1.0, 0, 0)) 13 | self.assertEqual(color.g, 1.0) 14 | 15 | def test_color_b(self): 16 | color = Color((0, 0, 1.0, 0)) 17 | self.assertEqual(color.b, 1.0) 18 | 19 | def test_color_a(self): 20 | color = Color((0, 0, 0, 1.00)) 21 | self.assertEqual(color.a, 1.0) 22 | -------------------------------------------------------------------------------- /Lib/fontParts/test/test_features.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import collections 3 | 4 | 5 | class TestFeatures(unittest.TestCase): 6 | 7 | def getFeatures_generic(self): 8 | features, _ = self.objectGenerator("features") 9 | features.text = "# test" 10 | return features 11 | 12 | # ---- 13 | # repr 14 | # ---- 15 | 16 | def test_reprContents(self): 17 | font, _ = self.objectGenerator("font") 18 | features = font.features 19 | features.text = "# test" 20 | value = features._reprContents() 21 | self.assertIsInstance(value, list) 22 | for i in value: 23 | self.assertIsInstance(i, str) 24 | 25 | def test_reprContents_noFont(self): 26 | features, _ = self.objectGenerator("features") 27 | features.text = "# test" 28 | value = features._reprContents() 29 | self.assertIsInstance(value, list) 30 | self.assertListEqual(value, []) 31 | 32 | # ---- 33 | # Text 34 | # ---- 35 | 36 | def test_text_get(self): 37 | features = self.getFeatures_generic() 38 | self.assertEqual( 39 | features.text, 40 | "# test" 41 | ) 42 | 43 | def test_text_valid_set(self): 44 | features = self.getFeatures_generic() 45 | features.text = "# foo" 46 | self.assertEqual( 47 | features.text, 48 | "# foo" 49 | ) 50 | 51 | def test_text_set_none(self): 52 | features = self.getFeatures_generic() 53 | features.text = None 54 | self.assertIsNone(features.text) 55 | 56 | def test_text_invalid_set(self): 57 | features = self.getFeatures_generic() 58 | with self.assertRaises(TypeError): 59 | features.text = 123 60 | 61 | # ------- 62 | # Parents 63 | # ------- 64 | 65 | def test_get_parent_font(self): 66 | font, _ = self.objectGenerator("font") 67 | features = font.features 68 | features.text = "# Test" 69 | self.assertIsNotNone(features.font) 70 | self.assertEqual( 71 | features.font, 72 | font 73 | ) 74 | 75 | def test_get_parent_noFont(self): 76 | features = self.getFeatures_generic() 77 | self.assertIsNone(features.font) 78 | 79 | def test_set_parent_font(self): 80 | font, _ = self.objectGenerator("font") 81 | features = self.getFeatures_generic() 82 | features.font = font 83 | self.assertIsNotNone(features.font) 84 | self.assertEqual( 85 | features.font, 86 | font 87 | ) 88 | 89 | def test_set_parent_font_none(self): 90 | features = self.getFeatures_generic() 91 | features.font = None 92 | self.assertIsNone(features.font) 93 | 94 | def test_set_parent_font_exists(self): 95 | font, _ = self.objectGenerator("font") 96 | otherFont, _ = self.objectGenerator("font") 97 | features = font.features 98 | features.text = "# Test" 99 | with self.assertRaises(AssertionError): 100 | features.font = otherFont 101 | 102 | # ---- 103 | # Hash 104 | # ---- 105 | 106 | def test_hash(self): 107 | features = self.getFeatures_generic() 108 | self.assertEqual( 109 | isinstance(features, collections.abc.Hashable), 110 | True 111 | ) 112 | 113 | # -------- 114 | # Equality 115 | # -------- 116 | 117 | def test_object_equal_self(self): 118 | features_one = self.getFeatures_generic() 119 | self.assertEqual( 120 | features_one, 121 | features_one 122 | ) 123 | 124 | def test_object_not_equal_other(self): 125 | features_one = self.getFeatures_generic() 126 | features_two = self.getFeatures_generic() 127 | self.assertNotEqual( 128 | features_one, 129 | features_two 130 | ) 131 | 132 | def test_object_equal_self_variable_assignment(self): 133 | features_one = self.getFeatures_generic() 134 | a = features_one 135 | a.text += "# testing" 136 | self.assertEqual( 137 | features_one, 138 | a 139 | ) 140 | 141 | def test_object_not_equal_self_variable_assignment(self): 142 | features_one = self.getFeatures_generic() 143 | features_two = self.getFeatures_generic() 144 | a = features_one 145 | self.assertNotEqual( 146 | features_two, 147 | a 148 | ) 149 | -------------------------------------------------------------------------------- /Lib/fontParts/test/test_fuzzyNumber.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from fontParts.base.base import FuzzyNumber 3 | 4 | 5 | class TestFuzzyNumber(unittest.TestCase): 6 | 7 | def __init__(self, methodName): 8 | unittest.TestCase.__init__(self, methodName) 9 | 10 | def test_init(self): 11 | fuzzyNumber1 = FuzzyNumber(value=0, threshold=1) 12 | fuzzyNumber2 = FuzzyNumber(2, 3) 13 | self.assertEqual([fuzzyNumber1.value, fuzzyNumber1.threshold], 14 | [0, 1]) 15 | self.assertEqual([fuzzyNumber2.value, fuzzyNumber2.threshold], 16 | [2, 3]) 17 | 18 | def test_repr(self): 19 | fuzzyNumber = FuzzyNumber(0, 1) 20 | self.assertEqual(repr(fuzzyNumber), "[0.000000 1.000000]") 21 | 22 | def test_comparison(self): 23 | fuzzyNumber1 = FuzzyNumber(value=0, threshold=1) 24 | self.assertEqual(fuzzyNumber1, 0) 25 | self.assertTrue(fuzzyNumber1 < 1) 26 | self.assertFalse(fuzzyNumber1 < -0.000001) 27 | self.assertFalse(fuzzyNumber1 < 0) 28 | 29 | fuzzyNumber2 = FuzzyNumber(value=0.999999, threshold=1) 30 | self.assertEqual( 31 | repr(sorted([fuzzyNumber1, fuzzyNumber2])), 32 | "[[0.000000 1.000000], [0.999999 1.000000]]" 33 | ) 34 | self.assertFalse(fuzzyNumber1 < fuzzyNumber2) 35 | 36 | fuzzyNumber2 = FuzzyNumber(value=1, threshold=1) 37 | self.assertEqual( 38 | repr(sorted([fuzzyNumber1, fuzzyNumber2])), 39 | "[[0.000000 1.000000], [1.000000 1.000000]]" 40 | ) 41 | self.assertTrue(fuzzyNumber1 < fuzzyNumber2) 42 | 43 | fuzzyNumber2 = FuzzyNumber(value=-0.999999, threshold=1) 44 | self.assertEqual( 45 | repr(sorted([fuzzyNumber1, fuzzyNumber2])), 46 | "[[0.000000 1.000000], [-0.999999 1.000000]]" 47 | ) 48 | self.assertFalse(fuzzyNumber1 > fuzzyNumber2) 49 | 50 | fuzzyNumber2 = FuzzyNumber(value=-1, threshold=1) 51 | self.assertEqual( 52 | repr(sorted([fuzzyNumber1, fuzzyNumber2])), 53 | "[[-1.000000 1.000000], [0.000000 1.000000]]" 54 | ) 55 | self.assertTrue(fuzzyNumber1 > fuzzyNumber2) 56 | 57 | # equal 58 | self.assertEqual(fuzzyNumber1, fuzzyNumber1) 59 | self.assertNotEqual(fuzzyNumber1, fuzzyNumber2) 60 | 61 | # complex sorting 62 | fuzzyNumber2 = FuzzyNumber(value=0.999999, threshold=1) 63 | self.assertEqual( 64 | repr(sorted([(fuzzyNumber1, 20), (fuzzyNumber2, 10)])), 65 | "[([0.999999 1.000000], 10), ([0.000000 1.000000], 20)]" 66 | ) 67 | 68 | -------------------------------------------------------------------------------- /Lib/fontParts/test/test_info.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import collections 3 | 4 | 5 | class TestInfo(unittest.TestCase): 6 | 7 | def getInfo_generic(self): 8 | info, _ = self.objectGenerator("info") 9 | info.unitsPerEm = 1000 10 | return info 11 | 12 | # ---------- 13 | # Dimensions 14 | # ---------- 15 | 16 | def test_get_unitsPerEm(self): 17 | info = self.getInfo_generic() 18 | self.assertEqual( 19 | info.unitsPerEm, 20 | 1000 21 | ) 22 | 23 | def test_set_valid_unitsPerEm_int(self): 24 | info = self.getInfo_generic() 25 | info.unitsPerEm = 2000 26 | self.assertEqual( 27 | info.unitsPerEm, 28 | 2000 29 | ) 30 | 31 | def test_set_valid_unitsPerEm_float(self): 32 | info = self.getInfo_generic() 33 | info.unitsPerEm = 2000.1 34 | self.assertEqual( 35 | info.unitsPerEm, 36 | 2000.1 37 | ) 38 | 39 | def test_set_invalid_unitsPerEm_negative(self): 40 | info = self.getInfo_generic() 41 | with self.assertRaises(ValueError): 42 | info.unitsPerEm = -1000 43 | 44 | def test_set_invalid_unitsPerEm_string(self): 45 | info = self.getInfo_generic() 46 | with self.assertRaises(ValueError): 47 | info.unitsPerEm = "abc" 48 | 49 | # ---- 50 | # Hash 51 | # ---- 52 | 53 | def test_hash(self): 54 | info = self.getInfo_generic() 55 | self.assertEqual( 56 | isinstance(info, collections.abc.Hashable), 57 | True 58 | ) 59 | 60 | # -------- 61 | # Equality 62 | # -------- 63 | 64 | def test_object_equal_self(self): 65 | info_one = self.getInfo_generic() 66 | self.assertEqual( 67 | info_one, 68 | info_one 69 | ) 70 | 71 | def test_object_not_equal_other(self): 72 | info_one = self.getInfo_generic() 73 | info_two = self.getInfo_generic() 74 | self.assertNotEqual( 75 | info_one, 76 | info_two 77 | ) 78 | 79 | def test_object_equal_self_variable_assignment(self): 80 | info_one = self.getInfo_generic() 81 | a = info_one 82 | self.assertEqual( 83 | info_one, 84 | a 85 | ) 86 | 87 | def test_object_not_equal_other_variable_assignment(self): 88 | info_one = self.getInfo_generic() 89 | info_two = self.getInfo_generic() 90 | a = info_one 91 | self.assertNotEqual( 92 | info_two, 93 | a 94 | ) 95 | 96 | # ----- 97 | # Round 98 | # ----- 99 | 100 | def test_round_unitsPerEm(self): 101 | info = self.getInfo_generic() 102 | info.unitsPerEm = 2000.125 103 | info.round() 104 | self.assertEqual( 105 | info.unitsPerEm, 106 | 2000 107 | ) 108 | 109 | # ------ 110 | # Update 111 | # ------ 112 | 113 | def test_update(self): 114 | from fontTools.ufoLib import fontInfoAttributesVersion3ValueData 115 | info1 = self.getInfo_generic() 116 | info1.familyName = "test1" 117 | info1.unitsPerEm = 1000 118 | info2 = self.getInfo_generic() 119 | info2.familyName = "test2" 120 | info2.unitsPerEm = 2000 121 | info1.update(info2) 122 | self.assertEqual(info1.familyName, "test2") 123 | self.assertEqual(info1.unitsPerEm, 2000) 124 | 125 | # ---- 126 | # Copy 127 | # ---- 128 | def test_copy(self): 129 | info1 = self.getInfo_generic() 130 | info1.postscriptBlueValues = [-10, 0, 50, 60] 131 | 132 | info2 = info1.copy() 133 | info2.postscriptBlueValues[0] = -2 134 | 135 | self.assertNotEqual(info1.postscriptBlueValues, info2.postscriptBlueValues) 136 | 137 | # ------------- 138 | # Interpolation 139 | # ------------- 140 | 141 | def test_interpolate_unitsPerEm_without_rounding(self): 142 | interpolated_font, _ = self.objectGenerator("font") 143 | font_min, _ = self.objectGenerator("font") 144 | font_max, _ = self.objectGenerator("font") 145 | font_min.info.unitsPerEm = 1000 146 | font_max.info.unitsPerEm = 2000 147 | interpolated_font.info.interpolate(0.5154, font_min.info, font_max.info, round=False) 148 | self.assertEqual( 149 | interpolated_font.info.unitsPerEm, 150 | 1515.4 151 | ) 152 | 153 | def test_interpolate_unitsPerEm_with_rounding(self): 154 | interpolated_font, _ = self.objectGenerator("font") 155 | font_min, _ = self.objectGenerator("font") 156 | font_max, _ = self.objectGenerator("font") 157 | font_min.info.unitsPerEm = 1000 158 | font_max.info.unitsPerEm = 2000 159 | interpolated_font.info.interpolate(0.5154, font_min.info, font_max.info, round=True) 160 | self.assertEqual( 161 | interpolated_font.info.unitsPerEm, 162 | 1515 163 | ) 164 | -------------------------------------------------------------------------------- /Lib/fontParts/test/test_lib.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import collections 3 | 4 | 5 | class TestLib(unittest.TestCase): 6 | 7 | def getLib_generic(self): 8 | lib, _ = self.objectGenerator("lib") 9 | lib.update({ 10 | "key 1": ["A", "B", "C"], 11 | "key 2": "x", 12 | "key 3": [], 13 | "key 4": 20 14 | }) 15 | return lib 16 | 17 | # ---- 18 | # repr 19 | # ---- 20 | 21 | def test_reprContents(self): 22 | lib = self.getLib_generic() 23 | value = lib._reprContents() 24 | self.assertIsInstance(value, list) 25 | for i in value: 26 | self.assertIsInstance(i, str) 27 | 28 | # --- 29 | # len 30 | # --- 31 | 32 | def test_len_initial(self): 33 | lib = self.getLib_generic() 34 | self.assertEqual( 35 | len(lib), 36 | 4 37 | ) 38 | 39 | def test_len_clear(self): 40 | lib = self.getLib_generic() 41 | lib.clear() 42 | self.assertEqual( 43 | len(lib), 44 | 0 45 | ) 46 | 47 | # ------- 48 | # Parents 49 | # ------- 50 | 51 | # Glyph 52 | 53 | def test_get_parent_glyph(self): 54 | glyph, _ = self.objectGenerator("glyph") 55 | lib, _ = self.objectGenerator("lib") 56 | lib.glyph = glyph 57 | self.assertIsNotNone(lib.glyph) 58 | self.assertEqual( 59 | lib.glyph, 60 | glyph 61 | ) 62 | 63 | def test_get_parent_noGlyph(self): 64 | lib, _ = self.objectGenerator("lib") 65 | self.assertIsNone(lib.font) 66 | self.assertIsNone(lib.layer) 67 | self.assertIsNone(lib.glyph) 68 | 69 | def test_set_parent_glyph(self): 70 | glyph, _ = self.objectGenerator("glyph") 71 | lib, _ = self.objectGenerator("lib") 72 | lib.glyph = glyph 73 | self.assertIsNotNone(lib.glyph) 74 | self.assertEqual( 75 | lib.glyph, 76 | glyph 77 | ) 78 | 79 | # Font 80 | 81 | def test_get_parent_font(self): 82 | font, _ = self.objectGenerator("font") 83 | layer = font.newLayer("L") 84 | glyph = layer.newGlyph("X") 85 | lib, _ = self.objectGenerator("lib") 86 | lib.glyph = glyph 87 | self.assertIsNotNone(lib.font) 88 | self.assertEqual( 89 | lib.font, 90 | font 91 | ) 92 | 93 | def test_get_parent_noFont(self): 94 | layer, _ = self.objectGenerator("layer") 95 | glyph = layer.newGlyph("X") 96 | lib, _ = self.objectGenerator("lib") 97 | lib.glyph = glyph 98 | self.assertIsNone(lib.font) 99 | 100 | # ------- 101 | # Queries 102 | # ------- 103 | 104 | def test_keys(self): 105 | lib = self.getLib_generic() 106 | self.assertEqual( 107 | sorted(lib.keys()), 108 | ["key 1", "key 2", "key 3", "key 4"] 109 | ) 110 | 111 | def test_contains_found(self): 112 | lib = self.getLib_generic() 113 | self.assertTrue("key 4" in lib) 114 | 115 | def test_contains_not_found(self): 116 | lib = self.getLib_generic() 117 | self.assertFalse("key five" in lib) 118 | 119 | def test_get_found(self): 120 | lib = self.getLib_generic() 121 | self.assertEqual( 122 | lib["key 1"], 123 | ["A", "B", "C"] 124 | ) 125 | 126 | # ---- 127 | # Hash 128 | # ---- 129 | 130 | def test_hash(self): 131 | lib = self.getLib_generic() 132 | self.assertEqual( 133 | isinstance(lib, collections.abc.Hashable), 134 | True 135 | ) 136 | 137 | # -------- 138 | # Equality 139 | # -------- 140 | 141 | def test_object_equal_self(self): 142 | lib_one = self.getLib_generic() 143 | self.assertEqual( 144 | lib_one, 145 | lib_one 146 | ) 147 | 148 | def test_object_not_equal_other(self): 149 | lib_one = self.getLib_generic() 150 | lib_two = self.getLib_generic() 151 | self.assertNotEqual( 152 | lib_one, 153 | lib_two 154 | ) 155 | 156 | def test_object_equal_self_variable_assignment(self): 157 | lib_one = self.getLib_generic() 158 | a = lib_one 159 | self.assertEqual( 160 | lib_one, 161 | a 162 | ) 163 | 164 | def test_object_not_equal_other_variable_assignment(self): 165 | lib_one = self.getLib_generic() 166 | lib_two = self.getLib_generic() 167 | a = lib_one 168 | self.assertNotEqual( 169 | lib_two, 170 | a 171 | ) 172 | -------------------------------------------------------------------------------- /Lib/fontParts/ui.py: -------------------------------------------------------------------------------- 1 | from fontParts.world import _EnvironmentDispatcher 2 | 3 | 4 | def AskString(message, value='', title='FontParts'): 5 | """ 6 | An ask a string dialog, a `message` is required. 7 | Optionally a `value` and `title` can be provided. 8 | 9 | :: 10 | 11 | from fontParts.ui import AskString 12 | print(AskString("who are you?")) 13 | 14 | """ 15 | return dispatcher["AskString"](message=message, value=value, title=title) 16 | 17 | 18 | def AskYesNoCancel(message, title='FontParts', default=0, informativeText=""): 19 | """ 20 | An ask yes, no or cancel dialog, a `message` is required. 21 | Optionally a `title`, `default` and `informativeText` can be provided. 22 | The `default` option is to indicate which button is the default button. 23 | 24 | :: 25 | 26 | from fontParts.ui import AskYesNoCancel 27 | print(AskYesNoCancel("who are you?")) 28 | 29 | """ 30 | return dispatcher["AskYesNoCancel"](message=message, title=title, 31 | default=default, informativeText=informativeText) 32 | 33 | 34 | def FindGlyph(aFont, message="Search for a glyph:", title='FontParts'): 35 | """ 36 | A dialog to search a glyph for a provided font. 37 | Optionally a `message`, `title` and `allFonts` can be provided. 38 | 39 | 40 | from fontParts.ui import FindGlyph 41 | from fontParts.world import CurrentFont 42 | glyph = FindGlyph(CurrentFont()) 43 | print(glyph) 44 | 45 | """ 46 | return dispatcher["FindGlyph"](aFont=aFont, message=message, title=title) 47 | 48 | 49 | def GetFile(message=None, title=None, directory=None, fileName=None, 50 | allowsMultipleSelection=False, fileTypes=None): 51 | """ 52 | An get file dialog. 53 | Optionally a `message`, `title`, `directory`, `fileName` and 54 | `allowsMultipleSelection` can be provided. 55 | 56 | :: 57 | 58 | from fontParts.ui import GetFile 59 | print(GetFile()) 60 | 61 | """ 62 | return dispatcher["GetFile"](message=message, title=title, directory=directory, 63 | fileName=fileName, 64 | allowsMultipleSelection=allowsMultipleSelection, 65 | fileTypes=fileTypes) 66 | 67 | 68 | def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, 69 | allowsMultipleSelection=False, fileTypes=None): 70 | """ 71 | An get file or folder dialog. 72 | Optionally a `message`, `title`, `directory`, `fileName`, 73 | `allowsMultipleSelection` and `fileTypes` can be provided. 74 | 75 | :: 76 | 77 | from fontParts.ui import GetFileOrFolder 78 | print(GetFileOrFolder()) 79 | 80 | """ 81 | return dispatcher["GetFileOrFolder"](message=message, title=title, 82 | directory=directory, fileName=fileName, 83 | allowsMultipleSelection=allowsMultipleSelection, 84 | fileTypes=fileTypes) 85 | 86 | 87 | def Message(message, title='FontParts', informativeText=""): 88 | """ 89 | An message dialog. 90 | Optionally a `message`, `title` and `informativeText` can be provided. 91 | 92 | :: 93 | 94 | from fontParts.ui import Message 95 | print(Message("This is a message")) 96 | 97 | """ 98 | return dispatcher["Message"](message=message, title=title, 99 | informativeText=informativeText) 100 | 101 | 102 | def PutFile(message=None, fileName=None): 103 | """ 104 | An put file dialog. 105 | Optionally a `message` and `fileName` can be provided. 106 | 107 | :: 108 | 109 | from fontParts.ui import PutFile 110 | print(PutFile()) 111 | 112 | """ 113 | return dispatcher["PutFile"](message=message, fileName=fileName) 114 | 115 | 116 | def SearchList(items, message="Select an item:", title='FontParts'): 117 | """ 118 | A dialgo to search a given list. 119 | Optionally a `message`, `title` and `allFonts` can be provided. 120 | 121 | :: 122 | 123 | from fontParts.ui import SearchList 124 | result = SearchList(["a", "b", "c"]) 125 | print(result) 126 | 127 | """ 128 | return dispatcher["SearchList"](items=items, message=message, title=title) 129 | 130 | 131 | def SelectFont(message="Select a font:", title='FontParts', allFonts=None): 132 | """ 133 | Select a font from all open fonts. 134 | Optionally a `message`, `title` and `allFonts` can be provided. 135 | If `allFonts` is `None` it will list all open fonts. 136 | 137 | :: 138 | 139 | from fontParts.ui import SelectFont 140 | font = SelectFont() 141 | print(font) 142 | 143 | """ 144 | return dispatcher["SelectFont"](message=message, title=title, allFonts=allFonts) 145 | 146 | 147 | def SelectGlyph(aFont, message="Select a glyph:", title='FontParts'): 148 | """ 149 | Select a glyph for a given font. 150 | Optionally a `message` and `title` can be provided. 151 | 152 | :: 153 | 154 | from fontParts.ui import SelectGlyph 155 | font = CurrentFont() 156 | glyph = SelectGlyph(font) 157 | print(glyph) 158 | 159 | """ 160 | return dispatcher["SelectGlyph"](aFont=aFont, message=message, title=title) 161 | 162 | 163 | def ProgressBar(title="RoboFab...", ticks=None, label=""): 164 | """ 165 | A progess bar dialog. 166 | Optionally a `title`, `ticks` and `label` can be provided. 167 | 168 | :: 169 | 170 | from fontParts.ui import ProgressBar 171 | 172 | bar = ProgressBar() 173 | # do something 174 | bar.close() 175 | 176 | """ 177 | return dispatcher["ProgressBar"](title=title, ticks=ticks, label=label) 178 | 179 | 180 | # ---------- 181 | # Dispatcher 182 | # ---------- 183 | 184 | dispatcher = _EnvironmentDispatcher([ 185 | "AskString", 186 | "AskYesNoCancel", 187 | "FindGlyph", 188 | "GetFile", 189 | "GetFolder", 190 | "GetFileOrFolder", 191 | "Message", 192 | "OneList", 193 | "PutFile", 194 | "SearchList", 195 | "SelectFont", 196 | "SelectGlyph", 197 | "ProgressBar", 198 | ]) 199 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include tox.ini 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | |CI Build Status| |Coverage| |PyPI| |Versions| 2 | 3 | FontParts 4 | ~~~~~~~~~ 5 | 6 | An API for interacting with the parts of fonts during the font 7 | development process. FontParts is the replacement for 8 | `RoboFab `__. The project has a 9 | `MIT open-source licence `__. 10 | 11 | The documentation is at 12 | `fontparts.readthedocs.io `__. 13 | 14 | *This is a work in progress. We are still working out the API, abstract 15 | implementation, example implementation, test suite and documentation.* 16 | 17 | Want to contribute? 18 | ------------------- 19 | 20 | Thank you! Please see the `CONTRIBUTING.rst `_ file for a guide on how to help. 21 | 22 | Also, feedback is very much welcome, please open an issue when you run 23 | into something that you wish fontParts did/didn't do. 24 | 25 | 26 | Installation 27 | ~~~~~~~~~~~~ 28 | 29 | FontParts requires `Python `__ 3.8 or later. 30 | 31 | The package is listed in the Python Package Index (PyPI), so you can 32 | install it with `pip `__: 33 | 34 | .. code:: sh 35 | 36 | pip install fontParts 37 | 38 | If you would like to contribute to its development, you can clone the 39 | repository from Github, install the package in 'editable' mode and 40 | modify the source code in place. We recommend creating a virtual 41 | environment, using `virtualenv `__ or `venv `__ module. 42 | 43 | .. code:: sh 44 | 45 | # download the source code to 'fontParts' folder 46 | git clone https://github.com/robofab-developers/fontParts.git 47 | cd fontParts 48 | 49 | # create new virtual environment called e.g. 'fontParts-venv', or anything you like 50 | python -m virtualenv fontParts-venv 51 | 52 | # source the `activate` shell script to enter the environment (Un\*x); to exit, just type `deactivate` 53 | . fontParts-venv/bin/activate 54 | 55 | # to activate the virtual environment in Windows `cmd.exe`, do 56 | fontParts-venv\Scripts\activate.bat 57 | 58 | # install in 'editable' mode 59 | pip install -e . 60 | 61 | Roadmap 62 | ~~~~~~~ 63 | 64 | We are currently working towards the 1.0 release. 65 | 66 | * **0.8** Initial releases. Python 2 & 3. 67 | * **0.9** Python 3 only. 68 | * **1.0** Documentation and testing complete. 69 | * **1.5** Removal of ``Deprecated``. Released 1 year after 1.0. 70 | 71 | Testing 72 | ~~~~~~~ 73 | 74 | Testing is setup so that each environment that includes fontParts 75 | can provides the objects needed to run a common set of tests. 76 | This makes testing very easy for environments that use fontParts (for 77 | example, see the fontshell 78 | `test.py `__ 79 | script), but it means testing is different than other python packages. 80 | 81 | Automated testing of the package is done in the fontshell environment. 82 | fontshell is fontParts for the commandline, implemented with 83 | `defcon `__ and is included 84 | as part of the fontParts package. 85 | 86 | Before you can run the test suite you’ll need to install the test dependencies: 87 | 88 | .. code:: sh 89 | 90 | pip install -r requirements-dev.txt 91 | 92 | To run the test suite you can do: 93 | 94 | .. code:: sh 95 | 96 | python Lib/fontParts/fontshell/test.py 97 | 98 | To test in other environments, run the test script provided by that environment. 99 | 100 | You can also use `tox `__ to 101 | automatically run tests on different Python versions in isolated virtual 102 | environments. 103 | 104 | .. code:: sh 105 | 106 | pip install tox 107 | tox 108 | 109 | Note that when you run ``tox`` without arguments, the tests are executed 110 | for all the environments listed in tox.ini's ``envlist``. In our case, 111 | this is Python 3.6, so for this to work the ``python3.6`` executables must 112 | be available in your ``PATH``. 113 | 114 | You can specify an alternative environment list via the ``-e`` option, 115 | or the ``TOXENV`` environment variable: 116 | 117 | .. code:: sh 118 | 119 | tox -e py39-nocov 120 | TOXENV="py36-cov,htmlcov" tox 121 | 122 | .. |CI Build Status| image:: https://github.com/robotools/fontParts/workflows/Tests/badge.svg 123 | :target: https://github.com/robotools/fontParts/actions?query=workflow%3ATests 124 | .. |PyPI| image:: https://img.shields.io/pypi/v/fontParts.svg 125 | :target: https://pypi.org/project/fontParts 126 | .. |Versions| image:: https://img.shields.io/badge/python-3.8%2C%203.9%2C%203.10%2C%203.11-blue.svg 127 | :alt: Python Versions 128 | .. |Coverage| image:: https://codecov.io/gh/robotools/fontParts/branch/master/graph/badge.svg 129 | :target: https://codecov.io/gh/robotools/fontParts 130 | -------------------------------------------------------------------------------- /documentation/source/_static/fontparts-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_static/fontparts-map.png -------------------------------------------------------------------------------- /documentation/source/_static/full_ltgray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_static/full_ltgray.png -------------------------------------------------------------------------------- /documentation/source/_static/mono_ltgray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_static/mono_ltgray.png -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/intro.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

FontParts is a Python API for programmatically creating and editing parts of fonts during the type design process, and it is application-independent to allow scripts to be portable across multiple applications. It is the replacement for RoboFab.

6 |
7 | learn more 8 |
9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | 3 | 4 | 5 | {%- block extrahead %} 6 | 7 | 8 | 9 | {% endblock %} 10 | 11 | {%- block header %} 12 | 16 | {% endblock %} 17 | 18 | {% block relbar1 %}{% endblock %} 19 | {% block sidebar1 %}{% endblock %} 20 | 21 | {% block document %} 22 | {%- if pagename == "index" %} 23 | {%- include "intro.html" %} 24 | {%- endif %} 25 |
26 | 29 | 69 |
70 |
71 | {% block body %} 72 | {% endblock %} 73 | 87 |
88 |
89 | {% block relbar2 %}{% endblock %} 90 | {% block sidebar2 %}{% endblock %} 91 |
92 | {% endblock %} 93 | 94 | {%- block footer %} 95 | 120 | {%- endblock %} 121 | 122 | 123 | {% set script_files = script_files + ['_static/js/fontparts_theme.js'] %} -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/navbar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/relations.html: -------------------------------------------------------------------------------- 1 | {%- if prev %} 2 |
3 |

{{ _('Previous') }}

4 |

5 | {{ prev.title }} 6 |

7 |
8 | {%- endif %} 9 | {%- if next %} 10 |
11 |

{{ _('Next') }}

12 |

13 | {{ next.title }} 14 |

15 |
16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/search.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% set title = _('Search') %} 3 | {% set script_files = script_files + ['_static/searchtools.js'] %} 4 | {% block extrahead %} 5 | 8 | {{ super() }} 9 | {% endblock %} 10 | {% block body %} 11 |

{{ _('Search') }}

12 |
13 | 14 |

15 | {% trans %}Please activate JavaScript to enable the search 16 | functionality.{% endtrans %} 17 |

18 |
19 |

20 | {% trans %}The search function will automatically search for all keywords in the query. Pages 21 | containing fewer keywords than searched won't appear in the result list.{% endtrans %} 22 |

23 | {% if search_performed %} 24 |

{{ _('Search Results') }}

25 | {% if not search_results %} 26 |

{{ _('Your search did not match any results.') }}

27 | {% endif %} 28 | {% endif %} 29 |
30 | {% if search_results %} 31 |
    32 | {% for href, caption, context in search_results %} 33 |
  • {{ caption }} 34 |
    {{ context|e }}
    35 |
  • 36 | {% endfor %} 37 |
38 | {% endif %} 39 |
40 | {% endblock %} -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/searchbox.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts.sass: -------------------------------------------------------------------------------- 1 | // fonts 2 | @import url('https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700|Source+Sans+Pro:400,400i,700') 3 | 4 | 5 | // Sass partials 6 | @import 'sass_partials/reset.sass' 7 | @import 'sass_partials/variables.sass' 8 | @import 'sass_partials/elements.sass' 9 | @import 'sass_partials/navbar.sass' 10 | @import 'sass_partials/search.sass' 11 | @import 'sass_partials/sidebar.sass' 12 | @import 'sass_partials/content.sass' 13 | @import 'sass_partials/general.sass' 14 | @import 'sass_partials/animations.sass' 15 | @import 'sass_partials/objects-index.sass' 16 | @import 'sass_partials/syntax-highlighting.sass' -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts_logo/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_themes/fontPartsTheme/static/fontparts_logo/favicon.png -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts_logo/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | Asset 17 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts_logo/fp-monogram.svg: -------------------------------------------------------------------------------- 1 | 2 | FontParts 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts_logo/full_ltgray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_themes/fontPartsTheme/static/fontparts_logo/full_ltgray.png -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/fontparts_logo/mono_ltgray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robotools/fontParts/dc032d6e8f24668664adb0b712090c2d3f24a7ec/documentation/source/_themes/fontPartsTheme/static/fontparts_logo/mono_ltgray.png -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/icons/github-icon.svg: -------------------------------------------------------------------------------- 1 | github-icon -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/icons/icon-search.svg: -------------------------------------------------------------------------------- 1 | Asset 4 -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/icons/nav.svg: -------------------------------------------------------------------------------- 1 | 2 | nav 3 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/js/fontparts_theme.js: -------------------------------------------------------------------------------- 1 | function collapseSidebar() { 2 | var sidebar = document.querySelector('#sidebar'); 3 | 4 | if (sidebar != null) { 5 | sidebar.classList.add('mobile-slideout') 6 | } 7 | }; 8 | 9 | window.addEventListener('DOMContentLoaded', function () { 10 | collapseSidebar() 11 | 12 | var navButton = document.querySelector('#nav-button'); 13 | navButton.addEventListener('click', toggleNav) 14 | }, true); 15 | 16 | function toggleNav() { 17 | var fpNavSidebar = document.querySelector('#sidebar'); 18 | fpNavSidebar.classList.toggle('expanded') 19 | 20 | var fpNavOverlay = document.querySelector('#mobile-nav-overlay'); 21 | fpNavOverlay.classList.toggle('hidden') 22 | fpNavOverlay.addEventListener('click', toggleNav) 23 | 24 | var fpNavIcon = document.querySelector('#nav-icon'); 25 | fpNavIcon.classList.toggle('open') 26 | 27 | } -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass2css.py: -------------------------------------------------------------------------------- 1 | # compile sass into css 2 | 3 | import os 4 | import os.path 5 | import subprocess 6 | 7 | # sass (CSS extension language) compiler 8 | # http://sass-lang.com/install 9 | 10 | # install postCSS and autoprixer with: 11 | ## npm install -g postcss-cli-simple 12 | ## npm install -g autoprefixer 13 | 14 | def compileSass(sassPath): 15 | ''' 16 | Compile a sass file (and dependencies) into a single css file. 17 | 18 | ''' 19 | cssPath = os.path.splitext(sassPath)[0] + ".css" 20 | # subprocess.call(["sass", sassPath, cssPath]) 21 | print("Compiling Sass") 22 | 23 | process = subprocess.Popen(["sass", sassPath, cssPath]) 24 | process.wait() 25 | 26 | def autoprefixCSS(sassPath): 27 | ''' 28 | Take CSS file and automatically add browser prefixes with postCSS autoprefixer 29 | 30 | ''' 31 | print("Autoprefixing CSS") 32 | cssPath = os.path.splitext(sassPath)[0] + ".css" 33 | command = "postcss --use autoprefixer --autoprefixer.browsers '> 5%' -o" + cssPath + " " + cssPath 34 | subprocess.call(command, shell=True) 35 | 36 | # gets path for directory of sass2css.py 37 | baseFolder = os.path.split(os.path.abspath(__file__))[0] 38 | 39 | for f in os.listdir(baseFolder): 40 | name, extension = os.path.splitext(f) 41 | 42 | # note: be sure that you import files from /partials into the the main.sass file, or code won't make it into CSS 43 | if extension == ".sass": 44 | sassPath = os.path.join(baseFolder, f) 45 | compileSass(sassPath) 46 | autoprefixCSS(sassPath) 47 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/animations.sass: -------------------------------------------------------------------------------- 1 | .fade-in 2 | opacity: 0 3 | animation: fadeIn 2s .5s ease forwards 4 | 5 | @keyframes fadeIn 6 | from 7 | opacity: 0 8 | to 9 | opacity: 1 -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/content.sass: -------------------------------------------------------------------------------- 1 | #content 2 | vertical-align: top 3 | margin-bottom: $padding 4 | display: inline-block 5 | padding:0 1rem 6 | 7 | @media screen and (max-width: $tablet-portrait) 8 | width: 100vw 9 | order: -1 10 | 11 | h1 + .section h2:first-of-type 12 | padding-top: 0 13 | 14 | p 15 | max-width: $max-text-width 16 | 17 | ul li 18 | margin-bottom: 1em 19 | max-width: $max-text-width 20 | word-wrap: break-word 21 | // text-indent: -0.5em 22 | 23 | dl ul li, ul.simple li 24 | margin-bottom: 0 25 | 26 | ul li:before 27 | content: "–" 28 | position: absolute 29 | transform: translateX(-1rem) 30 | 31 | .toctree-wrapper 32 | ul li 33 | margin-top: 0em 34 | margin-bottom: 0em 35 | 36 | 37 | dt code, dt span 38 | display: inline-block 39 | @media screen and (max-width: $tablet-portrait) 40 | word-break: break-word 41 | 42 | #fp-object-tree 43 | @media screen and (min-width: $computer-average) 44 | transform: scale(1.05) 45 | margin-left: -2.5% 46 | margin-right: -2.5% 47 | 48 | #pagination 49 | display: grid 50 | grid-template-columns: 1fr 1fr 51 | grid-gap: $padding 52 | margin-top: 4*$padding 53 | margin-bottom: 6*$padding 54 | 55 | @media screen and (min-width: 1200px) 56 | margin-left: -$padding 57 | margin-right: -$padding 58 | 59 | 60 | 61 | 62 | //////// TODO: if only 1 link, make full-width 63 | 64 | @media screen and (max-width: $tablet-portrait) 65 | word-break: break-word 66 | grid-template-columns: 1fr 67 | margin-left: $padding 68 | margin-right: $padding 69 | 70 | a 71 | display: block 72 | background: $offwhite-gray 73 | border: $focus-border 74 | border-color: $offwhite-gray 75 | border-radius: 1em 76 | padding: $padding 77 | padding-bottom: 2 * $padding 78 | transition: $short-transition 79 | 80 | &:focus 81 | outline: none 82 | border: $focus-border 83 | 84 | p:after 85 | font-family: $monofont 86 | display: block 87 | color: rgba(0,0,0,0) 88 | transform: translateY(0.4em) 89 | 90 | &#prev-link 91 | p:last-child:after 92 | content: "<<<<" 93 | &#next-link 94 | p:last-child:after 95 | content: ">>>>" 96 | 97 | &:hover 98 | border-color: $blue 99 | 100 | &:hover, &:focus 101 | background: $blue 102 | color: #fff 103 | 104 | p:first-child 105 | color: white 106 | opacity: 0.5 107 | 108 | p:after 109 | color: rgba(0,0,0,0.2) 110 | transition: $short-transition 111 | 112 | 113 | &#prev-link 114 | p:last-child:after 115 | animation: pulse-left 1s linear infinite 116 | 117 | &#next-link 118 | p:last-child:after 119 | animation: pulse-right 1s linear infinite 120 | 121 | p 122 | margin: 0 123 | font-family: $sansfont 124 | 125 | &:first-child 126 | color: $dark-navy 127 | text-transform: uppercase 128 | transition: $short-transition 129 | 130 | &:last-child 131 | font-size: 1.5em 132 | font-weight: bold 133 | 134 | 135 | 136 | &>#next-link 137 | text-align: right 138 | 139 | @keyframes pulse-right 140 | 0% 141 | content: ">>>>>\0000a0" 142 | 15% 143 | content: ">>>>\0000a0>" 144 | 30% 145 | content: ">>>\0000a0>>" 146 | 45% 147 | content: ">>\0000a0>>>" 148 | 60% 149 | content: ">\0000a0>>>>" 150 | 75% 151 | content: "\0000a0>>>>>" 152 | 100% 153 | content: "\0000a0>>>>>" 154 | @keyframes pulse-left 155 | 0% 156 | content: "\0000a0<<<<<" 157 | 15% 158 | content: "<\0000a0<<<<" 159 | 30% 160 | content: "<<\0000a0<<<" 161 | 45% 162 | content: "<<<\0000a0<<" 163 | 60% 164 | content: "<<<<\0000a0<" 165 | 75% 166 | content: "<<<<<\0000a0" 167 | 100% 168 | content: "<<<<<\0000a0" -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/elements.sass: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////// 2 | //////////// basic html elements and styles //////////// 3 | //////////////////////////////////////////////////////// 4 | 5 | ::selection 6 | background: $green-highlight // rgba($greener, 0.35) 7 | 8 | body 9 | margin: 0 10 | font-family: $sansfont 11 | font-size: 1.15em 12 | line-height: 1.6em 13 | position: relative 14 | min-height: 100vh 15 | 16 | pre, code, tt 17 | font-family: $monofont 18 | font-size: 0.95em 19 | font-style: normal 20 | font-weight: normal 21 | white-space: pre-wrap 22 | 23 | pre, code, tt, .method dt 24 | @media (max-width: 600px) 25 | font-size: 1rem 26 | 27 | 28 | pre 29 | background: $offwhite-gray 30 | border: $lighter-gray solid 1px 31 | 32 | 33 | 34 | // span.pre, dt code 35 | dt code, p code 36 | background: $offwhite-gray 37 | border: none 38 | border-radius: .25em 39 | padding: 0.125em 40 | margin-left: -0.125em 41 | margin-right: -0.125em 42 | 43 | dt, dt code, dt span, dt em 44 | font-family: $monofont 45 | font-weight: bold 46 | font-style: normal 47 | 48 | dt a, dt a span 49 | font-family: $sansfont 50 | font-weight: normal 51 | 52 | table span.pre 53 | background: none 54 | border: none 55 | 56 | a, a:active 57 | text-decoration: none 58 | font-style: normal 59 | color: $blue 60 | transition: .25s 61 | 62 | 63 | &:hover 64 | color: $green 65 | 66 | &.underline 67 | border-bottom: 1px solid white 68 | 69 | &:hover 70 | border-bottom: 1px solid $green 71 | 72 | &:focus 73 | outline: 2px solid $green 74 | outline-offset: 4px 75 | 76 | 77 | button, a.button 78 | border: none 79 | display: inline-block 80 | font-family: $monofont 81 | font-size: $font-smallish 82 | background: $blue 83 | border-radius: 12px 84 | padding: 8px 16px 85 | color: white 86 | transition: .25s 87 | &:hover 88 | background: $green 89 | color: white 90 | cursor: pointer 91 | 92 | &:focus 93 | outline: 2px solid $green 94 | outline-offset: 4px 95 | 96 | hr 97 | color: none 98 | background-color: none 99 | border: none 100 | border-top: $border 101 | height: 1px 102 | margin: 2em 0 103 | 104 | dl 105 | margin: 0 106 | padding-bottom: $padding 107 | 108 | dt 109 | padding-top:$header-height 110 | 111 | .rst-badge dl, 112 | .rst-badge dt 113 | padding-top: 0 114 | margin: 0 115 | font-family: $sansfont 116 | 117 | dd 118 | @media screen and (max-width: $tablet-portrait) 119 | margin-left: 0 120 | padding-left: 0 121 | 122 | h1, h2, h3 123 | font-family: $sansfont 124 | color: black 125 | line-height: 1.2em 126 | padding-top: $header-height //helps position headline visibly when it is directly linked to 127 | margin-top: 0 128 | margin-bottom: 1rem 129 | 130 | h1 131 | font-size: 2.25em 132 | 133 | h2 134 | font-size: 1.75em 135 | 136 | ol 137 | margin-left: -1em 138 | max-width: 100% 139 | 140 | ul 141 | // padding-left: 0 142 | // margin-left: 1em 143 | // text-indent: -1em 144 | ul 145 | list-style: none 146 | ul.simple 147 | // text-indent: -0.5em // 0em 148 | code 149 | padding: 0 0.05em 150 | white-space: pre-line 151 | pre 152 | padding: 1em 153 | white-space: pre-wrap 154 | img 155 | width: 100% 156 | table 157 | border-collapse: collapse 158 | border: none 159 | overflow-x: auto 160 | display: block 161 | max-width: 90vw 162 | // border: solid $lighter-gray 1px 163 | tr 164 | border: none 165 | th, td 166 | padding: 0.5em 167 | border: solid 1px #DDD 168 | th 169 | background-color: #DDD 170 | a.headerlink, 171 | a.headerlink:visited 172 | visibility: hidden 173 | .note 174 | padding: $padding 175 | border-radius: $rounded-corner-1 176 | background-color: rgba($blue, 0.1) 177 | border: rgba($blue, 0.5) solid 1px 178 | 179 | @media (min-width: $tablet-portrait) 180 | margin-left: -$padding 181 | p 182 | margin: 0 183 | .admonition-title 184 | // font-weight: bold 185 | color: $blue 186 | margin-bottom: 0 187 | 188 | .highlight 189 | background: transparent !important 190 | max-width: 100vw 191 | @media (max-width: $tablet-portrait) 192 | // margin-left: -$padding 193 | 194 | pre 195 | border-radius: $rounded-corner-1 196 | overflow: auto 197 | 198 | h1:hover > a.headerlink, 199 | h2:hover > a.headerlink, 200 | h3:hover > a.headerlink, 201 | h4:hover > a.headerlink, 202 | h5:hover > a.headerlink, 203 | h6:hover > a.headerlink 204 | visibility: visible -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/general.sass: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////// 2 | //////////// general layout and overall styles //////////// 3 | /////////////////////////////////////////////////////////// 4 | 5 | 6 | 7 | .central-column-wrapper 8 | width: 100% 9 | max-width: $central-column-wrapper 10 | margin-left: auto 11 | margin-right: auto 12 | 13 | .central-column-wrapper-wide 14 | @extend .central-column-wrapper 15 | max-width: $central-column-wrapper-wide 16 | 17 | 18 | .columns-2 19 | display: grid 20 | grid-template-columns: auto auto 21 | grid-gap: $padding 22 | @media (max-width: $tablet-portrait) 23 | grid-template-columns: auto 24 | 25 | 26 | 27 | .hidden--small-screen 28 | @media (max-width: $tablet-landscape) 29 | display: none !important 30 | 31 | .hidden--non-small-screen 32 | @media (min-width: $tablet-landscape + 1) 33 | display: none !important 34 | 35 | #wrapper 36 | min-height: calc(100vh - #{$header-height}) 37 | display: grid 38 | grid-template-columns: 260px 1fr 39 | // grid-template-areas: ". content" 40 | position: relative 41 | // margin: $padding 42 | // padding: calc(5vw + #{$header-height}) $padding 5vw 43 | // display: block 44 | 45 | 46 | 47 | 48 | 49 | .document 50 | padding-top: $header-height 51 | min-height: 100vh 52 | 53 | #intro 54 | padding: 2em $padding 55 | // text-align: center 56 | border-bottom: $border 57 | background: $dark-navy 58 | width: 100% 59 | min-height: calc(90vh - #{$header-height}) 60 | margin: auto 61 | display: flex 62 | align-items: center 63 | 64 | img 65 | display: block 66 | width: 100% 67 | margin-left: auto 68 | margin-right: auto 69 | margin-bottom: 10vh 70 | transition: .25s ease 71 | max-width: 100vw 72 | 73 | @media (min-width: $tablet-landscape) 74 | transition: .25s ease 75 | max-width: 90vw 76 | p 77 | margin:0 78 | margin-bottom:$padding 79 | max-width: 55ch 80 | color: $lighter-gray 81 | 82 | a 83 | color: $lighter-gray 84 | border-bottom: 1px solid $lighter-gray 85 | 86 | &:hover 87 | color: $green 88 | border-bottom: 1px solid $green 89 | 90 | ul 91 | margin:0 92 | display: flex 93 | justify-content: space-between 94 | justify-items: flex-end 95 | flex-flow: row wrap 96 | max-width: 400px 97 | li 98 | display: inline-block 99 | list-style: none 100 | margin-bottom:$padding 101 | 102 | .columns-2 103 | grid-template-columns: 2fr 1fr 104 | margin-left: auto 105 | margin-right: auto 106 | .button 107 | float: right 108 | 109 | @media (max-width: $tablet-landscape) 110 | grid-template-columns: auto 111 | 112 | .button 113 | float: left 114 | 115 | 116 | 117 | 118 | 119 | .button #download 120 | // margin-right: 2em 121 | 122 | 123 | 124 | 125 | .toctree-wrapper 126 | ul 127 | text-indent: 0 128 | 129 | #designers, #developers 130 | display: inline-block 131 | vertical-align: top 132 | width: 48% 133 | 134 | #developers 135 | float: right 136 | 137 | #footer 138 | @extend #header 139 | height: $footer-height 140 | position: absolute 141 | z-index: 1 142 | width: 100% 143 | padding: $padding*1.5 $padding 144 | 145 | &>div 146 | display: flex 147 | align-items: flex-start 148 | justify-content: space-between 149 | 150 | @media (max-width: $tablet-landscape) 151 | flex-flow: row wrap 152 | a 153 | width: 100% 154 | 155 | p 156 | margin: 0 157 | 158 | div:first-child 159 | order: 2 160 | 161 | @media (max-width: $tablet-landscape) 162 | order: 0 163 | padding-bottom: 1em 164 | 165 | #home_button 166 | position: fixed 167 | bottom: 1em 168 | right: 1em 169 | z-index: 4 170 | opacity: .15 171 | background: black 172 | padding: 20px 173 | border-radius: 12px 174 | color: white 175 | transition: opacity .75s 176 | &:hover 177 | opacity: 1 178 | 179 | .icon-24 180 | // max-width: 24px 181 | // max-height: 24px 182 | width: 24px 183 | height: 24px 184 | 185 | // overriding styles in injected readthedocs versions toggle 186 | 187 | .rst-versions 188 | position: absolute !important 189 | bottom: -$footer-height !important 190 | right: $padding !important 191 | font-size: 1rem !important 192 | z-index: 1 !important 193 | width: auto 194 | border-radius: .5rem 195 | margin: 1rem 0 196 | .rst-current-version 197 | width: auto 198 | height: 2rem 199 | border-radius: .5rem 200 | 201 | /* ---------- media queries ---------- */ 202 | 203 | 204 | @media screen and (max-width: $tablet-landscape) 205 | #wrapper 206 | // display: block 207 | grid-template-columns: 1fr 208 | #designers, #developers 209 | display: block 210 | width: 100% 211 | #content 212 | // margin-right: $contentWidth 213 | width: 100vw 214 | order: -1 215 | #sidebar 216 | display: block 217 | width: 100% 218 | min-height: none 219 | #sidebar-inner-wrapper 220 | position: relative 221 | 222 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/navbar.sass: -------------------------------------------------------------------------------- 1 | #header 2 | height: $header-height 3 | position: fixed 4 | z-index: 2 5 | width: 100% 6 | background: black 7 | // padding: 0 $padding * 2 8 | font-family: $monofont 9 | font-size: 0.75em 10 | color: white 11 | display: flex 12 | align-items: center 13 | a 14 | color: white 15 | a:hover 16 | color: $green 17 | font-weight: bold 18 | 19 | .navbar 20 | display: flex 21 | width: 100% 22 | padding-left: $padding 23 | padding-right: $padding 24 | 25 | ul 26 | padding-left: 0 27 | white-space: nowrap 28 | ul, li, #searchbox 29 | display: inline-block 30 | zoom: 1 31 | 32 | nav 33 | display: flex 34 | align-items: center 35 | justify-content: space-between 36 | width: 100% 37 | 38 | div 39 | display: flex 40 | justify-content: space-between 41 | align-items: center 42 | 43 | a:not(:last-child) 44 | margin-right: 1rem 45 | 46 | svg 47 | width: 32px 48 | 49 | .navbar a svg path, 50 | .navbar a svg line 51 | transition: 0.25s ease 52 | 53 | .navbar a svg:hover path 54 | fill: $green !important 55 | 56 | .navbar a svg:hover line 57 | stroke: $green !important 58 | 59 | .navbar button 60 | background: none 61 | padding: 0 62 | transition: .25s ease 63 | outline: none 64 | 65 | svg 66 | width: 32px 67 | 68 | line 69 | transition: .25s ease 70 | 71 | &:hover, &:focus 72 | 73 | 74 | svg line 75 | stroke: $green !important 76 | 77 | &#line1 78 | transform: rotate(-45deg) scale(0.8, 1) 79 | 80 | &#line2 81 | transform-origin: 100% 50% 82 | transform: scale(0.8, 1) 83 | 84 | &#line3 85 | transform: rotate(45deg) scale(0.8, 1) 86 | 87 | svg.open 88 | line 89 | &#line1 90 | transform: rotate(45deg) scale(0.8, 1) 91 | 92 | &#line2 93 | transform-origin: 0% 50% 94 | transform: scale(0.8, 1) 95 | 96 | &#line3 97 | transform: rotate(-45deg) scale(0.8, 1) 98 | 99 | .inline-items 100 | margin-top: 0 101 | li 102 | border-left: $border 103 | margin-left: 0 104 | padding-left: 10px 105 | padding-right: 10px 106 | &:first-child 107 | border: none 108 | margin-left: 0 109 | padding-left: 0 110 | padding-right: 10px 111 | &:last-child 112 | padding-right: 0 113 | input 114 | border: none 115 | border-radius: .25rem 116 | height: 1.5rem 117 | padding: .25rem -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/objects-index.sass: -------------------------------------------------------------------------------- 1 | .genindex-jumpbox 2 | position: sticky 3 | top: 3.5rem 4 | background: $offwhite-gray 5 | padding: 0 1rem 6 | margin: 1rem -1rem 0 7 | border-radius: 1rem 8 | color: rgba($light-gray,0.5) 9 | z-index: 1 10 | min-height: 2.5rem 11 | display: flex 12 | align-items: center 13 | justify-content: space-between 14 | flex-wrap: wrap 15 | 16 | @media (max-width: $tablet-landscape) 17 | padding: 0 2rem 18 | margin: 1rem -2rem 0 19 | 20 | strong 21 | color: inherit 22 | a 23 | font-family: $monofont 24 | padding: 0.5em 0.25rem 25 | // color: white 26 | background: rgba($light-gray,0) 27 | transition: .25s ease 28 | &:hover 29 | color: white 30 | background: rgba($light-gray,1) 31 | 32 | #content 33 | .genindextable 34 | ul 35 | padding-left: 0.75em 36 | // &:first-child 37 | // padding-left: 0.5em 38 | ul li 39 | margin: 0 40 | line-height: 1.25 41 | ul li:before 42 | font-family: $monofont 43 | content: '→' 44 | color: $light-gray 45 | 46 | ul:not(:first-child) li:before 47 | content: '•' 48 | 49 | a 50 | display: inline-block 51 | margin-bottom: 0.75em -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/reset.sass: -------------------------------------------------------------------------------- 1 | /* ---------- resets etc ---------- */ 2 | 3 | html 4 | -moz-box-sizing: border-box 5 | -webkit-box-sizing: border-box 6 | box-sizing: border-box 7 | 8 | *, 9 | *:before, 10 | *:after 11 | box-sizing: inherit -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/search.sass: -------------------------------------------------------------------------------- 1 | #searchbox 2 | width: 60vw 3 | 4 | #searchbox form 5 | font-family: $monofont 6 | display: flex 7 | height: 1.75rem 8 | padding-left: 1.75rem 9 | margin-right: 1rem 10 | justify-content: flex-end 11 | 12 | #searchbox img 13 | display: block 14 | width: 16px 15 | height: 16px 16 | position: relative 17 | top: 6px 18 | left: 24px 19 | 20 | #searchbox input[type="text"] 21 | border-radius: 0.25rem 22 | font-family: $monofont 23 | font-size: 1em 24 | background: transparent 25 | border: #444 1px solid 26 | color: white 27 | transition: 0.25s ease 28 | height: 100% 29 | padding-left: 1.75rem 30 | width: 60% 31 | min-width: 100px 32 | max-width: 200px 33 | 34 | &:hover 35 | border: $green 1px solid 36 | 37 | 38 | &:focus 39 | outline: none 40 | background: #444 41 | border: #444 1px solid 42 | // border: none // white 1px solid 43 | width: 100% 44 | max-width: 400px 45 | 46 | + button 47 | background: $blue 48 | border: $blue 1px solid 49 | 50 | #searchbox p 51 | display: none 52 | 53 | 54 | .highlighted 55 | background: $green-highlight 56 | border: $green 1px solid 57 | border-radius: $rounded-corner-2 58 | font-style: normal 59 | 60 | #content #search-results 61 | ul 62 | padding-left: 0 63 | 64 | li 65 | margin-bottom: $padding*2 66 | 67 | &:before 68 | content: none 69 | a 70 | font-family: $sansfont 71 | font-size: 1.5rem -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/sidebar.sass: -------------------------------------------------------------------------------- 1 | #sidebar 2 | position: sticky 3 | top: $header-height 4 | max-height: calc(100vh - #{$header-height}) 5 | max-width: $sidebarWidth 6 | z-index: 1 7 | padding: 0 0 0 1rem 8 | font-size: 1rem 9 | font-family: $sansfont 10 | background: white 11 | display: flex 12 | 13 | ul 14 | margin: 0 15 | margin-bottom: $padding 16 | 17 | li 18 | padding: 0 19 | 20 | h3 21 | margin-bottom: 0.5em 22 | font-size: 1rem 23 | font-weight: bold 24 | 25 | .caption 26 | margin: 0 27 | text-transform: uppercase 28 | font-size: 80% 29 | letter-spacing: 0.1em 30 | 31 | @media (max-width: $tablet-landscape) 32 | 33 | z-index: 2 34 | 35 | &.mobile-slideout 36 | position: fixed 37 | width: $sidebarWidth 38 | max-width: 100vh 39 | height: calc(100vh - #{$header-height}) 40 | left: 100vw 41 | top: $header-height 42 | transition: 0.5s ease 43 | border-right: none 44 | padding: 0 45 | overflow-y: auto 46 | opacity: 0 47 | 48 | .sidebar-inner-wrapper 49 | padding: 0 1rem 2rem 1rem 50 | transform: none 51 | overflow-y: show 52 | 53 | &.expanded 54 | opacity: 1 55 | transform: translateX(-100%) 56 | transition: 0.5s ease 57 | 58 | // the below wrapper div could probably be refactored to be eliminated – the stickiness now happens on the parent 59 | 60 | .sidebar-inner-wrapper 61 | width: 100% 62 | padding: 3rem 1rem 4rem 1rem //3rem 1rem 63 | overflow-y: auto 64 | transform: translateX(-1rem) 65 | border-right: solid #dddddd 1px 66 | 67 | 68 | @media screen and (max-width: $tablet-landscape) 69 | max-height: none 70 | padding-left: 2em 71 | 72 | a 73 | display: block 74 | margin-left: -0.5em 75 | padding: 0.25em 0.5em 76 | border-radius: 0.5em 77 | background: white 78 | border: white solid .125em 79 | transition: background 1s ease-out 80 | line-height: 1 81 | color: black 82 | display: grid 83 | grid-template-columns: auto 1fr 84 | align-items: center 85 | 86 | span 87 | padding-left: .5em 88 | 89 | 90 | a:focus 91 | outline: 0 92 | border: $focus-border 93 | // box-shadow: 0px 0px .75em $greener 94 | 95 | a:hover 96 | color: $blue 97 | background: $offwhite-gray// rgba($greener, 0.35) 98 | transition: background .125s ease 99 | 100 | a.current 101 | background: $blue //$offwhite-gray 102 | color: white 103 | 104 | &>ul 105 | padding: 0 106 | 107 | ul 108 | padding-left: 1em 109 | 110 | &>li>a 111 | color: black 112 | font-weight: bold 113 | 114 | // &>ul ul 115 | // height: 0px 116 | // overflow: hidden 117 | 118 | #mobile-nav-overlay 119 | position: fixed 120 | width: 100vw 121 | height: 100vh 122 | background: rgba($dark-navy, 0.75) 123 | opacity: 1 124 | pointer-events: all 125 | transition: .25s ease 126 | cursor: e-resize 127 | z-index: 2 128 | top: $header-height 129 | 130 | &.hidden 131 | opacity: 0 132 | pointer-events: none 133 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/syntax-highlighting.sass: -------------------------------------------------------------------------------- 1 | body 2 | .highlight 3 | .k 4 | color: $blue 5 | 6 | .gp 7 | color: $red 8 | 9 | .s2 10 | color: $mid-blue 11 | 12 | .ow, .nb 13 | color: $mid-green 14 | 15 | .bp 16 | color: $purple 17 | 18 | .c1 // comments 19 | font-style: normal -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/static/sass_partials/variables.sass: -------------------------------------------------------------------------------- 1 | ///////////// fonts ///////////// 2 | 3 | $monofont: 'Source Code Pro', 'Menlo', 'Andale Mono', 'Monaco', 'Consolas', 'Courier' 4 | $sansfont: 'Source Sans Pro', Lucida Grande, Geneva, Arial, Verdana, sans-serif 5 | $italicfont: Georgia, serif 6 | 7 | ///////////// type scale ////////////// 8 | 9 | $font-small: 0.75em 10 | $font-smallish: 0.875em 11 | $font-normal: 1em 12 | 13 | $max-text-width: 70ch 14 | 15 | 16 | ///////////// sizing, etc ///////////// 17 | 18 | $padding: 1rem 19 | $border: 3px solid black 20 | 21 | $rounded-corner-1: 0.5rem 22 | $rounded-corner-2: 0.25rem 23 | 24 | $width-flexing-margin: calc(4rem + 2.5vw) 25 | 26 | $header-height: 3rem 27 | $sidebar-sticky-height: $header-height + 1rem 28 | $footer-height: 16rem 29 | 30 | $sidebarWidth: 18.75rem // remove? 31 | $contentWidth: $sidebarWidth + $padding // remove? 32 | 33 | $central-column-wrapper: 50rem // 50rem is 800px 34 | $central-column-wrapper-wide: $central-column-wrapper * 2 35 | 36 | 37 | ///////////// colors ///////////// 38 | 39 | $green: #66DD00 40 | $greener: #16ff00 41 | $blue: #0062FF 42 | $dark-navy: #081b3c 43 | $light-gray: #888888 44 | $lighter-gray: #dddddd 45 | $offwhite-gray: whitesmoke 46 | 47 | // extra syntax highlights 48 | $red: #ff5329 49 | $mid-blue: #267ed7 50 | $mid-green: #008e20 51 | $purple: #8800ff 52 | 53 | $green-highlight: rgba($greener, 0.35) 54 | 55 | ///////////// repeating styles ////////// 56 | 57 | $focus-border: $green solid .125em 58 | 59 | $short-transition: .25s ease 60 | 61 | // ////////// breakpoints ///////// 62 | 63 | $computer-average: 1200px 64 | $tablet-landscape: 1000px 65 | $tablet-portrait: 800px -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = fontparts.css 4 | 5 | [options] 6 | rightsidebar = false 7 | stickysidebar = false 8 | collapsiblesidebar = false 9 | externalrefs = true 10 | 11 | bodyfont = "Lucida Grande", Monospace 12 | headerfont = Menlo, Monospace 13 | 14 | bgcolor = #FFFFFF 15 | textcolor = #3B3A3A 16 | linkcolor = #FF0000 17 | headercolor1 = #00FF00 18 | headercolor2 = #0000FF 19 | headerlinkcolor = #F2462C 20 | 21 | codebgcolor = #FFFFFF 22 | codetextcolor = #3B3A3A 23 | -------------------------------------------------------------------------------- /documentation/source/_themes/fontPartsTheme/versions.html: -------------------------------------------------------------------------------- 1 | 2 | {% if READTHEDOCS %} 3 | {# Add rst-badge after rst-versions for small badge style. #} 4 |
5 | 6 | Read the Docs 7 | v: {{ current_version }} 8 | 9 | 10 |
11 |
12 |
{{ _('Versions') }}
13 | {% for slug, url in versions %} 14 |
{{ slug }}
15 | {% endfor %} 16 |
17 |
18 |
{{ _('Downloads') }}
19 | {% for type, url in downloads %} 20 |
{{ type }}
21 | {% endfor %} 22 |
23 |
24 |
{{ _('On Read the Docs') }}
25 |
26 | {{ _('Project Home') }} 27 |
28 |
29 | {{ _('Builds') }} 30 |
31 |
32 |
33 | {% trans %}Free document hosting provided by Read the Docs.{% endtrans %} 34 | 35 |
36 |
37 | {% endif %} -------------------------------------------------------------------------------- /documentation/source/contents.rst: -------------------------------------------------------------------------------- 1 | Designers 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Type Designers 7 | 8 | gettingstarted/index 9 | objectref/index 10 | 11 | 12 | 13 | Developers 14 | ========== 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :caption: Software Developers 19 | 20 | environments/index 21 | development/index -------------------------------------------------------------------------------- /documentation/source/development/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | #################### 4 | Developing FontParts 5 | #################### 6 | 7 | You want to help with developing FontParts? Yay! 8 | 9 | We are mostly focused on documenting the objects and building a test suite. We'll eventually need bits of code here and there. If you have an idea for a new API or want to discuss one of the testing APIs, cool. 10 | 11 | .. _developing-proposals: 12 | 13 | ********* 14 | Proposals 15 | ********* 16 | 17 | Want to suggest a new font part for FontParts? It's best to do this as an issue on the `FontParts GitHub `_ repository. Please present why you think this needs to be added. Before you do so, please make sure you understand the goals of the project, the existing API and so on. 18 | 19 | .. _developing-bug-reports: 20 | 21 | 22 | *********** 23 | Bug Reports 24 | *********** 25 | 26 | Notice a bug when using FontParts? Is it a bug in a specific application? If so, please report the bug to the application developer. If it's not specific to a particular application, please open an issue on GitHub or, if you really can't `open an issue on GitHub `_, send a message to the `RoboFab mailing list `_ 27 | 28 | 29 | .. _developing-coding: 30 | 31 | ****** 32 | Coding 33 | ****** 34 | 35 | Take a look at the `open issues `_ and see if there is anything there that you want to work on. Please try to follow the general coding style of the library so that everything has the same level of readability. 36 | 37 | This library follows much of PEP8, with a couple of exceptions. You’ll see camelCase. We like camelCase. The standard line length is also 90 characters. If possible, try to keep lines to 80, but 90 comes in handy occasionally. You’ll also notice that some builtin names are redefined in as variables in methods. It’s impossible not to use ``type`` in a package dealing with fonts. 38 | 39 | ********************* 40 | Writing Documentation 41 | ********************* 42 | 43 | We really need help with adding the formatted documentation strings to the base objects. The API documentation is generated from those. Here's a :doc:`style guide `. Please look at the `Documentation project `_ on GitHub and see if there is anything you want to work on. If there is, ask to be assigned to that issue, and then follow the style guide. A good place to look for examples of the object documentation is the `glyph object `_. 44 | 45 | ********** 46 | Test Suite 47 | ********** 48 | 49 | We also really need help in finishing up the test suite. You can see what needs to be done in the `Tests project `_ on GitHub. Pick something you want to write tests for and ask to be assigned to that issue. More information about writing tests is :doc:`here `. 50 | -------------------------------------------------------------------------------- /documentation/source/development/testing.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | ####### 4 | Testing 5 | ####### 6 | 7 | The test cases are located in ``fontParts.test.test_*``. 8 | 9 | ============== 10 | Test Structure 11 | ============== 12 | 13 | :: 14 | 15 | import unittest 16 | from fontParts.base import FontPartsError 17 | 18 | class TestFoo(unittest.TestObject): 19 | 20 | # -------------- 21 | # Section Header 22 | # -------------- 23 | 24 | def getFoo_generic(self): 25 | # code for building the object 26 | 27 | def test_bar(self): 28 | # Code for testing the bar attribute. 29 | 30 | def test_changeSomething(self): 31 | # Code for testing the changeSomething method. 32 | 33 | 34 | ================ 35 | Test Definitions 36 | ================ 37 | 38 | The test definitions should be developed by following the :ref:`FontParts API documentation `. These break down into two categories. 39 | 40 | #. attributes 41 | #. methods 42 | 43 | These will be covered in detail below. In general follow these guidelines when developing 44 | 45 | #. Keep the test focused on what is relevant to what is being tested. Don't test file saving within an attribute test in a sub-sub-sub-sub object. 46 | #. Make the tests as atomic as possible. Don't modify lots of parts of an object during a single test. That makes the tests very hard to debug. 47 | #. Keep the code clear and concise so that it is easy to see what is being tested. Add documentation to clarify anything that is ambiguous. Try to imagine someone trying to debug a failure of this test five years from now. Will they be able to tell what is going on in the code? 48 | #. If testing an edge case, make notes defining where this situation is happening, why it is important and so on. Edge case tests often are hyper-specific to one version of one environment and thus have a limited lifespan. This needs to be made clear for future reference. 49 | #. Test valid and invalid input. The base implementation's normalizers define what is valid and invalid. Use this as a reference. 50 | #. Only test one thing per test case. Tests are **not** a place to avoid repeated code, it's much easier to debug an error in a test when that test is only doing one thing. 51 | 52 | Testing Attributes 53 | ------------------ 54 | 55 | Attribute testing uses the method name structure ``test_attributeName``. If more than one method is needed due to length or complexity, the additional methods use the name structure ``test_attributeNameDescriptionOfWhatThisTests``. 56 | 57 | :: 58 | 59 | def test_bar_get(self): 60 | foo, unrequested = self.getFoo_generic() 61 | # get 62 | self.assertEqual( 63 | foo.bar, 64 | "barbarbar" 65 | ) 66 | 67 | def test_bar_set_valid(self): 68 | foo, unrequested = self.getFoo_generic() 69 | # set: valid data 70 | foo.bar = "heyheyhey" 71 | self.assertEqual( 72 | foo.bar, 73 | "heyheyhey" 74 | ) 75 | 76 | def test_bar_set_invalid(self): 77 | foo, unrequested = self.getFoo_generic() 78 | # set: invalid data 79 | with self.assertRaises(FontPartsError): 80 | foo.bar = 123 81 | 82 | def test_barSettingNoneShouldFail(self): 83 | foo, unrequested = self.getFoo_barNontShouldFail() 84 | with self.assertRaises(FontPartsError): 85 | foo.bar = None 86 | 87 | Getting 88 | ^^^^^^^ 89 | 90 | When testing getting an attribute, test the following: 91 | 92 | * All valid return data types. Use the case definitions to specify these. 93 | * (How should invalid types be handled? Is that completely the responsibility of the environment?) 94 | 95 | Setting 96 | ^^^^^^^ 97 | 98 | When testing setting an attribute, test the following: 99 | 100 | * All valid input data types. For example if setting accepts a number, test int and float. If pos/neg values are allowed, test both. 101 | * A representative sample of invalid data types/values. 102 | 103 | If an attribute does not support setting, it should be tested to make sure that an attempt to set raises the appropriate error. 104 | 105 | Testing Methods 106 | --------------- 107 | 108 | Testing methods should be done atomically, modifying a single argument at a time. For example, if a method takes x and y arguments, test each of these as independently as possible. The following should be tested for each argument: 109 | 110 | * All valid input data types. For example if setting accepts a number, test int and float. If pos/neg values are allowed, test both. 111 | * A representative sample of invalid data types/values. 112 | 113 | :: 114 | 115 | def test_changeSomething(self): 116 | bar, unrequested = self.getBar_something() 117 | bar.changeSomething(x=100, y=100) 118 | self.assertEqual( 119 | bar.thing, 120 | (100, 100) 121 | ) 122 | 123 | def test_changeSomething_invalid_x(self): 124 | bar, unrequested = self.getBar_something() 125 | with self.assertRaises(FontPartsError): 126 | bar.changeSomething(x=None, y=100) 127 | 128 | def test_changeSomething_invalid_y(self): 129 | bar, unrequested = self.getBar_something() 130 | with self.assertRaises(FontPartsError): 131 | bar.changeSomething(x=100, y=None) 132 | 133 | =================== 134 | Objects for Testing 135 | =================== 136 | 137 | Objects for testing are defined in methods with the name structure ``getFoo_description``. The base object will be generated by the environment by calling ``self.objectGenerator("classIdentifier")``. This will return a fontParts wrapped object ready for population and testing. It will also return a list of objects that were/are required for generating/retaining the requested object. For example, if an environment doesn't support orphan glyphs, the unrequested list may contain a parent font. The objects in the unrequested list must not be used within tests. 138 | 139 | :: 140 | 141 | def getFoo_generic(self): 142 | foo = self.objectGenerator("foo") 143 | foo.bar = "barbarbar" 144 | return foo, [] 145 | 146 | ===== 147 | To Do 148 | ===== 149 | 150 | - Establish tests for pen protocol in test_glyph. 151 | -------------------------------------------------------------------------------- /documentation/source/environments/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | ###################### 4 | Implementing FontParts 5 | ###################### 6 | 7 | The whole point of FontParts is to present a common API to scripters. So, obviously, the way to implement it is to develop an API that is compliant with the :ref:`object documentation `. That's going to be a non-trivial amount of work, so we offer a less laborious alternative: we provide a set of :ref:`base objects ` that can be subclassed and privately mapped to an environment's native API. If you don't want to use these base objects, you can implement the API all on your own. You just have to make sure that your implementation is compatible. 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :includehidden: 13 | 14 | testing/index 15 | objects/index 16 | layers/index 17 | -------------------------------------------------------------------------------- /documentation/source/environments/layers/index.rst: -------------------------------------------------------------------------------- 1 | ###### 2 | Layers 3 | ###### 4 | 5 | There are two primary layer models in the font world: 6 | 7 | - font level layers: In this model, all glyphs have the same layers. A good example of this is a chromatic font. 8 | - glyph level layers: In this model, individual glyphs may have their own unique layers. 9 | 10 | fontParts supports both of these models. Both fonts and glyphs have fully developed layer APIs:: 11 | 12 | font = CurrentFont() 13 | foregroundLayer = font.getLayer("foreground") 14 | backgroundLayer = font.getLayer("background") 15 | 16 | glyph = font["A"] 17 | foregroundGlyph = glyph.getLayer("foreground") 18 | backgroundGlyph = glyph.getLayer("background") 19 | 20 | A font-level layer is a font-like object. Essentially, a layer has the same glyph management behavior as a font:: 21 | 22 | font = CurrentFont() 23 | foreground = font.getLayer("foreground") 24 | glyph = foreground.newGlyph("A") 25 | 26 | A glyph-level layer is identical to a glyph object:: 27 | 28 | font = CurrentFont() 29 | glyph = font["A"] 30 | foreground = glyph.getLayer("foreground") 31 | background = glyph.getLayer("background") 32 | 33 | When a scripter is addressing a font or glyph without specifying a specific layer, the action is performed on the "default" (or primary) layer. For example, in the original Fontographer there were two layers: foreground and background. The foreground was the primary layer and it contained the primary data that would be compiled into a font binary. In multi-layered glyph editing environments, designers can specify which layer should be considered primary. This layer is the "default" layer in fontParts. Thus:: 34 | 35 | font = CurrentFont() 36 | glyph1 = font["A"] 37 | glyph2 = font.newGlyph("B") 38 | 39 | The `glyph1` object will reference the A's "foreground" layer and the "foreground" layer will contain a new glyph named "B". 40 | 41 | fontParts delegates the implementation to the environment subclasses. Given that an environment can only support font-level layers *or* glyph-level layers, the following algorithms can be used to simulate the model that the environment doesn't support. 42 | 43 | Simulating glyph-level layers. 44 | ============================== 45 | 46 | 1. Get the parent font. 47 | 2. Iterate through all of the font's layers. 48 | 3. If the glyph's name is in the layer, grab the glyph from the layer. 49 | 4. Return all found glyphs. 50 | 51 | Simulating font-level layers. 52 | ============================= 53 | 54 | 1. Iterate over all glyphs. 55 | 2. For every layer in the glyph, create a global mapping of layer name to glyphs containing a layer with the same name. 56 | -------------------------------------------------------------------------------- /documentation/source/environments/objects/anchor.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Anchor 5 | ****** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseAnchor._get_color 10 | .. automethod:: BaseAnchor._get_identifier 11 | .. automethod:: BaseAnchor._get_name 12 | .. automethod:: BaseAnchor._get_x 13 | .. automethod:: BaseAnchor._get_y 14 | .. automethod:: BaseAnchor._set_color 15 | .. automethod:: BaseAnchor._set_name 16 | .. automethod:: BaseAnchor._set_x 17 | .. automethod:: BaseAnchor._set_y 18 | 19 | May Override 20 | ------------ 21 | .. automethod:: BaseAnchor._init 22 | .. automethod:: BaseAnchor._moveBy 23 | .. automethod:: BaseAnchor._rotateBy 24 | .. automethod:: BaseAnchor._scaleBy 25 | .. automethod:: BaseAnchor._skewBy 26 | .. automethod:: BaseAnchor._transformBy 27 | .. automethod:: BaseAnchor.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/bpoint.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | BPoint 5 | ****** 6 | 7 | Must Override 8 | ------------- 9 | 10 | May Override 11 | ------------ 12 | .. automethod:: BaseBPoint._get_anchor 13 | .. automethod:: BaseBPoint._get_bcpIn 14 | .. automethod:: BaseBPoint._get_bcpOut 15 | .. automethod:: BaseBPoint._get_index 16 | .. automethod:: BaseBPoint._get_type 17 | .. automethod:: BaseBPoint._init 18 | .. automethod:: BaseBPoint._moveBy 19 | .. automethod:: BaseBPoint._rotateBy 20 | .. automethod:: BaseBPoint._scaleBy 21 | .. automethod:: BaseBPoint._set_anchor 22 | .. automethod:: BaseBPoint._set_bcpIn 23 | .. automethod:: BaseBPoint._set_bcpOut 24 | .. automethod:: BaseBPoint._set_type 25 | .. automethod:: BaseBPoint._skewBy 26 | .. automethod:: BaseBPoint._transformBy 27 | .. automethod:: BaseBPoint.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/component.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Component 5 | ********* 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseComponent._decompose 10 | .. automethod:: BaseComponent._get_baseGlyph 11 | .. automethod:: BaseComponent._get_identifier 12 | .. automethod:: BaseComponent._get_transformation 13 | .. automethod:: BaseComponent._set_baseGlyph 14 | .. automethod:: BaseComponent._set_index 15 | .. automethod:: BaseComponent._set_transformation 16 | 17 | May Override 18 | ------------ 19 | .. automethod:: BaseComponent._draw 20 | .. automethod:: BaseComponent._drawPoints 21 | .. automethod:: BaseComponent._get_bounds 22 | .. automethod:: BaseComponent._get_index 23 | .. automethod:: BaseComponent._get_offset 24 | .. automethod:: BaseComponent._get_scale 25 | .. automethod:: BaseComponent._init 26 | .. automethod:: BaseComponent._moveBy 27 | .. automethod:: BaseComponent._pointInside 28 | .. automethod:: BaseComponent._rotateBy 29 | .. automethod:: BaseComponent._round 30 | .. automethod:: BaseComponent._scaleBy 31 | .. automethod:: BaseComponent._set_offset 32 | .. automethod:: BaseComponent._set_scale 33 | .. automethod:: BaseComponent._skewBy 34 | .. automethod:: BaseComponent._transformBy 35 | .. automethod:: BaseComponent.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/contour.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Contour 5 | ******* 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseContour._getPoint 10 | .. automethod:: BaseContour._get_clockwise 11 | .. automethod:: BaseContour._get_identifier 12 | .. automethod:: BaseContour._insertPoint 13 | .. automethod:: BaseContour._lenPoints 14 | .. automethod:: BaseContour._removePoint 15 | .. automethod:: BaseContour._set_index 16 | 17 | May Override 18 | ------------ 19 | .. automethod:: BaseContour._appendBPoint 20 | .. automethod:: BaseContour._appendSegment 21 | .. automethod:: BaseContour._autoStartSegment 22 | .. automethod:: BaseContour._draw 23 | .. automethod:: BaseContour._drawPoints 24 | .. automethod:: BaseContour._get_bounds 25 | .. automethod:: BaseContour._get_index 26 | .. automethod:: BaseContour._get_points 27 | .. automethod:: BaseContour._get_segments 28 | .. automethod:: BaseContour._init 29 | .. automethod:: BaseContour._insertBPoint 30 | .. automethod:: BaseContour._insertSegment 31 | .. automethod:: BaseContour._len__segments 32 | .. automethod:: BaseContour._moveBy 33 | .. automethod:: BaseContour._pointInside 34 | .. automethod:: BaseContour._removeSegment 35 | .. automethod:: BaseContour._reverse 36 | .. automethod:: BaseContour._rotateBy 37 | .. automethod:: BaseContour._round 38 | .. automethod:: BaseContour._scaleBy 39 | .. automethod:: BaseContour._setStartSegment 40 | .. automethod:: BaseContour._set_clockwise 41 | .. automethod:: BaseContour._skewBy 42 | .. automethod:: BaseContour._transformBy -------------------------------------------------------------------------------- /documentation/source/environments/objects/features.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Features 5 | ******** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseFeatures._get_text 10 | .. automethod:: BaseFeatures._set_text 11 | 12 | May Override 13 | ------------ 14 | .. automethod:: BaseFeatures._init 15 | .. automethod:: BaseFeatures.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/font.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Font 5 | **** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseFont._close 10 | .. automethod:: BaseFont._generate 11 | .. automethod:: BaseFont._getGuideline 12 | .. automethod:: BaseFont._get_defaultLayer 13 | .. automethod:: BaseFont._get_features 14 | .. automethod:: BaseFont._get_glyphOrder 15 | .. automethod:: BaseFont._get_groups 16 | .. automethod:: BaseFont._get_info 17 | .. automethod:: BaseFont._get_kerning 18 | .. automethod:: BaseFont._get_layerOrder 19 | .. automethod:: BaseFont._get_layers 20 | .. automethod:: BaseFont._get_lib 21 | .. automethod:: BaseFont._get_path 22 | .. automethod:: BaseFont._init 23 | .. automethod:: BaseFont._lenGuidelines 24 | .. automethod:: BaseFont._newLayer 25 | .. automethod:: BaseFont._removeGuideline 26 | .. automethod:: BaseFont._removeLayer 27 | .. automethod:: BaseFont._save 28 | .. automethod:: BaseFont._set_defaultLayer 29 | .. automethod:: BaseFont._set_glyphOrder 30 | .. automethod:: BaseFont._set_layerOrder 31 | 32 | May Override 33 | ------------ 34 | .. automethod:: BaseFont._appendGuideline 35 | .. automethod:: BaseFont._autoUnicodes 36 | .. automethod:: BaseFont._clearGuidelines 37 | .. automethod:: BaseFont._contains 38 | .. automethod:: BaseFont._getItem 39 | .. automethod:: BaseFont._getLayer 40 | .. automethod:: BaseFont._get_guidelines 41 | .. automethod:: BaseFont._insertGlyph 42 | .. automethod:: BaseFont._interpolate 43 | .. automethod:: BaseFont._isCompatible 44 | .. automethod:: BaseFont._iter 45 | .. automethod:: BaseFont._keys 46 | .. automethod:: BaseFont._len 47 | .. automethod:: BaseFont._newGlyph 48 | .. automethod:: BaseFont._removeGlyph 49 | .. automethod:: BaseFont._round -------------------------------------------------------------------------------- /documentation/source/environments/objects/glyph.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Glyph 5 | ***** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseGlyph._addImage 10 | .. automethod:: BaseGlyph._autoUnicodes 11 | .. automethod:: BaseGlyph._clearImage 12 | .. automethod:: BaseGlyph._getAnchor 13 | .. automethod:: BaseGlyph._getComponent 14 | .. automethod:: BaseGlyph._getContour 15 | .. automethod:: BaseGlyph._getGuideline 16 | .. automethod:: BaseGlyph._get_height 17 | .. automethod:: BaseGlyph._get_image 18 | .. automethod:: BaseGlyph._get_lib 19 | .. automethod:: BaseGlyph._get_markColor 20 | .. automethod:: BaseGlyph._get_name 21 | .. automethod:: BaseGlyph._get_note 22 | .. automethod:: BaseGlyph._get_unicodes 23 | .. automethod:: BaseGlyph._get_width 24 | .. automethod:: BaseGlyph._lenAnchors 25 | .. automethod:: BaseGlyph._lenComponents 26 | .. automethod:: BaseGlyph._lenContours 27 | .. automethod:: BaseGlyph._lenGuidelines 28 | .. automethod:: BaseGlyph._newLayer 29 | .. automethod:: BaseGlyph._removeAnchor 30 | .. automethod:: BaseGlyph._removeComponent 31 | .. automethod:: BaseGlyph._removeContour 32 | .. automethod:: BaseGlyph._removeGuideline 33 | .. automethod:: BaseGlyph._removeOverlap 34 | .. automethod:: BaseGlyph._set_height 35 | .. automethod:: BaseGlyph._set_markColor 36 | .. automethod:: BaseGlyph._set_name 37 | .. automethod:: BaseGlyph._set_note 38 | .. automethod:: BaseGlyph._set_unicodes 39 | .. automethod:: BaseGlyph._set_width 40 | 41 | May Override 42 | ------------ 43 | .. automethod:: BaseGlyph.__add__ 44 | .. automethod:: BaseGlyph.__div__ 45 | .. automethod:: BaseGlyph.__mul__ 46 | .. automethod:: BaseGlyph.__rmul__ 47 | .. automethod:: BaseGlyph.__sub__ 48 | .. automethod:: BaseGlyph._appendAnchor 49 | .. automethod:: BaseGlyph._appendComponent 50 | .. automethod:: BaseGlyph._appendContour 51 | .. automethod:: BaseGlyph._appendGlyph 52 | .. automethod:: BaseGlyph._appendGuideline 53 | .. automethod:: BaseGlyph._clear 54 | .. automethod:: BaseGlyph._clearAnchors 55 | .. automethod:: BaseGlyph._clearComponents 56 | .. automethod:: BaseGlyph._clearContours 57 | .. automethod:: BaseGlyph._clearGuidelines 58 | .. automethod:: BaseGlyph._decompose 59 | .. automethod:: BaseGlyph._getLayer 60 | .. automethod:: BaseGlyph._get_anchors 61 | .. automethod:: BaseGlyph._get_bottomMargin 62 | .. automethod:: BaseGlyph._get_bounds 63 | .. automethod:: BaseGlyph._get_components 64 | .. automethod:: BaseGlyph._get_contours 65 | .. automethod:: BaseGlyph._get_guidelines 66 | .. automethod:: BaseGlyph._get_leftMargin 67 | .. automethod:: BaseGlyph._get_rightMargin 68 | .. automethod:: BaseGlyph._get_topMargin 69 | .. automethod:: BaseGlyph._get_unicode 70 | .. automethod:: BaseGlyph._init 71 | .. automethod:: BaseGlyph._interpolate 72 | .. automethod:: BaseGlyph._isCompatible 73 | .. automethod:: BaseGlyph._iterContours 74 | .. automethod:: BaseGlyph._moveBy 75 | .. automethod:: BaseGlyph._pointInside 76 | .. automethod:: BaseGlyph._removeLayer 77 | .. automethod:: BaseGlyph._rotateBy 78 | .. automethod:: BaseGlyph._round 79 | .. automethod:: BaseGlyph._scaleBy 80 | .. automethod:: BaseGlyph._set_bottomMargin 81 | .. automethod:: BaseGlyph._set_leftMargin 82 | .. automethod:: BaseGlyph._set_rightMargin 83 | .. automethod:: BaseGlyph._set_topMargin 84 | .. automethod:: BaseGlyph._set_unicode 85 | .. automethod:: BaseGlyph._skewBy 86 | .. automethod:: BaseGlyph._transformBy -------------------------------------------------------------------------------- /documentation/source/environments/objects/groups.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Groups 5 | ****** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseGroups._contains 10 | .. automethod:: BaseGroups._delItem 11 | .. automethod:: BaseGroups._getItem 12 | .. automethod:: BaseGroups._items 13 | .. automethod:: BaseGroups._setItem 14 | 15 | May Override 16 | ------------ 17 | .. automethod:: BaseGroups._clear 18 | .. automethod:: BaseGroups._findGlyph 19 | .. automethod:: BaseGroups._get 20 | .. automethod:: BaseGroups._init 21 | .. automethod:: BaseGroups._iter 22 | .. automethod:: BaseGroups._keys 23 | .. automethod:: BaseGroups._len 24 | .. automethod:: BaseGroups._pop 25 | .. automethod:: BaseGroups._update 26 | .. automethod:: BaseGroups._values -------------------------------------------------------------------------------- /documentation/source/environments/objects/guideline.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Guideline 5 | ********* 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseGuideline._get_angle 10 | .. automethod:: BaseGuideline._get_color 11 | .. automethod:: BaseGuideline._get_identifier 12 | .. automethod:: BaseGuideline._get_name 13 | .. automethod:: BaseGuideline._get_x 14 | .. automethod:: BaseGuideline._get_y 15 | .. automethod:: BaseGuideline._set_angle 16 | .. automethod:: BaseGuideline._set_color 17 | .. automethod:: BaseGuideline._set_name 18 | .. automethod:: BaseGuideline._set_x 19 | .. automethod:: BaseGuideline._set_y 20 | 21 | May Override 22 | ------------ 23 | .. automethod:: BaseGuideline._get_index 24 | .. automethod:: BaseGuideline._init 25 | .. automethod:: BaseGuideline._moveBy 26 | .. automethod:: BaseGuideline._rotateBy 27 | .. automethod:: BaseGuideline._round 28 | .. automethod:: BaseGuideline._scaleBy 29 | .. automethod:: BaseGuideline._skewBy 30 | .. automethod:: BaseGuideline._transformBy 31 | .. automethod:: BaseGuideline.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/image.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Image 5 | ***** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseImage._get_color 10 | .. automethod:: BaseImage._get_data 11 | .. automethod:: BaseImage._get_transformation 12 | .. automethod:: BaseImage._set_color 13 | .. automethod:: BaseImage._set_data 14 | .. automethod:: BaseImage._set_transformation 15 | 16 | May Override 17 | ------------ 18 | .. automethod:: BaseImage._get_offset 19 | .. automethod:: BaseImage._get_scale 20 | .. automethod:: BaseImage._init 21 | .. automethod:: BaseImage._moveBy 22 | .. automethod:: BaseImage._rotateBy 23 | .. automethod:: BaseImage._round 24 | .. automethod:: BaseImage._scaleBy 25 | .. automethod:: BaseImage._set_offset 26 | .. automethod:: BaseImage._set_scale 27 | .. automethod:: BaseImage._skewBy 28 | .. automethod:: BaseImage._transformBy 29 | .. automethod:: BaseImage.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/info.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Info 5 | **** 6 | 7 | Must Override 8 | ------------- 9 | 10 | May Override 11 | ------------ 12 | .. automethod:: BaseInfo._getAttr 13 | .. automethod:: BaseInfo._init 14 | .. automethod:: BaseInfo._interpolate 15 | .. automethod:: BaseInfo._round 16 | .. automethod:: BaseInfo._setAttr 17 | .. automethod:: BaseInfo.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/kerning.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Kerning 5 | ******* 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseKerning._contains 10 | .. automethod:: BaseKerning._delItem 11 | .. automethod:: BaseKerning._getItem 12 | .. automethod:: BaseKerning._items 13 | .. automethod:: BaseKerning._setItem 14 | 15 | May Override 16 | ------------ 17 | .. automethod:: BaseKerning._clear 18 | .. automethod:: BaseKerning._get 19 | .. automethod:: BaseKerning._init 20 | .. automethod:: BaseKerning._interpolate 21 | .. automethod:: BaseKerning._iter 22 | .. automethod:: BaseKerning._keys 23 | .. automethod:: BaseKerning._len 24 | .. automethod:: BaseKerning._pop 25 | .. automethod:: BaseKerning._round 26 | .. automethod:: BaseKerning._scale 27 | .. automethod:: BaseKerning._update 28 | .. automethod:: BaseKerning._values -------------------------------------------------------------------------------- /documentation/source/environments/objects/layer.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Layer 5 | ***** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseLayer._getItem 10 | .. automethod:: BaseLayer._get_color 11 | .. automethod:: BaseLayer._get_lib 12 | .. automethod:: BaseLayer._get_name 13 | .. automethod:: BaseLayer._keys 14 | .. automethod:: BaseLayer._newGlyph 15 | .. automethod:: BaseLayer._removeGlyph 16 | .. automethod:: BaseLayer._set_color 17 | .. automethod:: BaseLayer._set_name 18 | 19 | May Override 20 | ------------ 21 | .. automethod:: BaseLayer._autoUnicodes 22 | .. automethod:: BaseLayer._contains 23 | .. automethod:: BaseLayer._init 24 | .. automethod:: BaseLayer._insertGlyph 25 | .. automethod:: BaseLayer._interpolate 26 | .. automethod:: BaseLayer._isCompatible 27 | .. automethod:: BaseLayer._iter 28 | .. automethod:: BaseLayer._len 29 | .. automethod:: BaseLayer._round -------------------------------------------------------------------------------- /documentation/source/environments/objects/lib.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Lib 5 | *** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BaseLib._contains 10 | .. automethod:: BaseLib._delItem 11 | .. automethod:: BaseLib._getItem 12 | .. automethod:: BaseLib._items 13 | .. automethod:: BaseLib._setItem 14 | 15 | May Override 16 | ------------ 17 | .. automethod:: BaseLib._clear 18 | .. automethod:: BaseLib._get 19 | .. automethod:: BaseLib._init 20 | .. automethod:: BaseLib._iter 21 | .. automethod:: BaseLib._keys 22 | .. automethod:: BaseLib._len 23 | .. automethod:: BaseLib._pop 24 | .. automethod:: BaseLib._update 25 | .. automethod:: BaseLib._values 26 | -------------------------------------------------------------------------------- /documentation/source/environments/objects/normalizers.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base.normalizers 3 | 4 | ########### 5 | Normalizers 6 | ########### 7 | 8 | ******* 9 | Kerning 10 | ******* 11 | 12 | .. autofunction:: normalizeKerningKey 13 | .. autofunction:: normalizeKerningValue 14 | 15 | ****** 16 | Groups 17 | ****** 18 | 19 | .. autofunction:: normalizeGroupKey 20 | .. autofunction:: normalizeGroupValue 21 | 22 | ******** 23 | Features 24 | ******** 25 | 26 | .. autofunction:: normalizeFeatureText 27 | 28 | *** 29 | Lib 30 | *** 31 | 32 | .. autofunction:: normalizeLibKey 33 | .. autofunction:: normalizeLibValue 34 | 35 | ****** 36 | Layers 37 | ****** 38 | 39 | .. autofunction:: normalizeLayerOrder 40 | .. autofunction:: normalizeDefaultLayer 41 | .. autofunction:: normalizeDefaultLayerName 42 | .. autofunction:: normalizeLayerName 43 | 44 | ****** 45 | Glyphs 46 | ****** 47 | 48 | .. autofunction:: normalizeGlyph 49 | .. autofunction:: normalizeGlyphOrder 50 | 51 | Identification 52 | ============== 53 | 54 | .. autofunction:: normalizeGlyphName 55 | .. autofunction:: normalizeGlyphUnicodes 56 | .. autofunction:: normalizeGlyphUnicode 57 | 58 | Metrics 59 | ======= 60 | 61 | .. autofunction:: normalizeGlyphWidth 62 | .. autofunction:: normalizeGlyphLeftMargin 63 | .. autofunction:: normalizeGlyphRightMargin 64 | .. autofunction:: normalizeGlyphHeight 65 | .. autofunction:: normalizeGlyphBottomMargin 66 | .. autofunction:: normalizeGlyphTopMargin 67 | 68 | ******** 69 | Contours 70 | ******** 71 | 72 | .. autofunction:: normalizeContourIndex 73 | .. autofunction:: normalizeContour 74 | 75 | ****** 76 | Points 77 | ****** 78 | 79 | .. autofunction:: normalizePointType 80 | .. autofunction:: normalizePointName 81 | 82 | ******** 83 | Segments 84 | ******** 85 | 86 | .. autofunction:: normalizeSegmentType 87 | 88 | ******* 89 | BPoints 90 | ******* 91 | 92 | .. autofunction:: normalizeBPointType 93 | 94 | ********** 95 | Components 96 | ********** 97 | 98 | .. autofunction:: normalizeComponentIndex 99 | .. autofunction:: normalizeComponentScale 100 | 101 | ******* 102 | Anchors 103 | ******* 104 | 105 | .. autofunction:: normalizeAnchorIndex 106 | .. autofunction:: normalizeAnchorName 107 | 108 | **** 109 | Note 110 | **** 111 | 112 | .. autofunction:: normalizeGlyphNote 113 | 114 | ********** 115 | Guidelines 116 | ********** 117 | 118 | .. autofunction:: normalizeGuideline 119 | .. autofunction:: normalizeGuidelineIndex 120 | .. autofunction:: normalizeGuidelineAngle 121 | .. autofunction:: normalizeGuidelineName 122 | 123 | ******* 124 | Generic 125 | ******* 126 | 127 | .. autofunction:: normalizeInternalObjectType 128 | 129 | Positions 130 | ========= 131 | 132 | .. autofunction:: normalizeX 133 | .. autofunction:: normalizeY 134 | .. autofunction:: normalizeCoordinateTuple 135 | .. autofunction:: normalizeBoundingBox 136 | 137 | Identification 138 | ============== 139 | 140 | .. autofunction:: normalizeIndex 141 | .. autofunction:: normalizeIdentifier 142 | .. autofunction:: normalizeColor 143 | 144 | Interpolation 145 | ============= 146 | 147 | .. autofunction:: normalizeInterpolationFactor 148 | 149 | Transformations 150 | =============== 151 | 152 | .. autofunction:: normalizeTransformationMatrix 153 | .. autofunction:: normalizeTransformationOffset 154 | .. autofunction:: normalizeTransformationRotationAngle 155 | .. autofunction:: normalizeTransformationSkewAngle 156 | .. autofunction:: normalizeTransformationScale 157 | 158 | Files 159 | ===== 160 | 161 | .. autofunction:: normalizeFilePath 162 | .. autofunction:: normalizeFileFormatVersion 163 | 164 | Standard 165 | ======== 166 | 167 | .. autofunction:: normalizeBoolean 168 | .. autofunction:: normalizeVisualRounding 169 | 170 | -------------------------------------------------------------------------------- /documentation/source/environments/objects/point.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Point 5 | ***** 6 | 7 | Must Override 8 | ------------- 9 | .. automethod:: BasePoint._get_identifier 10 | .. automethod:: BasePoint._get_name 11 | .. automethod:: BasePoint._get_smooth 12 | .. automethod:: BasePoint._get_type 13 | .. automethod:: BasePoint._get_x 14 | .. automethod:: BasePoint._get_y 15 | .. automethod:: BasePoint._set_name 16 | .. automethod:: BasePoint._set_smooth 17 | .. automethod:: BasePoint._set_type 18 | .. automethod:: BasePoint._set_x 19 | .. automethod:: BasePoint._set_y 20 | 21 | May Override 22 | ------------ 23 | .. automethod:: BasePoint._get_index 24 | .. automethod:: BasePoint._init 25 | .. automethod:: BasePoint._moveBy 26 | .. automethod:: BasePoint._rotateBy 27 | .. automethod:: BasePoint._round 28 | .. automethod:: BasePoint._scaleBy 29 | .. automethod:: BasePoint._skewBy 30 | .. automethod:: BasePoint._transformBy 31 | .. automethod:: BasePoint.copyData -------------------------------------------------------------------------------- /documentation/source/environments/objects/segment.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | Segment 5 | ******* 6 | 7 | Must Override 8 | ------------- 9 | 10 | May Override 11 | ------------ 12 | .. automethod:: BaseSegment._getItem 13 | .. automethod:: BaseSegment._get_base_offCurve 14 | .. automethod:: BaseSegment._get_index 15 | .. automethod:: BaseSegment._get_offCurve 16 | .. automethod:: BaseSegment._get_onCurve 17 | .. automethod:: BaseSegment._get_points 18 | .. automethod:: BaseSegment._get_smooth 19 | .. automethod:: BaseSegment._get_type 20 | .. automethod:: BaseSegment._init 21 | .. automethod:: BaseSegment._iterPoints 22 | .. automethod:: BaseSegment._len 23 | .. automethod:: BaseSegment._moveBy 24 | .. automethod:: BaseSegment._rotateBy 25 | .. automethod:: BaseSegment._scaleBy 26 | .. automethod:: BaseSegment._set_smooth 27 | .. automethod:: BaseSegment._set_type 28 | .. automethod:: BaseSegment._skewBy 29 | .. automethod:: BaseSegment._transformBy 30 | .. automethod:: BaseSegment.copyData -------------------------------------------------------------------------------- /documentation/source/environments/testing/index.rst: -------------------------------------------------------------------------------- 1 | 2 | ####### 3 | Testing 4 | ####### 5 | 6 | .. _implementing-testing: 7 | 8 | A test suite is provided to test any implementation, either subclassed from the base objects or implemented independently. The suite has been designed to be environment and format agnostic. Environment developers only need to implement a function that provides objects for testing and a simple Python script that sends the function to the test suite. 9 | 10 | Testing an environment 11 | ====================== 12 | 13 | The main thing that an environment needs to implement is the test object generator. This should create an object for the requested class identifier. :: 14 | 15 | def MyAppObjectGenerator(classIdentifier): 16 | unrequested = [] 17 | obj = myApp.foo.bar.something.hi(classIdentifier) 18 | return obj, unrequested 19 | 20 | If an environment does not allow orphan objects, parent objects may create the parent objects and store them in a list. The function must return the generated objects and the list of unrequested objects (or an empty list if no parent objects were generated). 21 | 22 | The class identifiers are as follows: 23 | 24 | * font 25 | * info 26 | * groups 27 | * kerning 28 | * features 29 | * lib 30 | * layer 31 | * glyph 32 | * contour 33 | * segment 34 | * bpoint 35 | * point 36 | * component 37 | * anchor 38 | * image 39 | * guideline 40 | 41 | Once an environment has developed this function, all that remains is to pass the function to the test runner:: 42 | 43 | from fontParts.test import testEnvironment 44 | 45 | if __name__ == "__main__": 46 | testEnvironment(MyAppObjectGenerator) 47 | 48 | This can then be executed and the report will be printed. 49 | 50 | .. :note:: 51 | 52 | It is up to each environment to ensure that the bridge from the environment's native objects to the fontParts wrappers is working properly. This has to be done on an environment by environment basis since the native objects are not consistently implemented. -------------------------------------------------------------------------------- /documentation/source/gettingstarted/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | ############### 4 | Getting Started 5 | ############### 6 | 7 | These need to be ported and updated from RoboFab's documentation. 8 | 9 | For a quick start, here's the sample code from the introduction ported to fontparts:: 10 | 11 | from fontParts.world import OpenFont 12 | 13 | font = OpenFont("/path/to/my/font.ufo") 14 | 15 | for glyph in font: 16 | glyph.leftMargin = glyph.leftMargin + 10 17 | glyph.rightMargin = glyph.rightMargin + 10 18 | 19 | Find more of the original samples at https://github.com/robotools/robofab/tree/master/Docs/Examples 20 | -------------------------------------------------------------------------------- /documentation/source/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | History 4 | ======= 5 | 6 | FontParts is based on RoboFab. RoboFab was based on RoboFog. RoboFog was a fork of Fontographer with a built-in Python interpreter. The Fontographer core was wrapped with a lovely Python API. For example, to modify the spacing in all characters ("glyphs" wasn't a standard term back then) in the current font you'd do this:: 7 | 8 | font = CurrentFont() 9 | 10 | for character in font: 11 | character.leftMargin = character.leftMargin + 10 12 | character.rightMargin = character.rightMargin + 10 13 | 14 | When RoboFog could no longer be updated, lots of us designers switched to FontLab. We had *lots* of RoboFog scripts that were critical parts of our workflows and we needed them to work in FontLab right away. FontLab had a built-in Python interpreter, but the API for interacting with the FontLab core was very different from the API in RoboFog. So, a few of us (Erik, Just, Tal) wrote a library called RoboFab that implemented an API that was very similar to the RoboFog API. Designers could take their existing scripts, modify them a tiny bit and they would just work in FontLab. For example, here's how the above script would have been modified:: 15 | 16 | from robofab.world import CurrentFont 17 | 18 | font = CurrentFont() 19 | 20 | for character in font: 21 | character.leftMargin = character.leftMargin + 10 22 | character.rightMargin = character.rightMargin + 10 23 | 24 | This proved to be incredibly useful and it gave us the idea that a universal, environment independent scripting API would be a very good thing to have. So, we extended RoboFab to work in other environments. For example, to get the above script to work outside of any font editor, you would have done this:: 25 | 26 | from robofab.world import OpenFont 27 | 28 | font = OpenFont("/path/to/my/font.ufo") 29 | 30 | for character in font: 31 | character.leftMargin = character.leftMargin + 10 32 | character.rightMargin = character.rightMargin + 10 33 | 34 | Did you notice that the important parts of the script are completely unchanged? Sure, this is a simple two line example, but imagine that you have a suite of tools made of hundreds of thousands of lines of code. *Portable APIs are awesome!!!!!!* 35 | 36 | This was very stable and worked reliably for over a decade. New font editors came along. New font formats came along. New ideas came along. RoboFab was not built in a way that made it easy to add all of these new things while making the old things keep working. We tried, hard, to make it work, but it wasn't possible. We decided that the way forward was to start over from scratch. That idea became FontParts. 37 | 38 | TL;DR: FontParts is a new implementation of ideas that have worked nearly flawlessly for over two decades. 39 | 40 | But why isn't it called RoboFab? 41 | -------------------------------- 42 | 43 | Good question. Well, it's not 100% compatible with RoboFab, so we couldn't just drop it in place without breaking some working scripts. So, it needed a new name. Erik came up with the name "FontParts" because, you know, it represents parts of fonts. 44 | 45 | Design Goals 46 | ============ 47 | 48 | The RoboFog API was quite simple and memorable. FontParts should follow the same model. 49 | 50 | * It should be easy to understand. The main users of this API will be typeface designers, not professional coders. 51 | * The objects, methods, arguments and return values should be memorable. We don't want designers to have to spend a lot of time trying to remember how to do basic things. 52 | * It should look Pythonic. Python is a very legible language. That's great, but it can get uglified when ``environmentsStart_wrapping_lowerLevelAPIs``. We want the FontParts API to look like Python code so that it is easy to read. 53 | -------------------------------------------------------------------------------- /documentation/source/objectref/fontpartsworld/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | ################ 4 | fontParts.world 5 | ################ 6 | 7 | .. module:: fontParts.world 8 | 9 | .. note:: 10 | We still need to decide if we need a ``world`` module or if we should recommend namespace injection. 11 | 12 | .. autofunction:: AllFonts 13 | .. autofunction:: NewFont 14 | .. autofunction:: OpenFont 15 | .. autofunction:: OpenFonts 16 | .. autofunction:: CurrentFont 17 | .. autofunction:: CurrentLayer 18 | .. autofunction:: CurrentGlyph 19 | .. autofunction:: CurrentContours 20 | .. autofunction:: CurrentSegments 21 | .. autofunction:: CurrentPoints 22 | .. autofunction:: CurrentComponents 23 | .. autofunction:: CurrentAnchors 24 | .. autofunction:: CurrentGuidelines 25 | .. autofunction:: FontList 26 | .. autoclass:: BaseFontList -------------------------------------------------------------------------------- /documentation/source/objectref/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | 3 | ################ 4 | Object Reference 5 | ################ 6 | 7 | FontParts scripts are built on with objects that represent fonts, glyphs, contours and so on. The objects are obtained through :ref:`fontparts-world`. 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :includehidden: 13 | 14 | objects/index 15 | valuetypes/index 16 | fontpartsworld/index 17 | 18 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/anchor.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ###### 5 | Anchor 6 | ###### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | Anchors are single points in a glyph which are not part of a contour. They can be used as reference positions for doing things like assembling components. In most font editors, anchors have a special appearance and can be edited. 13 | 14 | :: 15 | 16 | glyph = CurrentGlyph() 17 | for anchor in glyph.anchors: 18 | print(anchor) 19 | 20 | ******** 21 | Overview 22 | ******** 23 | 24 | Copy 25 | ==== 26 | 27 | .. autosummary:: 28 | :nosignatures: 29 | 30 | BaseAnchor.copy 31 | 32 | Parents 33 | ======= 34 | 35 | .. autosummary:: 36 | :nosignatures: 37 | 38 | BaseAnchor.glyph 39 | BaseAnchor.layer 40 | BaseAnchor.font 41 | 42 | Identification 43 | ============== 44 | 45 | .. autosummary:: 46 | :nosignatures: 47 | 48 | BaseAnchor.name 49 | BaseAnchor.color 50 | BaseAnchor.identifier 51 | BaseAnchor.index 52 | 53 | Coordinate 54 | ========== 55 | 56 | .. autosummary:: 57 | :nosignatures: 58 | 59 | BaseAnchor.x 60 | BaseAnchor.y 61 | 62 | Transformations 63 | =============== 64 | 65 | .. autosummary:: 66 | :nosignatures: 67 | 68 | BaseAnchor.transformBy 69 | BaseAnchor.moveBy 70 | BaseAnchor.scaleBy 71 | BaseAnchor.rotateBy 72 | BaseAnchor.skewBy 73 | 74 | Normalization 75 | ============= 76 | 77 | .. autosummary:: 78 | :nosignatures: 79 | 80 | BaseAnchor.round 81 | 82 | Environment 83 | =========== 84 | 85 | .. autosummary:: 86 | :nosignatures: 87 | 88 | BaseAnchor.naked 89 | BaseAnchor.changed 90 | 91 | ********* 92 | Reference 93 | ********* 94 | 95 | .. autoclass:: BaseAnchor 96 | 97 | Copy 98 | ==== 99 | 100 | .. automethod:: BaseAnchor.copy 101 | 102 | Parents 103 | ======= 104 | 105 | .. autoattribute:: BaseAnchor.glyph 106 | .. autoattribute:: BaseAnchor.layer 107 | .. autoattribute:: BaseAnchor.font 108 | 109 | Identification 110 | ============== 111 | 112 | .. autoattribute:: BaseAnchor.name 113 | .. autoattribute:: BaseAnchor.color 114 | .. autoattribute:: BaseAnchor.identifier 115 | .. autoattribute:: BaseAnchor.index 116 | 117 | Coordinate 118 | ========== 119 | 120 | .. autoattribute:: BaseAnchor.x 121 | .. autoattribute:: BaseAnchor.y 122 | 123 | Transformations 124 | =============== 125 | 126 | .. automethod:: BaseAnchor.transformBy 127 | .. automethod:: BaseAnchor.moveBy 128 | .. automethod:: BaseAnchor.scaleBy 129 | .. automethod:: BaseAnchor.rotateBy 130 | .. automethod:: BaseAnchor.skewBy 131 | 132 | Normalization 133 | ============= 134 | 135 | .. automethod:: BaseAnchor.round 136 | 137 | Environment 138 | =========== 139 | 140 | .. automethod:: BaseAnchor.naked 141 | .. automethod:: BaseAnchor.changed 142 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/bpoint.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ###### 5 | bPoint 6 | ###### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | The :class:`bPoint ` is a point object which mimics the old “Bezier Point” from RoboFog. It has attributes for :attr:`bcpIn `, anchor, bcpOut and type. The coordinates in bcpIn and bcpOut are relative to the position of the anchor. For instance, if the bcpIn is 20 units to the left of the anchor, its coordinates would be (-20,0), regardless of the coordinates of the anchor itself. Also: bcpIn will be (0,0) when it is “on top of the anchor”, i.e. when there is no bcp it will still have a value. The parent of a bPoint is usually a :class:`Contour `. 13 | 14 | 15 | 16 | ******** 17 | Overview 18 | ******** 19 | 20 | Parents 21 | ======= 22 | 23 | .. autosummary:: 24 | :nosignatures: 25 | 26 | BaseBPoint.contour 27 | BaseBPoint.glyph 28 | BaseBPoint.layer 29 | BaseBPoint.font 30 | 31 | Identification 32 | ============== 33 | 34 | .. autosummary:: 35 | :nosignatures: 36 | 37 | BaseBPoint.index 38 | 39 | Attributes 40 | ========== 41 | 42 | .. autosummary:: 43 | :nosignatures: 44 | 45 | BaseBPoint.type 46 | 47 | Points 48 | ====== 49 | 50 | .. autosummary:: 51 | :nosignatures: 52 | 53 | BaseBPoint.anchor 54 | BaseBPoint.bcpIn 55 | BaseBPoint.bcpOut 56 | 57 | Transformations 58 | =============== 59 | 60 | .. autosummary:: 61 | :nosignatures: 62 | 63 | BaseBPoint.transformBy 64 | BaseBPoint.moveBy 65 | BaseBPoint.scaleBy 66 | BaseBPoint.rotateBy 67 | BaseBPoint.skewBy 68 | 69 | Normalization 70 | ============= 71 | 72 | .. autosummary:: 73 | :nosignatures: 74 | 75 | BaseBPoint.round 76 | 77 | Environment 78 | =========== 79 | 80 | .. autosummary:: 81 | :nosignatures: 82 | 83 | BaseBPoint.naked 84 | BaseBPoint.changed 85 | 86 | 87 | ********* 88 | Reference 89 | ********* 90 | 91 | .. autoclass:: BaseBPoint 92 | 93 | Parents 94 | ======= 95 | 96 | .. autoattribute:: BaseBPoint.contour 97 | .. autoattribute:: BaseBPoint.glyph 98 | .. autoattribute:: BaseBPoint.layer 99 | .. autoattribute:: BaseBPoint.font 100 | 101 | Identification 102 | ============== 103 | 104 | .. autoattribute:: BaseBPoint.index 105 | 106 | Attributes 107 | ========== 108 | 109 | .. autoattribute:: BaseBPoint.type 110 | 111 | Points 112 | ====== 113 | 114 | .. autoattribute:: BaseBPoint.anchor 115 | .. autoattribute:: BaseBPoint.bcpIn 116 | .. autoattribute:: BaseBPoint.bcpOut 117 | 118 | Transformations 119 | =============== 120 | 121 | .. automethod:: BaseBPoint.transformBy 122 | .. automethod:: BaseBPoint.moveBy 123 | .. automethod:: BaseBPoint.scaleBy 124 | .. automethod:: BaseBPoint.rotateBy 125 | .. automethod:: BaseBPoint.skewBy 126 | 127 | Normalization 128 | ============= 129 | 130 | .. automethod:: BaseBPoint.round 131 | 132 | Environment 133 | =========== 134 | 135 | .. automethod:: BaseBPoint.naked 136 | .. automethod:: BaseBPoint.changed 137 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/component.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ######### 5 | Component 6 | ######### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | A component can be a part of a glyph, and it is a reference to another glyph in the same font. With components you can make glyphs depend on other glyphs. Changes to the base glyph will reflect in the component as well. 13 | 14 | The parent of a component is usually a glyph. Components can be decomposed: they replace themselves with the actual outlines from the base glyph. When that happens, the link between the original and the component is broken: changes to the base glyph will no longer reflect in the glyph that had the component. 15 | 16 | ******** 17 | Overview 18 | ******** 19 | 20 | Parents 21 | ======= 22 | 23 | .. autosummary:: 24 | :nosignatures: 25 | 26 | BaseComponent.glyph 27 | BaseComponent.layer 28 | BaseComponent.font 29 | 30 | Copy 31 | ==== 32 | 33 | .. autosummary:: 34 | :nosignatures: 35 | 36 | BaseComponent.copy 37 | 38 | Identification 39 | ============== 40 | 41 | .. autosummary:: 42 | :nosignatures: 43 | 44 | BaseComponent.identifier 45 | BaseComponent.index 46 | 47 | Attributes 48 | ========== 49 | 50 | .. autosummary:: 51 | :nosignatures: 52 | 53 | BaseComponent.baseGlyph 54 | BaseComponent.transformation 55 | BaseComponent.offset 56 | BaseComponent.scale 57 | 58 | Queries 59 | ======= 60 | 61 | .. autosummary:: 62 | :nosignatures: 63 | 64 | BaseComponent.bounds 65 | BaseComponent.pointInside 66 | 67 | Pens and Drawing 68 | ================ 69 | 70 | .. autosummary:: 71 | :nosignatures: 72 | 73 | BaseComponent.draw 74 | BaseComponent.drawPoints 75 | 76 | Transformations 77 | =============== 78 | 79 | .. autosummary:: 80 | :nosignatures: 81 | 82 | BaseComponent.transformBy 83 | BaseComponent.moveBy 84 | BaseComponent.scaleBy 85 | BaseComponent.rotateBy 86 | BaseComponent.skewBy 87 | 88 | Normalization 89 | ============= 90 | 91 | .. autosummary:: 92 | :nosignatures: 93 | 94 | BaseComponent.decompose 95 | BaseComponent.round 96 | 97 | Environment 98 | =========== 99 | 100 | .. autosummary:: 101 | :nosignatures: 102 | 103 | BaseComponent.naked 104 | BaseComponent.changed 105 | 106 | ********* 107 | Reference 108 | ********* 109 | 110 | .. autoclass:: BaseComponent 111 | 112 | Parents 113 | ======= 114 | 115 | .. autoattribute:: BaseComponent.glyph 116 | .. autoattribute:: BaseComponent.layer 117 | .. autoattribute:: BaseComponent.font 118 | 119 | Copy 120 | ==== 121 | 122 | .. automethod:: BaseComponent.copy 123 | 124 | Identification 125 | ============== 126 | 127 | .. autoattribute:: BaseComponent.identifier 128 | .. autoattribute:: BaseComponent.index 129 | 130 | Attributes 131 | ========== 132 | 133 | .. autoattribute:: BaseComponent.baseGlyph 134 | .. autoattribute:: BaseComponent.transformation 135 | .. autoattribute:: BaseComponent.offset 136 | .. autoattribute:: BaseComponent.scale 137 | 138 | Queries 139 | ======= 140 | 141 | .. autoattribute:: BaseComponent.bounds 142 | .. automethod:: BaseComponent.pointInside 143 | 144 | Pens and Drawing 145 | ================ 146 | 147 | .. automethod:: BaseComponent.draw 148 | .. automethod:: BaseComponent.drawPoints 149 | 150 | Transformations 151 | =============== 152 | 153 | .. automethod:: BaseComponent.transformBy 154 | .. automethod:: BaseComponent.moveBy 155 | .. automethod:: BaseComponent.scaleBy 156 | .. automethod:: BaseComponent.rotateBy 157 | .. automethod:: BaseComponent.skewBy 158 | 159 | Normalization 160 | ============= 161 | 162 | .. automethod:: BaseComponent.decompose 163 | .. automethod:: BaseComponent.round 164 | 165 | Environment 166 | =========== 167 | 168 | .. automethod:: BaseComponent.naked 169 | .. automethod:: BaseComponent.changed 170 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/contour.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ####### 5 | Contour 6 | ####### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | A Contour is a single path of any number of points. A Glyph usually consists of a couple of contours, and this is the object that represents each one. The :class:`Contour ` object offers access to the outline matter in various ways. The parent of :class:`Contour ` is usually :class:`Glyph `. 13 | 14 | ******** 15 | Overview 16 | ******** 17 | 18 | Copy 19 | ==== 20 | 21 | .. autosummary:: 22 | :nosignatures: 23 | 24 | BaseContour.copy 25 | 26 | Parents 27 | ======= 28 | 29 | .. autosummary:: 30 | :nosignatures: 31 | 32 | BaseContour.glyph 33 | BaseContour.layer 34 | BaseContour.font 35 | 36 | Identification 37 | ============== 38 | 39 | .. autosummary:: 40 | :nosignatures: 41 | 42 | BaseContour.identifier 43 | BaseContour.index 44 | 45 | Winding Direction 46 | ================= 47 | 48 | .. autosummary:: 49 | :nosignatures: 50 | 51 | BaseContour.clockwise 52 | BaseContour.reverse 53 | 54 | Queries 55 | ======= 56 | 57 | .. autosummary:: 58 | :nosignatures: 59 | 60 | BaseContour.bounds 61 | BaseContour.pointInside 62 | 63 | Pens and Drawing 64 | ================ 65 | 66 | .. autosummary:: 67 | :nosignatures: 68 | 69 | BaseContour.draw 70 | BaseContour.drawPoints 71 | 72 | Segments 73 | ======== 74 | 75 | .. autosummary:: 76 | :nosignatures: 77 | 78 | BaseContour.segments 79 | BaseContour.__len__ 80 | BaseContour.__iter__ 81 | BaseContour.__getitem__ 82 | BaseContour.appendSegment 83 | BaseContour.insertSegment 84 | BaseContour.removeSegment 85 | BaseContour.setStartSegment 86 | BaseContour.autoStartSegment 87 | 88 | bPoints 89 | ======= 90 | 91 | .. autosummary:: 92 | :nosignatures: 93 | 94 | BaseContour.bPoints 95 | BaseContour.appendBPoint 96 | BaseContour.insertBPoint 97 | 98 | Points 99 | ====== 100 | 101 | .. autosummary:: 102 | :nosignatures: 103 | 104 | BaseContour.points 105 | BaseContour.appendPoint 106 | BaseContour.insertPoint 107 | BaseContour.removePoint 108 | 109 | Transformations 110 | =============== 111 | 112 | .. autosummary:: 113 | :nosignatures: 114 | 115 | BaseContour.transformBy 116 | BaseContour.moveBy 117 | BaseContour.scaleBy 118 | BaseContour.rotateBy 119 | BaseContour.skewBy 120 | 121 | Normalization 122 | ============= 123 | 124 | .. autosummary:: 125 | :nosignatures: 126 | 127 | BaseContour.round 128 | 129 | Environment 130 | =========== 131 | 132 | .. autosummary:: 133 | :nosignatures: 134 | 135 | BaseContour.naked 136 | BaseContour.changed 137 | 138 | ********* 139 | Reference 140 | ********* 141 | 142 | .. autoclass:: BaseContour 143 | 144 | Copy 145 | ==== 146 | 147 | .. automethod:: BaseContour.copy 148 | 149 | Parents 150 | ======= 151 | 152 | .. autoattribute:: BaseContour.glyph 153 | .. autoattribute:: BaseContour.layer 154 | .. autoattribute:: BaseContour.font 155 | 156 | Identification 157 | ============== 158 | 159 | .. autoattribute:: BaseContour.identifier 160 | .. autoattribute:: BaseContour.index 161 | 162 | Winding Direction 163 | ================= 164 | 165 | .. autoattribute:: BaseContour.clockwise 166 | .. automethod:: BaseContour.reverse 167 | 168 | Queries 169 | ======= 170 | 171 | .. autoattribute:: BaseContour.bounds 172 | .. automethod:: BaseContour.pointInside 173 | 174 | Pens and Drawing 175 | ================ 176 | 177 | .. automethod:: BaseContour.draw 178 | .. automethod:: BaseContour.drawPoints 179 | 180 | Segments 181 | ======== 182 | 183 | .. autoattribute:: BaseContour.segments 184 | .. automethod:: BaseContour.__len__ 185 | .. automethod:: BaseContour.__iter__ 186 | .. automethod:: BaseContour.__getitem__ 187 | .. automethod:: BaseContour.appendSegment 188 | .. automethod:: BaseContour.insertSegment 189 | .. automethod:: BaseContour.removeSegment 190 | .. automethod:: BaseContour.setStartSegment 191 | .. automethod:: BaseContour.autoStartSegment 192 | 193 | bPoints 194 | ======= 195 | 196 | .. autoattribute:: BaseContour.bPoints 197 | .. automethod:: BaseContour.appendBPoint 198 | .. automethod:: BaseContour.insertBPoint 199 | 200 | Points 201 | ====== 202 | 203 | .. autoattribute:: BaseContour.points 204 | .. automethod:: BaseContour.appendPoint 205 | .. automethod:: BaseContour.insertPoint 206 | .. automethod:: BaseContour.removePoint 207 | 208 | Transformations 209 | =============== 210 | 211 | .. automethod:: BaseContour.transformBy 212 | .. automethod:: BaseContour.moveBy 213 | .. automethod:: BaseContour.scaleBy 214 | .. automethod:: BaseContour.rotateBy 215 | .. automethod:: BaseContour.skewBy 216 | 217 | Normalization 218 | ============= 219 | 220 | .. automethod:: BaseContour.round 221 | 222 | Environment 223 | =========== 224 | 225 | .. automethod:: BaseContour.naked 226 | .. automethod:: BaseContour.changed 227 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/features.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ######## 5 | Features 6 | ######## 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | Features is text in the `Adobe Font Development Kit `_ for OpenType `.fea syntax `_ that describes the OpenType features of your font. The `OpenType Cookbook `_ is a great place to start learning how to write features. Your features must be self-contained; for example, any glyph or mark classes must be defined within the file. No assumption should be made about the validity of the syntax, and FontParts does not check the validity of the syntax. 13 | 14 | .. note:: It is important to note that the features file may contain data that is a duplicate of or data that is in conflict with the data in :class:`BaseKerning`, :class:`BaseGroups`, and :class:`BaseInfo`. Synchronization is up to the user and application developers. 15 | 16 | :: 17 | 18 | font = CurrentFont() 19 | print(font.features) 20 | 21 | ******** 22 | Overview 23 | ******** 24 | 25 | .. autosummary:: 26 | :nosignatures: 27 | 28 | BaseFeatures.copy 29 | BaseFeatures.font 30 | BaseFeatures.text 31 | 32 | ********* 33 | Reference 34 | ********* 35 | 36 | .. autoclass:: BaseFeatures 37 | 38 | Copy 39 | ==== 40 | 41 | .. automethod:: BaseFeatures.copy 42 | 43 | Parents 44 | ======= 45 | 46 | .. autoattribute:: BaseFeatures.font 47 | 48 | Attributes 49 | ========== 50 | 51 | .. autoattribute:: BaseFeatures.text 52 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/font.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | #### 5 | Font 6 | #### 7 | 8 | .. note:: 9 | 10 | This section needs to contain the following: 11 | 12 | * description of what this is ✓ 13 | * sub-object with basic usage ✓ 14 | * bridge to default layer for glyphs for backwards compatibility ✗ 15 | * glyph interaction with basic usage ✗ 16 | 17 | *********** 18 | Description 19 | *********** 20 | 21 | The :class:`Font ` object is the central part that connects all glyphs with font information like names, key dimensions etc. 22 | 23 | :class:`Font ` objects behave like dictionaries: the glyph name is the key and the returned value is a :class:`Glyph ` object for that glyph. If the glyph does not exist, :class:`Font ` will raise an ``IndexError``. 24 | 25 | :class:`Font ` has a couple of important sub-objects which are worth checking out. The font’s kerning is stored in a :class:`Kerning ` object and can be reached as an attribute at ``Font.kerning``. Fontnames, key dimensions, flags etc are stored in a :class:`Info ` object which is available through ``Font.info``. The ``Font.lib`` is a :class:`Lib ` object which behaves as a dictionary. 26 | 27 | ******** 28 | Overview 29 | ******** 30 | 31 | Copy 32 | ==== 33 | 34 | .. autosummary:: 35 | :nosignatures: 36 | 37 | BaseFont.copy 38 | 39 | File Operations 40 | =============== 41 | 42 | .. autosummary:: 43 | :nosignatures: 44 | 45 | BaseFont.path 46 | BaseFont.save 47 | BaseFont.generate 48 | 49 | Sub-Objects 50 | =========== 51 | 52 | .. autosummary:: 53 | :nosignatures: 54 | 55 | BaseFont.info 56 | BaseFont.groups 57 | BaseFont.kerning 58 | BaseFont.features 59 | BaseFont.lib 60 | BaseFont.tempLib 61 | 62 | Layers 63 | ====== 64 | 65 | .. autosummary:: 66 | :nosignatures: 67 | 68 | BaseFont.layers 69 | BaseFont.layerOrder 70 | BaseFont.defaultLayer 71 | BaseFont.getLayer 72 | BaseFont.newLayer 73 | BaseFont.removeLayer 74 | BaseFont.insertLayer 75 | BaseFont.duplicateLayer 76 | 77 | Glyphs 78 | ====== 79 | 80 | .. autosummary:: 81 | :nosignatures: 82 | 83 | BaseFont.__len__ 84 | BaseFont.keys 85 | BaseFont.glyphOrder 86 | BaseFont.__iter__ 87 | BaseFont.__contains__ 88 | BaseFont.__getitem__ 89 | BaseFont.newGlyph 90 | BaseFont.insertGlyph 91 | BaseFont.removeGlyph 92 | 93 | ********* 94 | Reference 95 | ********* 96 | 97 | .. autoclass:: BaseFont 98 | 99 | Copy 100 | ==== 101 | 102 | .. automethod:: BaseFont.copy 103 | 104 | File Operations 105 | =============== 106 | 107 | .. autoattribute:: BaseFont.path 108 | .. automethod:: BaseFont.save 109 | .. automethod:: BaseFont.close 110 | .. automethod:: BaseFont.generate 111 | 112 | Sub-Objects 113 | =========== 114 | 115 | .. autoattribute:: BaseFont.info 116 | .. autoattribute:: BaseFont.groups 117 | .. autoattribute:: BaseFont.kerning 118 | .. autoattribute:: BaseFont.features 119 | .. autoattribute:: BaseFont.lib 120 | 121 | Layers 122 | ====== 123 | 124 | .. autoattribute:: BaseFont.layers 125 | .. autoattribute:: BaseFont.layerOrder 126 | .. autoattribute:: BaseFont.defaultLayer 127 | .. automethod:: BaseFont.getLayer 128 | .. automethod:: BaseFont.newLayer 129 | .. automethod:: BaseFont.removeLayer 130 | .. automethod:: BaseFont.insertLayer 131 | 132 | Glyphs 133 | ====== 134 | 135 | Interacting with glyphs at the font level is a shortcut for interacting with glyphs in the default layer. :: 136 | 137 | >>> glyph = font.newGlyph("A") 138 | 139 | Does the same thing as:: 140 | 141 | >>> glyph = font.getLayer(font.defaultLayerName).newGlyph("A") 142 | 143 | .. automethod:: BaseFont.__len__ 144 | .. automethod:: BaseFont.keys 145 | .. autoattribute:: BaseFont.glyphOrder 146 | .. automethod:: BaseFont.__iter__ 147 | .. automethod:: BaseFont.__contains__ 148 | .. automethod:: BaseFont.__getitem__ 149 | .. automethod:: BaseFont.newGlyph 150 | .. automethod:: BaseFont.insertGlyph 151 | .. automethod:: BaseFont.removeGlyph 152 | 153 | Guidelines 154 | ========== 155 | 156 | .. autoattribute:: BaseFont.guidelines 157 | .. automethod:: BaseFont.appendGuideline 158 | .. automethod:: BaseFont.removeGuideline 159 | .. automethod:: BaseFont.clearGuidelines 160 | 161 | Interpolation 162 | ============= 163 | 164 | .. automethod:: BaseFont.isCompatible 165 | .. automethod:: BaseFont.interpolate 166 | 167 | Normalization 168 | ============= 169 | 170 | .. automethod:: BaseFont.round 171 | .. automethod:: BaseFont.autoUnicodes 172 | 173 | Environment 174 | =========== 175 | 176 | .. automethod:: BaseFont.naked 177 | .. automethod:: BaseFont.changed 178 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/groups.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ###### 5 | Groups 6 | ###### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | Groups are collections of glyphs. Groups are used for many things, from OpenType features, kerning, or just keeping track of a collection of related glyphs. The name of the group must be at least one character, with no limit to the maximum length for the name, nor any limit on the characters used in a name. With the exception of the kerning groups defined below, glyphs may be in more than one group and they may appear within the same group more than once. Glyphs in the groups are not required to be in the font. 13 | 14 | Groups behave like a Python dictionary. Anything you can do with a dictionary in Python, you can do with Groups. 15 | 16 | :: 17 | 18 | font = CurrentFont() 19 | for name, members in font.groups.items(): 20 | print(name) 21 | print(members) 22 | 23 | It is important to understand that any changes to the returned group contents will not be reflected in the groups object. This means that the following will not update the font's groups: 24 | 25 | :: 26 | 27 | group = list(font.groups["myGroup"]) 28 | group.remove("A") 29 | 30 | If one wants to make a change to the group contents, one should do the following instead: 31 | 32 | :: 33 | 34 | group = list(font.groups["myGroup"]) 35 | group.remove("A") 36 | font.groups["myGroup"] = group 37 | 38 | Kerning Groups 39 | ============== 40 | 41 | Groups may be used as members of kerning pairs in :class:`BaseKerning`. These groups are divided into two types: groups that appear on the first side of a kerning pair and groups that appear on the second side of a kerning pair. 42 | 43 | Kerning groups must begin with standard prefixes. The prefix for groups intended for use in the first side of a kerning pair is ``public.kern1.``. The prefix for groups intended for use in the second side of a kerning pair is ``public.kern2.``. One or more characters must follow the prefix. 44 | 45 | Kerning groups must strictly adhere to the following rules: 46 | 47 | #. Kerning group names must begin with the appropriate prefix. 48 | #. Only kerning groups are allowed to use the kerning group prefixes in their names. 49 | #. Kerning groups are not required to appear in the kerning pairs. 50 | #. Glyphs must not appear in more than one kerning group per side. 51 | 52 | These rules come from the `Unified Font Object `_, more information on implementation details for application developers can be found there. 53 | 54 | ******** 55 | Overview 56 | ******** 57 | 58 | .. autosummary:: 59 | :nosignatures: 60 | 61 | BaseGroups.copy 62 | BaseGroups.font 63 | BaseGroups.__contains__ 64 | BaseGroups.__delitem__ 65 | BaseGroups.__getitem__ 66 | BaseGroups.__iter__ 67 | BaseGroups.__len__ 68 | BaseGroups.__setitem__ 69 | BaseGroups.clear 70 | BaseGroups.get 71 | BaseGroups.items 72 | BaseGroups.keys 73 | BaseGroups.pop 74 | BaseGroups.update 75 | BaseGroups.values 76 | BaseGroups.findGlyph 77 | BaseGroups.naked 78 | BaseGroups.changed 79 | 80 | ********* 81 | Reference 82 | ********* 83 | 84 | .. autoclass:: BaseGroups 85 | 86 | Copy 87 | ==== 88 | 89 | .. automethod:: BaseGroups.copy 90 | 91 | Parents 92 | ======= 93 | 94 | * :attr:`~BaseGroups.font` The groups' parent :class:`BaseFont`. 95 | 96 | Dictionary 97 | ========== 98 | 99 | .. automethod:: BaseGroups.__contains__ 100 | .. automethod:: BaseGroups.__delitem__ 101 | .. automethod:: BaseGroups.__getitem__ 102 | .. automethod:: BaseGroups.__iter__ 103 | .. automethod:: BaseGroups.__len__ 104 | .. automethod:: BaseGroups.__setitem__ 105 | .. automethod:: BaseGroups.clear 106 | .. automethod:: BaseGroups.get 107 | .. automethod:: BaseGroups.items 108 | .. automethod:: BaseGroups.keys 109 | .. automethod:: BaseGroups.pop 110 | .. automethod:: BaseGroups.update 111 | .. automethod:: BaseGroups.values 112 | 113 | Queries 114 | ======= 115 | 116 | .. automethod:: BaseGroups.findGlyph 117 | 118 | Environment 119 | =========== 120 | 121 | .. automethod:: BaseGroups.naked 122 | .. automethod:: BaseGroups.changed 123 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/guideline.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ######### 5 | Guideline 6 | ######### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | Guidelines are reference lines in a glyph that are not part of a contour or the generated font data. They are defined by a point and an angle; the guideline extends from the point in both directions on the specified angle. They are most often used to keep track of design information for a font ('my overshoots should be here') or to measure positions in a glyph ('line the ends of my serifs on this line'). They can also be used as reference positions for doing things like assembling components. In most font editors, guidelines have a special appearance and can be edited. 13 | 14 | :: 15 | 16 | glyph = CurrentGlyph() 17 | for guideline in glyph.guidelines: 18 | print(guideline) 19 | 20 | ******** 21 | Overview 22 | ******** 23 | 24 | Copy 25 | ==== 26 | 27 | .. autosummary:: 28 | :nosignatures: 29 | 30 | BaseGuideline.copy 31 | 32 | Parents 33 | ======= 34 | 35 | .. autosummary:: 36 | :nosignatures: 37 | 38 | BaseGuideline.glyph 39 | BaseGuideline.layer 40 | BaseGuideline.font 41 | 42 | Identification 43 | ============== 44 | 45 | .. autosummary:: 46 | :nosignatures: 47 | 48 | BaseGuideline.name 49 | BaseGuideline.color 50 | BaseGuideline.identifier 51 | BaseGuideline.index 52 | 53 | Attributes 54 | ========== 55 | 56 | .. autosummary:: 57 | :nosignatures: 58 | 59 | BaseGuideline.x 60 | BaseGuideline.y 61 | BaseGuideline.angle 62 | 63 | Transformations 64 | =============== 65 | 66 | .. autosummary:: 67 | :nosignatures: 68 | 69 | BaseGuideline.transformBy 70 | BaseGuideline.moveBy 71 | BaseGuideline.scaleBy 72 | BaseGuideline.rotateBy 73 | BaseGuideline.skewBy 74 | 75 | Normalization 76 | ============= 77 | 78 | .. autosummary:: 79 | :nosignatures: 80 | 81 | BaseGuideline.round 82 | 83 | Environment 84 | =========== 85 | 86 | .. autosummary:: 87 | :nosignatures: 88 | 89 | BaseGuideline.naked 90 | BaseGuideline.changed 91 | 92 | ********* 93 | Reference 94 | ********* 95 | 96 | .. autoclass:: BaseGuideline 97 | 98 | Copy 99 | ==== 100 | 101 | .. automethod:: BaseGuideline.copy 102 | 103 | Parents 104 | ======= 105 | 106 | .. autoattribute:: BaseGuideline.glyph 107 | .. autoattribute:: BaseGuideline.layer 108 | .. autoattribute:: BaseGuideline.font 109 | 110 | Identification 111 | ============== 112 | 113 | .. autoattribute:: BaseGuideline.name 114 | .. autoattribute:: BaseGuideline.color 115 | .. autoattribute:: BaseGuideline.identifier 116 | .. autoattribute:: BaseGuideline.index 117 | 118 | Attributes 119 | ========== 120 | 121 | .. autoattribute:: BaseGuideline.x 122 | .. autoattribute:: BaseGuideline.y 123 | .. autoattribute:: BaseGuideline.angle 124 | 125 | Transformations 126 | =============== 127 | 128 | .. automethod:: BaseGuideline.transformBy 129 | .. automethod:: BaseGuideline.moveBy 130 | .. automethod:: BaseGuideline.scaleBy 131 | .. automethod:: BaseGuideline.rotateBy 132 | .. automethod:: BaseGuideline.skewBy 133 | 134 | Normalization 135 | ============= 136 | 137 | .. automethod:: BaseGuideline.round 138 | 139 | Environment 140 | =========== 141 | 142 | .. automethod:: BaseGuideline.naked 143 | .. automethod:: BaseGuideline.changed 144 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/image.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ##### 5 | Image 6 | ##### 7 | 8 | ******** 9 | Overview 10 | ******** 11 | 12 | .. autosummary:: 13 | :nosignatures: 14 | 15 | BaseImage.copy 16 | BaseImage.glyph 17 | BaseImage.layer 18 | BaseImage.font 19 | BaseImage.data 20 | BaseImage.color 21 | BaseImage.transformation 22 | BaseImage.offset 23 | BaseImage.scale 24 | BaseImage.transformBy 25 | BaseImage.moveBy 26 | BaseImage.scaleBy 27 | BaseImage.rotateBy 28 | BaseImage.skewBy 29 | BaseImage.round 30 | BaseImage.naked 31 | BaseImage.changed 32 | 33 | 34 | ********* 35 | Reference 36 | ********* 37 | 38 | .. autoclass:: BaseImage 39 | 40 | Copy 41 | ==== 42 | 43 | .. automethod:: BaseImage.copy 44 | 45 | Parents 46 | ======= 47 | 48 | .. autoattribute:: BaseImage.glyph 49 | .. autoattribute:: BaseImage.layer 50 | .. autoattribute:: BaseImage.font 51 | 52 | Attributes 53 | ========== 54 | 55 | .. autoattribute:: BaseImage.data 56 | .. autoattribute:: BaseImage.color 57 | .. autoattribute:: BaseImage.transformation 58 | .. autoattribute:: BaseImage.offset 59 | .. autoattribute:: BaseImage.scale 60 | 61 | Transformations 62 | =============== 63 | 64 | .. automethod:: BaseImage.transformBy 65 | .. automethod:: BaseImage.moveBy 66 | .. automethod:: BaseImage.scaleBy 67 | .. automethod:: BaseImage.rotateBy 68 | .. automethod:: BaseImage.skewBy 69 | 70 | Normalization 71 | ============= 72 | 73 | .. automethod:: BaseImage.round 74 | 75 | Environment 76 | =========== 77 | 78 | .. automethod:: BaseImage.naked 79 | .. automethod:: BaseImage.changed 80 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/info.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | #### 5 | Info 6 | #### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | The :class:`Info ` object contains all names, numbers, URLs, dimensions, values, etc. that would otherwise clutter up the font object. You don't have to create a :class:`Info ` object yourself, :class:`Font ` makes one when it is created. 13 | 14 | :class:`Info ` validates any value set for a `Info ` item, but does not check if the data is sane (i.e., you can set valid but incorrect data). 15 | 16 | The :class:`Info ` object (as any other fontParts object) does not allow to modify mutable containers (like lists) in-place. Always get a value, modify it and then set it back to perform an edit. 17 | 18 | For a list of info attributes, refer to the `UFO fontinfo.plist Specification `_. 19 | 20 | ******** 21 | Overview 22 | ******** 23 | 24 | .. autosummary:: 25 | :nosignatures: 26 | 27 | BaseInfo.copy 28 | BaseInfo.font 29 | BaseInfo.interpolate 30 | BaseInfo.round 31 | BaseInfo.update 32 | BaseInfo.naked 33 | BaseInfo.changed 34 | 35 | ********* 36 | Reference 37 | ********* 38 | 39 | .. autoclass:: BaseInfo 40 | 41 | Copy 42 | ==== 43 | 44 | .. automethod:: BaseInfo.copy 45 | 46 | Parents 47 | ======= 48 | 49 | .. autoattribute:: BaseInfo.font 50 | 51 | Interpolation 52 | ============= 53 | 54 | .. automethod:: BaseInfo.interpolate 55 | 56 | Normalization 57 | ============= 58 | 59 | .. automethod:: BaseInfo.round 60 | 61 | Update 62 | ====== 63 | 64 | .. automethod:: BaseInfo.update 65 | 66 | Environment 67 | =========== 68 | 69 | .. automethod:: BaseInfo.naked 70 | .. automethod:: BaseInfo.changed 71 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/kerning.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ####### 5 | Kerning 6 | ####### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | Kerning groups must begin with standard prefixes. The prefix for groups intended for use in the first side of a kerning pair is ``public.kern1.``. The prefix for groups intended for use in the second side of a kerning pair is ``public.kern2.``. One or more characters must follow the prefix. 13 | 14 | Kerning groups must strictly adhere to the following rules: 15 | 16 | #. Kerning group names must begin with the appropriate prefix. 17 | #. Only kerning groups are allowed to use the kerning group prefixes in their names. 18 | #. Kerning groups are not required to appear in the kerning pairs. 19 | #. Glyphs must not appear in more than one kerning group per side. 20 | 21 | These rules come from the `Unified Font Object `_, more information on implementation details for application developers can be found there. 22 | 23 | ******** 24 | Overview 25 | ******** 26 | 27 | Copy 28 | ==== 29 | 30 | .. autosummary:: 31 | :nosignatures: 32 | 33 | BaseKerning.copy 34 | 35 | Parents 36 | ======= 37 | 38 | .. autosummary:: 39 | :nosignatures: 40 | 41 | BaseKerning.font 42 | 43 | Dictionary 44 | ========== 45 | 46 | .. autosummary:: 47 | :nosignatures: 48 | 49 | BaseKerning.__len__ 50 | BaseKerning.keys 51 | BaseKerning.items 52 | BaseKerning.values 53 | BaseKerning.__contains__ 54 | BaseKerning.__setitem__ 55 | BaseKerning.__getitem__ 56 | BaseKerning.get 57 | BaseKerning.find 58 | BaseKerning.__delitem__ 59 | BaseKerning.pop 60 | BaseKerning.__iter__ 61 | BaseKerning.update 62 | BaseKerning.clear 63 | 64 | Transformations 65 | =============== 66 | 67 | .. autosummary:: 68 | :nosignatures: 69 | 70 | BaseKerning.scaleBy 71 | 72 | Interpolation 73 | ============= 74 | 75 | .. autosummary:: 76 | :nosignatures: 77 | 78 | BaseKerning.interpolate 79 | 80 | Normalization 81 | ============= 82 | 83 | .. autosummary:: 84 | :nosignatures: 85 | 86 | BaseKerning.round 87 | 88 | Environment 89 | =========== 90 | 91 | .. autosummary:: 92 | :nosignatures: 93 | 94 | BaseKerning.naked 95 | BaseKerning.changed 96 | 97 | ********* 98 | Reference 99 | ********* 100 | 101 | .. autoclass:: BaseKerning 102 | 103 | Copy 104 | ==== 105 | 106 | .. automethod:: BaseKerning.copy 107 | 108 | Parents 109 | ======= 110 | 111 | .. autoattribute:: BaseKerning.font 112 | 113 | Dictionary 114 | ========== 115 | 116 | .. automethod:: BaseKerning.__len__ 117 | .. automethod:: BaseKerning.keys 118 | .. automethod:: BaseKerning.items 119 | .. automethod:: BaseKerning.values 120 | .. automethod:: BaseKerning.__contains__ 121 | .. automethod:: BaseKerning.__setitem__ 122 | .. automethod:: BaseKerning.__getitem__ 123 | .. automethod:: BaseKerning.get 124 | .. automethod:: BaseKerning.find 125 | .. automethod:: BaseKerning.__delitem__ 126 | .. automethod:: BaseKerning.pop 127 | .. automethod:: BaseKerning.__iter__ 128 | .. automethod:: BaseKerning.update 129 | .. automethod:: BaseKerning.clear 130 | 131 | Transformations 132 | =============== 133 | 134 | .. automethod:: BaseKerning.scaleBy 135 | 136 | Interpolation 137 | ============= 138 | 139 | .. automethod:: BaseKerning.interpolate 140 | 141 | Normalization 142 | ============= 143 | 144 | .. automethod:: BaseKerning.round 145 | 146 | Environment 147 | =========== 148 | 149 | .. automethod:: BaseKerning.naked 150 | .. automethod:: BaseKerning.changed 151 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/layer.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ##### 5 | Layer 6 | ##### 7 | 8 | .. note:: 9 | 10 | This section needs to contain the following: 11 | 12 | * description of what this is 13 | * sub-object with basic usage 14 | * glyph interaction with basic usage 15 | 16 | ******** 17 | Overview 18 | ******** 19 | 20 | Copy 21 | ==== 22 | 23 | .. autosummary:: 24 | :nosignatures: 25 | 26 | BaseLayer.copy 27 | 28 | Parents 29 | ======= 30 | 31 | .. autosummary:: 32 | :nosignatures: 33 | 34 | BaseLayer.font 35 | 36 | Attributes 37 | ========== 38 | 39 | .. autosummary:: 40 | :nosignatures: 41 | 42 | BaseLayer.name 43 | BaseLayer.color 44 | 45 | Sub-Objects 46 | =========== 47 | 48 | .. autosummary:: 49 | :nosignatures: 50 | 51 | BaseLayer.lib 52 | BaseLayer.tempLib 53 | 54 | Glyphs 55 | ====== 56 | 57 | .. autosummary:: 58 | :nosignatures: 59 | 60 | BaseLayer.__len__ 61 | BaseLayer.keys 62 | BaseLayer.__iter__ 63 | BaseLayer.__contains__ 64 | BaseLayer.__getitem__ 65 | BaseLayer.newGlyph 66 | BaseLayer.insertGlyph 67 | BaseLayer.removeGlyph 68 | 69 | Interpolation 70 | ============= 71 | 72 | .. autosummary:: 73 | :nosignatures: 74 | 75 | BaseLayer.isCompatible 76 | BaseLayer.interpolate 77 | 78 | Normalization 79 | ============= 80 | 81 | .. autosummary:: 82 | :nosignatures: 83 | 84 | BaseLayer.round 85 | BaseLayer.autoUnicodes 86 | 87 | Environment 88 | =========== 89 | 90 | .. autosummary:: 91 | :nosignatures: 92 | 93 | BaseLayer.naked 94 | BaseLayer.changed 95 | 96 | 97 | ********* 98 | Reference 99 | ********* 100 | 101 | .. autoclass:: BaseLayer 102 | 103 | Copy 104 | ==== 105 | 106 | .. automethod:: BaseLayer.copy 107 | 108 | Parents 109 | ======= 110 | 111 | .. autoattribute:: BaseLayer.font 112 | 113 | Attributes 114 | ========== 115 | 116 | .. autoattribute:: BaseLayer.name 117 | .. autoattribute:: BaseLayer.color 118 | 119 | Sub-Objects 120 | =========== 121 | 122 | .. autoattribute:: BaseLayer.lib 123 | 124 | Glyphs 125 | ====== 126 | 127 | .. automethod:: BaseLayer.__len__ 128 | .. automethod:: BaseLayer.keys 129 | .. automethod:: BaseLayer.__iter__ 130 | .. automethod:: BaseLayer.__contains__ 131 | .. automethod:: BaseLayer.__getitem__ 132 | .. automethod:: BaseLayer.newGlyph 133 | .. automethod:: BaseLayer.insertGlyph 134 | .. automethod:: BaseLayer.removeGlyph 135 | 136 | Interpolation 137 | ============= 138 | 139 | .. automethod:: BaseLayer.isCompatible 140 | .. automethod:: BaseLayer.interpolate 141 | 142 | Normalization 143 | ============= 144 | 145 | .. automethod:: BaseLayer.round 146 | .. automethod:: BaseLayer.autoUnicodes 147 | 148 | Environment 149 | =========== 150 | 151 | .. automethod:: BaseLayer.naked 152 | .. automethod:: BaseLayer.changed 153 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/lib.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ### 5 | Lib 6 | ### 7 | 8 | ******** 9 | Overview 10 | ******** 11 | 12 | .. autosummary:: 13 | :nosignatures: 14 | 15 | BaseLib.copy 16 | BaseLib.glyph 17 | BaseLib.font 18 | BaseLib.__len__ 19 | BaseLib.keys 20 | BaseLib.items 21 | BaseLib.values 22 | BaseLib.__contains__ 23 | BaseLib.__setitem__ 24 | BaseLib.__getitem__ 25 | BaseLib.get 26 | BaseLib.__delitem__ 27 | BaseLib.pop 28 | BaseLib.__iter__ 29 | BaseLib.update 30 | BaseLib.clear 31 | BaseLib.naked 32 | BaseLib.changed 33 | 34 | 35 | ********* 36 | Reference 37 | ********* 38 | 39 | .. autoclass:: BaseLib 40 | 41 | Copy 42 | ==== 43 | 44 | .. automethod:: BaseLib.copy 45 | 46 | Parents 47 | ======= 48 | 49 | .. autoattribute:: BaseLib.glyph 50 | .. autoattribute:: BaseLib.font 51 | 52 | Dictionary 53 | ========== 54 | 55 | .. automethod:: BaseLib.__len__ 56 | .. automethod:: BaseLib.keys 57 | .. automethod:: BaseLib.items 58 | .. automethod:: BaseLib.values 59 | .. automethod:: BaseLib.__contains__ 60 | .. automethod:: BaseLib.__setitem__ 61 | .. automethod:: BaseLib.__getitem__ 62 | .. automethod:: BaseLib.get 63 | .. automethod:: BaseLib.__delitem__ 64 | .. automethod:: BaseLib.pop 65 | .. automethod:: BaseLib.__iter__ 66 | .. automethod:: BaseLib.update 67 | .. automethod:: BaseLib.clear 68 | 69 | Environment 70 | =========== 71 | 72 | .. automethod:: BaseLib.naked 73 | .. automethod:: BaseLib.changed 74 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/point.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ##### 5 | Point 6 | ##### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | :class:`Point ` represents one single point with a particular coordinate in a contour. It is used to access off-curve and on-curve points alike. Its cousin :class:`BPoint ` also provides access to incoming and outgoing bcps. :class:`Point ` is exclusively only one single point. 13 | 14 | :: 15 | 16 | glyph = CurrentGlyph() 17 | for contour in glyph: 18 | for point in contour.points: 19 | print(point) 20 | 21 | ******** 22 | Overview 23 | ******** 24 | 25 | Copy 26 | ==== 27 | 28 | .. autosummary:: 29 | :nosignatures: 30 | 31 | BasePoint.copy 32 | 33 | Parents 34 | ======= 35 | 36 | .. autosummary:: 37 | :nosignatures: 38 | 39 | BasePoint.contour 40 | BasePoint.glyph 41 | BasePoint.layer 42 | BasePoint.font 43 | 44 | Identification 45 | ============== 46 | 47 | .. autosummary:: 48 | :nosignatures: 49 | 50 | BasePoint.name 51 | BasePoint.identifier 52 | BasePoint.index 53 | 54 | Coordinate 55 | ========== 56 | 57 | .. autosummary:: 58 | :nosignatures: 59 | 60 | BasePoint.x 61 | BasePoint.y 62 | 63 | Type 64 | ==== 65 | 66 | .. autosummary:: 67 | :nosignatures: 68 | 69 | BasePoint.type 70 | BasePoint.smooth 71 | 72 | Transformations 73 | =============== 74 | 75 | .. autosummary:: 76 | :nosignatures: 77 | 78 | BasePoint.transformBy 79 | BasePoint.moveBy 80 | BasePoint.scaleBy 81 | BasePoint.rotateBy 82 | BasePoint.skewBy 83 | 84 | Normalization 85 | ============= 86 | 87 | .. autosummary:: 88 | :nosignatures: 89 | 90 | BasePoint.round 91 | 92 | Environment 93 | =========== 94 | 95 | .. autosummary:: 96 | :nosignatures: 97 | 98 | BasePoint.naked 99 | BasePoint.changed 100 | 101 | ********* 102 | Reference 103 | ********* 104 | 105 | .. autoclass:: BasePoint 106 | 107 | Copy 108 | ==== 109 | 110 | .. automethod:: BasePoint.copy 111 | 112 | Parents 113 | ======= 114 | 115 | .. autoattribute:: BasePoint.contour 116 | .. autoattribute:: BasePoint.glyph 117 | .. autoattribute:: BasePoint.layer 118 | .. autoattribute:: BasePoint.font 119 | 120 | Identification 121 | ============== 122 | 123 | .. autoattribute:: BasePoint.name 124 | .. autoattribute:: BasePoint.identifier 125 | .. autoattribute:: BasePoint.index 126 | 127 | Coordinate 128 | ========== 129 | 130 | .. autoattribute:: BasePoint.x 131 | .. autoattribute:: BasePoint.y 132 | 133 | Type 134 | ==== 135 | 136 | .. autoattribute:: BasePoint.type 137 | .. autoattribute:: BasePoint.smooth 138 | 139 | Transformations 140 | =============== 141 | 142 | .. automethod:: BasePoint.transformBy 143 | .. automethod:: BasePoint.moveBy 144 | .. automethod:: BasePoint.scaleBy 145 | .. automethod:: BasePoint.rotateBy 146 | .. automethod:: BasePoint.skewBy 147 | 148 | Normalization 149 | ============= 150 | 151 | .. automethod:: BasePoint.round 152 | 153 | Environment 154 | =========== 155 | 156 | .. automethod:: BasePoint.naked 157 | .. automethod:: BasePoint.changed 158 | -------------------------------------------------------------------------------- /documentation/source/objectref/objects/segment.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: python 2 | .. module:: fontParts.base 3 | 4 | ####### 5 | Segment 6 | ####### 7 | 8 | *********** 9 | Description 10 | *********** 11 | 12 | A :class:`Contour ` object is a list of segments. A :class:`Segment ` is a list of points with some special attributes and methods. 13 | 14 | ******** 15 | Overview 16 | ******** 17 | 18 | Parents 19 | ======= 20 | 21 | .. autosummary:: 22 | :nosignatures: 23 | 24 | BaseSegment.contour 25 | BaseSegment.glyph 26 | BaseSegment.layer 27 | BaseSegment.font 28 | 29 | Identification 30 | ============== 31 | 32 | .. autosummary:: 33 | :nosignatures: 34 | 35 | BaseSegment.index 36 | 37 | Attributes 38 | ========== 39 | 40 | .. autosummary:: 41 | :nosignatures: 42 | 43 | BaseSegment.type 44 | BaseSegment.smooth 45 | 46 | Points 47 | ====== 48 | 49 | .. autosummary:: 50 | :nosignatures: 51 | 52 | BaseSegment.points 53 | BaseSegment.onCurve 54 | BaseSegment.offCurve 55 | 56 | Transformations 57 | =============== 58 | 59 | .. autosummary:: 60 | :nosignatures: 61 | 62 | BaseSegment.transformBy 63 | BaseSegment.moveBy 64 | BaseSegment.scaleBy 65 | BaseSegment.rotateBy 66 | BaseSegment.skewBy 67 | 68 | Normalization 69 | ============= 70 | 71 | .. autosummary:: 72 | :nosignatures: 73 | 74 | BaseSegment.round 75 | 76 | Environment 77 | =========== 78 | 79 | .. autosummary:: 80 | :nosignatures: 81 | 82 | BaseSegment.naked 83 | BaseSegment.changed 84 | 85 | ********* 86 | Reference 87 | ********* 88 | 89 | .. autoclass:: BaseSegment 90 | 91 | Parents 92 | ======= 93 | 94 | .. autoattribute:: BaseSegment.contour 95 | .. autoattribute:: BaseSegment.glyph 96 | .. autoattribute:: BaseSegment.layer 97 | .. autoattribute:: BaseSegment.font 98 | 99 | Identification 100 | ============== 101 | 102 | .. autoattribute:: BaseSegment.index 103 | 104 | Attributes 105 | ========== 106 | 107 | .. autoattribute:: BaseSegment.type 108 | .. autoattribute:: BaseSegment.smooth 109 | 110 | Points 111 | ====== 112 | 113 | .. autoattribute:: BaseSegment.points 114 | .. autoattribute:: BaseSegment.onCurve 115 | .. autoattribute:: BaseSegment.offCurve 116 | 117 | Transformations 118 | =============== 119 | 120 | .. automethod:: BaseSegment.transformBy 121 | .. automethod:: BaseSegment.moveBy 122 | .. automethod:: BaseSegment.scaleBy 123 | .. automethod:: BaseSegment.rotateBy 124 | .. automethod:: BaseSegment.skewBy 125 | 126 | Normalization 127 | ============= 128 | 129 | .. automethod:: BaseSegment.round 130 | 131 | Environment 132 | =========== 133 | 134 | .. automethod:: BaseSegment.naked 135 | .. automethod:: BaseSegment.changed 136 | 137 | -------------------------------------------------------------------------------- /documentation/source/objectref/valuetypes/index.rst: -------------------------------------------------------------------------------- 1 | ################## 2 | Common Value Types 3 | ################## 4 | 5 | FontParts scripts are built on with objects that represent fonts, glyphs, contours and so on. The objects are obtained through :ref:`fontparts-world`. 6 | 7 | 8 | .. _fontparts-objects: 9 | 10 | FontParts uses some common value types. 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | :hidden: 15 | 16 | valuetypes 17 | 18 | .. _type-string: 19 | 20 | String 21 | ------ 22 | 23 | Unicode (unencoded) or string. Internally everything is a unicode string. 24 | 25 | 26 | .. _type-int-float: 27 | 28 | Integer/Float 29 | ------------- 30 | 31 | Integers and floats are interchangeable in FontParts (unless the specification states that only one is allowed). 32 | 33 | 34 | .. _type-coordinate: 35 | 36 | Coordinate 37 | ---------- 38 | 39 | An immutable iterable containing two :ref:`type-int-float` representing: 40 | 41 | #. x 42 | #. y 43 | 44 | 45 | .. _type-angle: 46 | 47 | Angle 48 | ----- 49 | 50 | XXX define the angle specifications here. Direction, degrees, etc. This will always be a float. 51 | 52 | 53 | .. _type-identifier: 54 | 55 | Identifier 56 | ---------- 57 | 58 | A :ref:`type-string` following the `UFO identifier conventions `_. 59 | 60 | 61 | .. _type-color: 62 | 63 | Color 64 | ----- 65 | 66 | An immutable iterable containing four :ref:`type-int-float` representing: 67 | 68 | #. red 69 | #. green 70 | #. blue 71 | #. alpha 72 | 73 | Values are from 0 to 1.0. 74 | 75 | 76 | .. _type-transformation: 77 | 78 | Transformation Matrix 79 | --------------------- 80 | 81 | An immutable iterable defining a 2x2 transformation plus offset (aka Affine transform). The default is ``(1, 0, 0, 1, 0, 0)``. 82 | 83 | 84 | .. _type-immutable-list: 85 | 86 | Immutable List 87 | -------------- 88 | 89 | This must be an immutable, ordered iterable like a ``tuple``. -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] 3 | 4 | [tool.setuptools_scm] 5 | write_to = 'Lib/fontParts/_version.py' 6 | write_to_template = '__version__ = "{version}"' -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | virtualenv>=15.0 3 | tox>=2.3 4 | unittest2>=1.1.0 5 | coverage>=4.5.4 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | FontTools[ufo,lxml,unicode]==4.55.2 2 | fontMath==0.9.4 3 | defcon[pens]==0.10.3 4 | booleanOperations==0.9.0 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [sdist] 2 | formats = zip 3 | 4 | [metadata] 5 | license_file = LICENSE 6 | 7 | [pycodestyle] 8 | max-line-length = 500 9 | 10 | [options] 11 | setup_requires = setuptools_scm==8.1.0 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | from setuptools import setup, find_packages 3 | 4 | with open('README.rst', 'r') as f: 5 | long_description = f.read() 6 | 7 | setup_params = dict( 8 | name='fontParts', 9 | description=("An API for interacting with the parts of fonts " 10 | "during the font development process."), 11 | author='Just van Rossum, Tal Leming, Erik van Blokland, Ben Kiel, others', 12 | author_email='info@robofab.com', 13 | maintainer="Just van Rossum, Tal Leming, Erik van Blokland, Ben Kiel", 14 | maintainer_email="info@robofab.com", 15 | url='http://github.com/robotools/fontParts', 16 | license="OpenSource, MIT", 17 | platforms=["Any"], 18 | long_description=long_description, 19 | package_dir={'': 'Lib'}, 20 | packages=find_packages('Lib'), 21 | include_package_data=True, 22 | use_scm_version={ 23 | "write_to": 'Lib/fontParts/_version.py', 24 | "write_to_template": '__version__ = "{version}"', 25 | }, 26 | setup_requires=['setuptools_scm'], 27 | install_requires=[ 28 | "FontTools[ufo,lxml,unicode]>=3.32.0", 29 | "fontMath>=0.4.8", 30 | "defcon[pens]>=0.6.0", 31 | "booleanOperations>=0.9.0", 32 | ], 33 | classifiers=[ 34 | "Development Status :: 4 - Beta", 35 | "Environment :: Console", 36 | "Environment :: Other Environment", 37 | "Intended Audience :: Developers", 38 | "Intended Audience :: End Users/Desktop", 39 | "License :: OSI Approved :: MIT License", 40 | "Natural Language :: English", 41 | "Operating System :: OS Independent", 42 | "Programming Language :: Python", 43 | "Topic :: Multimedia :: Graphics", 44 | "Topic :: Multimedia :: Graphics :: Graphics Conversion", 45 | "Topic :: Software Development :: Libraries", 46 | ], 47 | python_requires='>=3.8', 48 | zip_safe=True, 49 | ) 50 | 51 | 52 | if __name__ == "__main__": 53 | setup(**setup_params) 54 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | minversion = 3.0 8 | envlist = py3{8,9,10,11}-cov, htmlcov 9 | 10 | [testenv] 11 | deps = 12 | cov: coverage>=4.3 13 | -rrequirements.txt 14 | install_command = 15 | pip install -v {opts} {packages} 16 | commands = 17 | cov: coverage run --parallel-mode Lib/fontParts/fontshell/test.py {posargs} 18 | !cov: python Lib/fontParts/fontshell/test.py {posargs} 19 | 20 | [testenv:htmlcov] 21 | deps = 22 | coverage>=4.3 23 | skip_install = true 24 | commands = 25 | coverage combine 26 | coverage html 27 | 28 | [testenv:codecov] 29 | passenv = * 30 | basepython = {env:TOXPYTHON:python} 31 | deps = 32 | coverage>=4.3 33 | codecov 34 | skip_install = true 35 | ignore_outcome = true 36 | commands = 37 | coverage combine 38 | codecov --env TOXENV 39 | --------------------------------------------------------------------------------