meta: remove robofab files

This commit is contained in:
Adrien Tétar 2015-11-08 11:11:11 +01:00
parent 5b43ab5b42
commit e345fec96f
126 changed files with 26 additions and 21836 deletions

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
__pycache__/
build/
dist/
.DS_Store
*.egg-info
*.pyc

View File

@ -40,7 +40,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'RoboFab'
project = u'ufoLib'
copyright = u'2011, Tal Leming, Erik van Blokland, Just van Rossum'
# The version info for the project you're documenting, acts as replacement for
@ -164,7 +164,7 @@ html_static_path = ['_static']
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'RoboFabdoc'
htmlhelp_basename = 'ufoLib-doc'
# -- Options for LaTeX output --------------------------------------------------
@ -183,7 +183,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'RoboFab.tex', u'RoboFab Documentation',
('index', 'ufoLib.tex', u'ufoLib Documentation',
u'Tal Leming, Erik van Blokland, Just van Rossum', 'manual'),
]
@ -213,7 +213,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'robofab', u'RoboFab Documentation',
('index', 'ufoLib', u'ufoLib Documentation',
[u'Tal Leming, Erik van Blokland, Just van Rossum'], 1)
]
@ -227,8 +227,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'RoboFab', u'RoboFab Documentation', u'Tal Leming, Erik van Blokland, Just van Rossum',
'RoboFab', 'One line description of project.', 'Miscellaneous'),
('index', 'ufoLib', u'ufoLib Documentation', u'Tal Leming, Erik van Blokland, Just van Rossum',
'ufoLib', 'One line description of project.', 'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.

View File

@ -3,50 +3,6 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to the RoboFab documentation!
=====================================
Welcome to the RoboFab documentation. At the time of this writing, the UFO3 implementation is in full swing. We're using this opportunity to remove some cruft and clean up.
RoboFab
=======
Objects
^^^^^^^
The Robofab object model.
.. toctree::
:maxdepth: 1
objects/objectsBase
objects/objectsRF
Pens
^^^^
Pen objects for all sorts of use.
.. toctree::
:maxdepth: 1
pens/rfUFOPen
pens/digestPen
pens/mathPens
pens/filterPen
pens/adapterPens
pens/boundsPen
pens/marginPen
pens/angledMarginPen
Interface
^^^^^^^^^
.. toctree::
:maxdepth: 1
interface/dialogs
UFOLib
======
@ -62,11 +18,9 @@ Tools for accessing, validating and making UFO and glif.
ufoLib/converters
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,11 +0,0 @@
.. highlight:: python
=======
Dialogs
=======
The dialogs module has a couple of basic dialogs and alerts that allow simple interactions.
.. automodule:: robofab.interface.all.dialogs
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
===========
objectsBase
===========
.. automodule:: robofab.objects.objectsBase
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
=========
objectsRF
=========
.. automodule:: robofab.objects.objectsRF
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
===========
adapterPens
===========
.. automodule:: robofab.pens.adapterPens
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
=========
digestPen
=========
.. automodule:: robofab.pens.digestPen
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
=========
filterPen
=========
.. automodule:: robofab.pens.filterPen
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
========
mathPens
========
.. automodule:: robofab.pens.mathPens
:inherited-members:
:members:

View File

@ -1,9 +0,0 @@
.. highlight:: python
========
rfUFOPen
========
.. automodule:: robofab.pens.rfUFOPen
:inherited-members:
:members:

View File

@ -1,86 +0,0 @@
"""
ROBOFAB
RoboFab is a Python library with objects
that deal with data usually associated
with fonts and type design.
DEVELOPERS
RoboFab is developed and maintained by
Tal Leming
Erik van Blokland
Just van Rossum
(in no particular order)
MORE INFO
The RoboFab homepage, documentation etc.
http://robofab.com
SVN REPOSITORY
http://svn.robofab.com
TRAC
http://code.robofab.com
RoboFab License Agreement
Copyright (c) 2005-2012, The RoboFab Developers:
Erik van Blokland
Tal Leming
Just van Rossum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the The RoboFab Developers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Up to date info on RoboFab:
http://robofab.com/
This is the BSD license:
http://www.opensource.org/licenses/BSD-3-Clause
HISTORY
RoboFab starts somewhere during the
TypoTechnica in Heidelberg, 2003.
DEPENDENCIES
RoboFab expects fontTools to be installed.
http://sourceforge.net/projects/fonttools/
Some of the RoboFab modules require data files
that are included in the source directory.
RoboFab likes to be able to calculate paths
to these data files all by itself, so keep them
together with the source files.
QUOTES
Yuri Yarmola:
"If data is somehow available to other programs
via some standard data-exchange interface which
can be accessed by some library in Python, you
can make a Python script that uses that library
to apply data to a font opened in FontLab."
W.A. Dwiggins:
"You will understand that I am not trying to
short-circuit any of your shop operations in
sending drawings of this kind. The closer I can
get to the machine the better the result.
Subtleties of curves are important, as you know,
and if I can make drawings that can be used in
the large size I have got one step closer to the
machine that cuts the punches." [1932]
"""
class RoboFabError(Exception): pass
class RoboFabWarning(Warning): pass
numberVersion = (1, 3, "developer", 0)
version = "1.3.0"

View File

@ -1,11 +0,0 @@
"""
Directory for contributed packages.
Packages stored here can be imported from
robofab.contrib.<packagename>
"""

View File

@ -1,625 +0,0 @@
"""A bunch of stuff useful for glyph name comparisons and such.
1. A group of sorted glyph name lists that can be called directly:
2. Some tools to work with glyph names to do things like build control strings."""
import string
######################################################
# THE LISTS
######################################################
uppercase_plain = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AE', 'OE', 'Oslash', 'Thorn', 'Eth',]
uppercase_accents = ['Aacute', 'Abreve', 'Acaron', 'Acircumflex', 'Adblgrave', 'Adieresis', 'Agrave', 'Amacron', 'Aogonek', 'Aring', 'Aringacute', 'Atilde', 'Bdotaccent', 'Cacute', 'Ccaron', 'Ccircumflex', 'Cdotaccent', 'Dcaron', 'Dcedilla', 'Ddotaccent', 'Eacute', 'Ebreve', 'Ecaron', 'Ecircumflex', 'Edblgrave', 'Edieresis', 'Edotaccent', 'Egrave', 'Emacron', 'Eogonek', 'Etilde', 'Fdotaccent', 'Gacute', 'Gbreve', 'Gcaron', 'Gcedilla', 'Gcircumflex', 'Gcommaaccent', 'Gdotaccent', 'Gmacron', 'Hcedilla', 'Hcircumflex', 'Hdieresis', 'Hdotaccent', 'Iacute', 'Ibreve', 'Icaron', 'Icircumflex', 'Idblgrave', 'Idieresis', 'Idieresisacute', 'Idieresisacute', 'Idotaccent', 'Igrave', 'Imacron', 'Iogonek', 'Itilde', 'Jcircumflex', 'Kacute', 'Kcaron', 'Kcedilla', 'Kcommaaccent', 'Lacute', 'Lcaron', 'Lcedilla', 'Lcommaaccent', 'Ldotaccent', 'Macute', 'Mdotaccent', 'Nacute', 'Ncaron', 'Ncedilla', 'Ncommaaccent', 'Ndotaccent', 'Ntilde', 'Oacute', 'Obreve', 'Ocaron', 'Ocircumflex', 'Odblgrave', 'Odieresis', 'Ograve', 'Ohorn', 'Ohungarumlaut', 'Omacron', 'Oogonek', 'Otilde', 'Pacute', 'Pdotaccent', 'Racute', 'Rcaron', 'Rcedilla', 'Rcommaaccent', 'Rdblgrave', 'Rdotaccent', 'Sacute', 'Scaron', 'Scedilla', 'Scircumflex', 'Scommaaccent', 'Sdotaccent', 'Tcaron', 'Tcedilla', 'Tcommaaccent', 'Tdotaccent', 'Uacute', 'Ubreve', 'Ucaron', 'Ucircumflex', 'Udblgrave', 'Udieresis', 'Udieresisacute', 'Udieresisacute', 'Udieresisgrave', 'Udieresisgrave', 'Ugrave', 'Uhorn', 'Uhungarumlaut', 'Umacron', 'Uogonek', 'Uring', 'Utilde', 'Vtilde', 'Wacute', 'Wcircumflex', 'Wdieresis', 'Wdotaccent', 'Wgrave', 'Xdieresis', 'Xdotaccent', 'Yacute', 'Ycircumflex', 'Ydieresis', 'Ydotaccent', 'Ygrave', 'Ytilde', 'Zacute', 'Zcaron', 'Zcircumflex', 'Zdotaccent', 'AEacute', 'Ccedilla', 'Oslashacute', 'Ldot']
uppercase_special_accents = ['Dcroat', 'Lslash', 'Hbar', 'Tbar', 'LL', 'Eng']
uppercase_ligatures = ['IJ']
uppercase = uppercase_plain+uppercase_accents+uppercase_special_accents+uppercase_ligatures
lowercase_plain = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'dotlessi', 'dotlessj', 'ae', 'oe', 'oslash', 'thorn', 'eth', 'germandbls', 'longs',]
lowercase_accents = ['aacute', 'abreve', 'acaron', 'acircumflex', 'adblgrave', 'adieresis', 'agrave', 'amacron', 'aogonek', 'aring', 'aringacute', 'atilde', 'bdotaccent', 'cacute', 'ccaron', 'ccircumflex', 'cdotaccent', 'dcaron', 'dcedilla', 'ddotaccent', 'dmacron', 'eacute', 'ebreve', 'ecaron', 'ecircumflex', 'edblgrave', 'edieresis', 'edotaccent', 'egrave', 'emacron', 'eogonek', 'etilde', 'fdotaccent', 'gacute', 'gbreve', 'gcaron', 'gcedilla', 'gcircumflex', 'gcommaaccent', 'gdotaccent', 'gmacron', 'hcedilla', 'hcircumflex', 'hdieresis', 'hdotaccent', 'iacute', 'ibreve', 'icaron', 'icircumflex', 'idblgrave', 'idieresis', 'idieresisacute', 'idieresisacute', 'igrave', 'imacron', 'iogonek', 'itilde', 'jcaron', 'jcircumflex', 'kacute', 'kcaron', 'kcedilla', 'kcommaaccent', 'lacute', 'lcaron', 'lcedilla', 'lcommaaccent', 'ldotaccent', 'macute', 'mdotaccent', 'nacute', 'ncaron', 'ncedilla', 'ncommaaccent', 'ndotaccent', 'ntilde', 'oacute', 'obreve', 'ocaron', 'ocircumflex', 'odblgrave', 'odieresis', 'ograve', 'ohorn', 'ohungarumlaut', 'omacron', 'oogonek', 'otilde', 'pacute', 'pdotaccent', 'racute', 'rcaron', 'rcedilla', 'rcommaaccent', 'rdblgrave', 'rdotaccent', 'sacute', 'scaron', 'scedilla', 'scircumflex', 'scommaaccent', 'sdotaccent', 'tcaron', 'tcedilla', 'tcommaaccent', 'tdieresis', 'tdotaccent', 'uacute', 'ubreve', 'ucaron', 'ucircumflex', 'udblgrave', 'udieresis', 'udieresisacute', 'udieresisacute', 'udieresisgrave', 'udieresisgrave', 'ugrave', 'uhorn', 'uhungarumlaut', 'umacron', 'uogonek', 'uring', 'utilde', 'vtilde', 'wacute', 'wcircumflex', 'wdieresis', 'wdotaccent', 'wgrave', 'wring', 'xdieresis', 'xdotaccent', 'yacute', 'ycircumflex', 'ydieresis', 'ydotaccent', 'ygrave', 'yring', 'ytilde', 'zacute', 'zcaron', 'zcircumflex', 'zdotaccent', 'aeacute', 'ccedilla', 'oslashacute', 'ldot', ]
lowercase_special_accents = ['dcroat', 'lslash', 'hbar', 'tbar', 'kgreenlandic', 'longs', 'll', 'eng']
lowercase_ligatures = ['fi', 'fl', 'ff', 'ffi', 'ffl', 'ij']
lowercase = lowercase_plain+lowercase_accents+lowercase_special_accents+lowercase_ligatures
smallcaps_plain = ['A.sc', 'B.sc', 'C.sc', 'D.sc', 'E.sc', 'F.sc', 'G.sc', 'H.sc', 'I.sc', 'J.sc', 'K.sc', 'L.sc', 'M.sc', 'N.sc', 'O.sc', 'P.sc', 'Q.sc', 'R.sc', 'S.sc', 'T.sc', 'U.sc', 'V.sc', 'W.sc', 'X.sc', 'Y.sc', 'Z.sc', 'AE.sc', 'OE.sc', 'Oslash.sc', 'Thorn.sc', 'Eth.sc', ]
smallcaps_accents = ['Aacute.sc', 'Acircumflex.sc', 'Adieresis.sc', 'Agrave.sc', 'Aring.sc', 'Atilde.sc', 'Ccedilla.sc', 'Eacute.sc', 'Ecircumflex.sc', 'Edieresis.sc', 'Egrave.sc', 'Iacute.sc', 'Icircumflex.sc', 'Idieresis.sc', 'Igrave.sc', 'Ntilde.sc', 'Oacute.sc', 'Ocircumflex.sc', 'Odieresis.sc', 'Ograve.sc', 'Otilde.sc', 'Scaron.sc', 'Uacute.sc', 'Ucircumflex.sc', 'Udieresis.sc', 'Ugrave.sc', 'Yacute.sc', 'Ydieresis.sc', 'Zcaron.sc', 'Ccedilla.sc', 'Lslash.sc', ]
smallcaps_special_accents = ['Dcroat.sc', 'Lslash.sc', 'Hbar.sc', 'Tbar.sc', 'LL.sc', 'Eng.sc']
smallcaps_ligatures = ['IJ.sc']
smallcaps = smallcaps_plain + smallcaps_accents + smallcaps_special_accents + smallcaps_ligatures
all_accents = uppercase_accents + uppercase_special_accents + lowercase_accents +lowercase_special_accents + smallcaps_accents + smallcaps_special_accents
digits = ['one', 'onefitted', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'zero']
digits_oldstyle = ['eight.oldstyle', 'five.oldstyle', 'four.oldstyle', 'nine.oldstyle', 'one.oldstyle', 'seven.oldstyle', 'six.oldstyle', 'three.oldstyle', 'two.oldstyle', 'zero.oldstyle']
digits_superior = ['eight.superior', 'five.superior', 'four.superior', 'nine.superior', 'one.superior', 'seven.superior', 'six.superior', 'three.superior', 'two.superior', 'zero.superior']
digits_inferior = ['eight.inferior', 'five.inferior', 'four.inferior', 'nine.inferior', 'one.inferior', 'seven.inferior', 'three.inferior', 'two.inferior', 'zero.inferior']
fractions = ['oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onequarter', 'threequarters', 'onethird', 'twothirds', 'onehalf']
currency = ['dollar', 'cent', 'currency', 'Euro', 'sterling', 'yen', 'florin', 'franc', 'lira']
currency_oldstyle = ['cent.oldstyle', 'dollar.oldstyle']
currency_superior = ['cent.superior', 'dollar.superior']
currency_inferior = ['cent.inferior', 'dollar.inferior']
inferior = ['eight.inferior', 'five.inferior', 'four.inferior', 'nine.inferior', 'one.inferior', 'seven.inferior', 'three.inferior', 'two.inferior', 'zero.inferior', 'cent.inferior', 'dollar.inferior', 'comma.inferior', 'hyphen.inferior', 'parenleft.inferior', 'parenright.inferior', 'period.inferior']
superior = ['eight.superior', 'five.superior', 'four.superior', 'nine.superior', 'one.superior', 'seven.superior', 'six.superior', 'three.superior', 'two.superior', 'zero.superior', 'cent.superior', 'dollar.superior', 'Rsmallinverted.superior', 'a.superior', 'b.superior', 'comma.superior', 'd.superior', 'equal.superior', 'e.superior', 'glottalstopreversed.superior', 'hhook.superior', 'h.superior', 'hyphen.superior', 'i.superior', 'j.superior', 'l.superior', 'm.superior', 'n.superior', 'o.superior', 'parenleft.superior', 'parenright.superior', 'period.superior', 'plus.superior', 'r.superior', 'rturned.superior', 's.superior', 't.superior', 'w.superior', 'x.superior', 'y.superior']
accents = ['acute', 'acutecomb', 'breve', 'caron', 'cedilla', 'circumflex', 'commaaccent', 'dblgrave', 'dieresis', 'dieresisacute', 'dieresisacute', 'dieresisgrave', 'dieresisgrave', 'dotaccent', 'grave', 'dblgrave', 'gravecomb', 'hungarumlaut', 'macron', 'ogonek', 'ring', 'ringacute', 'tilde', 'tildecomb', 'horn', 'Acute.sc', 'Breve.sc', 'Caron.sc', 'Cedilla.sc', 'Circumflex.sc', 'Dieresis.sc', 'Dotaccent.sc', 'Grave.sc', 'Hungarumlaut.sc', 'Macron.sc', 'Ogonek.sc', 'Ring.sc', 'Tilde.sc']
dashes = ['hyphen', 'endash', 'emdash', 'threequartersemdash', 'underscore', 'underscoredbl', 'figuredash']
legal = ['trademark', 'trademarksans', 'trademarkserif', 'copyright', 'copyrightsans', 'copyrightserif', 'registered', 'registersans', 'registerserif']
ligatures = ['fi', 'fl', 'ff', 'ffi', 'ffl', 'ij', 'IJ']
punctuation = ['period', 'periodcentered', 'comma', 'colon', 'semicolon', 'ellipsis', 'exclam', 'exclamdown', 'exclamdbl', 'question', 'questiondown']
numerical = ['percent', 'perthousand', 'infinity', 'numbersign', 'degree', 'colonmonetary', 'dotmath']
slashes = ['slash', 'backslash', 'bar', 'brokenbar', 'fraction']
special = ['ampersand', 'paragraph', 'section', 'bullet', 'dagger', 'daggerdbl', 'asterisk', 'at', 'asciicircum', 'asciitilde']
dependencies = {
'A': ['Aacute', 'Abreve', 'Acaron', 'Acircumflex', 'Adblgrave', 'Adieresis', 'Agrave', 'Amacron', 'Aogonek', 'Aring', 'Aringacute', 'Atilde'],
'B': ['Bdotaccent'],
'C': ['Cacute', 'Ccaron', 'Ccircumflex', 'Cdotaccent', 'Ccedilla'],
'D': ['Dcaron', 'Dcedilla', 'Ddotaccent'],
'E': ['Eacute', 'Ebreve', 'Ecaron', 'Ecircumflex', 'Edblgrave', 'Edieresis', 'Edotaccent', 'Egrave', 'Emacron', 'Eogonek', 'Etilde'],
'F': ['Fdotaccent'],
'G': ['Gacute', 'Gbreve', 'Gcaron', 'Gcedilla', 'Gcircumflex', 'Gcommaaccent', 'Gdotaccent', 'Gmacron'],
'H': ['Hcedilla', 'Hcircumflex', 'Hdieresis', 'Hdotaccent'],
'I': ['Iacute', 'Ibreve', 'Icaron', 'Icircumflex', 'Idblgrave', 'Idieresis', 'Idieresisacute', 'Idieresisacute', 'Idotaccent', 'Igrave', 'Imacron', 'Iogonek', 'Itilde'],
'J': ['Jcircumflex'],
'K': ['Kacute', 'Kcaron', 'Kcedilla', 'Kcommaaccent'],
'L': ['Lacute', 'Lcaron', 'Lcedilla', 'Lcommaaccent', 'Ldotaccent', 'Ldot'],
'M': ['Macute', 'Mdotaccent'],
'N': ['Nacute', 'Ncaron', 'Ncedilla', 'Ncommaaccent', 'Ndotaccent', 'Ntilde'],
'O': ['Oacute', 'Obreve', 'Ocaron', 'Ocircumflex', 'Odblgrave', 'Odieresis', 'Ograve', 'Ohorn', 'Ohungarumlaut', 'Omacron', 'Oogonek', 'Otilde'],
'P': ['Pacute', 'Pdotaccent'],
'R': ['Racute', 'Rcaron', 'Rcedilla', 'Rcommaaccent', 'Rdblgrave', 'Rdotaccent'],
'S': ['Sacute', 'Scaron', 'Scedilla', 'Scircumflex', 'Scommaaccent', 'Sdotaccent'],
'T': ['Tcaron', 'Tcedilla', 'Tcommaaccent', 'Tdotaccent'],
'U': ['Uacute', 'Ubreve', 'Ucaron', 'Ucircumflex', 'Udblgrave', 'Udieresis', 'Udieresisacute', 'Udieresisacute', 'Udieresisgrave', 'Udieresisgrave', 'Ugrave', 'Uhorn', 'Uhungarumlaut', 'Umacron', 'Uogonek', 'Uring', 'Utilde'],
'V': ['Vtilde'],
'W': ['Wacute', 'Wcircumflex', 'Wdieresis', 'Wdotaccent', 'Wgrave'],
'X': ['Xdieresis', 'Xdotaccent'],
'Y': ['Yacute', 'Ycircumflex', 'Ydieresis', 'Ydotaccent', 'Ygrave', 'Ytilde'],
'Z': ['Zacute', 'Zcaron', 'Zcircumflex', 'Zdotaccent'],
'AE': ['AEacute'],
'Oslash': ['Oslashacute'],
'a': ['aacute', 'abreve', 'acaron', 'acircumflex', 'adblgrave', 'adieresis', 'agrave', 'amacron', 'aogonek', 'aring', 'aringacute', 'atilde'],
'b': ['bdotaccent'],
'c': ['cacute', 'ccaron', 'ccircumflex', 'cdotaccent', 'ccedilla'],
'd': ['dcaron', 'dcedilla', 'ddotaccent', 'dmacron'],
'e': ['eacute', 'ebreve', 'ecaron', 'ecircumflex', 'edblgrave', 'edieresis', 'edotaccent', 'egrave', 'emacron', 'eogonek', 'etilde'],
'f': ['fdotaccent'],
'g': ['gacute', 'gbreve', 'gcaron', 'gcedilla', 'gcircumflex', 'gcommaaccent', 'gdotaccent', 'gmacron'],
'h': ['hcedilla', 'hcircumflex', 'hdieresis', 'hdotaccent'],
'i': ['iacute', 'ibreve', 'icaron', 'icircumflex', 'idblgrave', 'idieresis', 'idieresisacute', 'idieresisacute', 'igrave', 'imacron', 'iogonek', 'itilde'],
'j': ['jcaron', 'jcircumflex'],
'k': ['kacute', 'kcaron', 'kcedilla', 'kcommaaccent'],
'l': ['lacute', 'lcaron', 'lcedilla', 'lcommaaccent', 'ldotaccent', 'ldot'],
'm': ['macute', 'mdotaccent'],
'n': ['nacute', 'ncaron', 'ncedilla', 'ncommaaccent', 'ndotaccent', 'ntilde'],
'o': ['oacute', 'obreve', 'ocaron', 'ocircumflex', 'odblgrave', 'odieresis', 'ograve', 'ohorn', 'ohungarumlaut', 'omacron', 'oogonek', 'otilde'],
'p': ['pacute', 'pdotaccent'],
'r': ['racute', 'rcaron', 'rcedilla', 'rcommaaccent', 'rdblgrave', 'rdotaccent'],
's': ['sacute', 'scaron', 'scedilla', 'scircumflex', 'scommaaccent', 'sdotaccent'],
't': ['tcaron', 'tcedilla', 'tcommaaccent', 'tdieresis', 'tdotaccent'],
'u': ['uacute', 'ubreve', 'ucaron', 'ucircumflex', 'udblgrave', 'udieresis', 'udieresisacute', 'udieresisacute', 'udieresisgrave', 'udieresisgrave', 'ugrave', 'uhorn', 'uhungarumlaut', 'umacron', 'uogonek', 'uring', 'utilde'],
'v': ['vtilde'],
'w': ['wacute', 'wcircumflex', 'wdieresis', 'wdotaccent', 'wgrave', 'wring'],
'x': ['xdieresis', 'xdotaccent'],
'y': ['yacute', 'ycircumflex', 'ydieresis', 'ydotaccent', 'ygrave', 'yring', 'ytilde'],
'z': ['zacute', 'zcaron', 'zcircumflex', 'zdotaccent'],
'ae': ['aeacute'],
'oslash': ['oslashacute'],
}
######################################################
# MISC TOOLS
######################################################
def breakSuffix(glyphname):
"""
Breaks the glyphname into a two item list
0: glyphname
1: suffix
if a suffix is not found it returns None
"""
if glyphname.find('.') != -1:
split = glyphname.split('.')
return split
else:
return None
def findAccentBase(accentglyph):
"""Return the base glyph of an accented glyph
for example: Ugrave.sc returns U.sc"""
base = splitAccent(accentglyph)[0]
return base
def splitAccent(accentglyph):
"""
Split an accented glyph into a two items
0: base glyph
1: accent list
for example: Yacute.scalt45 returns: (Y.scalt45, [acute])
and: aacutetilde.alt45 returns (a.alt45, [acute, tilde])
"""
base = None
suffix = ''
accentList=[]
broken = breakSuffix(accentglyph)
if broken is not None:
suffix = broken[1]
base = broken[0]
else:
base=accentglyph
ogbase=base
temp_special = lowercase_special_accents + uppercase_special_accents
if base in lowercase_plain + uppercase_plain + smallcaps_plain:
pass
elif base not in temp_special:
for accent in accents:
if base.find(accent) != -1:
base = base.replace(accent, '')
accentList.append(accent)
counter={}
for accent in accentList:
counter[ogbase.find(accent)] = accent
counterList = counter.keys()
counterList.sort()
finalAccents = []
for i in counterList:
finalAccents.append(counter[i])
accentList = finalAccents
if len(suffix) != 0:
base = '.'.join([base, suffix])
return base, accentList
######################################################
# UPPER, LOWER, SMALL
######################################################
casedict = {
'germandbls' : 'S/S',
'dotlessi' : 'I',
'dotlessj' : 'J',
'ae' : 'AE',
'aeacute' : 'AEacute',
'oe' : 'OE',
'll' : 'LL'
}
casedictflip = {}
smallcapscasedict = {
'germandbls' : 'S.sc/S.sc',
'question' : 'question.sc',
'questiondown' : 'questiondown.sc',
'exclam' : 'exclam.sc',
'exclamdown' : 'exclamdown.sc',
'ampersand' : 'ampersand.sc'
}
class _InternalCaseFunctions:
"""internal functions for doing gymnastics with the casedicts"""
def expandsmallcapscasedict(self):
for i in casedict.values():
if i not in smallcapscasedict.keys():
if len(i) > 1:
if i[:1].upper() == i[:1]:
smallcapscasedict[i] = i[:1] + i[1:] + '.sc'
for i in uppercase:
if i + '.sc' in smallcaps:
if i not in smallcapscasedict.keys():
smallcapscasedict[i] = i + '.sc'
def flipcasedict(self):
for i in casedict.keys():
if i.find('dotless') != -1:
i = i.replace('dotless', '')
casedictflip[casedict[i]] = i
def expandcasedict(self):
for i in lowercase_ligatures:
casedict[i] = i.upper()
for i in lowercase:
if i not in casedict.keys():
if string.capitalize(i) in uppercase:
casedict[i] = string.capitalize(i)
def upper(glyphstring):
"""Convert all possible characters to uppercase in a glyph string."""
_InternalCaseFunctions().expandcasedict()
uc = []
for i in glyphstring.split('/'):
if i.find('.sc') != -1:
if i[-3] != '.sc':
x = i.replace('.sc', '.')
else:
x = i.replace('.sc', '')
i = x
suffix = ''
bS = breakSuffix(i)
if bS is not None:
suffix = bS[1]
i = bS[0]
if i in casedict.keys():
i = casedict[i]
if len(suffix) != 0:
i = '.'.join([i, suffix])
uc.append(i)
return '/'.join(uc)
def lower(glyphstring):
"""Convert all possible characters to lowercase in a glyph string."""
_InternalCaseFunctions().expandcasedict()
_InternalCaseFunctions().flipcasedict()
lc = []
for i in glyphstring.split('/'):
if i.find('.sc') != -1:
if i[-3] != '.sc':
x = i.replace('.sc', '.')
else:
x = i.replace('.sc', '')
i = x
suffix = ''
bS = breakSuffix(i)
if breakSuffix(i) is not None:
suffix = bS[1]
i = bS[0]
if i in casedictflip.keys():
i = casedictflip[i]
if len(suffix) != 0:
i = '.'.join([i, suffix])
lc.append(i)
return '/'.join(lc)
def small(glyphstring):
"""Convert all possible characters to smallcaps in a glyph string."""
_InternalCaseFunctions().expandcasedict()
_InternalCaseFunctions().expandsmallcapscasedict()
sc = []
for i in glyphstring.split('/'):
suffix = ''
bS = breakSuffix(i)
if bS is not None:
suffix = bS[1]
if suffix == 'sc':
suffix = ''
i = bS[0]
if i in lowercase:
if i not in smallcapscasedict.keys():
i = casedict[i]
if i in smallcapscasedict.keys():
i = smallcapscasedict[i]
if i != 'S.sc/S.sc':
if len(suffix) != 0:
if i[-3:] == '.sc':
i = ''.join([i, suffix])
else:
i = '.'.join([i, suffix])
sc.append(i)
return '/'.join(sc)
######################################################
# CONTROL STRING TOOLS
######################################################
controldict = {
'UC' : ['/H/H', '/H/O/H/O', '/O/O'],
'LC' : ['/n/n', '/n/o/n/o', '/o/o'],
'SC' : ['/H.sc/H.sc', '/H.sc/O.sc/H.sc/O.sc', '/O.sc/O.sc'],
'DIGITS' : ['/one/one', '/one/zero/one/zero', '/zero/zero'],
}
def controls(glyphname):
"""Send this a glyph name and get a control string
with all glyphs separated by slashes."""
controlslist = []
for value in controldict.values():
for v in value:
for i in v.split('/'):
if len(i) > 0:
if i not in controlslist:
controlslist.append(i)
cs = ''
if glyphname in controlslist:
for key in controldict.keys():
for v in controldict[key]:
if glyphname in v.split('/'):
con = controldict[key]
striptriple = []
hold1 = ''
hold2 = ''
for i in ''.join(con).split('/'):
if len(i) != 0:
if i == hold1 and i == hold2:
pass
else:
striptriple.append(i)
hold1 = hold2
hold2 = i
constr = '/' + '/'.join(striptriple)
# this is a bit of a hack since FL seems to have trouble
# when it encounters the same string more than once.
# so, let's stick the glyph at the end to differentiate it.
# for example: HHOHOOH and HHOHOOO
cs = constr + '/' + glyphname
else:
suffix = ''
bS = breakSuffix(glyphname)
if bS is not None:
suffix = bS[1]
glyphname = bS[0]
if suffix[:2] == 'sc':
controls = controldict['SC']
elif glyphname in uppercase:
controls = controldict['UC']
elif glyphname in lowercase:
controls = controldict['LC']
elif glyphname in digits:
controls = controldict['DIGITS']
else:
controls = controldict['UC']
if len(suffix) != 0:
glyphname = '.'.join([glyphname, suffix])
cs = controls[0] + '/' + glyphname + controls[1] + '/' + glyphname + controls[2]
return cs
def sortControlList(list):
"""Roughly sort a list of control strings."""
controls = []
for v in controldict.values():
for w in v:
for x in w.split('/'):
if len(x) is not None:
if x not in controls:
controls.append(x)
temp_digits = digits + digits_oldstyle + fractions
temp_currency = currency + currency_oldstyle
ss_uppercase = []
ss_lowercase = []
ss_smallcaps = []
ss_digits = []
ss_currency = []
ss_other = []
for i in list:
glyphs = i.split('/')
c = glyphs[2]
for glyph in glyphs:
if len(glyph) is not None:
if glyph not in controls:
c = glyph
if c in uppercase:
ss_uppercase.append(i)
elif c in lowercase:
ss_lowercase.append(i)
elif c in smallcaps:
ss_smallcaps.append(i)
elif c in temp_digits:
ss_digits.append(i)
elif c in temp_currency:
ss_currency.append(i)
else:
ss_other.append(i)
ss_uppercase.sort()
ss_lowercase.sort()
ss_smallcaps.sort()
ss_digits.sort()
ss_currency.sort()
ss_other.sort()
return ss_uppercase + ss_lowercase + ss_smallcaps + ss_digits + ss_currency + ss_other
# under contruction!
kerncontroldict = {
'UC/UC' : ['/H/H', '/H/O/H/O/O'],
'UC/LC' : ['', '/n/n/o/n/e/r/s'],
'UC/SORTS' : ['/H/H', '/H/O/H/O/O'],
'UC/DIGITS' : ['/H/H', '/H/O/H/O/O'],
'LC/LC' : ['/n/n', '/n/o/n/o/o'],
'LC/SORTS' : ['/n/n', '/n/o/n/o/o'],
'LC/DIGITS' : ['', '/n/n/o/n/e/r/s'],
'SC/SC' : ['/H.sc/H.sc', '/H.sc/O.sc/H.sc/O.sc/O.sc'],
'UC/SC' : ['', '/H.sc/H.sc/O.sc/H.sc/O.sc/O.sc'],
'SC/SORTS' : ['/H.sc/H.sc', '/H.sc/O.sc/H.sc/O.sc/O.sc'],
'SC/DIGITS' : ['', '/H.sc/H.sc/O.sc/H.sc/O.sc/O.sc'],
'DIGITS/DIGITS' : ['/H/H', '/H/O/H/O/O'],
'DIGITS/SORTS' : ['/H/H', '/H/O/H/O/O'],
'SORTS/SORTS' : ['/H/H', '/H/O/H/O/O'],
}
def kernControls(leftglyphname, rightglyphname):
"""build a control string based on the left glyph and right glyph"""
sorts = currency + accents + dashes + legal + numerical + slashes + special
l = leftglyphname
r = rightglyphname
lSuffix = ''
rSuffix = ''
bSL = breakSuffix(l)
if bSL is not None:
lSuffix = bSL[1]
l = bSL[0]
bSR = breakSuffix(r)
if bSR is not None:
rSuffix = bSR[1]
r = bSR[0]
if lSuffix[:2] == 'sc' or rSuffix[:2] == 'sc':
if l in uppercase or r in uppercase:
controls = kerncontroldict['UC/SC']
elif l in digits or r in digits:
controls = kerncontroldict['SC/DIGITS']
elif l in sorts or r in sorts:
controls = kerncontroldict['SC/SORTS']
else:
controls = kerncontroldict['SC/SC']
elif l in uppercase or r in uppercase:
if l in lowercase or r in lowercase:
controls = kerncontroldict['UC/LC']
elif l in digits or r in digits:
controls = kerncontroldict['UC/DIGITS']
elif l in sorts or r in sorts:
controls = kerncontroldict['UC/SORTS']
else:
controls = kerncontroldict['UC/UC']
elif l in lowercase or r in lowercase:
if l in uppercase or r in uppercase:
controls = kerncontroldict['UC/LC']
elif l in digits or r in digits:
controls = kerncontroldict['LC/DIGITS']
elif l in sorts or r in sorts:
controls = kerncontroldict['LC/SORTS']
else:
controls = kerncontroldict['LC/LC']
elif l in digits or r in digits:
if l in uppercase or r in uppercase:
controls = kerncontroldict['UC/DIGITS']
elif l in lowercase or r in lowercase:
controls = kerncontroldict['LC/DIGITS']
elif l in sorts or r in sorts:
controls = kerncontroldict['DIGITS/SORTS']
else:
controls = kerncontroldict['DIGITS/DIGITS']
elif l in sorts and r in sorts:
controls = kerncontroldict['SORTS/SORTS']
else:
controls = kerncontroldict['UC/UC']
if len(lSuffix) != 0:
l = '.'.join([l, lSuffix])
if len(rSuffix) != 0:
r = '.'.join([r, rSuffix])
cs = controls[0] + '/' + l + '/' + r + controls[1]
return cs
######################################################
class _testing:
def __init__(self):
print
print '##### testing!'
# self.listtest()
# self.accentbasetest()
# self.controlstest()
self.upperlowersmalltest()
# self.stringsorttest()
def listtest(self):
testlist = [
uppercase,
uppercase_accents,
lowercase,
lowercase_accents,
smallcaps,
smallcaps_accents,
digits,
digits_oldstyle,
digits_superior,
digits_inferior,
fractions,
currency,
currency_oldstyle,
currency_superior,
currency_inferior,
inferior,
superior,
accents,
dashes,
legal,
ligatures,
punctuation,
numerical,
slashes,
special
]
for i in testlist:
print i
def accentbasetest(self):
print findAccentBase('Adieresis')
print findAccentBase('Adieresis.sc')
print findAccentBase('Thorn.sc')
print findAccentBase('notaralglyphname')
def controlstest(self):
print kernControls('A', 'a.swash')
print kernControls('A.sc', '1')
print kernControls('bracket.sc', 'germandbls')
print kernControls('2', 'X')
print kernControls('Y', 'X')
print kernControls('Y.alt', 'X')
print kernControls('Y.scalt', 'X')
#print controls('x')
#print controls('germandbls')
#print controls('L')
#print controls('L.sc')
#print controls('Z.sc')
#print controls('seven')
#print controls('question')
#print controls('unknown')
def upperlowersmalltest(self):
u = upper('/H/i/Z.sc/ampersand.sc/dotlessi/germandbls/four.superior/LL')
l = lower('/H/I/Z.sc/ampersand.sc/dotlessi/germandbls/four.superior/LL')
s = small('/H/i/Z.sc/ampersand.alt/dotlessi/germandbls/four.superior/LL')
print u
print l
print s
print lower(u)
print upper(l)
print upper(s)
print lower(s)
def stringsorttest(self):
sample = "/H/H/Euro/H/O/H/O/Euro/O/O /H/H/R/H/O/H/O/R/O/O /H/H/question/H/O/H/O/question/O/O /H/H/sterling/H/O/H/O/sterling/O/O /n/n/r/n/o/n/o/r/o/o"
list = string.split(sample, ' ')
x = sortControlList(list)
print x
if __name__ == '__main__':
_testing()

View File

@ -1,14 +0,0 @@
"""
Directory for interface related modules. Stuff like widgets,
dialog modules. Please keep them sorted by platform.
interfaces/all : modules that are platform independent
interfaces/mac : modules that are mac specific
interfaces/win : modules that are windows specific
"""

View File

@ -1,14 +0,0 @@
"""
Directory for interface related modules. Stuff like widgets,
dialog modules. Please keep them sorted by platform.
interfaces/all : modules that are platform independent
interfaces/mac : modules that are mac specific
interfaces/win : modules that are windows specific
"""

View File

@ -1,240 +0,0 @@
"""
Restructured dialogs for Robofab
dialog file dialogs
* FontLab 5.04 10.6 dialogKit fl internal * theoretically that should work under windows as well, untested
* FontLab 5.1 10.6 dialogKit fl internal
* FontLab 5.1 10.7 raw cocao fl internal
Glyphs any vanilla vanilla
RoboFont any vanilla vanilla
This module does a fair amount of sniffing in order to guess
which dialogs to load. Linux and Windows environments are
underrepresented at the moment. Following the prototypes in dialogs_default.py
it is possible (with knowledge of the platform) to extend support.
The platformApplicationSupport table contains very specific
versions with which certain apps need to work. Moving forward,
it is likely that these versions will change and need to be updated.
# this calls the new dialogs infrastructure:
from robofab.interface.all.dialogs import Message
# this calls the old original legacy dialogs infrastructure:
from robofab.interface.all.dialogs_legacy import Message
"""
# determine platform and application
import sys, os
import platform as _platform
__verbose__ = False
platform = None
platformVersion = None
application = None
applicationVersion = None
if sys.platform in (
'mac',
'darwin',
):
platform = "mac"
v = _platform.mac_ver()[0]
platformVersion = float('.'.join(v.split('.')[:2]))
elif sys.platform in (
'linux1',
'linux2', # Ubuntu = others?
):
platform = "linux"
elif os.name == 'nt':
platform = "win"
# determine application
try:
# FontLab
# alternative syntax to cheat on the FL import filtering in RF
__import__("FL")
application = "fontlab"
#applicationVersion = fl.version
except ImportError:
pass
if application is None:
try:
# RoboFont
import mojo
application = 'robofont'
try:
from AppKit import NSBundle
b = NSBundle.mainBundle()
applicationVersion = b.infoDictionary()["CFBundleVersion"]
except ImportError:
pass
except ImportError:
pass
if application is None:
try:
# Glyphs
import GlyphsApp
application = "glyphs"
except ImportError:
pass
if application is None:
try:
# fontforge
# note that in some configurations, fontforge can be imported in other pythons as well
# so the availability of the fontforge module is no garuantee that we are in fontforge.
import fontforge
application = "fontforge"
except ImportError:
pass
pyVersion = sys.version_info[:3]
# with that out of the way, perhaps we can have a look at where we are
# and which modules we have available. This maps any number of platform / application
# combinations so an independent list of module names. That should make it
# possible to map multiple things to one module.
platformApplicationSupport = [
#
# switchboard for platform, application, python version -> dialog implementations
# platform applicatiom python sub module
# | | | |
('mac', 'fontlab', (2,3,5), "dialogs_fontlab_legacy1"),
# because FontLab 5.01 and earlier on 2.3.5 can run EasyDialogs
# | | | |
#('mac', 'fontlab', (2,5,4), "dialogs_fontlab_legacy1"),
('mac', 'fontlab', (2,5,4), "dialogs_fontlab_legacy2"),
# because FontLab 5.1 on mac 10.6 should theoretically be able to run cocoa dialogs,
# but they are very unreliable. So until we know what's going on, FL5.1 on 10.6
# is going to have to live with DialogKit dialogs.
# | | | |
('mac', 'fontlab', None, "dialogs_fontlab_legacy2"),
# because FontLab 5.1 on mac, 10.7+ should run cocoa / vanilla
# | | | |
('mac', None, None, "dialogs_mac_vanilla"),
# perhaps nonelab scripts can run vanilla as well?
# | | | |
('win', None, None, "dialogs_legacy"),
# older windows stuff might be able to use the legacy dialogs
]
platformModule = None
foundPlatformModule = False
dialogs = {}
if __verbose__:
print "robofab.interface.all __init__ - finding out where we were."
# do we have a support module?
for pl, app, py, platformApplicationModuleName in platformApplicationSupport:
if __verbose__:
print "looking at", pl, app, py, platformApplicationModuleName
if pl is None or pl == platform:
if app is None or app == application:
if py is None or py == pyVersion:
break
if __verbose__:
print "nope"
if __verbose__:
print "searched for", pl, app, py, platformApplicationModuleName
# preload the namespace with default functions that do nothing but raise NotImplementedError
from robofab.interface.all.dialogs_default import *
# now import the module we selected.
if platformApplicationModuleName == "dialogs_fontlab_legacy1":
try:
from robofab.interface.all.dialogs_fontlab_legacy1 import *
foundPlatformModule = True
if __verbose__:
print "loaded robofab.interface.all.dialogs_fontlab_legacy1"
if platform == "mac":
from robofab.interface.mac.getFileOrFolder import GetFile, GetFileOrFolder
except ImportError:
print "can't import", platformApplicationModuleName
elif platformApplicationModuleName == "dialogs_fontlab_legacy2":
try:
from robofab.interface.all.dialogs_fontlab_legacy2 import *
foundPlatformModule = True
if __verbose__:
print "loaded robofab.interface.all.dialogs_fontlab_legacy2"
if platform == "mac":
from robofab.interface.all.dialogs_fontlab_legacy2 import AskString
except ImportError:
print "can't import", platformApplicationModuleName
elif platformApplicationModuleName == "dialogs_mac_vanilla":
try:
from robofab.interface.all.dialogs_mac_vanilla import *
foundPlatformModule = True
if __verbose__:
print "loaded robofab.interface.all.dialogs_mac_vanilla"
except ImportError:
print "can't import", platformApplicationModuleName
elif platformApplicationModuleName == "dialogs_legacy":
try:
from robofab.interface.all.dialogs_legacy import *
foundPlatformModule = True
if __verbose__:
print "loaded robofab.interface.all.dialogs_legacy"
except ImportError:
print "can't import", platformApplicationModuleName
__all__ = [
"AskString",
"AskYesNoCancel",
"FindGlyph",
"GetFile",
"GetFolder",
"GetFileOrFolder",
"Message",
"OneList",
"PutFile",
"SearchList",
"SelectFont",
"SelectGlyph",
"TwoChecks",
"TwoFields",
"ProgressBar",
]
def test():
"""This prints the basic parameters of the environment we're in, and the code we've loaded."""
print
print "testing RoboFab Dialogs:"
print "\tpython version:", pyVersion
print "\tplatform:", platform
print "\tapplication:", application
print "\tapplicationVersion:", applicationVersion
print "\tplatformVersion:", platformVersion
print "\tlooking for module:", platformApplicationModuleName
print "\t\tdid we find it?", foundPlatformModule
print
print "Available dialogs and source:"
for name in __all__:
if name in globals().keys():
print "\t", name, "\t", globals()[name].__module__
else:
print "\t", name, "\t not loaded."
if __name__ == "__main__":
test()

View File

@ -1,81 +0,0 @@
"""
Dialog prototypes.
These are loaded before any others. So if a specific platform implementation doesn't
have all functions, these will make sure a NotImplemtedError is raised.
"""
__all__ = [
"AskString",
"AskYesNoCancel",
"FindGlyph",
"GetFile",
"GetFolder",
"GetFileOrFolder",
"Message",
"OneList",
"PutFile",
"SearchList",
"SelectFont",
"SelectGlyph",
"TwoChecks",
"TwoFields",
"ProgressBar",
]
# start with all the defaults.
def AskString(message, value='', title='RoboFab'):
"""Prototype for AskString dialog. Should show a prompt, a text input box and OK button."""
raise NotImplementedError
def AskYesNoCancel(message, title='RoboFab', default=0):
"""Prototype for AskYesNoCancel dialog. Should show a prompt, Yes, No, Cancel buttons."""
raise NotImplementedError
def FindGlyph(font, message="Search for a glyph:", title='RoboFab'):
"""Prototype for FindGlyph dialog. Should show a list of glyph names of the current font, OK and Cancel buttons"""
raise NotImplementedError
def GetFile(message=None):
"""Prototype for GetFile dialog. Should offer a standard OS get file dialog."""
raise NotImplementedError
def GetFolder(message=None):
"""Prototype for GetFolder dialog. Should offer a standard OS get folder dialog."""
raise NotImplementedError
def GetFileOrFolder(message=None):
raise NotImplementedError
def Message(message, title='RoboFab'):
"""Prototype for Message dialog. Should offer a window with the message, OK button."""
raise NotImplementedError
def OneList(list, message="Select an item:", title='RoboFab'):
"""Prototype for OneList dialog."""
raise NotImplementedError
def PutFile(message=None, fileName=None):
raise NotImplementedError
def SearchList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
def SelectFont(message="Select a font:", title='RoboFab'):
raise NotImplementedError
def SelectGlyph(font, message="Select a glyph:", title='RoboFab'):
raise NotImplementedError
def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'):
raise PendingDeprecationWarning
def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'):
raise PendingDeprecationWarning
class ProgressBar(object):
pass

View File

@ -1,155 +0,0 @@
"""
Dialogs for FontLab < 5.1.
This one should be loaded for various platforms, using dialogKit
http://www.robofab.org/tools/dialogs.html
"""
from FL import *
from dialogKit import ModalDialog, Button, TextBox, EditText
__all__ = [
"AskString",
"AskYesNoCancel",
#"FindGlyph",
"GetFile",
"GetFolder",
"Message",
"OneList",
"PutFile",
#"SearchList",
#"SelectFont",
#"SelectGlyph",
#"TwoChecks",
#"TwoFields",
"ProgressBar",
]
def AskString(message, value='', title='RoboFab'):
dialogInstance = _AskStringDialog(message, value, title)
return dialogInstance.getValue()
class _AskStringDialog(object):
def __init__(self, message, value, title):
self.w = ModalDialog((360, 140), title)
self.w.button1 = Button((-100, -300, 80, 24), 'OK', callback=self.buttonCallback)
self.w.t = TextBox((5, 10, -5, 27), message)
self.w.inputValue = EditText((5, 35, -5, 50), value)
self.w.open()
def getValue(self):
return self.w.inputValue.get()
def buttonCallback(self, sender):
self.w.close()
def AskYesNoCancel(message, title='RoboFab', default=1, informativeText=None):
dialogInstance = _AskYesNoCancelDialog(message, title=title, default=default)
result = dialogInstance.getValue()
if result is None and default is not None:
return default
return result
class _AskYesNoCancelDialog(object):
def __init__(self, message, default=None, title="RoboFab"):
# default is ignord?
self.answer = -1
self.w = ModalDialog((360, 140), title, okCallback=self.buttonOKCallback)
self.w.noButton = Button((10, -35, 80, 24), 'No', callback=self.buttonNoCallback)
self.w.t = TextBox((5, 10, -5, 27), message)
self.w.open()
def getValue(self):
return self.answer
def buttonNoCallback(self, sender):
self.answer = 0
self.w.close()
def buttonOKCallback(self, sender):
self.answer = 1
self.w.close()
def FindGlyph(aFont, message="Search for a glyph:", title='RoboFab'):
raise NotImplementedError
def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
strFilter = "All Files (*.*)|*.*|"
defaultExt = ""
# using fontlab's internal file dialogs
return fl.GetFileName(1, defaultExt, message, strFilter)
def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False):
# using fontlab's internal file dialogs
if message is None:
message = ""
return fl.GetPathName(message)
def Message(message, title='RoboFab', informativeText=None):
""" Display a standard message dialog.
Note: informativeText is not supported on this platform.
"""
if informativeText is not None:
message = "%s\n%s"%(message, informativeText)
_MessageDialog(message, title)
class _MessageDialog(object):
def __init__(self, message, title):
self.w = ModalDialog((360, 100), title)
self.w.t = TextBox((5, 10, -5, -40), message)
self.w.open()
def PutFile(message=None, fileName=None):
# using fontlab's internal file dialogs
# message is not used
if message is None:
message = ""
if fileName is None:
fileName = ""
defaultExt = ""
return fl.GetFileName(0, defaultExt, fileName, '')
def OneList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
def SearchList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
def SelectFont(message="Select a font:", title='RoboFab'):
raise NotImplementedError
def SelectGlyph(font, message="Select a glyph:", title='RoboFab'):
raise NotImplementedError
def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'):
raise NotImplementedError
def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'):
raise NotImplementedError
class ProgressBar(object):
def __init__(self, title="RoboFab...", ticks=0, label=""):
self._tickValue = 1
fl.BeginProgress(title, ticks)
def getCurrentTick(self):
return self._tickValue
def tick(self, tickValue=None):
if not tickValue:
tickValue = self._tickValue
fl.TickProgress(tickValue)
self._tickValue = tickValue + 1
def label(self, label):
pass
def close(self):
fl.EndProgress()

View File

@ -1,380 +0,0 @@
"""
Dialogs for FontLab 5.1.
This might work in future versions of FontLab as well.
This is basically a butchered version of vanilla.dialogs.
No direct import of, or dependency on Vanilla
"""
#__import__("FL")
from FL import *
#from dialogKit import ModalDialog
#import objc
#from objc import selector
from Foundation import NSObject
from AppKit import *
# is this the magic that will make this work in FL 5.1
# however, what are the side effects?
NSApplication.sharedApplication()
__all__ = [
# "AskString",
"AskYesNoCancel",
# "FindGlyph",
"GetFile",
"GetFolder",
"GetFileOrFolder",
"Message",
# "OneList",
"PutFile",
# "SearchList",
# "SelectFont",
# "SelectGlyph",
# "TwoChecks",
# "TwoFields",
"ProgressBar",
]
class BaseMessageDialog(NSObject):
def initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_(self,
messageText="",
informativeText="",
alertStyle=NSInformationalAlertStyle,
buttonTitlesValues=None,
parentWindow=None,
resultCallback=None):
if buttonTitlesValues is None:
buttonTitlesValues = []
self = super(BaseMessageDialog, self).init()
self.retain()
self._resultCallback = resultCallback
self._buttonTitlesValues = buttonTitlesValues
#
alert = NSAlert.alloc().init()
alert.setMessageText_(messageText)
alert.setInformativeText_(informativeText)
alert.setAlertStyle_(alertStyle)
for buttonTitle, value in buttonTitlesValues:
alert.addButtonWithTitle_(buttonTitle)
self._value = None
code = alert.runModal()
self._translateValue(code)
return self
def _translateValue(self, code):
if code == NSAlertFirstButtonReturn:
value = 1
elif code == NSAlertSecondButtonReturn:
value = 2
elif code == NSAlertThirdButtonReturn:
value = 3
else:
value = code - NSAlertThirdButtonReturn + 3
self._value = self._buttonTitlesValues[value-1][1]
def windowWillClose_(self, notification):
self.autorelease()
class BasePutGetPanel(NSObject):
def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None):
self = super(BasePutGetPanel, self).init()
self.retain()
self._parentWindow = parentWindow
self._resultCallback = resultCallback
return self
def windowWillClose_(self, notification):
self.autorelease()
class PutFilePanel(BasePutGetPanel):
def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None):
self = super(PutFilePanel, self).initWithWindow_resultCallback_(parentWindow, resultCallback)
self.messageText = None
self.title = None
self.fileTypes = None
self.directory = None
self.fileName = None
self.canCreateDirectories = True
self.accessoryView = None
self._result = None
return self
def run(self):
panel = NSSavePanel.alloc().init()
if self.messageText:
panel.setMessage_(self.messageText)
if self.title:
panel.setTitle_(self.title)
if self.directory:
panel.setDirectory_(self.directory)
if self.fileTypes:
panel.setAllowedFileTypes_(self.fileTypes)
panel.setCanCreateDirectories_(self.canCreateDirectories)
panel.setCanSelectHiddenExtension_(True)
panel.setAccessoryView_(self.accessoryView)
if self._parentWindow is not None:
panel.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.directory, self.fileName, self._parentWindow, self, "savePanelDidEnd:returnCode:contextInfo:", 0)
else:
isOK = panel.runModalForDirectory_file_(self.directory, self.fileName)
if isOK == NSOKButton:
self._result = panel.filename()
def savePanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context):
panel.close()
if returnCode:
self._result = panel.filename()
if self._resultCallback is not None:
self._resultCallback(self._result)
savePanelDidEnd_returnCode_contextInfo_ = objc.selector(savePanelDidEnd_returnCode_contextInfo_, signature="v@:@ii")
class GetFileOrFolderPanel(BasePutGetPanel):
def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None):
self = super(GetFileOrFolderPanel, self).initWithWindow_resultCallback_(parentWindow, resultCallback)
self.messageText = None
self.title = None
self.directory = None
self.fileName = None
self.fileTypes = None
self.allowsMultipleSelection = False
self.canChooseDirectories = True
self.canChooseFiles = True
self.resolvesAliases = True
self._result = None
return self
def run(self):
panel = NSOpenPanel.alloc().init()
if self.messageText:
panel.setMessage_(self.messageText)
if self.title:
panel.setTitle_(self.title)
if self.directory:
panel.setDirectory_(self.directory)
if self.fileTypes:
panel.setAllowedFileTypes_(self.fileTypes)
panel.setCanChooseDirectories_(self.canChooseDirectories)
panel.setCanChooseFiles_(self.canChooseFiles)
panel.setAllowsMultipleSelection_(self.allowsMultipleSelection)
panel.setResolvesAliases_(self.resolvesAliases)
if self._parentWindow is not None:
panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.directory, self.fileName, self.fileTypes, self._parentWindow, self, "openPanelDidEnd:returnCode:contextInfo:", 0)
else:
isOK = panel.runModalForDirectory_file_types_(self.directory, self.fileName, self.fileTypes)
if isOK == NSOKButton:
self._result = panel.filenames()
def openPanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context):
panel.close()
if returnCode:
self._result = panel.filenames()
if self._resultCallback is not None:
self._resultCallback(self._result)
openPanelDidEnd_returnCode_contextInfo_ = objc.selector(openPanelDidEnd_returnCode_contextInfo_, signature="v@:@ii")
def Message(message="", title='noLongerUsed', informativeText=""):
"""Legacy robofab dialog compatible wrapper."""
#def _message(messageText="", informativeText="", alertStyle=NSInformationalAlertStyle, parentWindow=None, resultCallback=None):
resultCallback = None
alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_(
messageText=message,
informativeText=informativeText,
alertStyle=NSInformationalAlertStyle,
buttonTitlesValues=[("OK", 1)],
parentWindow=None,
resultCallback=None)
if resultCallback is None:
return 1
def AskYesNoCancel(message, title='noLongerUsed', default=None, informativeText=""):
"""
AskYesNoCancel Dialog
message the string
title* a title of the window
(may not be supported everywhere)
default* index number of which button should be default
(i.e. respond to return)
informativeText* A string with secundary information
* may not be supported everywhere
"""
parentWindow = None
alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_(
messageText=message,
informativeText=informativeText,
alertStyle=NSInformationalAlertStyle,
buttonTitlesValues=[("Cancel", -1), ("Yes", 1), ("No", 0)],
parentWindow=None,
resultCallback=None)
return alert._value
def _askYesNo(messageText="", informativeText="", alertStyle=NSInformationalAlertStyle, parentWindow=None, resultCallback=None):
parentWindow = None
alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_(
messageText=messageText, informativeText=informativeText, alertStyle=alertStyle, buttonTitlesValues=[("Yes", 1), ("No", 0)], parentWindow=parentWindow, resultCallback=resultCallback)
if resultCallback is None:
return alert._value
def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
""" Legacy robofab dialog compatible wrapper.
This will select UFO on OSX 10.7, FL5.1
"""
parentWindow = None
resultCallback=None
basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback)
basePanel.messageText = message
basePanel.title = title
basePanel.directory = directory
basePanel.fileName = fileName
basePanel.fileTypes = fileTypes
basePanel.allowsMultipleSelection = allowsMultipleSelection
basePanel.canChooseDirectories = False
basePanel.canChooseFiles = True
basePanel.run()
if basePanel._result is None:
return None
if not allowsMultipleSelection:
# compatibly return only one as we expect
return str(list(basePanel._result)[0])
else:
# return more if we explicitly expect
return [str(n) for n in list(basePanel._result)]
def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False):
parentWindow = None
resultCallback = None
basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback)
basePanel.messageText = message
basePanel.title = title
basePanel.directory = directory
basePanel.allowsMultipleSelection = allowsMultipleSelection
basePanel.canChooseDirectories = True
basePanel.canChooseFiles = False
basePanel.run()
if basePanel._result is None:
return None
if not allowsMultipleSelection:
# compatibly return only one as we expect
return str(list(basePanel._result)[0])
else:
# return more if we explicitly expect
return [str(n) for n in list(basePanel._result)]
def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None, parentWindow=None, resultCallback=None):
parentWindow = None
basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback)
basePanel.messageText = message
basePanel.title = title
basePanel.directory = directory
basePanel.fileName = fileName
basePanel.fileTypes = fileTypes
basePanel.allowsMultipleSelection = allowsMultipleSelection
basePanel.canChooseDirectories = True
basePanel.canChooseFiles = True
basePanel.run()
if basePanel._result is None:
return None
if not allowsMultipleSelection:
# compatibly return only one as we expect
return str(list(basePanel._result)[0])
else:
# return more if we explicitly expect
return [str(n) for n in list(basePanel._result)]
def PutFile(message=None, title=None, directory=None, fileName=None, canCreateDirectories=True, fileTypes=None):
parentWindow = None
resultCallback=None
accessoryView=None
basePanel = PutFilePanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback)
basePanel.messageText = message
basePanel.title = title
basePanel.directory = directory
basePanel.fileName = fileName
basePanel.fileTypes = fileTypes
basePanel.canCreateDirectories = canCreateDirectories
basePanel.accessoryView = accessoryView
basePanel.run()
return str(basePanel._result)
# we seem to have problems importing from here.
# so let's see what happens if we make the robofab compatible wrappers here as well.
# start with all the defaults.
#def AskString(message, value='', title='RoboFab'):
# raise NotImplementedError
def FindGlyph(aFont, message="Search for a glyph:", title='RoboFab'):
raise NotImplementedError
def OneList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
#def PutFile(message=None, fileName=None):
# raise NotImplementedError
def SearchList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
def SelectFont(message="Select a font:", title='RoboFab'):
raise NotImplementedError
def SelectGlyph(font, message="Select a glyph:", title='RoboFab'):
raise NotImplementedError
def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'):
raise NotImplementedError
def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'):
raise NotImplementedError
class ProgressBar(object):
def __init__(self, title="RoboFab...", ticks=0, label=""):
self._tickValue = 1
fl.BeginProgress(title, ticks)
def getCurrentTick(self):
return self._tickValue
def tick(self, tickValue=None):
if not tickValue:
tickValue = self._tickValue
fl.TickProgress(tickValue)
self._tickValue = tickValue + 1
def label(self, label):
pass
def close(self):
fl.EndProgress()

View File

@ -1,721 +0,0 @@
"""
Dialogs.
Cross-platform and cross-application compatible. Some of them anyway.
(Not all dialogs work on PCs outside of FontLab. Some dialogs are for FontLab only. Sorry.)
Mac and FontLab implementation written by the RoboFab development team.
PC implementation by Eigi Eigendorf and is (C)2002 Eigi Eigendorf.
"""
import os
import sys
from robofab import RoboFabError
from warnings import warn
MAC = False
PC = False
haveMacfs = False
if sys.platform in ('mac', 'darwin'):
MAC = True
elif os.name == 'nt':
PC = True
else:
warn("dialogs.py only supports Mac and PC platforms.")
pyVersion = sys.version_info[:3]
inFontLab = False
try:
from FL import *
inFontLab = True
except ImportError: pass
try:
import W
hasW = True
except ImportError:
hasW = False
try:
import dialogKit
hasDialogKit = True
except ImportError:
hasDialogKit = False
try:
import EasyDialogs
hasEasyDialogs = True
except:
hasEasyDialogs = False
if MAC:
if pyVersion < (2, 3, 0):
import macfs
haveMacfs = True
elif PC and not inFontLab:
from win32com.shell import shell
import win32ui
import win32con
def _raisePlatformError(dialog):
"""error raiser"""
if MAC:
p = 'Macintosh'
elif PC:
p = 'PC'
else:
p = sys.platform
raise RoboFabError("%s is not currently available on the %s platform"%(dialog, p))
class _FontLabDialogOneList:
"""A one list dialog for FontLab. This class should not be called directly. Use the OneList function."""
def __init__(self, list, message, title='RoboFab'):
self.message = message
self.selected = None
self.list = list
self.d = Dialog(self)
self.d.size = Point(250, 250)
self.d.title = title
self.d.Center()
self.d.AddControl(LISTCONTROL, Rect(12, 30, 238, 190), "list", STYLE_LIST, self.message)
self.list_index = 0
def Run(self):
return self.d.Run()
def on_cancel(self, code):
self.selected = None
def on_ok(self, code):
self.d.GetValue('list')
self.selected = self.list_index
class _FontLabDialogSearchList:
"""A dialog for searching through a list. It contains a text field and a results list FontLab. This class should not be called directly. Use the SearchList function."""
def __init__(self, aList, message, title="RoboFab"):
self.d = Dialog(self)
self.d.size = Point(250, 290)
self.d.title = title
self.d.Center()
self.message = message
self._fullContent = aList
self.possibleHits = list(aList)
self.possibleHits.sort()
self.possibleHits_index = 0
self.entryField = ""
self.selected = None
self.d.AddControl(STATICCONTROL, Rect(10, 10, 240, 30), "message", STYLE_LABEL, message)
self.d.AddControl(EDITCONTROL, Rect(10, 30, 240, aAUTO), "entryField", STYLE_EDIT, "")
self.d.AddControl(LISTCONTROL, Rect(12, 60, 238, 230), "possibleHits", STYLE_LIST, "")
def run(self):
self.d.Run()
def on_entryField(self, code):
self.d.GetValue("entryField")
entry = self.entryField
count = len(entry)
possibleHits = [
i for i in self._fullContent
if len(i) >= count
and i[:count] == entry
]
possibleHits.sort()
self.possibleHits = possibleHits
self.possibleHits_index = 0
self.d.PutValue("possibleHits")
def on_ok(self, code):
self.d.GetValue("possibleHits")
sel = self.possibleHits_index
if sel == -1:
self.selected = None
else:
self.selected = self.possibleHits[sel]
def on_cancel(self, code):
self.selected = None
class _FontLabDialogTwoFields:
"""A two field dialog for FontLab. This class should not be called directly. Use the TwoFields function."""
def __init__(self, title_1, value_1, title_2, value_2, title='RoboFab'):
self.d = Dialog(self)
self.d.size = Point(200, 125)
self.d.title = title
self.d.Center()
self.d.AddControl(EDITCONTROL, Rect(120, 10, aIDENT2, aAUTO), "v1edit", STYLE_EDIT, title_1)
self.d.AddControl(EDITCONTROL, Rect(120, 40, aIDENT2, aAUTO), "v2edit", STYLE_EDIT, title_2)
self.v1edit = value_1
self.v2edit = value_2
def Run(self):
return self.d.Run()
def on_cancel(self, code):
self.v1edit = None
self.v2edit = None
def on_ok(self, code):
self.d.GetValue("v1edit")
self.d.GetValue("v2edit")
self.v1 = self.v1edit
self.v2 = self.v2edit
class _FontLabDialogTwoChecks:
"""A two check box dialog for FontLab. This class should not be called directly. Use the TwoChecks function."""
def __init__(self, title_1, title_2, value1=1, value2=1, title='RoboFab'):
self.d = Dialog(self)
self.d.size = Point(200, 105)
self.d.title = title
self.d.Center()
self.d.AddControl(CHECKBOXCONTROL, Rect(10, 10, aIDENT2, aAUTO), "check1", STYLE_CHECKBOX, title_1)
self.d.AddControl(CHECKBOXCONTROL, Rect(10, 30, aIDENT2, aAUTO), "check2", STYLE_CHECKBOX, title_2)
self.check1 = value1
self.check2 = value2
def Run(self):
return self.d.Run()
def on_cancel(self, code):
self.check1 = None
self.check2 = None
def on_ok(self, code):
self.d.GetValue("check1")
self.d.GetValue("check2")
class _FontLabDialogAskString:
"""A one simple string prompt dialog for FontLab. This class should not be called directly. Use the GetString function."""
def __init__(self, message, value, title='RoboFab'):
self.d = Dialog(self)
self.d.size = Point(350, 130)
self.d.title = title
self.d.Center()
self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, aAUTO), "label", STYLE_LABEL, message)
self.d.AddControl(EDITCONTROL, Rect(aIDENT, 40, aIDENT, aAUTO), "value", STYLE_EDIT, '')
self.value=value
def Run(self):
return self.d.Run()
def on_cancel(self, code):
self.value = None
def on_ok(self, code):
self.d.GetValue("value")
class _FontLabDialogMessage:
"""A simple message dialog for FontLab. This class should not be called directly. Use the SimpleMessage function."""
def __init__(self, message, title='RoboFab'):
self.d = Dialog(self)
self.d.size = Point(350, 130)
self.d.title = title
self.d.Center()
self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, 80), "label", STYLE_LABEL, message)
def Run(self):
return self.d.Run()
class _FontLabDialogGetYesNoCancel:
"""A yes no cancel message dialog for FontLab. This class should not be called directly. Use the YesNoCancel function."""
def __init__(self, message, title='RoboFab'):
self.d = Dialog(self)
self.d.size = Point(350, 130)
self.d.title = title
self.d.Center()
self.d.ok = 'Yes'
self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, 80), "label", STYLE_LABEL, message)
self.d.AddControl(BUTTONCONTROL, Rect(100, 95, 172, 115), "button", STYLE_BUTTON, "No")
self.value = 0
def Run(self):
return self.d.Run()
def on_ok(self, code):
self.value = 1
def on_cancel(self, code):
self.value = -1
def on_button(self, code):
self.value = 0
self.d.End()
class _MacOneListW:
"""A one list dialog for Macintosh. This class should not be called directly. Use the OneList function."""
def __init__(self, list, message='Make a selection'):
import W
self.list = list
self.selected = None
self.w = W.ModalDialog((200, 240))
self.w.message = W.TextBox((10, 10, -10, 30), message)
self.w.list = W.List((10, 35, -10, -50), list)
self.w.l = W.HorizontalLine((10, -40, -10, 1), 1)
self.w.cancel = W.Button((10, -30, 87, -10), 'Cancel', self.cancel)
self.w.ok = W.Button((102, -30, 88, -10), 'OK', self.ok)
self.w.setdefaultbutton(self.w.ok)
self.w.bind('cmd.', self.w.cancel.push)
self.w.open()
def ok(self):
if len(self.w.list.getselection()) == 1:
self.selected = self.w.list.getselection()[0]
self.w.close()
def cancel(self):
self.selected = None
self.w.close()
class _MacTwoChecksW:
""" Version using W """
def __init__(self, title_1, title_2, value1=1, value2=1, title='RoboFab'):
import W
self.check1 = value1
self.check2 = value2
self.w = W.ModalDialog((200, 100))
self.w.check1 = W.CheckBox((10, 10, -10, 16), title_1, value=value1)
self.w.check2 = W.CheckBox((10, 35, -10, 16), title_2, value=value2)
self.w.l = W.HorizontalLine((10, 60, -10, 1), 1)
self.w.cancel = W.Button((10, 70, 85, 20), 'Cancel', self.cancel)
self.w.ok = W.Button((105, 70, 85, 20), 'OK', self.ok)
self.w.setdefaultbutton(self.w.ok)
self.w.bind('cmd.', self.w.cancel.push)
self.w.open()
def ok(self):
self.check1 = self.w.check1.get()
self.check2 = self.w.check2.get()
self.w.close()
def cancel(self):
self.check1 = None
self.check2 = None
self.w.close()
class ProgressBar:
def __init__(self, title='RoboFab...', ticks=0, label=''):
"""
A progress bar.
Availability: FontLab, Mac
"""
self._tickValue = 1
if inFontLab:
fl.BeginProgress(title, ticks)
elif MAC and hasEasyDialogs:
import EasyDialogs
self._bar = EasyDialogs.ProgressBar(title, maxval=ticks, label=label)
else:
_raisePlatformError('Progress')
def getCurrentTick(self):
return self._tickValue
def tick(self, tickValue=None):
"""
Tick the progress bar.
Availability: FontLab, Mac
"""
if not tickValue:
tickValue = self._tickValue
if inFontLab:
fl.TickProgress(tickValue)
elif MAC:
self._bar.set(tickValue)
else:
pass
self._tickValue = tickValue + 1
def label(self, label):
"""
Set the label on the progress bar.
Availability: Mac
"""
if inFontLab:
pass
elif MAC:
self._bar.label(label)
else:
pass
def close(self):
"""
Close the progressbar.
Availability: FontLab, Mac
"""
if inFontLab:
fl.EndProgress()
elif MAC:
del self._bar
else:
pass
def SelectFont(message="Select a font:", title='RoboFab'):
"""
Returns font instance if there is one, otherwise it returns None.
Availability: FontLab
"""
from robofab.world import RFont
if inFontLab:
list = []
for i in range(fl.count):
list.append(fl[i].full_name)
name = OneList(list, message, title)
if name is None:
return None
else:
return RFont(fl[list.index(name)])
else:
_raisePlatformError('SelectFont')
def SelectGlyph(font, message="Select a glyph:", title='RoboFab'):
"""
Returns glyph instance if there is one, otherwise it returns None.
Availability: FontLab
"""
from fontTools.misc.textTools import caselessSort
if inFontLab:
tl = font.keys()
list = caselessSort(tl)
glyphname = OneList(list, message, title)
if glyphname is None:
return None
else:
return font[glyphname]
else:
_raisePlatformError('SelectGlyph')
def FindGlyph(font, message="Search for a glyph:", title='RoboFab'):
"""
Returns glyph instance if there is one, otherwise it returns None.
Availability: FontLab
"""
if inFontLab:
glyphname = SearchList(font.keys(), message, title)
if glyphname is None:
return None
else:
return font[glyphname]
else:
_raisePlatformError('SelectGlyph')
def OneList(list, message="Select an item:", title='RoboFab'):
"""
Returns selected item, otherwise it returns None.
Availability: FontLab, Macintosh
"""
if inFontLab:
ol = _FontLabDialogOneList(list, message)
ol.Run()
selected = ol.selected
if selected is None:
return None
else:
try:
return list[selected]
except:
return None
elif MAC:
if hasW:
d = _MacOneListW(list, message)
sel = d.selected
if sel is None:
return None
else:
return list[sel]
else:
_raisePlatformError('OneList')
elif PC:
_raisePlatformError('OneList')
def SearchList(list, message="Select an item:", title='RoboFab'):
"""
Returns selected item, otherwise it returns None.
Availability: FontLab
"""
if inFontLab:
sl = _FontLabDialogSearchList(list, message, title)
sl.run()
selected = sl.selected
if selected is None:
return None
else:
return selected
else:
_raisePlatformError('SearchList')
def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'):
"""
Returns (value 1, value 2).
Availability: FontLab
"""
if inFontLab:
tf = _FontLabDialogTwoFields(title_1, value_1, title_2, value_2, title)
tf.Run()
try:
v1 = tf.v1
v2 = tf.v2
return (v1, v2)
except:
return None
else:
_raisePlatformError('TwoFields')
def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'):
"""
Returns check value:
1 if check box 1 is checked
2 if check box 2 is checked
3 if both are checked
0 if neither are checked
None if cancel is clicked.
Availability: FontLab, Macintosh
"""
tc = None
if inFontLab:
tc = _FontLabDialogTwoChecks(title_1, title_2, value1, value2, title)
tc.Run()
elif MAC:
if hasW:
tc = _MacTwoChecksW(title_1, title_2, value1, value2, title)
else:
_raisePlatformError('TwoChecks')
else:
_raisePlatformError('TwoChecks')
c1 = tc.check1
c2 = tc.check2
if c1 == 1 and c2 == 0:
return 1
elif c1 == 0 and c2 == 1:
return 2
elif c1 == 1 and c2 == 1:
return 3
elif c1 == 0 and c2 == 0:
return 0
else:
return None
def Message(message, title='RoboFab'):
"""
A simple message dialog.
Availability: FontLab, Macintosh
"""
if inFontLab:
_FontLabDialogMessage(message, title).Run()
elif MAC:
import EasyDialogs
EasyDialogs.Message(message)
else:
_raisePlatformError('Message')
def AskString(message, value='', title='RoboFab'):
"""
Returns entered string.
Availability: FontLab, Macintosh
"""
if inFontLab:
askString = _FontLabDialogAskString(message, value, title)
askString.Run()
v = askString.value
if v is None:
return None
else:
return v
elif MAC:
import EasyDialogs
askString = EasyDialogs.AskString(message)
if askString is None:
return None
if len(askString) == 0:
return None
else:
return askString
else:
_raisePlatformError('GetString')
def AskYesNoCancel(message, title='RoboFab', default=0):
"""
Returns 1 for 'Yes', 0 for 'No' and -1 for 'Cancel'.
Availability: FontLab, Macintosh
("default" argument only available on Macintosh)
"""
if inFontLab:
gync = _FontLabDialogGetYesNoCancel(message, title)
gync.Run()
v = gync.value
return v
elif MAC:
import EasyDialogs
gync = EasyDialogs.AskYesNoCancel(message, default=default)
return gync
else:
_raisePlatformError('GetYesNoCancel')
def GetFile(message=None):
"""
Select file dialog. Returns path if one is selected. Otherwise it returns None.
Availability: FontLab, Macintosh, PC
"""
path = None
if MAC:
if haveMacfs:
fss, ok = macfs.PromptGetFile(message)
if ok:
path = fss.as_pathname()
else:
from robofab.interface.mac.getFileOrFolder import GetFile
path = GetFile(message)
elif PC:
if inFontLab:
if not message:
message = ''
path = fl.GetFileName(1, message, '', '')
else:
openFlags = win32con.OFN_FILEMUSTEXIST|win32con.OFN_EXPLORER
mode_open = 1
myDialog = win32ui.CreateFileDialog(mode_open,None,None,openFlags)
myDialog.SetOFNTitle(message)
is_OK = myDialog.DoModal()
if is_OK == 1:
path = myDialog.GetPathName()
else:
_raisePlatformError('GetFile')
return path
def GetFolder(message=None):
"""
Select folder dialog. Returns path if one is selected. Otherwise it returns None.
Availability: FontLab, Macintosh, PC
"""
path = None
if MAC:
if haveMacfs:
fss, ok = macfs.GetDirectory(message)
if ok:
path = fss.as_pathname()
else:
from robofab.interface.mac.getFileOrFolder import GetFileOrFolder
# This _also_ allows the user to select _files_, but given the
# package/folder dichotomy, I think we have no other choice.
path = GetFileOrFolder(message)
elif PC:
if inFontLab:
if not message:
message = ''
path = fl.GetPathName('', message)
else:
myTuple = shell.SHBrowseForFolder(0, None, message, 64)
try:
path = shell.SHGetPathFromIDList(myTuple[0])
except:
pass
else:
_raisePlatformError('GetFile')
return path
GetDirectory = GetFolder
def PutFile(message=None, fileName=None):
"""
Save file dialog. Returns path if one is entered. Otherwise it returns None.
Availability: FontLab, Macintosh, PC
"""
path = None
if MAC:
if haveMacfs:
fss, ok = macfs.StandardPutFile(message, fileName)
if ok:
path = fss.as_pathname()
else:
import EasyDialogs
path = EasyDialogs.AskFileForSave(message, savedFileName=fileName)
elif PC:
if inFontLab:
if not message:
message = ''
if not fileName:
fileName = ''
path = fl.GetFileName(0, message, fileName, '')
else:
openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_EXPLORER
mode_save = 0
myDialog = win32ui.CreateFileDialog(mode_save, None, fileName, openFlags)
myDialog.SetOFNTitle(message)
is_OK = myDialog.DoModal()
if is_OK == 1:
path = myDialog.GetPathName()
else:
_raisePlatformError('GetFile')
return path
if __name__=='__main__':
import traceback
print "dialogs hasW", hasW
print "dialogs hasDialogKit", hasDialogKit
print "dialogs MAC", MAC
print "dialogs PC", PC
print "dialogs inFontLab", inFontLab
print "dialogs hasEasyDialogs", hasEasyDialogs
def tryDialog(dialogClass, args=None):
print
print "tryDialog:", dialogClass, "with args:", args
try:
if args is not None:
apply(dialogClass, args)
else:
apply(dialogClass)
except:
traceback.print_exc(limit=0)
tryDialog(TwoChecks, ('hello', 'world', 1, 0, 'ugh'))
tryDialog(TwoFields)
tryDialog(TwoChecks, ('hello', 'world', 1, 0, 'ugh'))
tryDialog(OneList, (['a', 'b', 'c'], 'hello world'))
tryDialog(Message, ('hello world',))
tryDialog(AskString, ('hello world',))
tryDialog(AskYesNoCancel, ('hello world',))
try:
b = ProgressBar('hello', 50, 'world')
for i in range(50):
if i == 25:
b.label('ugh.')
b.tick(i)
b.close()
except:
traceback.print_exc(limit=0)
#

View File

@ -1,262 +0,0 @@
"""
Dialogs for environments that support cocao / vanilla.
"""
import vanilla
from AppKit import NSApp, NSModalPanelWindowLevel, NSWindowCloseButton, NSWindowZoomButton, NSWindowMiniaturizeButton
__all__ = [
"AskString",
"AskYesNoCancel",
"FindGlyph",
"GetFile",
"GetFileOrFolder",
"GetFolder",
"Message",
"OneList",
"PutFile",
"SearchList",
"SelectFont",
"SelectGlyph",
# "TwoChecks",
# "TwoFields",
"ProgressBar",
]
class _ModalWindow(vanilla.Window):
nsWindowLevel = NSModalPanelWindowLevel
def __init__(self, *args, **kwargs):
super(_ModalWindow, self).__init__(*args, **kwargs)
self._window.standardWindowButton_(NSWindowCloseButton).setHidden_(True)
self._window.standardWindowButton_(NSWindowZoomButton).setHidden_(True)
self._window.standardWindowButton_(NSWindowMiniaturizeButton).setHidden_(True)
def open(self):
super(_ModalWindow, self).open()
self.center()
NSApp().runModalForWindow_(self._window)
def windowWillClose_(self, notification):
super(_ModalWindow, self).windowWillClose_(notification)
NSApp().stopModal()
class _baseWindowController(object):
def setUpBaseWindowBehavior(self):
self._getValue = None
self.w.okButton = vanilla.Button((-70, -30, -15, 20), "OK", callback=self.okCallback, sizeStyle="small")
self.w.setDefaultButton(self.w.okButton)
self.w.closeButton = vanilla.Button((-150, -30, -80, 20), "Cancel", callback=self.closeCallback, sizeStyle="small")
self.w.closeButton.bind(".", ["command"])
self.w.closeButton.bind(unichr(27), [])
def okCallback(self, sender):
self.w.close()
def closeCallback(self, sender):
self.w.close()
def get(self):
raise NotImplementedError
class _AskStringController(_baseWindowController):
def __init__(self, message, value, title):
self.w = _ModalWindow((370, 110), title)
self.w.infoText = vanilla.TextBox((15, 10, -15, 22), message)
self.w.input = vanilla.EditText((15, 40, -15, 22))
self.w.input.set(value)
self.setUpBaseWindowBehavior()
self.w.open()
def get(self):
return self.w.input.get()
class _listController(_baseWindowController):
def __init__(self, items, message, title, showSearch=False):
self.items = items
self.w = _ModalWindow((350, 300), title)
y = 10
self.w.infoText = vanilla.TextBox((15, y, -15, 22), message)
y += 25
if showSearch:
self.w.search = vanilla.SearchBox((15, y, -15, 22), callback=self.searchCallback)
y += 25
self.w.itemList = vanilla.List((15, y, -15, -40), self.items, allowsMultipleSelection=False)
self.setUpBaseWindowBehavior()
self.w.open()
def searchCallback(self, sender):
search = sender.get()
newItems = [item for item in self.items if repr(item).startswith(search)]
self.w.itemList.set(newItems)
if newItems:
self.w.itemList.setSelection([0])
def get(self):
index = self.w.itemList.getSelection()
if index:
index = index[0]
return self.w.itemList[index]
return None
def AskString(message, value='', title='RoboFab'):
"""
AskString Dialog
message the string
value a default value
title a title of the window (may not be supported everywhere)
"""
w = _AskStringController(message, value, title)
return w.get()
def AskYesNoCancel(message, title='RoboFab', default=0, informativeText=""):
"""
AskYesNoCancel Dialog
message the string
title* a title of the window
(may not be supported everywhere)
default* index number of which button should be default
(i.e. respond to return)
informativeText* A string with secundary information
* may not be supported everywhere
"""
import vanilla.dialogs
return vanilla.dialogs.askYesNoCancel(messageText=message, informativeText=informativeText)
def FindGlyph(aFont, message="Search for a glyph:", title='RoboFab'):
items = aFont.keys()
items.sort()
w = _listController(items, message, title, showSearch=True)
glyphName = w.get()
if glyphName is not None:
return aFont[glyphName]
return None
def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
result = vanilla.dialogs.getFile(messageText=message, title=title, directory=directory, fileName=fileName, allowsMultipleSelection=allowsMultipleSelection, fileTypes=fileTypes)
if result is None:
return None
if not allowsMultipleSelection:
return str(list(result)[0])
else:
return [str(n) for n in list(result)]
def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False):
result = vanilla.dialogs.getFolder(messageText=message, title=title, directory=directory, allowsMultipleSelection=allowsMultipleSelection)
if result is None:
return None
if not allowsMultipleSelection:
return str(list(result)[0])
else:
return [str(n) for n in list(result)]
def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
result = vanilla.dialogs.getFileOrFolder(messageText=message, title=title, directory=directory, fileName=fileName, allowsMultipleSelection=allowsMultipleSelection, fileTypes=fileTypes)
if result is None:
return None
if not allowsMultipleSelection:
return str(list(result)[0])
else:
return [str(n) for n in list(result)]
def Message(message, title='RoboFab', informativeText=""):
vanilla.dialogs.message(messageText=message, informativeText=informativeText)
def OneList(list, message="Select an item:", title='RoboFab'):
raise NotImplementedError
def PutFile(message=None, fileName=None):
return vanilla.dialogs.putFile(messageText=message, fileName=fileName)
def SearchList(list, message="Select an item:", title='RoboFab'):
w = _listController(list, message, title, showSearch=True)
return w.get()
def SelectFont(message="Select a font:", title='RoboFab', allFonts=None):
if allFonts is None:
from robofab.world import AllFonts
fonts = AllFonts()
else:
fonts = allFonts
data = dict()
for font in fonts:
data["%s" %font] = font
items = data.keys()
items.sort()
w = _listController(items, message, title, showSearch=False)
value = w.get()
return data.get(value, None)
def SelectGlyph(aFont, message="Select a glyph:", title='RoboFab'):
items = aFont.keys()
items.sort()
w = _listController(items, message, title, showSearch=False)
glyphName = w.get()
if glyphName is not None:
return aFont[glyphName]
return None
def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'):
raise NotImplementedError
def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'):
raise NotImplementedError
class ProgressBar(object):
def __init__(self, title="RoboFab...", ticks=None, label=""):
self.w = vanilla.Window((250, 60), title)
if ticks is None:
isIndeterminate = True
ticks = 0
else:
isIndeterminate = False
self.w.progress = vanilla.ProgressBar((15, 15, -15, 12), maxValue=ticks, isIndeterminate=isIndeterminate, sizeStyle="small")
self.w.text = vanilla.TextBox((15, 32, -15, 14), label, sizeStyle="small")
self.w.progress.start()
self.w.center()
self.w.open()
def close(self):
self.w.progress.stop()
self.w.close()
def getCurrentTick(self):
return self.w.progress.get()
def label(self, label):
self.w.text.set(label)
self.w.text._nsObject.display()
def tick(self, tickValue=None):
if tickValue is None:
self.w.progress.increment()
else:
self.w.progress.set(tickValue)
if __name__ == "__main__":
pass

View File

@ -1,10 +0,0 @@
"""
Directory for interface related modules.
Stuff for MacOSX, widgets, quartz
"""

View File

@ -1,80 +0,0 @@
"""This module provides two functions, very similar to
EasyDialogs.AskFileForOpen() and EasyDialogs.AskFolder(): GetFile() and
GetFileOrFolder(). The main difference is that the functions here fully
support "packages" or "bundles", ie. folders that appear to be files in
the finder and open/save dialogs. The second difference is that
GetFileOrFolder() allows the user to select a file _or_ a folder.
"""
__all__ = ["GetFile", "GetFileOrFolder"]
from EasyDialogs import _process_Nav_args, _interact
import Nav
import Carbon.File
# Lots of copy/paste from EasyDialogs.py, for one because althought the
# EasyDialogs counterparts take a million options, they don't take the
# one option I need: the flag to support packages...
kNavSupportPackages = 0x00001000
def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
"""Ask the user to select a file.
Some of these arguments are not supported:
title, directory, fileName, allowsMultipleSelection and fileTypes are here for compatibility reasons.
"""
default_flags = 0x56 | kNavSupportPackages
args, tpwanted = _process_Nav_args(default_flags, message=message)
_interact()
try:
rr = Nav.NavChooseFile(args)
good = 1
except Nav.error, arg:
if arg[0] != -128: # userCancelledErr
raise Nav.error, arg
return None
if not rr.validRecord or not rr.selection:
return None
if issubclass(tpwanted, Carbon.File.FSRef):
return tpwanted(rr.selection_fsr[0])
if issubclass(tpwanted, Carbon.File.FSSpec):
return tpwanted(rr.selection[0])
if issubclass(tpwanted, str):
return tpwanted(rr.selection_fsr[0].as_pathname())
if issubclass(tpwanted, unicode):
return tpwanted(rr.selection_fsr[0].as_pathname(), 'utf8')
raise TypeError, "Unknown value for argument 'wanted': %s" % repr(tpwanted)
def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None):
"""Ask the user to select a file or a folder.
Some of these arguments are not supported:
title, directory, fileName, allowsMultipleSelection and fileTypes are here for compatibility reasons.
"""
default_flags = 0x17 | kNavSupportPackages
args, tpwanted = _process_Nav_args(default_flags, message=message)
_interact()
try:
rr = Nav.NavChooseObject(args)
good = 1
except Nav.error, arg:
if arg[0] != -128: # userCancelledErr
raise Nav.error, arg
return None
if not rr.validRecord or not rr.selection:
return None
if issubclass(tpwanted, Carbon.File.FSRef):
return tpwanted(rr.selection_fsr[0])
if issubclass(tpwanted, Carbon.File.FSSpec):
return tpwanted(rr.selection[0])
if issubclass(tpwanted, str):
return tpwanted(rr.selection_fsr[0].as_pathname())
if issubclass(tpwanted, unicode):
return tpwanted(rr.selection_fsr[0].as_pathname(), 'utf8')
raise TypeError, "Unknown value for argument 'wanted': %s" % repr(tpwanted)

View File

@ -1,10 +0,0 @@
"""
Directory for interface related modules.
Stuff for Windows
"""

View File

@ -1,13 +0,0 @@
"""
arrayTools and bezierTools, originally from fontTools and using Numpy,
now in a pure python implementation. This should ease the Numpy dependency
for normal UFO input/output and basic scripting tasks.
comparison test and speedtest provided.
"""

View File

@ -1,160 +0,0 @@
#
# Various array and rectangle tools, but mostly rectangles, hence the
# name of this module (not).
#
"""
Rewritten to elimate the numpy dependency
"""
import math
def calcBounds(array):
"""Return the bounding rectangle of a 2D points array as a tuple:
(xMin, yMin, xMax, yMax)
"""
if len(array) == 0:
return 0, 0, 0, 0
xs = [x for x, y in array]
ys = [y for x, y in array]
return min(xs), min(ys), max(xs), max(ys)
def updateBounds(bounds, (x, y), min=min, max=max):
"""Return the bounding recangle of rectangle bounds and point (x, y)."""
xMin, yMin, xMax, yMax = bounds
return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
def pointInRect((x, y), rect):
"""Return True when point (x, y) is inside rect."""
xMin, yMin, xMax, yMax = rect
return (xMin <= x <= xMax) and (yMin <= y <= yMax)
def pointsInRect(array, rect):
"""Find out which points or array are inside rect.
Returns an array with a boolean for each point.
"""
if len(array) < 1:
return []
xMin, yMin, xMax, yMax = rect
return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
def vectorLength(vector):
"""Return the length of the given vector."""
x, y = vector
return math.sqrt(x**2 + y**2)
def asInt16(array):
"""Round and cast to 16 bit integer."""
return [int(math.floor(i+0.5)) for i in array]
def normRect((xMin, yMin, xMax, yMax)):
"""Normalize the rectangle so that the following holds:
xMin <= xMax and yMin <= yMax
"""
return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
def scaleRect((xMin, yMin, xMax, yMax), x, y):
"""Scale the rectangle by x, y."""
return xMin * x, yMin * y, xMax * x, yMax * y
def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
"""Offset the rectangle by dx, dy."""
return xMin+dx, yMin+dy, xMax+dx, yMax+dy
def insetRect((xMin, yMin, xMax, yMax), dx, dy):
"""Inset the rectangle by dx, dy on all sides."""
return xMin+dx, yMin+dy, xMax-dx, yMax-dy
def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
"""Return a boolean and a rectangle. If the input rectangles intersect, return
True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
rectangles don't intersect.
"""
xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
min(xMax1, xMax2), min(yMax1, yMax2))
if xMin >= xMax or yMin >= yMax:
return 0, (0, 0, 0, 0)
return 1, (xMin, yMin, xMax, yMax)
def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
"""Return the smallest rectangle in which both input rectangles are fully
enclosed. In other words, return the total bounding rectangle of both input
rectangles.
"""
xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
max(xMax1, xMax2), max(yMax1, yMax2))
return (xMin, yMin, xMax, yMax)
def rectCenter((xMin, yMin, xMax, yMax)):
"""Return the center of the rectangle as an (x, y) coordinate."""
return (xMin+xMax)/2, (yMin+yMax)/2
def intRect((xMin, yMin, xMax, yMax)):
"""Return the rectangle, rounded off to integer values, but guaranteeing that
the resulting rectangle is NOT smaller than the original.
"""
import math
xMin = int(math.floor(xMin))
yMin = int(math.floor(yMin))
xMax = int(math.ceil(xMax))
yMax = int(math.ceil(yMax))
return (xMin, yMin, xMax, yMax)
def _test():
"""
>>> import math
>>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
(0, 10, 80, 100)
>>> updateBounds((0, 0, 0, 0), (100, 100))
(0, 0, 100, 100)
>>> pointInRect((50, 50), (0, 0, 100, 100))
True
>>> pointInRect((0, 0), (0, 0, 100, 100))
True
>>> pointInRect((100, 100), (0, 0, 100, 100))
True
>>> not pointInRect((101, 100), (0, 0, 100, 100))
True
>>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
[True, True, True, False]
>>> vectorLength((3, 4))
5.0
>>> vectorLength((1, 1)) == math.sqrt(2)
True
>>> list(asInt16([0, 0.1, 0.5, 0.9]))
[0, 0, 1, 1]
>>> normRect((0, 10, 100, 200))
(0, 10, 100, 200)
>>> normRect((100, 200, 0, 10))
(0, 10, 100, 200)
>>> scaleRect((10, 20, 50, 150), 1.5, 2)
(15.0, 40, 75.0, 300)
>>> offsetRect((10, 20, 30, 40), 5, 6)
(15, 26, 35, 46)
>>> insetRect((10, 20, 50, 60), 5, 10)
(15, 30, 45, 50)
>>> insetRect((10, 20, 50, 60), -5, -10)
(5, 10, 55, 70)
>>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
>>> not intersects
True
>>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
>>> intersects
1
>>> rect
(5, 20, 20, 30)
>>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
(0, 10, 20, 50)
>>> rectCenter((0, 0, 100, 200))
(50, 100)
>>> rectCenter((0, 0, 100, 199.0))
(50, 99.5)
>>> intRect((0.9, 2.9, 3.1, 4.1))
(0, 2, 4, 5)
"""
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,410 +0,0 @@
"""fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
Rewritten to elimate the numpy dependency
"""
__all__ = [
"calcQuadraticBounds",
"calcCubicBounds",
"splitLine",
"splitQuadratic",
"splitCubic",
"splitQuadraticAtT",
"splitCubicAtT",
"solveQuadratic",
"solveCubic",
]
from robofab.misc.arrayTools import calcBounds
epsilon = 1e-12
def calcQuadraticBounds(pt1, pt2, pt3):
"""Return the bounding rectangle for a qudratic bezier segment.
pt1 and pt3 are the "anchor" points, pt2 is the "handle".
>>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
(0, 0, 100, 50.0)
>>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
(0.0, 0.0, 100, 100)
"""
(ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
ax2 = ax*2.0
ay2 = ay*2.0
roots = []
if ax2 != 0:
roots.append(-bx/ax2)
if ay2 != 0:
roots.append(-by/ay2)
points = [(ax*t*t + bx*t + cx, ay*t*t + by*t + cy) for t in roots if 0 <= t < 1] + [pt1, pt3]
return calcBounds(points)
def calcCubicBounds(pt1, pt2, pt3, pt4):
"""Return the bounding rectangle for a cubic bezier segment.
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
>>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
(0, 0, 100, 75.0)
>>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
(0.0, 0.0, 100, 100)
>>> calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0))
(35.566243270259356, 0, 64.43375672974068, 75.0)
"""
(ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
# calc first derivative
ax3 = ax * 3.0
ay3 = ay * 3.0
bx2 = bx * 2.0
by2 = by * 2.0
xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
roots = xRoots + yRoots
points = [(ax*t*t*t + bx*t*t + cx * t + dx, ay*t*t*t + by*t*t + cy * t + dy) for t in roots] + [pt1, pt4]
return calcBounds(points)
def splitLine(pt1, pt2, where, isHorizontal):
"""Split the line between pt1 and pt2 at position 'where', which
is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of two line segments if the
line was successfully split, or a list containing the original
line.
>>> printSegments(splitLine((0, 0), (100, 100), 50, True))
((0, 0), (50.0, 50.0))
((50.0, 50.0), (100, 100))
>>> printSegments(splitLine((0, 0), (100, 100), 100, True))
((0, 0), (100, 100))
>>> printSegments(splitLine((0, 0), (100, 100), 0, True))
((0, 0), (0.0, 0.0))
((0.0, 0.0), (100, 100))
>>> printSegments(splitLine((0, 0), (100, 100), 0, False))
((0, 0), (0.0, 0.0))
((0.0, 0.0), (100, 100))
"""
pt1x, pt1y = pt1
pt2x, pt2y = pt2
ax = (pt2x - pt1x)
ay = (pt2y - pt1y)
bx = pt1x
by = pt1y
ax1 = (ax, ay)[isHorizontal]
if ax == 0:
return [(pt1, pt2)]
t = float(where - (bx, by)[isHorizontal]) / ax
if 0 <= t < 1:
midPt = ax * t + bx, ay * t + by
return [(pt1, midPt), (midPt, pt2)]
else:
return [(pt1, pt2)]
def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
"""Split the quadratic curve between pt1, pt2 and pt3 at position 'where',
which is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of curve segments.
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
((0, 0), (50, 100), (100, 0))
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))
((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))
((0.0, 0.0), (12.5, 25.0), (25.0, 37.5))
((25.0, 37.5), (62.5, 75.0), (100.0, 0.0))
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))
((0.0, 0.0), (7.32233047034, 14.6446609407), (14.6446609407, 25.0))
((14.6446609407, 25.0), (50.0, 75.0), (85.3553390593, 25.0))
((85.3553390593, 25.0), (92.6776695297, 14.6446609407), (100.0, -7.1054273576e-15))
>>> # XXX I'm not at all sure if the following behavior is desirable:
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))
((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
((50.0, 50.0), (50.0, 50.0), (50.0, 50.0))
((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
"""
a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
c[isHorizontal] - where)
solutions = [t for t in solutions if 0 <= t < 1]
solutions.sort()
if not solutions:
return [(pt1, pt2, pt3)]
return _splitQuadraticAtT(a, b, c, *solutions)
def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
"""Split the cubic curve between pt1, pt2, pt3 and pt4 at position 'where',
which is an x coordinate if isHorizontal is False, a y coordinate if
isHorizontal is True. Return a list of curve segments.
>>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
((0, 0), (25, 100), (75, 100), (100, 0))
>>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))
((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
((50.0, 75.0), (68.75, 75.0), (87.5, 50.0), (100.0, 0.0))
>>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))
((0.0, 0.0), (2.2937927384, 9.17517095361), (4.79804488188, 17.5085042869), (7.47413641001, 25.0))
((7.47413641001, 25.0), (31.2886200204, 91.6666666667), (68.7113799796, 91.6666666667), (92.52586359, 25.0))
((92.52586359, 25.0), (95.2019551181, 17.5085042869), (97.7062072616, 9.17517095361), (100.0, 1.7763568394e-15))
"""
a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
solutions = solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
d[isHorizontal] - where)
solutions = [t for t in solutions if 0 <= t < 1]
solutions.sort()
if not solutions:
return [(pt1, pt2, pt3, pt4)]
return _splitCubicAtT(a, b, c, d, *solutions)
def splitQuadraticAtT(pt1, pt2, pt3, *ts):
"""Split the quadratic curve between pt1, pt2 and pt3 at one or more
values of t. Return a list of curve segments.
>>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
>>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))
((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
((50.0, 50.0), (62.5, 50.0), (75.0, 37.5))
((75.0, 37.5), (87.5, 25.0), (100.0, 0.0))
"""
a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
return _splitQuadraticAtT(a, b, c, *ts)
def splitCubicAtT(pt1, pt2, pt3, pt4, *ts):
"""Split the cubic curve between pt1, pt2, pt3 and pt4 at one or more
values of t. Return a list of curve segments.
>>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
((50.0, 75.0), (68.75, 75.0), (87.5, 50.0), (100.0, 0.0))
>>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))
((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
((50.0, 75.0), (59.375, 75.0), (68.75, 68.75), (77.34375, 56.25))
((77.34375, 56.25), (85.9375, 43.75), (93.75, 25.0), (100.0, 0.0))
"""
a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
return _splitCubicAtT(a, b, c, d, *ts)
def _splitQuadraticAtT(a, b, c, *ts):
ts = list(ts)
segments = []
ts.insert(0, 0.0)
ts.append(1.0)
ax, ay = a
bx, by = b
cx, cy = c
for i in range(len(ts) - 1):
t1 = ts[i]
t2 = ts[i+1]
delta = (t2 - t1)
# calc new a, b and c
a1x = ax * delta**2
a1y = ay * delta**2
b1x = (2*ax*t1 + bx) * delta
b1y = (2*ay*t1 + by) * delta
c1x = ax*t1**2 + bx*t1 + cx
c1y = ay*t1**2 + by*t1 + cy
pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
segments.append((pt1, pt2, pt3))
return segments
def _splitCubicAtT(a, b, c, d, *ts):
ts = list(ts)
ts.insert(0, 0.0)
ts.append(1.0)
segments = []
ax, ay = a
bx, by = b
cx, cy = c
dx, dy = d
for i in range(len(ts) - 1):
t1 = ts[i]
t2 = ts[i+1]
delta = (t2 - t1)
# calc new a, b, c and d
a1x = ax * delta**3
a1y = ay * delta**3
b1x = (3*ax*t1 + bx) * delta**2
b1y = (3*ay*t1 + by) * delta**2
c1x = (2*bx*t1 + cx + 3*ax*t1**2) * delta
c1y = (2*by*t1 + cy + 3*ay*t1**2) * delta
d1x = ax*t1**3 + bx*t1**2 + cx*t1 + dx
d1y = ay*t1**3 + by*t1**2 + cy*t1 + dy
pt1, pt2, pt3, pt4 = calcCubicPoints((a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y))
segments.append((pt1, pt2, pt3, pt4))
return segments
#
# Equation solvers.
#
from math import sqrt, acos, cos, pi
def solveQuadratic(a, b, c,
sqrt=sqrt):
"""Solve a quadratic equation where a, b and c are real.
a*x*x + b*x + c = 0
This function returns a list of roots. Note that the returned list
is neither guaranteed to be sorted nor to contain unique values!
"""
if abs(a) < epsilon:
if abs(b) < epsilon:
# We have a non-equation; therefore, we have no valid solution
roots = []
else:
# We have a linear equation with 1 root.
roots = [-c/b]
else:
# We have a true quadratic equation. Apply the quadratic formula to find two roots.
DD = b*b - 4.0*a*c
if DD >= 0.0:
rDD = sqrt(DD)
roots = [(-b+rDD)/2.0/a, (-b-rDD)/2.0/a]
else:
# complex roots, ignore
roots = []
return roots
def solveCubic(a, b, c, d,
abs=abs, pow=pow, sqrt=sqrt, cos=cos, acos=acos, pi=pi):
"""Solve a cubic equation where a, b, c and d are real.
a*x*x*x + b*x*x + c*x + d = 0
This function returns a list of roots. Note that the returned list
is neither guaranteed to be sorted nor to contain unique values!
"""
#
# adapted from:
# CUBIC.C - Solve a cubic polynomial
# public domain by Ross Cottrell
# found at: http://www.strangecreations.com/library/snippets/Cubic.C
#
if abs(a) < epsilon:
# don't just test for zero; for very small values of 'a' solveCubic()
# returns unreliable results, so we fall back to quad.
return solveQuadratic(b, c, d)
a = float(a)
a1 = b/a
a2 = c/a
a3 = d/a
Q = (a1*a1 - 3.0*a2)/9.0
R = (2.0*a1*a1*a1 - 9.0*a1*a2 + 27.0*a3)/54.0
R2_Q3 = R*R - Q*Q*Q
if R2_Q3 < 0:
theta = acos(R/sqrt(Q*Q*Q))
rQ2 = -2.0*sqrt(Q)
x0 = rQ2*cos(theta/3.0) - a1/3.0
x1 = rQ2*cos((theta+2.0*pi)/3.0) - a1/3.0
x2 = rQ2*cos((theta+4.0*pi)/3.0) - a1/3.0
return [x0, x1, x2]
else:
if Q == 0 and R == 0:
x = 0
else:
x = pow(sqrt(R2_Q3)+abs(R), 1/3.0)
x = x + Q/x
if R >= 0.0:
x = -x
x = x - a1/3.0
return [x]
#
# Conversion routines for points to parameters and vice versa
#
def calcQuadraticParameters(pt1, pt2, pt3):
x2, y2 = pt2
x3, y3 = pt3
cx, cy = pt1
bx = (x2 - cx) * 2.0
by = (y2 - cy) * 2.0
ax = x3 - cx - bx
ay = y3 - cy - by
return (ax, ay), (bx, by), (cx, cy)
def calcCubicParameters(pt1, pt2, pt3, pt4):
x2, y2 = pt2
x3, y3 = pt3
x4, y4 = pt4
dx, dy = pt1
cx = (x2 -dx) * 3.0
cy = (y2 -dy) * 3.0
bx = (x3 - x2) * 3.0 - cx
by = (y3 - y2) * 3.0 - cy
ax = x4 - dx - cx - bx
ay = y4 - dy - cy - by
return (ax, ay), (bx, by), (cx, cy), (dx, dy)
def calcQuadraticPoints(a, b, c):
ax, ay = a
bx, by = b
cx, cy = c
x1 = cx
y1 = cy
x2 = (bx * 0.5) + cx
y2 = (by * 0.5) + cy
x3 = ax + bx + cx
y3 = ay + by + cy
return (x1, y1), (x2, y2), (x3, y3)
def calcCubicPoints(a, b, c, d):
ax, ay = a
bx, by = b
cx, cy = c
dx, dy = d
x1 = dx
y1 = dy
x2 = (cx / 3.0) + dx
y2 = (cy / 3.0) + dy
x3 = (bx + cx) / 3.0 + x2
y3 = (by + cy) / 3.0 + y2
x4 = ax + dx + cx + bx
y4 = ay + dy + cy + by
return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
def _segmentrepr(obj):
"""
>>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
'(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
"""
try:
it = iter(obj)
except TypeError:
return str(obj)
else:
return "(%s)" % ", ".join([_segmentrepr(x) for x in it])
def printSegments(segments):
"""Helper for the doctests, displaying each segment in a list of
segments on a single line as a tuple.
"""
for segment in segments:
print _segmentrepr(segment)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,99 +0,0 @@
"""
Speed comparison between the fontTools numpy based arrayTools and bezierTools,
and the pure python implementation in robofab.path.arrayTools and robofab.path.bezierTools
"""
import time
from fontTools.misc import arrayTools
from fontTools.misc import bezierTools
import numpy
import robofab.misc.arrayTools as noNumpyArrayTools
import robofab.misc.bezierTools as noNumpyBezierTools
################
pt1 = (100, 100)
pt2 = (200, 20)
pt3 = (30, 580)
pt4 = (153, 654)
rect = [20, 20, 100, 100]
## loops
c = 10000
print "(loop %s)"%c
print "with numpy:"
print "calcQuadraticParameters\t\t",
n = time.time()
for i in range(c):
bezierTools.calcQuadraticParameters(pt1, pt2, pt3)
print time.time() - n
print "calcBounds\t\t\t",
n = time.time()
for i in range(c):
arrayTools.calcBounds([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3])
print time.time() - n
print "pointsInRect\t\t\t",
n = time.time()
for i in range(c):
arrayTools.pointsInRect([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt4], rect)
print time.time() - n
print "calcQuadraticBounds\t\t",
n = time.time()
for i in range(c):
bezierTools.calcQuadraticBounds(pt1, pt2, pt3)
print time.time() - n
print "calcCubicBounds\t\t\t",
n = time.time()
for i in range(c):
bezierTools.calcCubicBounds(pt1, pt2, pt3, pt4)
print time.time() - n
print
##############
print "no-numpy"
print "calcQuadraticParameters\t\t",
n = time.time()
for i in range(c):
noNumpyBezierTools.calcQuadraticParameters(pt1, pt2, pt3)
print time.time() - n
print "calcBounds\t\t\t",
n = time.time()
for i in range(c):
noNumpyArrayTools.calcBounds([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3])
print time.time() - n
print "pointsInRect\t\t\t",
n = time.time()
for i in range(c):
noNumpyArrayTools.pointsInRect([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt4], rect)
print time.time() - n
print "calcQuadraticBounds\t\t",
n = time.time()
for i in range(c):
noNumpyBezierTools.calcQuadraticBounds(pt1, pt2, pt3)
print time.time() - n
print "calcCubicBounds\t\t\t",
n = time.time()
for i in range(c):
noNumpyBezierTools.calcCubicBounds(pt1, pt2, pt3, pt4)
print time.time() - n

View File

@ -1,119 +0,0 @@
"""
doc test requires fontTools to compare and defon to make the test font.
"""
import random
from fontTools.pens.basePen import BasePen
from fontTools.misc import arrayTools
from fontTools.misc import bezierTools
import robofab.misc.arrayTools as noNumpyArrayTools
import robofab.misc.bezierTools as noNumpyBezierTools
def drawMoveTo(pen, maxBox):
pen.moveTo((maxBox*random.random(), maxBox*random.random()))
def drawLineTo(pen, maxBox):
pen.lineTo((maxBox*random.random(), maxBox*random.random()))
def drawCurveTo(pen, maxBox):
pen.curveTo((maxBox*random.random(), maxBox*random.random()),
(maxBox*random.random(), maxBox*random.random()),
(maxBox*random.random(), maxBox*random.random()))
def drawClosePath(pen):
pen.closePath()
def createRandomFont():
from defcon import Font
maxGlyphs = 1000
maxContours = 10
maxSegments = 10
maxBox = 700
drawCallbacks = [drawLineTo, drawCurveTo]
f = Font()
for i in range(maxGlyphs):
name = "%s" %i
f.newGlyph(name)
g = f[name]
g.width = maxBox
pen = g.getPen()
for c in range(maxContours):
drawMoveTo(pen, maxBox)
for s in range(maxSegments):
random.choice(drawCallbacks)(pen, maxBox)
drawClosePath(pen)
return f
class BoundsPen(BasePen):
def __init__(self, glyphSet, at, bt):
BasePen.__init__(self, glyphSet)
self.bounds = None
self._start = None
self._arrayTools = at
self._bezierTools = bt
def _moveTo(self, pt):
self._start = pt
def _addMoveTo(self):
if self._start is None:
return
bounds = self.bounds
if bounds:
self.bounds = self._arrayTools.updateBounds(bounds, self._start)
else:
x, y = self._start
self.bounds = (x, y, x, y)
self._start = None
def _lineTo(self, pt):
self._addMoveTo()
self.bounds = self._arrayTools.updateBounds(self.bounds, pt)
def _curveToOne(self, bcp1, bcp2, pt):
self._addMoveTo()
bounds = self.bounds
bounds = self._arrayTools.updateBounds(bounds, pt)
if not self._arrayTools.pointInRect(bcp1, bounds) or not self._arrayTools.pointInRect(bcp2, bounds):
bounds = self._arrayTools.unionRect(bounds, self._bezierTools.calcCubicBounds(
self._getCurrentPoint(), bcp1, bcp2, pt))
self.bounds = bounds
def _qCurveToOne(self, bcp, pt):
self._addMoveTo()
bounds = self.bounds
bounds = self._arrayTools.updateBounds(bounds, pt)
if not self._arrayTools.pointInRect(bcp, bounds):
bounds = self._arrayToolsunionRect(bounds, self._bezierTools.calcQuadraticBounds(
self._getCurrentPoint(), bcp, pt))
self.bounds = bounds
def _testFont(font):
succes = True
for glyph in font:
fontToolsBoundsPen = BoundsPen(font, arrayTools, bezierTools)
glyph.draw(fontToolsBoundsPen)
noNumpyBoundsPen = BoundsPen(font, noNumpyArrayTools, noNumpyBezierTools)
glyph.draw(noNumpyBoundsPen)
if fontToolsBoundsPen.bounds != noNumpyBoundsPen.bounds:
succes = False
return succes
def testCompareAgainstFontTools():
"""
>>> font = createRandomFont()
>>> _testFont(font)
True
"""
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,15 +0,0 @@
"""
Directory for modules supporting
Unified
Font
Objects
"""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
"""
Directory for all pen modules.
If you make a pen, put it here so that we can keep track of it.
"""

View File

@ -1,245 +0,0 @@
import math
from fontTools.pens.basePen import AbstractPen
from ufoLib.pointPen import AbstractPointPen
from robofab.pens.pointPen import BasePointToSegmentPen
class PointToSegmentPen(BasePointToSegmentPen):
"""Adapter class that converts the PointPen protocol to the
(Segment)Pen protocol.
"""
def __init__(self, segmentPen, outputImpliedClosingLine=False):
BasePointToSegmentPen.__init__(self)
self.pen = segmentPen
self.outputImpliedClosingLine = outputImpliedClosingLine
def _flushContour(self, segments):
assert len(segments) >= 1
pen = self.pen
if segments[0][0] == "move":
# It's an open path.
closed = False
points = segments[0][1]
assert len(points) == 1
movePt, smooth, name, kwargs = points[0]
del segments[0]
else:
# It's a closed path, do a moveTo to the last
# point of the last segment.
closed = True
segmentType, points = segments[-1]
movePt, smooth, name, kwargs = points[-1]
if movePt is None:
# quad special case: a contour with no on-curve points contains
# one "qcurve" segment that ends with a point that's None. We
# must not output a moveTo() in that case.
pass
else:
pen.moveTo(movePt)
outputImpliedClosingLine = self.outputImpliedClosingLine
nSegments = len(segments)
for i in range(nSegments):
segmentType, points = segments[i]
points = [pt for pt, smooth, name, kwargs in points]
if segmentType == "line":
assert len(points) == 1
pt = points[0]
if i + 1 != nSegments or outputImpliedClosingLine or not closed:
pen.lineTo(pt)
elif segmentType == "curve":
pen.curveTo(*points)
elif segmentType == "qcurve":
pen.qCurveTo(*points)
else:
assert 0, "illegal segmentType: %s" % segmentType
if closed:
pen.closePath()
else:
pen.endPath()
def addComponent(self, glyphName, transform):
self.pen.addComponent(glyphName, transform)
class SegmentToPointPen(AbstractPen):
"""Adapter class that converts the (Segment)Pen protocol to the
PointPen protocol.
"""
def __init__(self, pointPen, guessSmooth=True):
if guessSmooth:
self.pen = GuessSmoothPointPen(pointPen)
else:
self.pen = pointPen
self.contour = None
def _flushContour(self):
pen = self.pen
pen.beginPath()
for pt, segmentType in self.contour:
pen.addPoint(pt, segmentType=segmentType)
pen.endPath()
def moveTo(self, pt):
self.contour = []
self.contour.append((pt, "move"))
def lineTo(self, pt):
self.contour.append((pt, "line"))
def curveTo(self, *pts):
for pt in pts[:-1]:
self.contour.append((pt, None))
self.contour.append((pts[-1], "curve"))
def qCurveTo(self, *pts):
if pts[-1] is None:
self.contour = []
for pt in pts[:-1]:
self.contour.append((pt, None))
if pts[-1] is not None:
self.contour.append((pts[-1], "qcurve"))
def closePath(self):
if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]:
self.contour[0] = self.contour[-1]
del self.contour[-1]
else:
# There's an implied line at the end, replace "move" with "line"
# for the first point
pt, tp = self.contour[0]
if tp == "move":
self.contour[0] = pt, "line"
self._flushContour()
self.contour = None
def endPath(self):
self._flushContour()
self.contour = None
def addComponent(self, glyphName, transform):
assert self.contour is None
self.pen.addComponent(glyphName, transform)
class TransformPointPen(AbstractPointPen):
"""PointPen that transforms all coordinates, and passes them to another
PointPen. It also transforms the transformation given to addComponent().
"""
def __init__(self, outPen, transformation):
if not hasattr(transformation, "transformPoint"):
from fontTools.misc.transform import Transform
transformation = Transform(*transformation)
self._transformation = transformation
self._transformPoint = transformation.transformPoint
self._outPen = outPen
self._stack = []
def beginPath(self):
self._outPen.beginPath()
def endPath(self):
self._outPen.endPath()
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
pt = self._transformPoint(pt)
self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
def addComponent(self, glyphName, transformation):
transformation = self._transformation.transform(transformation)
self._outPen.addComponent(glyphName, transformation)
class GuessSmoothPointPen(AbstractPointPen):
"""Filtering PointPen that tries to determine whether an on-curve point
should be "smooth", ie. that it's a "tangent" point or a "curve" point.
"""
def __init__(self, outPen):
self._outPen = outPen
self._points = None
def _flushContour(self):
points = self._points
nPoints = len(points)
if not nPoints:
return
if points[0][1] == "move":
# Open path.
indices = range(1, nPoints - 1)
elif nPoints > 1:
# Closed path. To avoid having to mod the contour index, we
# simply abuse Python's negative index feature, and start at -1
indices = range(-1, nPoints - 1)
else:
# closed path containing 1 point (!), ignore.
indices = []
for i in indices:
pt, segmentType, dummy, name, kwargs = points[i]
if segmentType is None:
continue
prev = i - 1
next = i + 1
if points[prev][1] is not None and points[next][1] is not None:
continue
# At least one of our neighbors is an off-curve point
pt = points[i][0]
prevPt = points[prev][0]
nextPt = points[next][0]
if pt != prevPt and pt != nextPt:
dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1]
dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1]
a1 = math.atan2(dx1, dy1)
a2 = math.atan2(dx2, dy2)
if abs(a1 - a2) < 0.05:
points[i] = pt, segmentType, True, name, kwargs
for pt, segmentType, smooth, name, kwargs in points:
self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
def beginPath(self):
assert self._points is None
self._points = []
self._outPen.beginPath()
def endPath(self):
self._flushContour()
self._outPen.endPath()
self._points = None
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._points.append((pt, segmentType, False, name, kwargs))
def addComponent(self, glyphName, transformation):
assert self._points is None
self._outPen.addComponent(glyphName, transformation)
if __name__ == "__main__":
from fontTools.pens.basePen import _TestPen as PSPen
from robofab.pens.printingPens import PrintingPointPen
segmentPen = PSPen(None)
# pen = PointToSegmentPen(SegmentToPointPen(PointToSegmentPen(PSPen(None))))
pen = PointToSegmentPen(SegmentToPointPen(PrintingPointPen()))
# pen = PrintingPointPen()
pen = PointToSegmentPen(PSPen(None), outputImpliedClosingLine=False)
# pen.beginPath()
# pen.addPoint((50, 50), name="an anchor")
# pen.endPath()
pen.beginPath()
pen.addPoint((-100, 0), segmentType="line")
pen.addPoint((0, 0), segmentType="line")
pen.addPoint((0, 100), segmentType="line")
pen.addPoint((30, 200))
pen.addPoint((50, 100), name="superbezcontrolpoint!")
pen.addPoint((70, 200))
pen.addPoint((100, 100), segmentType="curve")
pen.addPoint((100, 0), segmentType="line")
pen.endPath()
# pen.addComponent("a", (1, 0, 0, 1, 100, 200))

View File

@ -1,144 +0,0 @@
from robofab.world import RFont
from fontTools.pens.basePen import BasePen
from robofab.misc.arrayTools import updateBounds, pointInRect, unionRect
from robofab.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
from robofab.pens.filterPen import _estimateCubicCurveLength, _getCubicPoint
import math
__all__ = ["AngledMarginPen", "getAngledMargins",
"setAngledLeftMargin", "setAngledRightMargin",
"centerAngledMargins"]
class AngledMarginPen(BasePen):
"""
Pen to calculate the margins according to a slanted coordinate system. Slant angle comes from font.info.italicAngle.
- this pen works on the on-curve points, and approximates the distance to curves.
- results will be float.
- when used in FontLab, the resulting margins may be slightly different from the values originally set, due to rounding errors.
Notes:
- similar to what RoboFog used to do.
- RoboFog had a special attribute for "italicoffset", horizontal shift of all glyphs. This is missing in Robofab.
"""
def __init__(self, glyphSet, width, italicAngle):
BasePen.__init__(self, glyphSet)
self.width = width
self._angle = math.radians(90+italicAngle)
self.maxSteps = 100
self.margin = None
self._left = None
self._right = None
self._start = None
self.currentPt = None
def _getAngled(self, pt):
print "_getAngled", pt
r = (self.width + (pt[1] / math.tan(self._angle)))-pt[0]
l = pt[0]-((pt[1] / math.tan(self._angle)))
if self._right is None:
self._right = r
else:
self._right = min(self._right, r)
if self._left is None:
self._left = l
else:
self._left = min(self._left, l)
self.margin = self._left, self._right
def _moveTo(self, pt):
self._start = self.currentPt = pt
def _addMoveTo(self):
if self._start is None:
return
self._start = self.currentPt = None
def _lineTo(self, pt):
self._addMoveTo()
print "_lineTo"
self._getAngled(pt)
def _curveToOne(self, pt1, pt2, pt3):
step = 1.0/self.maxSteps
factors = range(0, self.maxSteps+1)
for i in factors:
print "_curveToOne", i
pt = _getCubicPoint(i*step, self.currentPt, pt1, pt2, pt3)
self._getAngled(pt)
self.currentPt = pt3
def _qCurveToOne(self, bcp, pt):
self._addMoveTo()
# add curve tracing magic here.
print "_qCurveToOne"
self._getAngled(pt)
self.currentPt = pt3
def getAngledMargins(glyph, font):
"""Convenience function, returns the angled margins for this glyph. Adjusted for font.info.italicAngle."""
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle)
glyph.draw(pen)
return pen.margin
def setAngledLeftMargin(glyph, font, value):
"""Convenience function, sets the left angled margin to value. Adjusted for font.info.italicAngle."""
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle)
g.draw(pen)
isLeft, isRight = pen.margin
glyph.leftMargin += value-isLeft
def setAngledRightMargin(glyph, font, value):
"""Convenience function, sets the right angled margin to value. Adjusted for font.info.italicAngle."""
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle)
g.draw(pen)
isLeft, isRight = pen.margin
glyph.rightMargin += value-isRight
def centerAngledMargins(glyph, font):
"""Convenience function, centers the glyph on angled margins."""
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle)
g.draw(pen)
isLeft, isRight = pen.margin
setAngledLeftMargin(glyph, font, (isLeft+isRight)*.5)
setAngledRightMargin(glyph, font, (isLeft+isRight)*.5)
def guessItalicOffset(glyph, font):
"""Guess the italic offset based on the margins of a symetric glyph.
For instance H or I.
"""
l, r = getAngledMargins(glyph, font)
return l - (l+r)*.5
if __name__ == "__main__":
def makeTestGlyph():
# make a simple glyph that we can test the pens with.
from robofab.objects.objectsRF import RGlyph
testGlyph = RGlyph()
testGlyph.name = "testGlyph"
testGlyph.width = 1000
pen = testGlyph.getPen()
pen.moveTo((100, 100))
pen.lineTo((900, 100))
pen.lineTo((900, 800))
pen.lineTo((100, 800))
# a curve
pen.curveTo((120, 700), (120, 300), (100, 100))
pen.closePath()
return testGlyph
def angledMarginPenTest():
testGlyph = makeTestGlyph()
glyphSet = {}
testPen = AngledMarginPen(glyphSet, width=0, italicAngle=10)
testGlyph.draw(testPen)
print testPen.margin
angledMarginPenTest()

View File

@ -1,122 +0,0 @@
from fontTools.pens.basePen import BasePen
from robofab.misc.arrayTools import updateBounds, pointInRect, unionRect
from robofab.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
__all__ = ["BoundsPen", "ControlBoundsPen"]
class ControlBoundsPen(BasePen):
"""Pen to calculate the "control bounds" of a shape. This is the
bounding box of all control points __on closed paths__, so may be larger than the
actual bounding box if there are curves that don't have points
on their extremes.
Single points, or anchors, are ignored.
When the shape has been drawn, the bounds are available as the
'bounds' attribute of the pen object. It's a 4-tuple:
(xMin, yMin, xMax, yMax)
This replaces fontTools/pens/boundsPen (temporarily?)
The fontTools bounds pen takes lose anchor points into account,
this one doesn't.
"""
def __init__(self, glyphSet):
BasePen.__init__(self, glyphSet)
self.bounds = None
self._start = None
def _moveTo(self, pt):
self._start = pt
def _addMoveTo(self):
if self._start is None:
return
bounds = self.bounds
if bounds:
self.bounds = updateBounds(bounds, self._start)
else:
x, y = self._start
self.bounds = (x, y, x, y)
self._start = None
def _lineTo(self, pt):
self._addMoveTo()
self.bounds = updateBounds(self.bounds, pt)
def _curveToOne(self, bcp1, bcp2, pt):
self._addMoveTo()
bounds = self.bounds
bounds = updateBounds(bounds, bcp1)
bounds = updateBounds(bounds, bcp2)
bounds = updateBounds(bounds, pt)
self.bounds = bounds
def _qCurveToOne(self, bcp, pt):
self._addMoveTo()
bounds = self.bounds
bounds = updateBounds(bounds, bcp)
bounds = updateBounds(bounds, pt)
self.bounds = bounds
class BoundsPen(ControlBoundsPen):
"""Pen to calculate the bounds of a shape. It calculates the
correct bounds even when the shape contains curves that don't
have points on their extremes. This is somewhat slower to compute
than the "control bounds".
When the shape has been drawn, the bounds are available as the
'bounds' attribute of the pen object. It's a 4-tuple:
(xMin, yMin, xMax, yMax)
"""
def _curveToOne(self, bcp1, bcp2, pt):
self._addMoveTo()
bounds = self.bounds
bounds = updateBounds(bounds, pt)
if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
bounds = unionRect(bounds, calcCubicBounds(
self._getCurrentPoint(), bcp1, bcp2, pt))
self.bounds = bounds
def _qCurveToOne(self, bcp, pt):
self._addMoveTo()
bounds = self.bounds
bounds = updateBounds(bounds, pt)
if not pointInRect(bcp, bounds):
bounds = unionRect(bounds, calcQuadraticBounds(
self._getCurrentPoint(), bcp, pt))
self.bounds = bounds
if __name__ == "__main__":
def makeTestGlyph():
# make a simple glyph that we can test the pens with.
from robofab.objects.objectsRF import RGlyph
testGlyph = RGlyph()
testGlyph.name = "testGlyph"
testGlyph.width = 1000
pen = testGlyph.getPen()
pen.moveTo((100, 100))
pen.lineTo((900, 100))
pen.lineTo((900, 800))
pen.lineTo((100, 800))
# a curve
pen.curveTo((120, 700), (120, 300), (100, 100))
pen.closePath()
return testGlyph
def controlBoundsPenTest():
testGlyph = makeTestGlyph()
glyphSet = {}
testPen = ControlBoundsPen(glyphSet)
testGlyph.draw(testPen)
assert testPen.bounds == (100, 100, 900, 800)
controlBoundsPenTest()

View File

@ -1,86 +0,0 @@
from ufoLib.pointPen import AbstractPointPen
class DigestPointPen(AbstractPointPen):
"""This calculates a tuple representing the structure and values in a glyph:
- including coordinates
- including components
>>> from robofab.pens.digestPen import DigestPointPen
>>> pen = DigestPointPen()
>>> g = CurrentGlyph()
>>> g.drawPoints(pen)
>>> pen.getDigest()
('beginPath', ((25, 425), 'line', False, None),((25, 0), 'line', False, None),((95, 0), 'line', False, None),((95, 425), 'line', False, None), 'endPath',
'beginPath', ((25, 595), 'line', False, None),((25, 491), 'line', False, None),((95, 491), 'line', False, None),((95, 595), 'line', False, None), 'endPath')
"""
def __init__(self, ignoreSmoothAndName=False):
self._data = []
self.ignoreSmoothAndName = ignoreSmoothAndName
def beginPath(self):
self._data.append('beginPath')
def endPath(self):
self._data.append('endPath')
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
if self.ignoreSmoothAndName:
self._data.append((pt, segmentType))
else:
self._data.append((pt, segmentType, smooth, name))
def addComponent(self, baseGlyphName, transformation):
t = []
for v in transformation:
if int(v) == v:
t.append(int(v))
else:
t.append(v)
self._data.append((baseGlyphName, tuple(t)))
def getDigest(self):
"""Return the digest as a tuple with all coordinates of all points."""
return tuple(self._data)
def getDigestPointsOnly(self, needSort=True):
""" Return the digest as a tuple with all coordinates of all points,
- but without smooth info or drawing instructions.
- For instance if you want to compare 2 glyphs in shape,
but not interpolatability.
"""
points = []
from types import TupleType
for item in self._data:
if type(item) == TupleType:
points.append(item[0])
if needSort:
points.sort()
return tuple(points)
class DigestPointStructurePen(DigestPointPen):
"""This calculates a tuple representing the structure and values in a glyph:
- excluding coordinates
- excluding components
>>> from robofab.pens.digestPen import DigestPointStructurePen
>>> pen = DigestPointStructurePen()
>>> g = CurrentGlyph()
>>> g.drawPoints(pen)
>>> pen.getDigest()
('beginPath', 'line', 'line', 'line', 'line', 'endPath', 'beginPath', 'line', 'line', 'line', 'line', 'endPath')
"""
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._data.append(segmentType)
def addComponent(self, baseGlyphName, transformation):
self._data.append(baseGlyphName)

View File

@ -1,228 +0,0 @@
"""
=============================
Filters: chaining pen objects
=============================
By chaining two (or more) pen objects together, the first pen can function as a filter to the second. The first pen receives data from (for instance) glyph.draw() or glyph.drawPoints(). The filter pen then processes the data and passes new, fresh data to the second pen. In turn the second pen can draw in a glyph.
Filterpens need to be initialised with a second pen object that will receive the processed data. The examples in this module, ThresholdPen and FlattenPen, have proven useful in a number of projects. For convenienve, each pen has a wrapper function that takes care of making a new glyph to dump the processed data in, and making the appropriate pen instances. But these are non-normative examples.
All pens in this module subclass from fontTools.pens.basePen.
"""
from fontTools.pens.basePen import AbstractPen, BasePen
from ufoLib.pointPen import AbstractPointPen
from robofab.objects.objectsRF import RGlyph as _RGlyph
from robofab.objects.objectsBase import _interpolatePt
import math
#
# threshold filtering
#
def distance(pt1, pt2):
return math.sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
class ThresholdPen(AbstractPen):
"""This pen only draws segments longer in length than the threshold value.
- otherPen: a different segment pen object this filter should draw the results with.
- threshold: the minimum length of a segment
"""
def __init__(self, otherPen, threshold=10):
self.threshold = threshold
self._lastPt = None
self.otherPen = otherPen
def moveTo(self, pt):
self._lastPt = pt
self.otherPen.moveTo(pt)
def lineTo(self, pt, smooth=False):
if self.threshold <= distance(pt, self._lastPt):
self.otherPen.lineTo(pt)
self._lastPt = pt
def curveTo(self, pt1, pt2, pt3):
if self.threshold <= distance(pt3, self._lastPt):
self.otherPen.curveTo(pt1, pt2, pt3)
self._lastPt = pt3
def qCurveTo(self, *points):
if self.threshold <= distance(points[-1], self._lastPt):
self.otherPen.qCurveTo(*points)
self._lastPt = points[-1]
def closePath(self):
self.otherPen.closePath()
def endPath(self):
self.otherPen.endPath()
def addComponent(self, glyphName, transformation):
self.otherPen.addComponent(glyphName, transformation)
def thresholdGlyph(aGlyph, threshold=10):
"""Convenience function that applies the **ThresholdPen** to a glyph. Returns a new glyph object (from objectsRF.RGlyph)."""
from robofab.pens.adapterPens import PointToSegmentPen
new = _RGlyph()
filterpen = ThresholdPen(new.getPen(), threshold)
wrappedPen = PointToSegmentPen(filterpen)
aGlyph.drawPoints(wrappedPen)
aGlyph.clear()
aGlyph.appendGlyph(new)
aGlyph.update()
return aGlyph
#
# Curve flattening
#
def _estimateCubicCurveLength(pt0, pt1, pt2, pt3, precision=10):
"""Estimate the length of this curve by iterating
through it and averaging the length of the flat bits.
"""
points = []
length = 0
step = 1.0/precision
factors = range(0, precision+1)
for i in factors:
points.append(_getCubicPoint(i*step, pt0, pt1, pt2, pt3))
for i in range(len(points)-1):
pta = points[i]
ptb = points[i+1]
length += distance(pta, ptb)
return length
def _mid((x0, y0), (x1, y1)):
"""(Point, Point) -> Point\nReturn the point that lies in between the two input points."""
return 0.5 * (x0 + x1), 0.5 * (y0 + y1)
def _getCubicPoint(t, pt0, pt1, pt2, pt3):
if t == 0:
return pt0
if t == 1:
return pt3
if t == 0.5:
a = _mid(pt0, pt1)
b = _mid(pt1, pt2)
c = _mid(pt2, pt3)
d = _mid(a, b)
e = _mid(b, c)
return _mid(d, e)
else:
cx = (pt1[0] - pt0[0]) * 3
cy = (pt1[1] - pt0[1]) * 3
bx = (pt2[0] - pt1[0]) * 3 - cx
by = (pt2[1] - pt1[1]) * 3 - cy
ax = pt3[0] - pt0[0] - cx - bx
ay = pt3[1] - pt0[1] - cy - by
t3 = t ** 3
t2 = t * t
x = ax * t3 + bx * t2 + cx * t + pt0[0]
y = ay * t3 + by * t2 + cy * t + pt0[1]
return x, y
class FlattenPen(BasePen):
"""This filter pen processes the contours into a series of straight lines by flattening the curves.
- otherPen: a different segment pen object this filter should draw the results with.
- approximateSegmentLength: the length you want the flattened segments to be (roughly).
- segmentLines: whether to cut straight lines into segments as well.
- filterDoubles: don't draw if a segment goes to the same coordinate.
"""
def __init__(self, otherPen, approximateSegmentLength=5, segmentLines=False, filterDoubles=True):
self.approximateSegmentLength = approximateSegmentLength
BasePen.__init__(self, {})
self.otherPen = otherPen
self.currentPt = None
self.firstPt = None
self.segmentLines = segmentLines
self.filterDoubles = filterDoubles
def _moveTo(self, pt):
self.otherPen.moveTo(pt)
self.currentPt = pt
self.firstPt = pt
def _lineTo(self, pt):
if self.filterDoubles:
if pt == self.currentPt:
return
if not self.segmentLines:
self.otherPen.lineTo(pt)
self.currentPt = pt
return
d = distance(self.currentPt, pt)
maxSteps = int(round(d / self.approximateSegmentLength))
if maxSteps < 1:
self.otherPen.lineTo(pt)
self.currentPt = pt
return
step = 1.0/maxSteps
factors = range(0, maxSteps+1)
for i in factors[1:]:
self.otherPen.lineTo(_interpolatePt(self.currentPt, pt, i*step))
self.currentPt = pt
def _curveToOne(self, pt1, pt2, pt3):
est = _estimateCubicCurveLength(self.currentPt, pt1, pt2, pt3)/self.approximateSegmentLength
maxSteps = int(round(est))
falseCurve = (pt1==self.currentPt) and (pt2==pt3)
if maxSteps < 1 or falseCurve:
self.otherPen.lineTo(pt3)
self.currentPt = pt3
return
step = 1.0/maxSteps
factors = range(0, maxSteps+1)
for i in factors[1:]:
pt = _getCubicPoint(i*step, self.currentPt, pt1, pt2, pt3)
self.otherPen.lineTo(pt)
self.currentPt = pt3
def _closePath(self):
self.lineTo(self.firstPt)
self.otherPen.closePath()
self.currentPt = None
def _endPath(self):
self.otherPen.endPath()
self.currentPt = None
def addComponent(self, glyphName, transformation):
self.otherPen.addComponent(glyphName, transformation)
def flattenGlyph(aGlyph, threshold=10, segmentLines=True):
"""Convenience function that applies the **FlattenPen** pen to a glyph. Returns a new glyph object."""
from robofab.pens.adapterPens import PointToSegmentPen
if len(aGlyph.contours) == 0:
return
new = _RGlyph()
writerPen = new.getPen()
filterpen = FlattenPen(writerPen, threshold, segmentLines)
wrappedPen = PointToSegmentPen(filterpen)
aGlyph.drawPoints(wrappedPen)
aGlyph.clear()
aGlyph.appendGlyph(new)
aGlyph.update()
return aGlyph

View File

@ -1,274 +0,0 @@
"""Pens for creating glyphs in FontLab."""
__all__ = ["FLPen", "FLPointPen", "drawFLGlyphOntoPointPen"]
from FL import *
try:
from fl_cmd import *
except ImportError:
print "The fl_cmd module is not available here. flPen.py"
from robofab.tools.toolsFL import NewGlyph
from ufoLib.pointPen import AbstractPointPen
from robofab.pens.adapterPens import SegmentToPointPen
def roundInt(x):
return int(round(x))
class FLPen(SegmentToPointPen):
def __init__(self, glyph):
SegmentToPointPen.__init__(self, FLPointPen(glyph))
class FLPointPen(AbstractPointPen):
def __init__(self, glyph):
if hasattr(glyph, "isRobofab"):
self.glyph = glyph.naked()
else:
self.glyph = glyph
self.currentPath = None
def beginPath(self):
self.currentPath = []
def endPath(self):
# Love is... abstracting away FL's madness.
path = self.currentPath
self.currentPath = None
glyph = self.glyph
if len(path) == 1 and path[0][3] is not None:
# Single point on the contour, it has a name. Make it an anchor.
x, y = path[0][0]
name = path[0][3]
anchor = Anchor(name, roundInt(x), roundInt(y))
glyph.anchors.append(anchor)
return
firstOnCurveIndex = None
for i in range(len(path)):
if path[i][1] is not None:
firstOnCurveIndex = i
break
if firstOnCurveIndex is None:
# TT special case: on-curve-less contour. FL doesn't support that,
# so we insert an implied point at the end.
x1, y1 = path[0][0]
x2, y2 = path[-1][0]
impliedPoint = 0.5 * (x1 + x2), 0.5 * (y1 + y2)
path.append((impliedPoint, "qcurve", True, None))
firstOnCurveIndex = 0
path = path[firstOnCurveIndex + 1:] + path[:firstOnCurveIndex + 1]
firstPoint, segmentType, smooth, name = path[-1]
closed = True
if segmentType == "move":
path = path[:-1]
closed = False
# XXX The contour is not closed, but I can't figure out how to
# create an open contour in FL. Creating one by hand shows type"0x8011"
# for a move node in an open contour, but I'm not able to access
# that flag.
elif segmentType == "line":
# The contour is closed and ends in a lineto, which is redundant
# as it's implied by closepath.
path = path[:-1]
x, y = firstPoint
node = Node(nMOVE, Point(roundInt(x), roundInt(y)))
if smooth and closed:
if segmentType == "line" or path[0][1] == "line":
node.alignment = nFIXED
else:
node.alignment = nSMOOTH
glyph.Insert(node, len(glyph))
segment = []
nPoints = len(path)
for i in range(nPoints):
pt, segmentType, smooth, name = path[i]
segment.append(pt)
if segmentType is None:
continue
if segmentType == "curve":
if len(segment) < 2:
segmentType = "line"
elif len(segment) == 2:
segmentType = "qcurve"
if segmentType == "qcurve":
for x, y in segment[:-1]:
glyph.Insert(Node(nOFF, Point(roundInt(x), roundInt(y))), len(glyph))
x, y = segment[-1]
node = Node(nLINE, Point(roundInt(x), roundInt(y)))
glyph.Insert(node, len(glyph))
elif segmentType == "curve":
if len(segment) == 3:
cubicSegments = [segment]
else:
from fontTools.pens.basePen import decomposeSuperBezierSegment
cubicSegments = decomposeSuperBezierSegment(segment)
nSegments = len(cubicSegments)
for i in range(nSegments):
pt1, pt2, pt3 = cubicSegments[i]
x, y = pt3
node = Node(nCURVE, Point(roundInt(x), roundInt(y)))
node.points[1].x, node.points[1].y = roundInt(pt1[0]), roundInt(pt1[1])
node.points[2].x, node.points[2].y = roundInt(pt2[0]), roundInt(pt2[1])
if i != nSegments - 1:
node.alignment = nSMOOTH
glyph.Insert(node, len(self.glyph))
elif segmentType == "line":
assert len(segment) == 1, segment
x, y = segment[0]
node = Node(nLINE, Point(roundInt(x), roundInt(y)))
glyph.Insert(node, len(glyph))
else:
assert 0, "unsupported curve type (%s)" % segmentType
if smooth:
if i + 1 < nPoints or closed:
# Can't use existing node, as you can't change node attributes
# AFTER it's been appended to the glyph.
node = glyph[-1]
if segmentType == "line" or path[(i+1) % nPoints][1] == "line":
# tangent
node.alignment = nFIXED
else:
# curve
node.alignment = nSMOOTH
segment = []
if closed:
# we may have output a node too much
node = glyph[-1]
if node.type == nLINE and (node.x, node.y) == (roundInt(firstPoint[0]), roundInt(firstPoint[1])):
glyph.DeleteNode(len(glyph) - 1)
def addPoint(self, pt, segmentType=None, smooth=None, name=None, **kwargs):
self.currentPath.append((pt, segmentType, smooth, name))
def addComponent(self, baseName, transformation):
assert self.currentPath is None
# make base glyph if needed, Component() needs the index
NewGlyph(self.glyph.parent, baseName, updateFont=False)
baseIndex = self.glyph.parent.FindGlyph(baseName)
if baseIndex == -1:
raise KeyError, "couldn't find or make base glyph"
xx, xy, yx, yy, dx, dy = transformation
# XXX warn when xy or yx != 0
new = Component(baseIndex, Point(dx, dy), Point(xx, yy))
self.glyph.components.append(new)
def drawFLGlyphOntoPointPen(flGlyph, pen):
"""Draw a FontLab glyph onto a PointPen."""
for anchor in flGlyph.anchors:
pen.beginPath()
pen.addPoint((anchor.x, anchor.y), name=anchor.name)
pen.endPath()
for contour in _getContours(flGlyph):
pen.beginPath()
for pt, segmentType, smooth in contour:
pen.addPoint(pt, segmentType=segmentType, smooth=smooth)
pen.endPath()
for baseGlyph, tranform in _getComponents(flGlyph):
pen.addComponent(baseGlyph, tranform)
class FLPointContourPen(FLPointPen):
"""Same as FLPointPen, except that it ignores components."""
def addComponent(self, baseName, transformation):
pass
NODE_TYPES = {nMOVE: "move", nLINE: "line", nCURVE: "curve",
nOFF: None}
def _getContours(glyph):
contours = []
for i in range(len(glyph)):
node = glyph[i]
segmentType = NODE_TYPES[node.type]
if segmentType == "move":
contours.append([])
for pt in node.points[1:]:
contours[-1].append(((pt.x, pt.y), None, False))
smooth = node.alignment != nSHARP
contours[-1].append(((node.x, node.y), segmentType, smooth))
for contour in contours:
# filter out or change the move
movePt, segmentType, smooth = contour[0]
assert segmentType == "move"
lastSegmentType = contour[-1][1]
if movePt == contour[-1][0] and lastSegmentType == "curve":
contour[0] = contour[-1]
contour.pop()
elif lastSegmentType is None:
contour[0] = movePt, "qcurve", smooth
else:
assert lastSegmentType in ("line", "curve")
contour[0] = movePt, "line", smooth
# change "line" to "qcurve" if appropriate
previousSegmentType = "ArbitraryValueOtherThanNone"
for i in range(len(contour)):
pt, segmentType, smooth = contour[i]
if segmentType == "line" and previousSegmentType is None:
contour[i] = pt, "qcurve", smooth
previousSegmentType = segmentType
return contours
def _simplifyValues(*values):
"""Given a set of numbers, convert items to ints if they are
integer float values, eg. 0.0, 1.0."""
newValues = []
for v in values:
i = int(v)
if v == i:
v = i
newValues.append(v)
return newValues
def _getComponents(glyph):
components = []
for comp in glyph.components:
baseName = glyph.parent[comp.index].name
dx, dy = comp.delta.x, comp.delta.y
sx, sy = comp.scale.x, comp.scale.y
dx, dy, sx, sy = _simplifyValues(dx, dy, sx, sy)
components.append((baseName, (sx, 0, 0, sy, dx, dy)))
return components
def test():
g = fl.glyph
g.Clear()
p = PLPen(g)
p.moveTo((50, 50))
p.lineTo((150,50))
p.lineTo((170, 200), smooth=2)
p.curveTo((173, 225), (150, 250), (120, 250), smooth=1)
p.curveTo((85, 250), (50, 200), (50, 200))
p.closePath()
p.moveTo((300, 300))
p.lineTo((400, 300))
p.curveTo((450, 325), (450, 375), (400, 400))
p.qCurveTo((400, 500), (350, 550), (300, 500), (300, 400))
p.closePath()
p.setWidth(600)
p.setNote("Hello, this is a note")
p.addAnchor("top", (250, 600))
fl.UpdateGlyph(-1)
fl.UpdateFont(-1)
if __name__ == "__main__":
test()

View File

@ -1,183 +0,0 @@
from fontTools.pens.basePen import AbstractPen, BasePen
from robofab.misc.bezierTools import splitLine, splitCubic
from sets import Set
class MarginPen(BasePen):
"""
Pen to calculate the margins at a given height or width.
- isHorizontal = True: slice the glyph at y=value.
- isHorizontal = False: slice the glyph at x=value.
>>> f = CurrentFont()
>>> g = CurrentGlyph()
>>> pen = MarginPen(f, 200, isHorizontal=True)
>>> g.draw(pen)
>>> print pen.getMargins()
(75.7881, 181.9713)
>>> pen = MarginPen(f, 200, isHorizontal=False)
>>> g.draw(pen)
>>> print pen.getMargins()
(26.385, 397.4469)
>>> print pen.getAll()
[75.7881, 181.9713]
>>> pen = MarginPen(f, 200, isHorizontal=False)
>>> g.draw(pen)
>>> print pen.getMargins()
(26.385, 397.4469)
>>> print pen.getAll()
[26.385, 171.6137, 268.0, 397.4469]
Possible optimisation:
Initialise the pen object with a list of points we want to measure,
then draw the glyph once, but do the splitLine() math for all measure points.
"""
def __init__(self, glyphSet, value, isHorizontal=True):
BasePen.__init__(self, glyphSet)
self.value = value
self.hits = {}
self.filterDoubles = True
self.contourIndex = None
self.startPt = None
self.isHorizontal = isHorizontal
def _moveTo(self, pt):
self.currentPt = pt
self.startPt = pt
if self.contourIndex is None:
self.contourIndex = 0
else:
self.contourIndex += 1
def _lineTo(self, pt):
if self.filterDoubles:
if pt == self.currentPt:
return
hits = splitLine(self.currentPt, pt, self.value, self.isHorizontal)
if len(hits)>1:
# result will be 2 tuples of 2 coordinates
# first two points: start to intersect
# second two points: intersect to end
# so, second point in first tuple is the intersect
# then, the first coordinate of that point is the x.
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
if self.isHorizontal:
self.hits[self.contourIndex].append(round(hits[0][-1][0], 4))
else:
self.hits[self.contourIndex].append(round(hits[0][-1][1], 4))
if self.isHorizontal and pt[1] == self.value:
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt[0])
elif (not self.isHorizontal) and (pt[0] == self.value):
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt[1])
self.currentPt = pt
def _curveToOne(self, pt1, pt2, pt3):
hits = splitCubic(self.currentPt, pt1, pt2, pt3, self.value, self.isHorizontal)
for i in range(len(hits)-1):
# a number of intersections is possible. Just take the
# last point of each segment.
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
if self.isHorizontal:
self.hits[self.contourIndex].append(round(hits[i][-1][0], 4))
else:
self.hits[self.contourIndex].append(round(hits[i][-1][1], 4))
if self.isHorizontal and pt3[1] == self.value:
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt3[0])
if (not self.isHorizontal) and (pt3[0] == self.value):
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt3[1])
self.currentPt = pt3
def _closePath(self):
if self.currentPt != self.startPt:
self._lineTo(self.startPt)
self.currentPt = self.startPt = None
def _endPath(self):
self.currentPt = None
def addComponent(self, baseGlyph, transformation):
if self.glyphSet is None:
return
if baseGlyph in self.glyphSet:
glyph = self.glyphSet[baseGlyph]
if glyph is not None:
glyph.draw(self)
def getMargins(self):
"""Return the extremes of the slice for all contours combined, i.e. the whole glyph."""
allHits = []
for index, pts in self.hits.items():
allHits.extend(pts)
if allHits:
return min(allHits), max(allHits)
return None
def getContourMargins(self):
"""Return the extremes of the slice for each contour."""
allHits = {}
for index, pts in self.hits.items():
unique = list(Set(pts))
unique.sort()
allHits[index] = unique
return allHits
def getAll(self):
"""Return all the slices."""
allHits = []
for index, pts in self.hits.items():
allHits.extend(pts)
unique = list(Set(allHits))
unique = list(unique)
unique.sort()
return unique
if __name__ == "__main__":
def makeTestGlyph():
# make a simple glyph that we can test the pens with.
from robofab.objects.objectsRF import RGlyph
testGlyph = RGlyph()
testGlyph.name = "testGlyph"
testGlyph.width = 1000
pen = testGlyph.getPen()
pen.moveTo((100, 100))
pen.lineTo((900, 100))
pen.lineTo((900, 800))
pen.lineTo((100, 800))
# a curve
pen.curveTo((120, 700), (120, 300), (100, 100))
pen.closePath()
return testGlyph
def controlBoundsPenTest():
testGlyph = makeTestGlyph()
glyphSet = {}
value = 200
isHorizontal = True
testPen = MarginPen(glyphSet, value, isHorizontal)
testGlyph.draw(testPen)
assert testPen.getAll() == [107.5475, 200.0, 300.0]
controlBoundsPenTest()

View File

@ -1,186 +0,0 @@
"""Some pens that are needed during glyph math"""
from ufoLib.pointPen import AbstractPointPen
from robofab.pens.pointPen import BasePointToSegmentPen
class GetMathDataPointPen(AbstractPointPen):
"""
Point pen that converts all "line" segments into
curve segments containing two off curve points.
"""
def __init__(self):
self.contours = []
self.components = []
self.anchors = []
self._points = []
def _flushContour(self):
points = self._points
if len(points) == 1:
segmentType, pt, smooth, name = points[0]
self.anchors.append((pt, name))
else:
self.contours.append([])
prevOnCurve = None
offCurves = []
# deal with the first point
segmentType, pt, smooth, name = points[0]
# if it is an offcurve, add it to the offcurve list
if segmentType is None:
offCurves.append((segmentType, pt, smooth, name))
# if it is a line, change the type to curve and add it to the contour
# create offcurves corresponding with the last oncurve and
# this point and add them to the points list
elif segmentType == "line":
prevOnCurve = pt
self.contours[-1].append(("curve", pt, False, name))
lastPoint = points[-1][1]
points.append((None, lastPoint, False, None))
points.append((None, pt, False, None))
# a move, curve or qcurve. simple append the data.
else:
self.contours[-1].append((segmentType, pt, smooth, name))
prevOnCurve = pt
# now go through the rest of the points
for segmentType, pt, smooth, name in points[1:]:
# store the off curves
if segmentType is None:
offCurves.append((segmentType, pt, smooth, name))
continue
# make off curve corresponding the the previous
# on curve an dthis point
if segmentType == "line":
segmentType = "curve"
offCurves.append((None, prevOnCurve, False, None))
offCurves.append((None, pt, False, None))
# add the offcurves to the contour
for offCurve in offCurves:
self.contours[-1].append(offCurve)
# add the oncurve to the contour
self.contours[-1].append((segmentType, pt, smooth, name))
# reset the stored data
prevOnCurve = pt
offCurves = []
# catch offcurves that belong to the first
if len(offCurves) != 0:
self.contours[-1].extend(offCurves)
def beginPath(self):
self._points = []
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._points.append((segmentType, pt, smooth, name))
def endPath(self):
self._flushContour()
def addComponent(self, baseGlyphName, transformation):
self.components.append((baseGlyphName, transformation))
def getData(self):
return {
'contours':list(self.contours),
'components':list(self.components),
'anchors':list(self.anchors)
}
class CurveSegmentFilterPointPen(AbstractPointPen):
"""
Filtering Point pen that turns curve segments that contain
unnecessary anchor points into line segments.
"""
# XXX it would be great if this also checked to see if the
# off curves are on the segment and therefre unneeded
def __init__(self, anotherPointPen):
self._pen = anotherPointPen
self._points = []
def _flushContour(self):
points = self._points
# an anchor
if len(points) == 1:
pt, segmentType, smooth, name = points[0]
self._pen.addPoint(pt, segmentType, smooth, name)
else:
prevOnCurve = None
offCurves = []
pointsToDraw = []
# deal with the first point
pt, segmentType, smooth, name = points[0]
# if it is an offcurve, add it to the offcurve list
if segmentType is None:
offCurves.append((pt, segmentType, smooth, name))
else:
# potential redundancy
if segmentType == "curve":
# gather preceding off curves
testOffCurves = []
lastPoint = None
for i in xrange(len(points)):
i = -i - 1
testPoint = points[i]
testSegmentType = testPoint[1]
if testSegmentType is not None:
lastPoint = testPoint[0]
break
testOffCurves.append(testPoint[0])
# if two offcurves exist we can test for redundancy
if len(testOffCurves) == 2:
if testOffCurves[1] == lastPoint and testOffCurves[0] == pt:
segmentType = "line"
# remove the last two points
points = points[:-2]
# add the point to the contour
pointsToDraw.append((pt, segmentType, smooth, name))
prevOnCurve = pt
for pt, segmentType, smooth, name in points[1:]:
# store offcurves
if segmentType is None:
offCurves.append((pt, segmentType, smooth, name))
continue
# curves are a potential redundancy
elif segmentType == "curve":
if len(offCurves) == 2:
# test for redundancy
if offCurves[0][0] == prevOnCurve and offCurves[1][0] == pt:
offCurves = []
segmentType = "line"
# add all offcurves
for offCurve in offCurves:
pointsToDraw.append(offCurve)
# add the on curve
pointsToDraw.append((pt, segmentType, smooth, name))
# reset the stored data
prevOnCurve = pt
offCurves = []
# catch any remaining offcurves
if len(offCurves) != 0:
for offCurve in offCurves:
pointsToDraw.append(offCurve)
# draw to the pen
for pt, segmentType, smooth, name in pointsToDraw:
self._pen.addPoint(pt, segmentType, smooth, name)
def beginPath(self):
self._points = []
self._pen.beginPath()
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._points.append((pt, segmentType, smooth, name))
def endPath(self):
self._flushContour()
self._pen.endPath()
def addComponent(self, baseGlyphName, transformation):
self._pen.addComponent(baseGlyphName, transformation)

View File

@ -1,172 +0,0 @@
from fontTools.pens.basePen import AbstractPen
from ufoLib.pointPen import AbstractPointPen
__all__ = ["AbstractPointPen", "BasePointToSegmentPen", "PrintingPointPen",
"PrintingSegmentPen", "SegmentPrintingPointPen"]
class BasePointToSegmentPen(AbstractPointPen):
"""Base class for retrieving the outline in a segment-oriented
way. The PointPen protocol is simple yet also a little tricky,
so when you need an outline presented as segments but you have
as points, do use this base implementation as it properly takes
care of all the edge cases.
"""
def __init__(self):
self.currentPath = None
def beginPath(self, **kwargs):
assert self.currentPath is None
self.currentPath = []
def _flushContour(self, segments):
"""Override this method.
It will be called for each non-empty sub path with a list
of segments: the 'segments' argument.
The segments list contains tuples of length 2:
(segmentType, points)
segmentType is one of "move", "line", "curve" or "qcurve".
"move" may only occur as the first segment, and it signifies
an OPEN path. A CLOSED path does NOT start with a "move", in
fact it will not contain a "move" at ALL.
The 'points' field in the 2-tuple is a list of point info
tuples. The list has 1 or more items, a point tuple has
four items:
(point, smooth, name, kwargs)
'point' is an (x, y) coordinate pair.
For a closed path, the initial moveTo point is defined as
the last point of the last segment.
The 'points' list of "move" and "line" segments always contains
exactly one point tuple.
"""
raise NotImplementedError
def endPath(self):
assert self.currentPath is not None
points = self.currentPath
self.currentPath = None
if not points:
return
if len(points) == 1:
# Not much more we can do than output a single move segment.
pt, segmentType, smooth, name, kwargs = points[0]
segments = [("move", [(pt, smooth, name, kwargs)])]
self._flushContour(segments)
return
segments = []
if points[0][1] == "move":
# It's an open contour, insert a "move" segment for the first
# point and remove that first point from the point list.
pt, segmentType, smooth, name, kwargs = points[0]
segments.append(("move", [(pt, smooth, name, kwargs)]))
points.pop(0)
else:
# It's a closed contour. Locate the first on-curve point, and
# rotate the point list so that it _ends_ with an on-curve
# point.
firstOnCurve = None
for i in range(len(points)):
segmentType = points[i][1]
if segmentType is not None:
firstOnCurve = i
break
if firstOnCurve is None:
# Special case for quadratics: a contour with no on-curve
# points. Add a "None" point. (See also the Pen protocol's
# qCurveTo() method and fontTools.pens.basePen.py.)
points.append((None, "qcurve", None, None, None))
else:
points = points[firstOnCurve+1:] + points[:firstOnCurve+1]
currentSegment = []
for pt, segmentType, smooth, name, kwargs in points:
currentSegment.append((pt, smooth, name, kwargs))
if segmentType is None:
continue
segments.append((segmentType, currentSegment))
currentSegment = []
self._flushContour(segments)
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self.currentPath.append((pt, segmentType, smooth, name, kwargs))
class PrintingPointPen(AbstractPointPen):
def __init__(self):
self.havePath = False
def beginPath(self, identifier=None, **kwargs):
self.havePath = True
args = []
if identifier is not None:
args.append("identifier=%r" % identifier)
if kwargs:
args.append("**%s" % kwargs)
print "pen.beginPath(%s)" % ", ".join(args)
def endPath(self):
self.havePath = False
print "pen.endPath()"
def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs):
assert self.havePath
args = ["(%s, %s)" % (pt[0], pt[1])]
if segmentType is not None:
args.append("segmentType=%r" % segmentType)
if smooth:
args.append("smooth=True")
if name is not None:
args.append("name=%r" % name)
if identifier is not None:
args.append("identifier=%r" % identifier)
if kwargs:
args.append("**%s" % kwargs)
print "pen.addPoint(%s)" % ", ".join(args)
def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
assert not self.havePath
args = [baseGlyphName, str(tuple(transformation))]
if identifier is not None:
args.append("identifier=%r" % identifier)
if kwargs:
args.append("**%s" % kwargs)
print "pen.addComponent(%s)" % ", ".join(args)
class PrintingSegmentPen(AbstractPen):
def moveTo(self, pt):
print "pen.moveTo(%s)" % (pt,)
def lineTo(self, pt):
print "pen.lineTo(%s)" % (pt,)
def curveTo(self, *pts):
print "pen.curveTo%s" % (pts,)
def qCurveTo(self, *pts):
print "pen.qCurveTo%s" % (pts,)
def closePath(self):
print "pen.closePath()"
def endPath(self):
print "pen.endPath()"
def addComponent(self, baseGlyphName, transformation):
print "pen.addComponent(%r, %s)" % (baseGlyphName, tuple(transformation))
class SegmentPrintingPointPen(BasePointToSegmentPen):
def _flushContour(self, segments):
from pprint import pprint
pprint(segments)
if __name__ == "__main__":
p = SegmentPrintingPointPen()
from robofab.test.test_pens import TestShapes
TestShapes.onCurveLessQuadShape(p)

View File

@ -1,84 +0,0 @@
from robofab.pens.pointPen import BasePointToSegmentPen
from ufoLib.pointPen import AbstractPointPen
"""
Printing pens print their data. Useful for demos and debugging.
"""
__all__ = ["PrintingPointPen", "PrintingSegmentPen", "SegmentPrintingPointPen"]
class PrintingPointPen(AbstractPointPen):
"""A PointPen that prints every step.
"""
def __init__(self):
self.havePath = False
def beginPath(self):
self.havePath = True
print "pen.beginPath()"
def endPath(self):
self.havePath = False
print "pen.endPath()"
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
assert self.havePath
args = ["(%s, %s)" % (pt[0], pt[1])]
if segmentType is not None:
args.append("segmentType=%r" % segmentType)
if smooth:
args.append("smooth=True")
if name is not None:
args.append("name=%r" % name)
if kwargs:
args.append("**%s" % kwargs)
print "pen.addPoint(%s)" % ", ".join(args)
def addComponent(self, baseGlyphName, transformation):
assert not self.havePath
print "pen.addComponent(%r, %s)" % (baseGlyphName, tuple(transformation))
from fontTools.pens.basePen import AbstractPen
class PrintingSegmentPen(AbstractPen):
"""A SegmentPen that prints every step.
"""
def moveTo(self, pt):
print "pen.moveTo(%s)" % (pt,)
def lineTo(self, pt):
print "pen.lineTo(%s)" % (pt,)
def curveTo(self, *pts):
print "pen.curveTo%s" % (pts,)
def qCurveTo(self, *pts):
print "pen.qCurveTo%s" % (pts,)
def closePath(self):
print "pen.closePath()"
def endPath(self):
print "pen.endPath()"
def addComponent(self, baseGlyphName, transformation):
print "pen.addComponent(%r, %s)" % (baseGlyphName, tuple(transformation))
class SegmentPrintingPointPen(BasePointToSegmentPen):
"""A SegmentPen that pprints every step.
"""
def _flushContour(self, segments):
from pprint import pprint
pprint(segments)
if __name__ == "__main__":
p = SegmentPrintingPointPen()
from robofab.test.test_pens import TestShapes
TestShapes.onCurveLessQuadShape(p)

View File

@ -1,25 +0,0 @@
from fontTools.pens.basePen import BasePen
class QuartzPen(BasePen):
"""Pen to draw onto a Quartz drawing context (Carbon.CG).
- OSX specific.
"""
def __init__(self, glyphSet, quartzContext):
BasePen.__init__(self, glyphSet)
self._context = quartzContext
def _moveTo(self, (x, y)):
self._context.CGContextMoveToPoint(x, y)
def _lineTo(self, (x, y)):
self._context.CGContextAddLineToPoint(x, y)
def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
self._context.CGContextAddCurveToPoint(x1, y1, x2, y2, x3, y3)
def _closePath(self):
self._context.closePath()

View File

@ -1,125 +0,0 @@
"""PointPen for reversing the winding direction of contours."""
__all__ = ["ReverseContourPointPen"]
from ufoLib.pointPen import AbstractPointPen
class ReverseContourPointPen(AbstractPointPen):
"""This is a PointPen that passes outline data to another PointPen, but
reversing the winding direction of all contours. Components are simply
passed through unchanged.
Closed contours are reversed in such a way that the first point remains
the first point.
"""
def __init__(self, outputPointPen):
self.pen = outputPointPen
self.currentContour = None # a place to store the points for the current sub path
def _flushContour(self):
pen = self.pen
contour = self.currentContour
if not contour:
pen.beginPath()
pen.endPath()
return
closed = contour[0][1] != "move"
if not closed:
lastSegmentType = "move"
else:
# Remove the first point and insert it at the end. When
# the list of points gets reversed, this point will then
# again be at the start. In other words, the following
# will hold:
# for N in range(len(originalContour)):
# originalContour[N] == reversedContour[-N]
contour.append(contour.pop(0))
# Find the first on-curve point.
firstOnCurve = None
for i in range(len(contour)):
if contour[i][1] is not None:
firstOnCurve = i
break
if firstOnCurve is None:
# There are no on-curve points, be basically have to
# do nothing but contour.reverse().
lastSegmentType = None
else:
lastSegmentType = contour[firstOnCurve][1]
contour.reverse()
if not closed:
# Open paths must start with a move, so we simply dump
# all off-curve points leading up to the first on-curve.
while contour[0][1] is None:
contour.pop(0)
pen.beginPath()
for pt, nextSegmentType, smooth, name in contour:
if nextSegmentType is not None:
segmentType = lastSegmentType
lastSegmentType = nextSegmentType
else:
segmentType = None
pen.addPoint(pt, segmentType=segmentType, smooth=smooth, name=name)
pen.endPath()
def beginPath(self):
assert self.currentContour is None
self.currentContour = []
self.onCurve = []
def endPath(self):
assert self.currentContour is not None
self._flushContour()
self.currentContour = None
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self.currentContour.append((pt, segmentType, smooth, name))
def addComponent(self, glyphName, transform):
assert self.currentContour is None
self.pen.addComponent(glyphName, transform)
if __name__ == "__main__":
from robofab.pens.printingPens import PrintingPointPen
pP = PrintingPointPen()
rP = ReverseContourPointPen(pP)
rP.beginPath()
rP.addPoint((339, -8), "curve")
rP.addPoint((502, -8))
rP.addPoint((635, 65))
rP.addPoint((635, 305), "curve")
rP.addPoint((635, 545))
rP.addPoint((504, 623))
rP.addPoint((340, 623), "curve")
rP.addPoint((177, 623))
rP.addPoint((43, 545))
rP.addPoint((43, 305), "curve")
rP.addPoint((43, 65))
rP.addPoint((176, -8))
rP.endPath()
rP.beginPath()
rP.addPoint((100, 100), "move", smooth=False, name='a')
rP.addPoint((150, 150))
rP.addPoint((200, 200))
rP.addPoint((250, 250), "curve", smooth=True, name='b')
rP.addPoint((300, 300), "line", smooth=False, name='c')
rP.addPoint((350, 350))
rP.addPoint((400, 400))
rP.addPoint((450, 450))
rP.addPoint((500, 500), "curve", smooth=True, name='d')
rP.addPoint((550, 550))
rP.addPoint((600, 600))
rP.addPoint((650, 650))
rP.addPoint((700, 700))
rP.addPoint((750, 750), "qcurve", smooth=False, name='e')
rP.endPath()

View File

@ -1,100 +0,0 @@
"""Pens for creating UFO glyphs."""
from robofab.objects.objectsBase import MOVE, LINE, CORNER, CURVE, QCURVE, OFFCURVE
from robofab.objects.objectsRF import RContour, RSegment, RPoint
from robofab.pens.pointPen import BasePointToSegmentPen
from robofab.pens.adapterPens import SegmentToPointPen
class RFUFOPen(SegmentToPointPen):
def __init__(self, glyph):
SegmentToPointPen.__init__(self, RFUFOPointPen(glyph))
class RFUFOPointPen(BasePointToSegmentPen):
"""Point pen for building objectsRF glyphs"""
def __init__(self, glyph):
BasePointToSegmentPen.__init__(self)
self.glyph = glyph
def _flushContour(self, segments):
#
# adapted from robofab.pens.adapterPens.PointToSegmentPen
#
assert len(segments) >= 1
# if we only have one point and it has a name, we must have an anchor
first = segments[0]
segmentType, points = first
pt, smooth, name, kwargs = points[0]
if len(segments) == 1 and name != None:
self.glyph.appendAnchor(name, pt)
return
# we must have a contour
contour = RContour()
contour.setParent(self.glyph)
if segments[0][0] == "move":
# It's an open path.
closed = False
points = segments[0][1]
assert len(points) == 1
movePt, smooth, name, kwargs = points[0]
del segments[0]
else:
# It's a closed path, do a moveTo to the last
# point of the last segment. only if it isn't a qcurve
closed = True
segmentType, points = segments[-1]
movePt, smooth, name, kwargs = points[-1]
## THIS IS STILL UNDECIDED!!!
# since objectsRF currently follows the FL model of not
# allowing open contours, remove the last segment
# since it is being replaced by a move
if segmentType == 'line':
del segments[-1]
# construct a move segment and apply it to the contou if we aren't dealing with a qcurve
segment = RSegment()
segment.setParent(contour)
segment.smooth = smooth
rPoint = RPoint(x=movePt[0], y=movePt[1], pointType=MOVE, name=name)
rPoint.setParent(segment)
segment.points = [rPoint]
contour.segments.append(segment)
# do the rest of the segments
for segmentType, points in segments:
points = [pt for pt, smooth, name, kwargs in points]
if segmentType == "line":
assert len(points) == 1
sType = LINE
elif segmentType == "curve":
sType = CURVE
elif segmentType == "qcurve":
sType = QCURVE
else:
assert 0, "illegal segmentType: %s" % segmentType
segment = RSegment()
segment.setParent(contour)
segment.smooth = smooth
rPoints = []
# handle the offCurves
for point in points[:-1]:
rPoint = RPoint(x=point[0], y=point[1], pointType=OFFCURVE, name=name)
rPoint.setParent(segment)
rPoints.append(rPoint)
# now the onCurve
point = points[-1]
rPoint = RPoint(x=point[0], y=point[1], pointType=sType, name=name)
rPoint.setParent(segment)
rPoints.append(rPoint)
# apply them to the segment
segment.points = rPoints
contour.segments.append(segment)
self.glyph.contours.append(contour)
def addComponent(self, glyphName, transform):
xx, xy, yx, yy, dx, dy = transform
self.glyph.appendComponent(baseGlyph=glyphName, offset=(dx, dy), scale=(xx, yy))

View File

@ -1,8 +0,0 @@
"""Directory for unit tests.
Modules here are typically named text_<something>.py, where <something> is
usually a module name, for example "test_flPen.py", but it can also be the name
of an area or concept to be tested, for example "test_drawing.py".
Testmodules should use the unittest framework.
"""

View File

@ -1,27 +0,0 @@
import os
import glob
import unittest
import robofab.test
if __name__ == "__main__":
testDir = os.path.dirname(robofab.test.__file__)
testFiles = glob.glob1(testDir, "test_*.py")
loader = unittest.TestLoader()
suites = []
for fileName in testFiles:
modName = "robofab.test." + fileName[:-3]
print "importing", fileName
try:
mod = __import__(modName, {}, {}, ["*"])
except ImportError:
print "*** skipped", fileName
continue
suites.append(loader.loadTestsFromModule(mod))
print "running tests..."
testRunner = unittest.TextTestRunner(verbosity=0)
testSuite = unittest.TestSuite(suites)
testRunner.run(testSuite)

View File

@ -1,566 +0,0 @@
"""Miscellaneous helpers for our test suite."""
import sys
import os
import types
import unittest
def getDemoFontPath():
"""Return the path to Data/DemoFont.ufo/."""
import robofab
root = os.path.dirname(os.path.dirname(os.path.dirname(robofab.__file__)))
return os.path.join(root, "Data", "DemoFont.ufo")
def getDemoFontGlyphSetPath():
"""Return the path to Data/DemoFont.ufo/glyphs/."""
return os.path.join(getDemoFontPath(), "glyphs")
def _gatherTestCasesFromCallerByMagic():
# UGLY magic: fetch TestClass subclasses from the globals of our
# caller's caller.
frame = sys._getframe(2)
return _gatherTestCasesFromDict(frame.f_globals)
def _gatherTestCasesFromDict(d):
testCases = []
for ob in d.values():
if isinstance(ob, type) and issubclass(ob, unittest.TestCase):
testCases.append(ob)
return testCases
def runTests(testCases=None, verbosity=1):
"""Run a series of tests."""
if testCases is None:
testCases = _gatherTestCasesFromCallerByMagic()
loader = unittest.TestLoader()
suites = []
for testCase in testCases:
suites.append(loader.loadTestsFromTestCase(testCase))
testRunner = unittest.TextTestRunner(verbosity=verbosity)
testSuite = unittest.TestSuite(suites)
testRunner.run(testSuite)
# font info values used by several tests
fontInfoVersion1 = {
"familyName" : "Some Font (Family Name)",
"styleName" : "Regular (Style Name)",
"fullName" : "Some Font-Regular (Postscript Full Name)",
"fontName" : "SomeFont-Regular (Postscript Font Name)",
"menuName" : "Some Font Regular (Style Map Family Name)",
"fontStyle" : 64,
"note" : "A note.",
"versionMajor" : 1,
"versionMinor" : 0,
"year" : 2008,
"copyright" : "Copyright Some Foundry.",
"notice" : "Some Font by Some Designer for Some Foundry.",
"trademark" : "Trademark Some Foundry",
"license" : "License info for Some Foundry.",
"licenseURL" : "http://somefoundry.com/license",
"createdBy" : "Some Foundry",
"designer" : "Some Designer",
"designerURL" : "http://somedesigner.com",
"vendorURL" : "http://somefoundry.com",
"unitsPerEm" : 1000,
"ascender" : 750,
"descender" : -250,
"capHeight" : 750,
"xHeight" : 500,
"defaultWidth" : 400,
"slantAngle" : -12.5,
"italicAngle" : -12.5,
"widthName" : "Medium (normal)",
"weightName" : "Medium",
"weightValue" : 500,
"fondName" : "SomeFont Regular (FOND Name)",
"otFamilyName" : "Some Font (Preferred Family Name)",
"otStyleName" : "Regular (Preferred Subfamily Name)",
"otMacName" : "Some Font Regular (Compatible Full Name)",
"msCharSet" : 0,
"fondID" : 15000,
"uniqueID" : 4000000,
"ttVendor" : "SOME",
"ttUniqueID" : "OpenType name Table Unique ID",
"ttVersion" : "OpenType name Table Version",
}
fontInfoVersion2 = {
"familyName" : "Some Font (Family Name)",
"styleName" : "Regular (Style Name)",
"styleMapFamilyName" : "Some Font Regular (Style Map Family Name)",
"styleMapStyleName" : "regular",
"versionMajor" : 1,
"versionMinor" : 0,
"year" : 2008,
"copyright" : "Copyright Some Foundry.",
"trademark" : "Trademark Some Foundry",
"unitsPerEm" : 1000,
"descender" : -250,
"xHeight" : 500,
"capHeight" : 750,
"ascender" : 750,
"italicAngle" : -12.5,
"note" : "A note.",
"openTypeHeadCreated" : "2000/01/01 00:00:00",
"openTypeHeadLowestRecPPEM" : 10,
"openTypeHeadFlags" : [0, 1],
"openTypeHheaAscender" : 750,
"openTypeHheaDescender" : -250,
"openTypeHheaLineGap" : 200,
"openTypeHheaCaretSlopeRise" : 1,
"openTypeHheaCaretSlopeRun" : 0,
"openTypeHheaCaretOffset" : 0,
"openTypeNameDesigner" : "Some Designer",
"openTypeNameDesignerURL" : "http://somedesigner.com",
"openTypeNameManufacturer" : "Some Foundry",
"openTypeNameManufacturerURL" : "http://somefoundry.com",
"openTypeNameLicense" : "License info for Some Foundry.",
"openTypeNameLicenseURL" : "http://somefoundry.com/license",
"openTypeNameVersion" : "OpenType name Table Version",
"openTypeNameUniqueID" : "OpenType name Table Unique ID",
"openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.",
"openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)",
"openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)",
"openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)",
"openTypeNameSampleText" : "Sample Text for Some Font.",
"openTypeNameWWSFamilyName" : "Some Font (WWS Family Name)",
"openTypeNameWWSSubfamilyName" : "Regular (WWS Subfamily Name)",
"openTypeOS2WidthClass" : 5,
"openTypeOS2WeightClass" : 500,
"openTypeOS2Selection" : [3],
"openTypeOS2VendorID" : "SOME",
"openTypeOS2Panose" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"openTypeOS2FamilyClass" : [1, 1],
"openTypeOS2UnicodeRanges" : [0, 1],
"openTypeOS2CodePageRanges" : [0, 1],
"openTypeOS2TypoAscender" : 750,
"openTypeOS2TypoDescender" : -250,
"openTypeOS2TypoLineGap" : 200,
"openTypeOS2WinAscent" : 750,
"openTypeOS2WinDescent" : -250,
"openTypeOS2Type" : [],
"openTypeOS2SubscriptXSize" : 200,
"openTypeOS2SubscriptYSize" : 400,
"openTypeOS2SubscriptXOffset" : 0,
"openTypeOS2SubscriptYOffset" : -100,
"openTypeOS2SuperscriptXSize" : 200,
"openTypeOS2SuperscriptYSize" : 400,
"openTypeOS2SuperscriptXOffset" : 0,
"openTypeOS2SuperscriptYOffset" : 200,
"openTypeOS2StrikeoutSize" : 20,
"openTypeOS2StrikeoutPosition" : 300,
"openTypeVheaVertTypoAscender" : 750,
"openTypeVheaVertTypoDescender" : -250,
"openTypeVheaVertTypoLineGap" : 200,
"openTypeVheaCaretSlopeRise" : 0,
"openTypeVheaCaretSlopeRun" : 1,
"openTypeVheaCaretOffset" : 0,
"postscriptFontName" : "SomeFont-Regular (Postscript Font Name)",
"postscriptFullName" : "Some Font-Regular (Postscript Full Name)",
"postscriptSlantAngle" : -12.5,
"postscriptUniqueID" : 4000000,
"postscriptUnderlineThickness" : 20,
"postscriptUnderlinePosition" : -200,
"postscriptIsFixedPitch" : False,
"postscriptBlueValues" : [500, 510],
"postscriptOtherBlues" : [-250, -260],
"postscriptFamilyBlues" : [500, 510],
"postscriptFamilyOtherBlues" : [-250, -260],
"postscriptStemSnapH" : [100, 120],
"postscriptStemSnapV" : [80, 90],
"postscriptBlueFuzz" : 1,
"postscriptBlueShift" : 7,
"postscriptBlueScale" : 0.039625,
"postscriptForceBold" : True,
"postscriptDefaultWidthX" : 400,
"postscriptNominalWidthX" : 400,
"postscriptWeightName" : "Medium",
"postscriptDefaultCharacter" : ".notdef",
"postscriptWindowsCharacterSet" : 1,
"macintoshFONDFamilyID" : 15000,
"macintoshFONDName" : "SomeFont Regular (FOND Name)",
}
fontInfoVersion3 = {
"familyName" : "Some Font (Family Name)",
"styleName" : "Regular (Style Name)",
"styleMapFamilyName" : "Some Font Regular (Style Map Family Name)",
"styleMapStyleName" : "regular",
"versionMajor" : 1,
"versionMinor" : 0,
"year" : 2008,
"copyright" : "Copyright Some Foundry.",
"trademark" : "Trademark Some Foundry",
"unitsPerEm" : 1000,
"descender" : -250,
"xHeight" : 500,
"capHeight" : 750,
"ascender" : 750,
"italicAngle" : -12.5,
"note" : "A note.",
"openTypeGaspRangeRecords" : [
dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]),
dict(rangeMaxPPEM=20, rangeGaspBehavior=[1]),
dict(rangeMaxPPEM=30, rangeGaspBehavior=[2]),
dict(rangeMaxPPEM=40, rangeGaspBehavior=[3]),
dict(rangeMaxPPEM=50, rangeGaspBehavior=[0, 1, 2, 3]),
dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])
],
"openTypeHeadCreated" : "2000/01/01 00:00:00",
"openTypeHeadLowestRecPPEM" : 10,
"openTypeHeadFlags" : [0, 1],
"openTypeHheaAscender" : 750,
"openTypeHheaDescender" : -250,
"openTypeHheaLineGap" : 200,
"openTypeHheaCaretSlopeRise" : 1,
"openTypeHheaCaretSlopeRun" : 0,
"openTypeHheaCaretOffset" : 0,
"openTypeNameDesigner" : "Some Designer",
"openTypeNameDesignerURL" : "http://somedesigner.com",
"openTypeNameManufacturer" : "Some Foundry",
"openTypeNameManufacturerURL" : "http://somefoundry.com",
"openTypeNameLicense" : "License info for Some Foundry.",
"openTypeNameLicenseURL" : "http://somefoundry.com/license",
"openTypeNameVersion" : "OpenType name Table Version",
"openTypeNameUniqueID" : "OpenType name Table Unique ID",
"openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.",
"openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)",
"openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)",
"openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)",
"openTypeNameSampleText" : "Sample Text for Some Font.",
"openTypeNameWWSFamilyName" : "Some Font (WWS Family Name)",
"openTypeNameWWSSubfamilyName" : "Regular (WWS Subfamily Name)",
"openTypeNameRecords" : [
dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."),
dict(nameID=2, platformID=1, encodingID=1, languageID=1, string="Name Record.")
],
"openTypeOS2WidthClass" : 5,
"openTypeOS2WeightClass" : 500,
"openTypeOS2Selection" : [3],
"openTypeOS2VendorID" : "SOME",
"openTypeOS2Panose" : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"openTypeOS2FamilyClass" : [1, 1],
"openTypeOS2UnicodeRanges" : [0, 1],
"openTypeOS2CodePageRanges" : [0, 1],
"openTypeOS2TypoAscender" : 750,
"openTypeOS2TypoDescender" : -250,
"openTypeOS2TypoLineGap" : 200,
"openTypeOS2WinAscent" : 750,
"openTypeOS2WinDescent" : 250,
"openTypeOS2Type" : [],
"openTypeOS2SubscriptXSize" : 200,
"openTypeOS2SubscriptYSize" : 400,
"openTypeOS2SubscriptXOffset" : 0,
"openTypeOS2SubscriptYOffset" : -100,
"openTypeOS2SuperscriptXSize" : 200,
"openTypeOS2SuperscriptYSize" : 400,
"openTypeOS2SuperscriptXOffset" : 0,
"openTypeOS2SuperscriptYOffset" : 200,
"openTypeOS2StrikeoutSize" : 20,
"openTypeOS2StrikeoutPosition" : 300,
"openTypeVheaVertTypoAscender" : 750,
"openTypeVheaVertTypoDescender" : -250,
"openTypeVheaVertTypoLineGap" : 200,
"openTypeVheaCaretSlopeRise" : 0,
"openTypeVheaCaretSlopeRun" : 1,
"openTypeVheaCaretOffset" : 0,
"postscriptFontName" : "SomeFont-Regular (Postscript Font Name)",
"postscriptFullName" : "Some Font-Regular (Postscript Full Name)",
"postscriptSlantAngle" : -12.5,
"postscriptUniqueID" : 4000000,
"postscriptUnderlineThickness" : 20,
"postscriptUnderlinePosition" : -200,
"postscriptIsFixedPitch" : False,
"postscriptBlueValues" : [500, 510],
"postscriptOtherBlues" : [-250, -260],
"postscriptFamilyBlues" : [500, 510],
"postscriptFamilyOtherBlues" : [-250, -260],
"postscriptStemSnapH" : [100, 120],
"postscriptStemSnapV" : [80, 90],
"postscriptBlueFuzz" : 1,
"postscriptBlueShift" : 7,
"postscriptBlueScale" : 0.039625,
"postscriptForceBold" : True,
"postscriptDefaultWidthX" : 400,
"postscriptNominalWidthX" : 400,
"postscriptWeightName" : "Medium",
"postscriptDefaultCharacter" : ".notdef",
"postscriptWindowsCharacterSet" : 1,
"macintoshFONDFamilyID" : 15000,
"macintoshFONDName" : "SomeFont Regular (FOND Name)",
"woffMajorVersion" : 1,
"woffMinorVersion" : 0,
"woffMetadataUniqueID" : dict(id="string"),
"woffMetadataVendor" : dict(name="Some Foundry", url="http://somefoundry.com"),
"woffMetadataCredits" : dict(
credits=[
dict(name="Some Designer"),
dict(name=""),
dict(name="Some Designer", url="http://somedesigner.com"),
dict(name="Some Designer", url=""),
dict(name="Some Designer", role="Designer"),
dict(name="Some Designer", role=""),
dict(name="Some Designer", dir="ltr"),
dict(name="rengiseD emoS", dir="rtl"),
{"name" : "Some Designer", "class" : "hello"},
{"name" : "Some Designer", "class" : ""},
]
),
"woffMetadataDescription" : dict(
url="http://somefoundry.com/foo/description",
text=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "foo"},
{"text" : "foo", "class" : ""},
]
),
"woffMetadataLicense" : dict(
url="http://somefoundry.com/foo/license",
id="foo",
text=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "foo"},
{"text" : "foo", "class" : ""},
]
),
"woffMetadataCopyright" : dict(
text=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "foo"},
{"text" : "foo", "class" : ""},
]
),
"woffMetadataTrademark" : dict(
text=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "foo"},
{"text" : "foo", "class" : ""},
]
),
"woffMetadataLicensee" : dict(
name="Some Licensee"
),
"woffMetadataExtensions" : [
dict(
# everything
names=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "hello"},
{"text" : "foo", "class" : ""},
],
items=[
# everything
dict(
id="foo",
names=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "hello"},
{"text" : "foo", "class" : ""},
],
values=[
dict(text="foo"),
dict(text=""),
dict(text="foo", language="bar"),
dict(text="foo", language=""),
dict(text="foo", dir="ltr"),
dict(text="foo", dir="rtl"),
{"text" : "foo", "class" : "hello"},
{"text" : "foo", "class" : ""},
]
),
# no id
dict(
names=[
dict(text="foo")
],
values=[
dict(text="foo")
]
)
]
),
# no names
dict(
items=[
dict(
id="foo",
names=[
dict(text="foo")
],
values=[
dict(text="foo")
]
)
]
),
],
"firstKerningGroupPrefix" : "@kern1",
"secondKerningGroupPrefix" : "@kern2",
"guidelines" : [
# ints
dict(x=100, y=200, angle=45),
# floats
dict(x=100.5, y=200.5, angle=45.5),
# edges
dict(x=0, y=0, angle=0),
dict(x=0, y=0, angle=360),
dict(x=0, y=0, angle=360.0),
# no y
dict(x=100),
# no x
dict(y=200),
# name
dict(x=100, y=200, angle=45, name="foo"),
dict(x=100, y=200, angle=45, name=""),
# identifier
dict(x=100, y=200, angle=45, identifier="guide1"),
dict(x=100, y=200, angle=45, identifier="guide2"),
dict(x=100, y=200, angle=45, identifier=u"\x20"),
dict(x=100, y=200, angle=45, identifier=u"\x7E"),
# colors
dict(x=100, y=200, angle=45, color="0,0,0,0"),
dict(x=100, y=200, angle=45, color="1,0,0,0"),
dict(x=100, y=200, angle=45, color="1,1,1,1"),
dict(x=100, y=200, angle=45, color="0,1,0,0"),
dict(x=100, y=200, angle=45, color="0,0,1,0"),
dict(x=100, y=200, angle=45, color="0,0,0,1"),
dict(x=100, y=200, angle=45, color="1, 0, 0, 0"),
dict(x=100, y=200, angle=45, color="0, 1, 0, 0"),
dict(x=100, y=200, angle=45, color="0, 0, 1, 0"),
dict(x=100, y=200, angle=45, color="0, 0, 0, 1"),
dict(x=100, y=200, angle=45, color=".5,0,0,0"),
dict(x=100, y=200, angle=45, color="0,.5,0,0"),
dict(x=100, y=200, angle=45, color="0,0,.5,0"),
dict(x=100, y=200, angle=45, color="0,0,0,.5"),
dict(x=100, y=200, angle=45, color=".5,1,1,1"),
dict(x=100, y=200, angle=45, color="1,.5,1,1"),
dict(x=100, y=200, angle=45, color="1,1,.5,1"),
dict(x=100, y=200, angle=45, color="1,1,1,.5"),
],
}
expectedFontInfo1To2Conversion = {
"familyName" : "Some Font (Family Name)",
"styleMapFamilyName" : "Some Font Regular (Style Map Family Name)",
"styleMapStyleName" : "regular",
"styleName" : "Regular (Style Name)",
"unitsPerEm" : 1000,
"ascender" : 750,
"capHeight" : 750,
"xHeight" : 500,
"descender" : -250,
"italicAngle" : -12.5,
"versionMajor" : 1,
"versionMinor" : 0,
"year" : 2008,
"copyright" : "Copyright Some Foundry.",
"trademark" : "Trademark Some Foundry",
"note" : "A note.",
"macintoshFONDFamilyID" : 15000,
"macintoshFONDName" : "SomeFont Regular (FOND Name)",
"openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)",
"openTypeNameDescription" : "Some Font by Some Designer for Some Foundry.",
"openTypeNameDesigner" : "Some Designer",
"openTypeNameDesignerURL" : "http://somedesigner.com",
"openTypeNameLicense" : "License info for Some Foundry.",
"openTypeNameLicenseURL" : "http://somefoundry.com/license",
"openTypeNameManufacturer" : "Some Foundry",
"openTypeNameManufacturerURL" : "http://somefoundry.com",
"openTypeNamePreferredFamilyName" : "Some Font (Preferred Family Name)",
"openTypeNamePreferredSubfamilyName": "Regular (Preferred Subfamily Name)",
"openTypeNameCompatibleFullName" : "Some Font Regular (Compatible Full Name)",
"openTypeNameUniqueID" : "OpenType name Table Unique ID",
"openTypeNameVersion" : "OpenType name Table Version",
"openTypeOS2VendorID" : "SOME",
"openTypeOS2WeightClass" : 500,
"openTypeOS2WidthClass" : 5,
"postscriptDefaultWidthX" : 400,
"postscriptFontName" : "SomeFont-Regular (Postscript Font Name)",
"postscriptFullName" : "Some Font-Regular (Postscript Full Name)",
"postscriptSlantAngle" : -12.5,
"postscriptUniqueID" : 4000000,
"postscriptWeightName" : "Medium",
"postscriptWindowsCharacterSet" : 1
}
expectedFontInfo2To1Conversion = {
"familyName" : "Some Font (Family Name)",
"menuName" : "Some Font Regular (Style Map Family Name)",
"fontStyle" : 64,
"styleName" : "Regular (Style Name)",
"unitsPerEm" : 1000,
"ascender" : 750,
"capHeight" : 750,
"xHeight" : 500,
"descender" : -250,
"italicAngle" : -12.5,
"versionMajor" : 1,
"versionMinor" : 0,
"copyright" : "Copyright Some Foundry.",
"trademark" : "Trademark Some Foundry",
"note" : "A note.",
"fondID" : 15000,
"fondName" : "SomeFont Regular (FOND Name)",
"fullName" : "Some Font Regular (Compatible Full Name)",
"notice" : "Some Font by Some Designer for Some Foundry.",
"designer" : "Some Designer",
"designerURL" : "http://somedesigner.com",
"license" : "License info for Some Foundry.",
"licenseURL" : "http://somefoundry.com/license",
"createdBy" : "Some Foundry",
"vendorURL" : "http://somefoundry.com",
"otFamilyName" : "Some Font (Preferred Family Name)",
"otStyleName" : "Regular (Preferred Subfamily Name)",
"otMacName" : "Some Font Regular (Compatible Full Name)",
"ttUniqueID" : "OpenType name Table Unique ID",
"ttVersion" : "OpenType name Table Version",
"ttVendor" : "SOME",
"weightValue" : 500,
"widthName" : "Medium (normal)",
"defaultWidth" : 400,
"fontName" : "SomeFont-Regular (Postscript Font Name)",
"fullName" : "Some Font-Regular (Postscript Full Name)",
"slantAngle" : -12.5,
"uniqueID" : 4000000,
"weightName" : "Medium",
"msCharSet" : 0,
"year" : 2008
}

View File

@ -1,111 +0,0 @@
import unittest
from cStringIO import StringIO
import sys
import ufoLib
from robofab.objects.objectsFL import NewFont
from robofab.test.testSupport import fontInfoVersion1, fontInfoVersion2
class RInfoRFTestCase(unittest.TestCase):
def testRoundTripVersion2(self):
font = NewFont()
infoObject = font.info
for attr, value in fontInfoVersion2.items():
if attr in infoObject._ufoToFLAttrMapping and infoObject._ufoToFLAttrMapping[attr]["nakedAttribute"] is None:
continue
setattr(infoObject, attr, value)
newValue = getattr(infoObject, attr)
self.assertEqual((attr, newValue), (attr, value))
font.close()
def testVersion2UnsupportedSet(self):
saveStderr = sys.stderr
saveStdout = sys.stdout
tempStderr = StringIO()
sys.stderr = tempStderr
sys.stdout = tempStderr
font = NewFont()
infoObject = font.info
requiredWarnings = []
try:
for attr, value in fontInfoVersion2.items():
if attr in infoObject._ufoToFLAttrMapping and infoObject._ufoToFLAttrMapping[attr]["nakedAttribute"] is not None:
continue
setattr(infoObject, attr, value)
s = "The attribute %s is not supported by FontLab." % attr
requiredWarnings.append((attr, s))
finally:
sys.stderr = saveStderr
sys.stdout = saveStdout
tempStderr = tempStderr.getvalue()
for attr, line in requiredWarnings:
self.assertEquals((attr, line in tempStderr), (attr, True))
font.close()
def testVersion2UnsupportedGet(self):
saveStderr = sys.stderr
saveStdout = sys.stdout
tempStderr = StringIO()
sys.stderr = tempStderr
sys.stdout = tempStderr
font = NewFont()
infoObject = font.info
requiredWarnings = []
try:
for attr, value in fontInfoVersion2.items():
if attr in infoObject._ufoToFLAttrMapping and infoObject._ufoToFLAttrMapping[attr]["nakedAttribute"] is not None:
continue
getattr(infoObject, attr, value)
s = "The attribute %s is not supported by FontLab." % attr
requiredWarnings.append((attr, s))
finally:
sys.stderr = saveStderr
sys.stdout = saveStdout
tempStderr = tempStderr.getvalue()
for attr, line in requiredWarnings:
self.assertEquals((attr, line in tempStderr), (attr, True))
font.close()
def testRoundTripVersion1(self):
font = NewFont()
infoObject = font.info
for attr, value in fontInfoVersion1.items():
if attr not in ufoLib.deprecatedFontInfoAttributesVersion2:
setattr(infoObject, attr, value)
for attr, expectedValue in fontInfoVersion1.items():
if attr not in ufoLib.deprecatedFontInfoAttributesVersion2:
value = getattr(infoObject, attr)
self.assertEqual((attr, expectedValue), (attr, value))
font.close()
def testVersion1DeprecationRoundTrip(self):
saveStderr = sys.stderr
saveStdout = sys.stdout
tempStderr = StringIO()
sys.stderr = tempStderr
sys.stdout = tempStderr
font = NewFont()
infoObject = font.info
requiredWarnings = []
try:
for attr, value in fontInfoVersion1.items():
if attr in ufoLib.deprecatedFontInfoAttributesVersion2:
setattr(infoObject, attr, value)
v = getattr(infoObject, attr)
self.assertEquals((attr, value), (attr, v))
s = "DeprecationWarning: The %s attribute has been deprecated." % attr
requiredWarnings.append((attr, s))
finally:
sys.stderr = saveStderr
sys.stdout = saveStdout
tempStderr = tempStderr.getvalue()
for attr, line in requiredWarnings:
self.assertEquals((attr, line in tempStderr), (attr, True))
font.close()
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,56 +0,0 @@
import unittest
from cStringIO import StringIO
import sys
import ufoLib
from robofab.objects.objectsRF import RInfo
from robofab.test.testSupport import fontInfoVersion1, fontInfoVersion2
class RInfoRFTestCase(unittest.TestCase):
def testRoundTripVersion2(self):
infoObject = RInfo()
for attr, value in fontInfoVersion2.items():
setattr(infoObject, attr, value)
newValue = getattr(infoObject, attr)
self.assertEqual((attr, newValue), (attr, value))
def testRoundTripVersion1(self):
infoObject = RInfo()
for attr, value in fontInfoVersion1.items():
if attr not in ufoLib.deprecatedFontInfoAttributesVersion2:
setattr(infoObject, attr, value)
for attr, expectedValue in fontInfoVersion1.items():
if attr not in ufoLib.deprecatedFontInfoAttributesVersion2:
value = getattr(infoObject, attr)
self.assertEqual((attr, expectedValue), (attr, value))
def testVersion1DeprecationRoundTrip(self):
"""
unittest doesn't catch warnings in self.assertRaises,
so some hackery is required to catch the warnings
that are raised when setting deprecated attributes.
"""
saveStderr = sys.stderr
tempStderr = StringIO()
sys.stderr = tempStderr
infoObject = RInfo()
requiredWarnings = []
try:
for attr, value in fontInfoVersion1.items():
if attr in ufoLib.deprecatedFontInfoAttributesVersion2:
setattr(infoObject, attr, value)
v = getattr(infoObject, attr)
self.assertEquals((attr, value), (attr, v))
s = "DeprecationWarning: The %s attribute has been deprecated." % attr
requiredWarnings.append((attr, s))
finally:
sys.stderr = saveStderr
tempStderr = tempStderr.getvalue()
for attr, line in requiredWarnings:
self.assertEquals((attr, line in tempStderr), (attr, True))
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,218 +0,0 @@
import robofab.interface.all.dialogs
reload(robofab.interface.all.dialogs)
from robofab.interface.all.dialogs import *
import unittest
__all__ = [
"AskString", #x
"AskYesNoCancel", #x
"FindGlyph",
"GetFile", #x
"GetFolder", #x
"GetFileOrFolder", #x
"Message", #x
"OneList",
"PutFile", #x
"SearchList",
"SelectFont",
"SelectGlyph",
"TwoChecks",
"TwoFields",
"ProgressBar",
]
class DialogRunner(object):
def __init__(self):
prompt = "The prompt for %s."
message = "The message for %s."
title = "The title for %s."
informativeText = "The informative text for %s."
fileTypes = ['ufo']
fileName = "The_filename.txt"
self.fonts = fonts = [self.makeTestFont(n) for n in range(4)]
t = "AskString"
try:
print "About to try", t
print "\t>>>", AskString(
message=prompt%t,
value='',
title=title%t
)
except NotImplementedError:
print t, "is not implemented."
t = "AskYesNoCancel"
try:
print "About to try", t
print "\t>>>", AskYesNoCancel(
message=prompt%t+" default set to 0",
title=title%t,
default=0,
informativeText=informativeText%t
)
print "\t>>>", AskYesNoCancel(
message=prompt%t+" default set to 1",
title=title%t,
default=1,
informativeText=informativeText%t
)
except NotImplementedError:
print t, "is not implemented."
t = "GetFile"
try:
print "About to try", t
print "\t>>>", GetFile(
message=message%t+" Only fileTypes "+`fileTypes`,
title=title%t,
directory=None,
fileName=fileName,
allowsMultipleSelection=False,
fileTypes=fileTypes
)
print "\t>>>", GetFile(
message=message%t+" All filetypes, allow multiple selection.",
title=title%t,
directory=None,
fileName=fileName,
allowsMultipleSelection=True,
fileTypes=None
)
except NotImplementedError:
print t, "is not implemented."
t = "GetFolder"
try:
print "About to try", t
print "\t>>>", GetFolder(
message=message%t,
title=title%t,
directory=None,
allowsMultipleSelection=False
)
print "\t>>>", GetFolder(
message=message%t + " Allow multiple selection.",
title=title%t,
directory=None,
allowsMultipleSelection=True
)
except NotImplementedError:
print t, "is not implemented."
t = "GetFileOrFolder"
try:
print "About to try", t
print "\t>>>", GetFileOrFolder(
message=message%t+" Only fileTypes "+`fileTypes`,
title=title%t,
directory=None,
fileName=fileName,
allowsMultipleSelection=False,
fileTypes=fileTypes
)
print "\t>>>", GetFileOrFolder(
message=message%t + " Allow multiple selection.",
title=title%t,
directory=None,
fileName=fileName,
allowsMultipleSelection=True,
fileTypes=None
)
except NotImplementedError:
print t, "is not implemented."
t = "Message"
try:
print "About to try", t
print "\t>>>", Message(
message=message%t,
title=title%t,
informativeText=informativeText%t
)
except NotImplementedError:
print t, "is not implemented."
t = "PutFile"
try:
print "About to try", t
print "\t>>>", PutFile(
message=message%t,
fileName=fileName,
)
except NotImplementedError:
print t, "is not implemented."
# t = "SelectFont"
# try:
#print "About to try", t
# print "\t>>>", SelectFont(
# message=message%t,
# title=title%t,
# allFonts=fonts,
# )
# except NotImplementedError:
# print t, "is not implemented."
# t = 'SelectGlyph'
# try:
#print "About to try", t
# print "\t>>>", SelectGlyph(
# font=fonts[0],
# message=message%t,
# title=title%t,
# )
# except NotImplementedError:
# print t, "is not implemented."
print 'No more tests.'
def makeTestFont(self, number):
from robofab.objects.objectsRF import RFont as _RFont
f = _RFont()
f.info.familyName = "TestFamily"
f.info.styleName = "weight%d"%number
f.info.postscriptFullName = "%s %s"%(f.info.familyName, f.info.styleName)
# make some glyphs
for name in ['A', 'B', 'C']:
g = f.newGlyph(name)
pen = g.getPen()
pen.moveTo((0,0))
pen.lineTo((500, 0))
pen.lineTo((500, 800))
pen.lineTo((0, 800))
pen.closePath()
return f
class DialogTests(unittest.TestCase):
def setUp(self):
from robofab.interface.all.dialogs import test
test()
def tearDown(self):
pass
def testDialogs(self):
import robofab.interface.all.dialogs
dialogModuleName = robofab.interface.all.dialogs.platformApplicationModuleName
application = robofab.interface.all.dialogs.application
if application is None and dialogModuleName == "dialogs_mac_vanilla":
# in vanilla, but not in a host application, run with executeVanillaTest
print
print "I'm running these tests with executeVanillaTest"
from vanilla.test.testTools import executeVanillaTest
executeVanillaTest(DialogRunner)
else:
print
print "I'm running these tests natively in"
DialogRunner()
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,565 +0,0 @@
import os
import shutil
import unittest
import tempfile
from robofab.plistlib import readPlist
import robofab
from robofab.ufoLib import UFOReader, UFOWriter
from robofab.test.testSupport import fontInfoVersion2, expectedFontInfo1To2Conversion
from robofab.objects.objectsFL import NewFont, OpenFont
vfbPath = os.path.dirname(robofab.__file__)
vfbPath = os.path.dirname(vfbPath)
vfbPath = os.path.dirname(vfbPath)
vfbPath = os.path.join(vfbPath, "TestData", "TestFont1.vfb")
ufoPath1 = os.path.dirname(robofab.__file__)
ufoPath1 = os.path.dirname(ufoPath1)
ufoPath1 = os.path.dirname(ufoPath1)
ufoPath1 = os.path.join(ufoPath1, "TestData", "TestFont1 (UFO1).ufo")
ufoPath2 = ufoPath1.replace("TestFont1 (UFO1).ufo", "TestFont1 (UFO2).ufo")
expectedFormatVersion1Features = """@myClass = [A B];
feature liga {
sub A A by b;
} liga;
"""
# robofab should remove these from the lib after a load.
removeFromFormatVersion1Lib = [
"org.robofab.opentype.classes",
"org.robofab.opentype.features",
"org.robofab.opentype.featureorder",
"org.robofab.postScriptHintData"
]
class ReadUFOFormatVersion1TestCase(unittest.TestCase):
def setUpFont(self, doInfo=False, doKerning=False, doGroups=False, doLib=False, doFeatures=False):
self.font = NewFont()
self.ufoPath = ufoPath1
self.font.readUFO(ufoPath1, doInfo=doInfo, doKerning=doKerning, doGroups=doGroups, doLib=doLib, doFeatures=doFeatures)
self.font.update()
def tearDownFont(self):
self.font.close()
self.font = None
def compareToUFO(self, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True):
reader = UFOReader(self.ufoPath)
results = {}
if doInfo:
infoMatches = True
info = self.font.info
for attr, expectedValue in expectedFontInfo1To2Conversion.items():
writtenValue = getattr(info, attr)
if expectedValue != writtenValue:
infoMatches = False
break
results["info"]= infoMatches
if doKerning:
kerning = self.font.kerning.asDict()
expectedKerning = reader.readKerning()
results["kerning"] = expectedKerning == kerning
if doGroups:
groups = dict(self.font.groups)
expectedGroups = reader.readGroups()
results["groups"] = expectedGroups == groups
if doFeatures:
features = self.font.features.text
expectedFeatures = expectedFormatVersion1Features
# FontLab likes to add lines to the features, so skip blank lines.
features = [line for line in features.splitlines() if line]
expectedFeatures = [line for line in expectedFeatures.splitlines() if line]
results["features"] = expectedFeatures == features
if doLib:
lib = dict(self.font.lib)
expectedLib = reader.readLib()
for key in removeFromFormatVersion1Lib:
if key in expectedLib:
del expectedLib[key]
results["lib"] = expectedLib == lib
return results
def testFull(self):
self.setUpFont(doInfo=True, doKerning=True, doGroups=True, doFeatures=True, doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont(doInfo=True)
otherResults = self.compareToUFO(doInfo=False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
info = self.font.info
for attr, expectedValue in expectedFontInfo1To2Conversion.items():
writtenValue = getattr(info, attr)
self.assertEqual((attr, expectedValue), (attr, writtenValue))
self.tearDownFont()
def testFeatures(self):
self.setUpFont(doFeatures=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testKerning(self):
self.setUpFont(doKerning=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testGroups(self):
self.setUpFont(doGroups=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testLib(self):
self.setUpFont(doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
class ReadUFOFormatVersion2TestCase(unittest.TestCase):
def setUpFont(self, doInfo=False, doKerning=False, doGroups=False, doLib=False, doFeatures=False):
self.font = NewFont()
self.ufoPath = ufoPath2
self.font.readUFO(ufoPath2, doInfo=doInfo, doKerning=doKerning, doGroups=doGroups, doLib=doLib, doFeatures=doFeatures)
self.font.update()
def tearDownFont(self):
self.font.close()
self.font = None
def compareToUFO(self, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True):
reader = UFOReader(self.ufoPath)
results = {}
if doInfo:
infoMatches = True
info = self.font.info
for attr, expectedValue in fontInfoVersion2.items():
# cheat by skipping attrs that aren't supported
if info._ufoToFLAttrMapping[attr]["nakedAttribute"] is None:
continue
writtenValue = getattr(info, attr)
if expectedValue != writtenValue:
infoMatches = False
break
results["info"]= infoMatches
if doKerning:
kerning = self.font.kerning.asDict()
expectedKerning = reader.readKerning()
results["kerning"] = expectedKerning == kerning
if doGroups:
groups = dict(self.font.groups)
expectedGroups = reader.readGroups()
results["groups"] = expectedGroups == groups
if doFeatures:
features = self.font.features.text
expectedFeatures = reader.readFeatures()
results["features"] = expectedFeatures == features
if doLib:
lib = dict(self.font.lib)
expectedLib = reader.readLib()
results["lib"] = expectedLib == lib
return results
def testFull(self):
self.setUpFont(doInfo=True, doKerning=True, doGroups=True, doFeatures=True, doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont(doInfo=True)
otherResults = self.compareToUFO(doInfo=False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
info = self.font.info
for attr, expectedValue in fontInfoVersion2.items():
# cheat by skipping attrs that aren't supported
if info._ufoToFLAttrMapping[attr]["nakedAttribute"] is None:
continue
writtenValue = getattr(info, attr)
self.assertEqual((attr, expectedValue), (attr, writtenValue))
self.tearDownFont()
def testFeatures(self):
self.setUpFont(doFeatures=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testKerning(self):
self.setUpFont(doKerning=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testGroups(self):
self.setUpFont(doGroups=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testLib(self):
self.setUpFont(doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
class WriteUFOFormatVersion1TestCase(unittest.TestCase):
def setUpFont(self, doInfo=False, doKerning=False, doGroups=False):
self.dstDir = tempfile.mktemp()
os.mkdir(self.dstDir)
self.font = OpenFont(vfbPath)
self.font.writeUFO(self.dstDir, doInfo=doInfo, doKerning=doKerning, doGroups=doGroups, formatVersion=1)
self.font.close()
def tearDownFont(self):
shutil.rmtree(self.dstDir)
def compareToUFO(self, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True):
readerExpected = UFOReader(ufoPath1)
readerWritten = UFOReader(self.dstDir)
results = {}
if doInfo:
matches = True
expectedPath = os.path.join(ufoPath1, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
if not os.path.exists(writtenPath):
matches = False
else:
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
for attr, expectedValue in expected.items():
if expectedValue != written[attr]:
matches = False
break
results["info"] = matches
if doKerning:
matches = True
expectedPath = os.path.join(ufoPath1, "kerning.plist")
writtenPath = os.path.join(self.dstDir, "kerning.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["kerning"] = matches
if doGroups:
matches = True
expectedPath = os.path.join(ufoPath1, "groups.plist")
writtenPath = os.path.join(self.dstDir, "groups.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["groups"] = matches
if doFeatures:
matches = True
featuresPath = os.path.join(self.dstDir, "features.fea")
libPath = os.path.join(self.dstDir, "lib.plist")
if os.path.exists(featuresPath):
matches = False
else:
fontLib = readPlist(libPath)
writtenText = [fontLib.get("org.robofab.opentype.classes", "")]
features = fontLib.get("org.robofab.opentype.features", {})
featureOrder= fontLib.get("org.robofab.opentype.featureorder", [])
for featureName in featureOrder:
writtenText.append(features.get(featureName, ""))
writtenText = "\n".join(writtenText)
# FontLab likes to add lines to the features, so skip blank lines.
expectedText = [line for line in expectedFormatVersion1Features.splitlines() if line]
writtenText = [line for line in writtenText.splitlines() if line]
matches = "\n".join(expectedText) == "\n".join(writtenText)
results["features"] = matches
if doLib:
matches = True
expectedPath = os.path.join(ufoPath1, "lib.plist")
writtenPath = os.path.join(self.dstDir, "lib.plist")
if not os.path.exists(writtenPath):
matches = False
else:
# the test file doesn't have the glyph order
# so purge it from the written
writtenLib = readPlist(writtenPath)
del writtenLib["org.robofab.glyphOrder"]
matches = readPlist(expectedPath) == writtenLib
results["lib"] = matches
return results
def testFull(self):
self.setUpFont(doInfo=True, doKerning=True, doGroups=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont(doInfo=True)
otherResults = self.compareToUFO(doInfo=False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
expectedPath = os.path.join(ufoPath1, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
for attr, expectedValue in expected.items():
self.assertEqual((attr, expectedValue), (attr, written[attr]))
self.tearDownFont()
def testFeatures(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], True)
self.tearDownFont()
def testKerning(self):
self.setUpFont(doKerning=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], False)
self.tearDownFont()
def testGroups(self):
self.setUpFont(doGroups=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], True)
self.tearDownFont()
def testLib(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
class WriteUFOFormatVersion2TestCase(unittest.TestCase):
def setUpFont(self, doInfo=False, doKerning=False, doGroups=False, doLib=False, doFeatures=False):
self.dstDir = tempfile.mktemp()
os.mkdir(self.dstDir)
self.font = OpenFont(vfbPath)
self.font.writeUFO(self.dstDir, doInfo=doInfo, doKerning=doKerning, doGroups=doGroups, doLib=doLib, doFeatures=doFeatures)
self.font.close()
def tearDownFont(self):
shutil.rmtree(self.dstDir)
def compareToUFO(self, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True):
readerExpected = UFOReader(ufoPath2)
readerWritten = UFOReader(self.dstDir)
results = {}
if doInfo:
matches = True
expectedPath = os.path.join(ufoPath2, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
if not os.path.exists(writtenPath):
matches = False
else:
dummyFont = NewFont()
_ufoToFLAttrMapping = dict(dummyFont.info._ufoToFLAttrMapping)
dummyFont.close()
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
for attr, expectedValue in expected.items():
# cheat by skipping attrs that aren't supported
if _ufoToFLAttrMapping[attr]["nakedAttribute"] is None:
continue
if expectedValue != written[attr]:
matches = False
break
results["info"] = matches
if doKerning:
matches = True
expectedPath = os.path.join(ufoPath2, "kerning.plist")
writtenPath = os.path.join(self.dstDir, "kerning.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["kerning"] = matches
if doGroups:
matches = True
expectedPath = os.path.join(ufoPath2, "groups.plist")
writtenPath = os.path.join(self.dstDir, "groups.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["groups"] = matches
if doFeatures:
matches = True
expectedPath = os.path.join(ufoPath2, "features.fea")
writtenPath = os.path.join(self.dstDir, "features.fea")
if not os.path.exists(writtenPath):
matches = False
else:
f = open(expectedPath, "r")
expectedText = f.read()
f.close()
f = open(writtenPath, "r")
writtenText = f.read()
f.close()
# FontLab likes to add lines to the features, so skip blank lines.
expectedText = [line for line in expectedText.splitlines() if line]
writtenText = [line for line in writtenText.splitlines() if line]
matches = "\n".join(expectedText) == "\n".join(writtenText)
results["features"] = matches
if doLib:
matches = True
expectedPath = os.path.join(ufoPath2, "lib.plist")
writtenPath = os.path.join(self.dstDir, "lib.plist")
if not os.path.exists(writtenPath):
matches = False
else:
# the test file doesn't have the glyph order
# so purge it from the written
writtenLib = readPlist(writtenPath)
del writtenLib["org.robofab.glyphOrder"]
matches = readPlist(expectedPath) == writtenLib
results["lib"] = matches
return results
def testFull(self):
self.setUpFont(doInfo=True, doKerning=True, doGroups=True, doFeatures=True, doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont(doInfo=True)
otherResults = self.compareToUFO(doInfo=False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
expectedPath = os.path.join(ufoPath2, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
dummyFont = NewFont()
_ufoToFLAttrMapping = dict(dummyFont.info._ufoToFLAttrMapping)
dummyFont.close()
for attr, expectedValue in expected.items():
# cheat by skipping attrs that aren't supported
if _ufoToFLAttrMapping[attr]["nakedAttribute"] is None:
continue
self.assertEqual((attr, expectedValue), (attr, written[attr]))
self.tearDownFont()
def testFeatures(self):
self.setUpFont(doFeatures=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testKerning(self):
self.setUpFont(doKerning=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testGroups(self):
self.setUpFont(doGroups=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], False)
self.tearDownFont()
def testLib(self):
self.setUpFont(doLib=True)
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], False)
self.assertEqual(otherResults["kerning"], False)
self.assertEqual(otherResults["groups"], False)
self.assertEqual(otherResults["features"], False)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,321 +0,0 @@
import os
import shutil
import unittest
import tempfile
from robofab.plistlib import readPlist
import robofab
from robofab.test.testSupport import fontInfoVersion2, expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion
from robofab.objects.objectsRF import NewFont, OpenFont
from robofab.ufoLib import UFOReader
ufoPath1 = os.path.dirname(robofab.__file__)
ufoPath1 = os.path.dirname(ufoPath1)
ufoPath1 = os.path.dirname(ufoPath1)
ufoPath1 = os.path.join(ufoPath1, "TestData", "TestFont1 (UFO1).ufo")
ufoPath2 = ufoPath1.replace("TestFont1 (UFO1).ufo", "TestFont1 (UFO2).ufo")
# robofab should remove these from the lib after a load.
removeFromFormatVersion1Lib = [
"org.robofab.opentype.classes",
"org.robofab.opentype.features",
"org.robofab.opentype.featureorder",
"org.robofab.postScriptHintData"
]
class ReadUFOFormatVersion1TestCase(unittest.TestCase):
def setUpFont(self):
self.font = OpenFont(ufoPath1)
self.font.update()
def tearDownFont(self):
self.font.close()
self.font = None
def compareToUFO(self, doInfo=True):
reader = UFOReader(ufoPath1)
results = {}
# info
infoMatches = True
info = self.font.info
for attr, expectedValue in expectedFontInfo1To2Conversion.items():
writtenValue = getattr(info, attr)
if expectedValue != writtenValue:
infoMatches = False
break
results["info"]= infoMatches
# kerning
kerning = self.font.kerning.asDict()
expectedKerning = reader.readKerning()
results["kerning"] = expectedKerning == kerning
# groups
groups = dict(self.font.groups)
expectedGroups = reader.readGroups()
results["groups"] = expectedGroups == groups
# features
features = self.font.features.text
f = open(os.path.join(ufoPath2, "features.fea"), "r")
expectedFeatures = f.read()
f.close()
match = True
features = [line for line in features.splitlines() if line]
expectedFeatures = [line for line in expectedFeatures.splitlines() if line]
if expectedFeatures != features or reader.readFeatures() != "":
match = False
results["features"] = match
# lib
lib = dict(self.font.lib)
expectedLib = reader.readLib()
for key in removeFromFormatVersion1Lib:
if key in expectedLib:
del expectedLib[key]
results["lib"] = expectedLib == lib
return results
def testFull(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont()
info = self.font.info
for attr, expectedValue in expectedFontInfo1To2Conversion.items():
writtenValue = getattr(info, attr)
self.assertEqual((attr, expectedValue), (attr, writtenValue))
self.tearDownFont()
class ReadUFOFormatVersion2TestCase(unittest.TestCase):
def setUpFont(self):
self.font = OpenFont(ufoPath2)
self.font.update()
def tearDownFont(self):
self.font.close()
self.font = None
def compareToUFO(self, doInfo=True):
reader = UFOReader(ufoPath2)
results = {}
# info
infoMatches = True
info = self.font.info
for attr, expectedValue in fontInfoVersion2.items():
writtenValue = getattr(info, attr)
if expectedValue != writtenValue:
infoMatches = False
break
results["info"]= infoMatches
# kerning
kerning = self.font.kerning.asDict()
expectedKerning = reader.readKerning()
results["kerning"] = expectedKerning == kerning
# groups
groups = dict(self.font.groups)
expectedGroups = reader.readGroups()
results["groups"] = expectedGroups == groups
# features
features = self.font.features.text
expectedFeatures = reader.readFeatures()
results["features"] = expectedFeatures == features
# lib
lib = dict(self.font.lib)
expectedLib = reader.readLib()
results["lib"] = expectedLib == lib
return results
def testFull(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
def testInfo(self):
self.setUpFont()
info = self.font.info
for attr, expectedValue in fontInfoVersion2.items():
writtenValue = getattr(info, attr)
self.assertEqual((attr, expectedValue), (attr, writtenValue))
self.tearDownFont()
class WriteUFOFormatVersion1TestCase(unittest.TestCase):
def setUpFont(self):
self.dstDir = tempfile.mktemp()
os.mkdir(self.dstDir)
self.font = OpenFont(ufoPath2)
self.font.save(self.dstDir, formatVersion=1)
def tearDownFont(self):
shutil.rmtree(self.dstDir)
def compareToUFO(self):
readerExpected = UFOReader(ufoPath1)
readerWritten = UFOReader(self.dstDir)
results = {}
# info
matches = True
expectedPath = os.path.join(ufoPath1, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
if not os.path.exists(writtenPath):
matches = False
else:
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
for attr, expectedValue in expected.items():
if expectedValue != written.get(attr):
matches = False
break
results["info"] = matches
# kerning
matches = True
expectedPath = os.path.join(ufoPath1, "kerning.plist")
writtenPath = os.path.join(self.dstDir, "kerning.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["kerning"] = matches
# groups
matches = True
expectedPath = os.path.join(ufoPath1, "groups.plist")
writtenPath = os.path.join(self.dstDir, "groups.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["groups"] = matches
# features
matches = True
expectedPath = os.path.join(ufoPath1, "features.fea")
writtenPath = os.path.join(self.dstDir, "features.fea")
if os.path.exists(writtenPath):
matches = False
results["features"] = matches
# lib
matches = True
expectedPath = os.path.join(ufoPath1, "lib.plist")
writtenPath = os.path.join(self.dstDir, "lib.plist")
if not os.path.exists(writtenPath):
matches = False
else:
writtenLib = readPlist(writtenPath)
matches = readPlist(expectedPath) == writtenLib
results["lib"] = matches
return results
def testFull(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
class WriteUFOFormatVersion2TestCase(unittest.TestCase):
def setUpFont(self):
self.dstDir = tempfile.mktemp()
os.mkdir(self.dstDir)
self.font = OpenFont(ufoPath2)
self.font.save(self.dstDir)
def tearDownFont(self):
shutil.rmtree(self.dstDir)
def compareToUFO(self):
readerExpected = UFOReader(ufoPath2)
readerWritten = UFOReader(self.dstDir)
results = {}
# info
matches = True
expectedPath = os.path.join(ufoPath2, "fontinfo.plist")
writtenPath = os.path.join(self.dstDir, "fontinfo.plist")
if not os.path.exists(writtenPath):
matches = False
else:
expected = readPlist(expectedPath)
written = readPlist(writtenPath)
for attr, expectedValue in expected.items():
if expectedValue != written[attr]:
matches = False
break
results["info"] = matches
# kerning
matches = True
expectedPath = os.path.join(ufoPath2, "kerning.plist")
writtenPath = os.path.join(self.dstDir, "kerning.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["kerning"] = matches
# groups
matches = True
expectedPath = os.path.join(ufoPath2, "groups.plist")
writtenPath = os.path.join(self.dstDir, "groups.plist")
if not os.path.exists(writtenPath):
matches = False
else:
matches = readPlist(expectedPath) == readPlist(writtenPath)
results["groups"] = matches
# features
matches = True
expectedPath = os.path.join(ufoPath2, "features.fea")
writtenPath = os.path.join(self.dstDir, "features.fea")
if not os.path.exists(writtenPath):
matches = False
else:
f = open(expectedPath, "r")
expectedText = f.read()
f.close()
f = open(writtenPath, "r")
writtenText = f.read()
f.close()
# FontLab likes to add lines to the features, so skip blank lines.
expectedText = [line for line in expectedText.splitlines() if line]
writtenText = [line for line in writtenText.splitlines() if line]
matches = "\n".join(expectedText) == "\n".join(writtenText)
results["features"] = matches
# lib
matches = True
expectedPath = os.path.join(ufoPath2, "lib.plist")
writtenPath = os.path.join(self.dstDir, "lib.plist")
if not os.path.exists(writtenPath):
matches = False
else:
writtenLib = readPlist(writtenPath)
matches = readPlist(expectedPath) == writtenLib
results["lib"] = matches
return results
def testFull(self):
self.setUpFont()
otherResults = self.compareToUFO()
self.assertEqual(otherResults["info"], True)
self.assertEqual(otherResults["kerning"], True)
self.assertEqual(otherResults["groups"], True)
self.assertEqual(otherResults["features"], True)
self.assertEqual(otherResults["lib"], True)
self.tearDownFont()
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,54 +0,0 @@
"""This test suite for various FontLab-specific tests."""
import FL # needed to quickly raise ImportError if run outside of FL
import os
import tempfile
import unittest
from robofab.world import NewFont
from robofab.test.testSupport import getDemoFontPath, getDemoFontGlyphSetPath
from robofab.tools.glifImport import importAllGlifFiles
from robofab.pens.digestPen import DigestPointPen
from robofab.pens.adapterPens import SegmentToPointPen
def getDigests(font):
digests = {}
for glyphName in font.keys():
pen = DigestPointPen()
font[glyphName].drawPoints(pen)
digests[glyphName] = pen.getDigest()
return digests
class FLTestCase(unittest.TestCase):
def testUFOVersusGlifImport(self):
font = NewFont()
font.readUFO(getDemoFontPath(), doProgress=False)
d1 = getDigests(font)
font.close(False)
font = NewFont()
importAllGlifFiles(font.naked(), getDemoFontGlyphSetPath(), doProgress=False)
d2 = getDigests(font)
self.assertEqual(d1, d2)
font.close(False)
def testTwoUntitledFonts(self):
font1 = NewFont()
font2 = NewFont()
font1.unitsPerEm = 1024
font2.unitsPerEm = 2048
self.assertNotEqual(font1.unitsPerEm, font2.unitsPerEm)
font1.update()
font2.update()
font1.close(False)
font2.close(False)
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,175 +0,0 @@
"""This test suite for ufo glyph methods"""
import unittest
import os
import tempfile
import shutil
from robofab.objects.objectsRF import RFont
from robofab.test.testSupport import getDemoFontPath
from robofab.pens.digestPen import DigestPointPen
from robofab.pens.adapterPens import SegmentToPointPen, FabToFontToolsPenAdapter
class ContourMethodsTestCase(unittest.TestCase):
def setUp(self):
self.font = RFont(getDemoFontPath())
def testReverseContour(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
for contour in glyph:
contour.reverseContour()
contour.reverseContour()
pen = DigestPointPen()
glyph.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after reversing twice" % glyph.name)
def testStartSegment(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
for contour in glyph:
contour.setStartSegment(2)
contour.setStartSegment(-2)
pen = DigestPointPen()
glyph.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after seting start segment twice" % glyph.name)
def testAppendSegment(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
for contour in glyph:
contour.insertSegment(2, "curve", [(100, 100), (200, 200), (300, 300)])
contour.removeSegment(2)
pen = DigestPointPen()
glyph.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after inserting and removing segment" % glyph.name)
class GlyphsMethodsTestCase(ContourMethodsTestCase):
def testCopyGlyph(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
copy = glyph.copy()
pen = DigestPointPen()
copy.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after copying" % glyph.name)
self.assertEqual(glyph.lib, copy.lib, "%r's lib not the same after copying" % glyph.name)
self.assertEqual(glyph.width, copy.width, "%r's width not the same after copying" % glyph.name)
self.assertEqual(glyph.unicodes, copy.unicodes, "%r's unicodes not the same after copying" % glyph.name)
def testMoveGlyph(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
glyph.move((100, 200))
glyph.move((-100, -200))
pen = DigestPointPen()
glyph.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after moving twice" % glyph.name)
def testScaleGlyph(self):
for glyph in self.font:
pen = DigestPointPen()
glyph.drawPoints(pen)
digest1 = pen.getDigest()
glyph.scale((2, 2))
glyph.scale((.5, .5))
pen = DigestPointPen()
glyph.drawPoints(pen)
digest2 = pen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same after scaling twice" % glyph.name)
def testSegmentPenInterface(self):
for glyph in self.font:
digestPen = DigestPointPen(ignoreSmoothAndName=True)
pen = SegmentToPointPen(digestPen)
glyph.draw(pen)
digest1 = digestPen.getDigest()
digestPen = DigestPointPen(ignoreSmoothAndName=True)
glyph.drawPoints(digestPen)
digest2 = digestPen.getDigest()
self.assertEqual(digest1, digest2, "%r not the same for gl.draw() and gl.drawPoints()" % glyph.name)
class SaveTestCase(ContourMethodsTestCase):
def testSaveAs(self):
path = tempfile.mktemp(".ufo")
try:
keys1 = self.font.keys()
self.font.save(path)
keys2 = self.font.keys()
keys1.sort()
keys2.sort()
self.assertEqual(keys1, keys2)
self.assertEqual(self.font.path, path)
font2 = RFont(path)
keys3 = font2.keys()
keys3.sort()
self.assertEqual(keys1, keys3)
finally:
if os.path.exists(path):
shutil.rmtree(path)
def testSaveAs2(self):
path = tempfile.mktemp(".ufo")
# copy a glyph
self.font["X"] = self.font["a"].copy()
# self.assertEqual(self.font["X"].name, "X")
# remove a glyph
self.font.removeGlyph("a")
keys1 = self.font.keys()
try:
self.font.save(path)
self.assertEqual(self.font.path, path)
keys2 = self.font.keys()
keys1.sort()
keys2.sort()
self.assertEqual(keys1, keys2)
font2 = RFont(path)
keys3 = font2.keys()
keys3.sort()
self.assertEqual(keys1, keys3)
finally:
if os.path.exists(path):
shutil.rmtree(path)
def testCustomFileNameScheme(self):
path = tempfile.mktemp(".ufo")
libKey = "org.robofab.glyphNameToFileNameFuncName"
self.font.lib[libKey] = "robofab.test.test_objectsUFO.testGlyphNameToFileName"
try:
self.font.save(path)
self.assertEqual(os.path.exists(os.path.join(path,
"glyphs", "test_a.glif")), True)
finally:
if os.path.exists(path):
shutil.rmtree(path)
def testGlyphNameToFileName(glyphName, glyphSet):
from robofab.glifLib import glyphNameToFileName
return "test_" + glyphNameToFileName(glyphName, glyphSet)
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,149 +0,0 @@
"""This test suite test general Pen stuff, it should not contain
FontLab-specific code.
"""
import unittest
from robofab.pens.digestPen import DigestPointPen
from robofab.pens.adapterPens import SegmentToPointPen, PointToSegmentPen
from robofab.pens.adapterPens import GuessSmoothPointPen
from robofab.pens.reverseContourPointPen import ReverseContourPointPen
from robofab.test.testSupport import getDemoFontGlyphSetPath
from ufoLib.glifLib import GlyphSet
class TestShapes:
# Collection of test shapes. It's probably better to add these as
# glyphs to the demo font.
def square(pen):
# a simple square as a closed path (100, 100, 600, 600)
pen.beginPath()
pen.addPoint((100, 100), "line")
pen.addPoint((100, 600), "line")
pen.addPoint((600, 600), "line")
pen.addPoint((600, 100), "line")
pen.endPath()
square = staticmethod(square)
def onCurveLessQuadShape(pen):
pen.beginPath()
pen.addPoint((100, 100))
pen.addPoint((100, 600))
pen.addPoint((600, 600))
pen.addPoint((600, 100))
pen.endPath()
onCurveLessQuadShape = staticmethod(onCurveLessQuadShape)
def openPath(pen):
# a simple square as a closed path (100, 100, 600, 600)
pen.beginPath()
pen.addPoint((100, 100), "move")
pen.addPoint((100, 600), "line")
pen.addPoint((600, 600), "line")
pen.addPoint((600, 100), "line")
pen.endPath()
openPath = staticmethod(openPath)
def circle(pen):
pen.beginPath()
pen.addPoint((0, 500), "curve")
pen.addPoint((0, 800))
pen.addPoint((200, 1000))
pen.addPoint((500, 1000), "curve")
pen.addPoint((800, 1000))
pen.addPoint((1000, 800))
pen.addPoint((1000, 500), "curve")
pen.addPoint((1000, 200))
pen.addPoint((800, 0))
pen.addPoint((500, 0), "curve")
pen.addPoint((200, 0))
pen.addPoint((0, 200))
pen.endPath()
circle = staticmethod(circle)
class RoundTripTestCase(unittest.TestCase):
def _doTest(self, shapeFunc, shapeName):
pen = DigestPointPen(ignoreSmoothAndName=True)
shapeFunc(pen)
digest1 = pen.getDigest()
digestPen = DigestPointPen(ignoreSmoothAndName=True)
pen = PointToSegmentPen(SegmentToPointPen(digestPen))
shapeFunc(pen)
digest2 = digestPen.getDigest()
self.assertEqual(digest1, digest2, "%r failed round tripping" % shapeName)
def testShapes(self):
for name in dir(TestShapes):
if name[0] != "_":
self._doTest(getattr(TestShapes, name), name)
def testShapesFromGlyphSet(self):
glyphSet = GlyphSet(getDemoFontGlyphSetPath())
for name in glyphSet.keys():
self._doTest(glyphSet[name].drawPoints, name)
def testGuessSmoothPen(self):
glyphSet = GlyphSet(getDemoFontGlyphSetPath())
for name in glyphSet.keys():
digestPen = DigestPointPen()
glyphSet[name].drawPoints(digestPen)
digest1 = digestPen.getDigest()
digestPen = DigestPointPen()
pen = GuessSmoothPointPen(digestPen)
glyphSet[name].drawPoints(pen)
digest2 = digestPen.getDigest()
self.assertEqual(digest1, digest2)
class ReverseContourTestCase(unittest.TestCase):
def testReverseContourClosedPath(self):
digestPen = DigestPointPen()
TestShapes.square(digestPen)
d1 = digestPen.getDigest()
digestPen = DigestPointPen()
pen = ReverseContourPointPen(digestPen)
pen.beginPath()
pen.addPoint((100, 100), "line")
pen.addPoint((600, 100), "line")
pen.addPoint((600, 600), "line")
pen.addPoint((100, 600), "line")
pen.endPath()
d2 = digestPen.getDigest()
self.assertEqual(d1, d2)
def testReverseContourOpenPath(self):
digestPen = DigestPointPen()
TestShapes.openPath(digestPen)
d1 = digestPen.getDigest()
digestPen = DigestPointPen()
pen = ReverseContourPointPen(digestPen)
pen.beginPath()
pen.addPoint((600, 100), "move")
pen.addPoint((600, 600), "line")
pen.addPoint((100, 600), "line")
pen.addPoint((100, 100), "line")
pen.endPath()
d2 = digestPen.getDigest()
self.assertEqual(d1, d2)
def testReversContourFromGlyphSet(self):
glyphSet = GlyphSet(getDemoFontGlyphSetPath())
digestPen = DigestPointPen()
glyphSet["testglyph1"].drawPoints(digestPen)
digest1 = digestPen.getDigest()
digestPen = DigestPointPen()
pen = ReverseContourPointPen(digestPen)
glyphSet["testglyph1.reversed"].drawPoints(pen)
digest2 = digestPen.getDigest()
self.assertEqual(digest1, digest2)
if __name__ == "__main__":
from robofab.test.testSupport import runTests
runTests()

View File

@ -1,110 +0,0 @@
def test():
"""
# some tests for the ps Hints operations
>>> from robofab.world import RFont, RGlyph
>>> g = RGlyph()
>>> g.psHints.isEmpty()
True
>>> h = RGlyph()
>>> i = g + h
>>> i.psHints.isEmpty()
True
>>> i = g - h
>>> i.psHints.isEmpty()
True
>>> i = g * 2
>>> i.psHints.isEmpty()
True
>>> i = g / 2
>>> i.psHints.isEmpty()
True
>>> g.psHints.vHints = [(100, 50), (200, 50)]
>>> g.psHints.hHints = [(100, 50), (200, 5)]
>>> not g.psHints.isEmpty()
True
>>> gc = g.copy()
>>> gc.psHints.asDict() == g.psHints.asDict()
True
# multiplication
>>> v = g.psHints * 2
>>> v.asDict() == {'vHints': [[200, 100], [400, 100]], 'hHints': [[200, 100], [400, 10]]}
True
# division
>>> v = g.psHints / 2
>>> v.asDict() == {'vHints': [[50.0, 25.0], [100.0, 25.0]], 'hHints': [[50.0, 25.0], [100.0, 2.5]]}
True
# multiplication with x, y, factor
# vertically oriented values should respond different
>>> v = g.psHints * (.5, 10)
>>> v.asDict() == {'vHints': [[1000, 500], [2000, 500]], 'hHints': [[50.0, 25.0], [100.0, 2.5]]}
True
# division with x, y, factor
# vertically oriented values should respond different
>>> v = g.psHints / (.5, 10)
>>> v.asDict() == {'vHints': [[10.0, 5.0], [20.0, 5.0]], 'hHints': [[200.0, 100.0], [400.0, 10.0]]}
True
# rounding to integer
>>> v = g.psHints / 2
>>> v.round()
>>> v.asDict() == {'vHints': [(50, 25), (100, 25)], 'hHints': [(50, 25), (100, 3)]}
True
# "ps hint values calculating with a glyph"
# ps hint values as part of glyphmath operations.
# multiplication
>>> h = g * 10
>>> h.psHints.asDict() == {'vHints': [[1000, 500], [2000, 500]], 'hHints': [[1000, 500], [2000, 50]]}
True
# division
>>> h = g / 2
>>> h.psHints.asDict() == {'vHints': [[50.0, 25.0], [100.0, 25.0]], 'hHints': [[50.0, 25.0], [100.0, 2.5]]}
True
# x, y factor multiplication
>>> h = g * (.5, 10)
>>> h.psHints.asDict() == {'vHints': [[1000, 500], [2000, 500]], 'hHints': [[50.0, 25.0], [100.0, 2.5]]}
True
# x, y factor division
>>> h = g / (.5, 10)
>>> h.psHints.asDict() == {'vHints': [[10.0, 5.0], [20.0, 5.0]], 'hHints': [[200.0, 100.0], [400.0, 10.0]]}
True
# "font ps hint values"
>>> f = RFont()
>>> f.psHints.isEmpty()
True
>>> f.psHints.blueScale = .5
>>> f.psHints.blueShift = 1
>>> f.psHints.blueFuzz = 1
>>> f.psHints.forceBold = True
>>> f.psHints.hStems = (100, 90)
>>> f.psHints.vStems = (500, 10)
>>> not f.psHints.isEmpty()
True
>>> f.insertGlyph(g, name="new")
<RGlyph for None.new>
>>> f["new"].psHints.asDict() == g.psHints.asDict()
True
"""
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,12 +0,0 @@
"""
Directory for all tool like code.
Stuff that doesn't really belong to objects, pens, compilers etc.
The code is split up into sections.
"""

View File

@ -1,348 +0,0 @@
"""A simple set of tools for building accented glyphs.
# Hey look! A demonstration:
from robofab.accentBuilder import AccentTools, buildRelatedAccentList
font = CurrentFont
# a list of accented glyphs that you want to build
myList=['Aacute', 'aacute']
# search for glyphs related to glyphs in myList and add them to myList
myList=buildRelatedAccentList(font, myList)+myList
# start the class
at=AccentTools(font, myList)
# clear away any anchors that exist (this is optional)
at.clearAnchors()
# add necessary anchors if you want to
at.buildAnchors(ucXOffset=20, ucYOffset=40, lcXOffset=15, lcYOffset=30)
# print a report of any errors that occured
at.printAnchorErrors()
# build the accented glyphs if you want to
at.buildAccents()
# print a report of any errors that occured
at.printAccentErrors()
"""
#XXX! This is *very* experimental! I think it works, but you never know.
from robofab.gString import lowercase_plain, accents, uppercase_plain, splitAccent, findAccentBase
from robofab.tools.toolsAll import readGlyphConstructions
import robofab
from robofab.interface.all.dialogs import ProgressBar
from robofab.world import RFWorld
inFontLab = RFWorld().inFontLab
anchorColor=125
accentColor=75
def stripSuffix(glyphName):
"""strip away unnecessary suffixes from a glyph name"""
if glyphName.find('.') != -1:
baseName = glyphName.split('.')[0]
if glyphName.find('.sc') != -1:
baseName = '.'.join([baseName, 'sc'])
return baseName
else:
return glyphName
def buildRelatedAccentList(font, list):
"""build a list of related glyphs suitable for use with AccentTools"""
searchList = []
baseGlyphs = {}
foundList = []
for glyphName in list:
splitNames = splitAccent(glyphName)
baseName = splitNames[0]
accentNames = splitNames[1]
if baseName not in searchList:
searchList.append(baseName)
if baseName not in baseGlyphs.keys():
baseGlyphs[baseName] = [accentNames]
else:
baseGlyphs[baseName].append(accentNames)
foundGlyphs = findRelatedGlyphs(font, searchList, doAccents=0)
for baseGlyph in foundGlyphs.keys():
for foundGlyph in foundGlyphs[baseGlyph]:
for accentNames in baseGlyphs[baseGlyph]:
foundList.append(makeAccentName(foundGlyph, accentNames))
return foundList
def findRelatedGlyphs(font, searchItem, doAccents=True):
"""Gather up a bunch of related glyph names. Send it either a
single glyph name 'a', or a list of glyph names ['a', 'x'] and it
returns a dict like: {'a': ['atilde', 'a.alt', 'a.swash']}. if doAccents
is False it will skip accented glyph names.
This is a relatively slow operation!"""
relatedGlyphs = {}
for name in font.keys():
base = name.split('.')[0]
if name not in relatedGlyphs.keys():
relatedGlyphs[name] = []
if base not in relatedGlyphs.keys():
relatedGlyphs[base] = []
if doAccents:
accentBase = findAccentBase(name)
if accentBase not in relatedGlyphs.keys():
relatedGlyphs[accentBase] = []
baseAccentBase = findAccentBase(base)
if baseAccentBase not in relatedGlyphs.keys():
relatedGlyphs[baseAccentBase] = []
if base != name and name not in relatedGlyphs[base]:
relatedGlyphs[base].append(name)
if doAccents:
if accentBase != name and name not in relatedGlyphs[accentBase]:
relatedGlyphs[accentBase].append(name)
if baseAccentBase != name and name not in relatedGlyphs[baseAccentBase]:
relatedGlyphs[baseAccentBase].append(name)
foundGlyphs = {}
if isinstance(searchItem, str):
searchList = [searchItem]
else:
searchList = searchItem
for glyph in searchList:
foundGlyphs[glyph] = relatedGlyphs[glyph]
return foundGlyphs
def makeAccentName(baseName, accentNames):
"""make an accented glyph name"""
if isinstance(accentNames, str):
accentNames = [accentNames]
build = []
if baseName.find('.') != -1:
base = baseName.split('.')[0]
suffix = baseName.split('.')[1]
else:
base = baseName
suffix = ''
build.append(base)
for accent in accentNames:
build.append(accent)
buildJoin = ''.join(build)
name = '.'.join([buildJoin, suffix])
return name
def nameBuster(glyphName, glyphConstruct):
stripedSuffixName = stripSuffix(glyphName)
suffix = None
errors = []
accentNames = []
baseName = glyphName
if glyphName.find('.') != -1:
suffix = glyphName.split('.')[1]
if glyphName.find('.sc') != -1:
suffix = glyphName.split('.sc')[1]
if stripedSuffixName not in glyphConstruct.keys():
errors.append('%s: %s not in glyph construction database'%(glyphName, stripedSuffixName))
else:
if suffix is None:
baseName = glyphConstruct[stripedSuffixName][0]
else:
if glyphName.find('.sc') != -1:
baseName = ''.join([glyphConstruct[stripedSuffixName][0], suffix])
else:
baseName = '.'.join([glyphConstruct[stripedSuffixName][0], suffix])
accentNames = glyphConstruct[stripedSuffixName][1:]
return (baseName, stripedSuffixName, accentNames, errors)
class AccentTools:
def __init__(self, font, accentList):
"""several tools for working with anchors and building accents"""
self.glyphConstructions = readGlyphConstructions()
self.accentList = accentList
self.anchorErrors = ['ANCHOR ERRORS:']
self.accentErrors = ['ACCENT ERRORS:']
self.font = font
def clearAnchors(self, doProgress=True):
"""clear all anchors in the font"""
tickCount = len(self.font)
if doProgress:
bar = ProgressBar("Cleaning all anchors...", tickCount)
tick = 0
for glyphName in self.accentList:
if doProgress:
bar.label(glyphName)
baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions)
existError = False
if len(errors) > 0:
existError = True
if not existError:
toClear = [baseName]
for accent, position in accentNames:
toClear.append(accent)
for glyphName in toClear:
try:
self.font[glyphName].clearAnchors()
except IndexError: pass
if doProgress:
bar.tick(tick)
tick = tick+1
if doProgress:
bar.close()
def buildAnchors(self, ucXOffset=0, ucYOffset=0, lcXOffset=0, lcYOffset=0, markGlyph=True, doProgress=True):
"""add the necessary anchors to the glyphs if they don't exist
some flag definitions:
uc/lc/X/YOffset=20 offset values for the anchors
markGlyph=1 mark the glyph that is created
doProgress=1 show a progress bar"""
accentOffset = 10
tickCount = len(self.accentList)
if doProgress:
bar = ProgressBar('Adding anchors...', tickCount)
tick = 0
for glyphName in self.accentList:
if doProgress:
bar.label(glyphName)
previousPositions = {}
baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions)
existError = False
if len(errors) > 0:
existError = True
for anchorError in errors:
self.anchorErrors.append(anchorError)
if not existError:
existError = False
try:
self.font[baseName]
except IndexError:
self.anchorErrors.append(' '.join([glyphName, ':', baseName, 'does not exist.']))
existError = True
for accentName, accentPosition in accentNames:
try:
self.font[accentName]
except IndexError:
self.anchorErrors.append(' '.join([glyphName, ':', accentName, 'does not exist.']))
existError = True
if not existError:
#glyph = self.font.newGlyph(glyphName, clear=True)
for accentName, accentPosition in accentNames:
if baseName.split('.')[0] in lowercase_plain:
xOffset = lcXOffset-accentOffset
yOffset = lcYOffset-accentOffset
else:
xOffset = ucXOffset-accentOffset
yOffset = ucYOffset-accentOffset
# should I add a cedilla and ogonek yoffset override here?
if accentPosition not in previousPositions.keys():
self._dropAnchor(self.font[baseName], accentPosition, xOffset, yOffset)
if markGlyph:
self.font[baseName].mark = anchorColor
if inFontLab:
self.font[baseName].update()
else:
self._dropAnchor(self.font[previousPositions[accentPosition]], accentPosition, xOffset, yOffset)
self._dropAnchor(self.font[accentName], accentPosition, accentOffset, accentOffset, doAccentPosition=1)
previousPositions[accentPosition] = accentName
if markGlyph:
self.font[accentName].mark = anchorColor
if inFontLab:
self.font[accentName].update()
if inFontLab:
self.font.update()
if doProgress:
bar.tick(tick)
tick = tick+1
if doProgress:
bar.close()
def printAnchorErrors(self):
"""print errors encounted during buildAnchors"""
if len(self.anchorErrors) == 1:
print 'No anchor errors encountered'
else:
for i in self.anchorErrors:
print i
def _dropAnchor(self, glyph, positionName, xOffset=0, yOffset=0, doAccentPosition=False):
"""anchor adding method. for internal use only."""
existingAnchorNames = []
for anchor in glyph.getAnchors():
existingAnchorNames.append(anchor.name)
if doAccentPosition:
positionName = ''.join(['_', positionName])
if positionName not in existingAnchorNames:
glyphLeft, glyphBottom, glyphRight, glyphTop = glyph.box
glyphXCenter = glyph.width/2
if positionName == 'top':
glyph.appendAnchor(positionName, (glyphXCenter, glyphTop+yOffset))
elif positionName == 'bottom':
glyph.appendAnchor(positionName, (glyphXCenter, glyphBottom-yOffset))
elif positionName == 'left':
glyph.appendAnchor(positionName, (glyphLeft-xOffset, glyphTop))
elif positionName == 'right':
glyph.appendAnchor(positionName, (glyphRight+xOffset, glyphTop))
elif positionName == '_top':
glyph.appendAnchor(positionName, (glyphXCenter, glyphBottom-yOffset))
elif positionName == '_bottom':
glyph.appendAnchor(positionName, (glyphXCenter, glyphTop+yOffset))
elif positionName == '_left':
glyph.appendAnchor(positionName, (glyphRight+xOffset, glyphTop))
elif positionName == '_right':
glyph.appendAnchor(positionName, (glyphLeft-xOffset, glyphTop))
if inFontLab:
glyph.update()
def buildAccents(self, clear=True, adjustWidths=True, markGlyph=True, doProgress=True):
"""build accented glyphs. some flag definitions:
clear=1 clear the glyphs if they already exist
markGlyph=1 mark the glyph that is created
doProgress=1 show a progress bar
adjustWidths=1 will fix right and left margins when left or right accents are added"""
tickCount = len(self.accentList)
if doProgress:
bar = ProgressBar('Building accented glyphs...', tickCount)
tick = 0
for glyphName in self.accentList:
if doProgress:
bar.label(glyphName)
existError = False
anchorError = False
baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions)
if len(errors) > 0:
existError = True
for accentError in errors:
self.accentErrors.append(accentError)
if not existError:
baseAnchors = []
try:
self.font[baseName]
except IndexError:
self.accentErrors.append('%s: %s does not exist.'%(glyphName, baseName))
existError = True
else:
for anchor in self.font[baseName].anchors:
baseAnchors.append(anchor.name)
for accentName, accentPosition in accentNames:
accentAnchors = []
try:
self.font[accentName]
except IndexError:
self.accentErrors.append('%s: %s does not exist.'%(glyphName, accentName))
existError = True
else:
for anchor in self.font[accentName].getAnchors():
accentAnchors.append(anchor.name)
if accentPosition not in baseAnchors:
self.accentErrors.append('%s: %s not in %s anchors.'%(glyphName, accentPosition, baseName))
anchorError = True
if ''.join(['_', accentPosition]) not in accentAnchors:
self.accentErrors.append('%s: %s not in %s anchors.'%(glyphName, ''.join(['_', accentPosition]), accentName))
anchorError = True
if not existError and not anchorError:
destination = self.font.compileGlyph(glyphName, baseName, self.glyphConstructions[stripedSuffixName][1:], adjustWidths)
if markGlyph:
destination.mark = accentColor
if doProgress:
bar.tick(tick)
tick = tick+1
if doProgress:
bar.close()
def printAccentErrors(self):
"""print errors encounted during buildAccents"""
if len(self.accentErrors) == 1:
print 'No accent errors encountered'
else:
for i in self.accentErrors:
print i

View File

@ -1,85 +0,0 @@
import re
featureRE = re.compile(
"^" # start of line
"\s*" #
"feature" # feature
"\s+" #
"(\w{4})" # four alphanumeric characters
"\s*" #
"\{" # {
, re.MULTILINE # run in multiline to preserve line seps
)
def splitFeaturesForFontLab(text):
"""
>>> result = splitFeaturesForFontLab(testText)
>>> result == expectedTestResult
True
"""
classes = ""
features = []
while text:
m = featureRE.search(text)
if m is None:
classes = text
text = ""
else:
start, end = m.span()
# if start is not zero, this is the first match
# and all previous lines are part of the "classes"
if start > 0:
assert not classes
classes = text[:start]
# extract the current feature
featureName = m.group(1)
featureText = text[start:end]
text = text[end:]
# grab all text before the next feature definition
# and add it to the current definition
if text:
m = featureRE.search(text)
if m is not None:
start, end = m.span()
featureText += text[:start]
text = text[start:]
else:
featureText += text
text = ""
# store the feature
features.append((featureName, featureText))
return classes, features
testText = """
@class1 = [a b c d];
feature liga {
sub f i by fi;
} liga;
@class2 = [x y z];
feature salt {
sub a by a.alt;
} salt; feature ss01 {sub x by x.alt} ss01;
feature ss02 {sub y by y.alt} ss02;
# feature calt {
# sub a b' by b.alt;
# } calt;
"""
expectedTestResult = (
"\n@class1 = [a b c d];\n",
[
("liga", "\nfeature liga {\n sub f i by fi;\n} liga;\n\n@class2 = [x y z];\n"),
("salt", "\nfeature salt {\n sub a by a.alt;\n} salt; feature ss01 {sub x by x.alt} ss01;\n"),
("ss02", "\nfeature ss02 {sub y by y.alt} ss02;\n\n# feature calt {\n# sub a b' by b.alt;\n# } calt;\n")
]
)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,95 +0,0 @@
"""Tool for exporting GLIFs from FontLab"""
import FL
import os
from robofab.interface.all.dialogs import ProgressBar
from robofab.glifLib import GlyphSet
from robofab.tools.glifImport import GlyphPlaceholder
from robofab.pens.flPen import drawFLGlyphOntoPointPen
def exportGlyph(glyphName, flGlyph, glyphSet):
"""Export a FontLab glyph."""
glyph = GlyphPlaceholder()
glyph.width = flGlyph.width
glyph.unicodes = flGlyph.unicodes
if flGlyph.note:
glyph.note = flGlyph.note
customdata = flGlyph.customdata
if customdata:
from cStringIO import StringIO
from robofab.plistlib import readPlist, Data
f = StringIO(customdata)
try:
glyph.lib = readPlist(f)
except: # XXX ugh, plistlib can raise lots of things
# Anyway, customdata does not contain valid plist data,
# but we don't need to toss it!
glyph.lib = {"org.robofab.fontlab.customdata": Data(customdata)}
def drawPoints(pen):
# whoohoo, nested scopes are cool.
drawFLGlyphOntoPointPen(flGlyph, pen)
glyphSet.writeGlyph(glyphName, glyph, drawPoints)
def exportGlyphs(font, glyphs=None, dest=None, doProgress=True, bar=None):
"""Export all glyphs in a FontLab font"""
if dest is None:
dir, base = os.path.split(font.file_name)
base = base.split(".")[0] + ".glyphs"
dest = os.path.join(dir, base)
if not os.path.exists(dest):
os.makedirs(dest)
glyphSet = GlyphSet(dest)
if glyphs is None:
indices = range(len(font))
else:
indices = []
for glyphName in glyphs:
indices.append(font.FindGlyph(glyphName))
barStart = 0
closeBar = False
if doProgress:
if not bar:
bar = ProgressBar("Exporting Glyphs", len(indices))
closeBar = True
else:
barStart = bar.getCurrentTick()
else:
bar = None
try:
done = {}
for i in range(len(indices)):
#if not (i % 10) and not bar.tick(i + barStart):
# raise KeyboardInterrupt
index = indices[i]
flGlyph = font[index]
if flGlyph is None:
continue
glyphName = flGlyph.name
if not glyphName:
print "can't dump glyph #%s, it has no glyph name" % i
else:
if glyphName in done:
n = 1
while ("%s#%s" % (glyphName, n)) in done:
n += 1
glyphName = "%s#%s" % (glyphName, n)
done[glyphName] = None
exportGlyph(glyphName, flGlyph, glyphSet)
if bar and not i % 10:
bar.tick(barStart + i)
# Write out contents.plist
glyphSet.writeContents()
except KeyboardInterrupt:
if bar:
bar.close()
bar = None
if bar and closeBar:
bar.close()

View File

@ -1,74 +0,0 @@
"""Tools for importing GLIFs into FontLab"""
import os
from FL import fl
from robofab.tools.toolsFL import NewGlyph, FontIndex
from robofab.pens.flPen import FLPointPen
from robofab.glifLib import GlyphSet
from robofab.interface.all.dialogs import ProgressBar, GetFolder
class GlyphPlaceholder:
pass
def importAllGlifFiles(font, dirName=None, doProgress=True, bar=None):
"""import all GLIFs into a FontLab font"""
if dirName is None:
if font.file_name:
dir, base = os.path.split(font.file_name)
base = base.split(".")[0] + ".glyphs"
dirName = os.path.join(dir, base)
else:
dirName = GetFolder("Please select a folder with .glif files")
glyphSet = GlyphSet(dirName)
glyphNames = glyphSet.keys()
glyphNames.sort()
barStart = 0
closeBar = False
if doProgress:
if not bar:
bar = ProgressBar("Importing Glyphs", len(glyphNames))
closeBar = True
else:
barStart = bar.getCurrentTick()
else:
bar = None
try:
for i in range(len(glyphNames)):
#if not (i % 10) and not bar.tick(barStart + i):
# raise KeyboardInterrupt
glyphName = glyphNames[i]
flGlyph = NewGlyph(font, glyphName, clear=True)
pen = FLPointPen(flGlyph)
glyph = GlyphPlaceholder()
glyphSet.readGlyph(glyphName, glyph, pen)
if hasattr(glyph, "width"):
flGlyph.width = int(round(glyph.width))
if hasattr(glyph, "unicodes"):
flGlyph.unicodes = glyph.unicodes
if hasattr(glyph, "note"):
flGlyph.note = glyph.note # XXX must encode
if hasattr(glyph, "lib"):
from cStringIO import StringIO
from robofab.plistlib import writePlist
lib = glyph.lib
if lib:
if len(lib) == 1 and "org.robofab.fontlab.customdata" in lib:
data = lib["org.robofab.fontlab.customdata"].data
else:
f = StringIO()
writePlist(glyph.lib, f)
data = f.getvalue()
flGlyph.customdata = data
# XXX the next bit is only correct when font is the current font :-(
fl.UpdateGlyph(font.FindGlyph(glyphName))
if bar and not i % 10:
bar.tick(barStart + i)
except KeyboardInterrupt:
if bar:
bar.close()
bar = None
fl.UpdateFont(FontIndex(font))
if bar and closeBar:
bar.close()

View File

@ -1,565 +0,0 @@
_glyphConstruction = """\
#
# RoboFab Glyph Construction Database
#
# format:
# Glyphname: BaseGlyph Accent.RelativePosition* Accent.RelativePosition*
# *RelativePosition can be top, bottom, left, right
#
# NOTE: this is not a comprehensive, or even accurate, glyph list.
# It was built by Python robots and, in many cases, by tired human hands.
# Please report any omissions, errors or praise to the local RoboFab authorities.
#
##: Uppercase
AEacute: AE acute.top
AEmacron: AE macron.top
Aacute: A acute.top
Abreve: A breve.top
Abreveacute: A breve.top acute.top
Abrevedotaccent: A breve.top dotaccent.bottom
Abrevegrave: A breve.top grave.top
Abrevetilde: A breve.top tilde.top
Acaron: A caron.top
Acircumflex: A circumflex.top
Acircumflexacute: A circumflex.top acute.top
Acircumflexdotaccent: A circumflex.top dotaccent.bottom
Acircumflexgrave: A circumflex.top grave.top
Acircumflextilde: A circumflex.top tilde.top
Adblgrave: A dblgrave.top
Adieresis: A dieresis.top
Adieresismacron: A dieresis.top macron.top
Adotaccent: A dotaccent.top
Adotaccentmacron: A dotaccent.top macron.top
Agrave: A grave.top
Amacron: A macron.top
Aogonek: A ogonek.bottom
Aring: A ring.top
Aringacute: A ring.top acute.top
Atilde: A tilde.top
Bdotaccent: B dotaccent.top
Cacute: C acute.top
Ccaron: C caron.top
Ccedilla: C cedilla.bottom
Ccedillaacute: C cedilla.bottom acute.top
Ccircumflex: C circumflex.top
Cdotaccent: C dotaccent.top
Dcaron: D caron.top
Dcedilla: D cedilla.bottom
Ddotaccent: D dotaccent.top
Eacute: E acute.top
Ebreve: E breve.top
Ecaron: E caron.top
Ecedilla: E cedilla.bottom
Ecedillabreve: E cedilla.bottom breve.top
Ecircumflex: E circumflex.top
Ecircumflexacute: E circumflex.top acute.top
Ecircumflexdotaccent: E circumflex.top dotaccent.bottom
Ecircumflexgrave: E circumflex.top grave.top
Ecircumflextilde: E circumflex.top tilde.top
Edblgrave: E dblgrave.top
Edieresis: E dieresis.top
Edotaccent: E dotaccent.top
Egrave: E grave.top
Emacron: E macron.top
Emacronacute: E macron.top acute.top
Emacrongrave: E macron.top grave.top
Eogonek: E ogonek.bottom
Etilde: E tilde.top
Fdotaccent: F dotaccent.top
Gacute: G acute.top
Gbreve: G breve.top
Gcaron: G caron.top
Gcedilla: G cedilla.bottom
Gcircumflex: G circumflex.top
Gcommaaccent: G commaaccent.bottom
Gdotaccent: G dotaccent.top
Gmacron: G macron.top
Hcaron: H caron.top
Hcedilla: H cedilla.top
Hcircumflex: H circumflex.top
Hdieresis: H dieresis.top
Hdotaccent: H dotaccent.top
Iacute: I acute.top
Ibreve: I breve.top
Icaron: I caron.top
Icircumflex: I circumflex.top
Idblgrave: I dblgrave.top
Idieresis: I dieresis.top
Idieresisacute: I dieresis.top acute.top
Idotaccent: I dotaccent.top
Igrave: I grave.top
Imacron: I macron.top
Iogonek: I ogonek.bottom
Itilde: I tilde.top
Jcircumflex: J circumflex.top
Kacute: K acute.top
Kcaron: K caron.top
Kcedilla: K cedilla.bottom
Kcommaaccent: K commaaccent.bottom
Lacute: L acute.top
Lcaron: L commaaccent.right
Lcedilla: L cedilla.bottom
Lcommaaccent: L commaaccent.bottom
Ldot: L dot.right
Ldotaccent: L dotaccent.bottom
Ldotaccentmacron: L dotaccent.bottom macron.top
Macute: M acute.top
Mdotaccent: M dotaccent.top
Nacute: N acute.top
Ncaron: N caron.top
Ncedilla: N cedilla.bottom
Ncommaaccent: N commaaccent.bottom
Ndotaccent: N dotaccent.top
Ngrave: N grave.top
Ntilde: N tilde.top
Oacute: O acute.top
Obreve: O breve.top
Ocaron: O caron.top
Ocircumflex: O circumflex.top
Ocircumflexacute: O circumflex.top acute.top
Ocircumflexdotaccent: O circumflex.top dotaccent.bottom
Ocircumflexgrave: O circumflex.top grave.top
Ocircumflextilde: O circumflex.top tilde.top
Odblgrave: O dblgrave.top
Odieresis: O dieresis.top
Odieresismacron: O dieresis.top macron.top
Ograve: O grave.top
Ohungarumlaut: O hungarumlaut.top
Omacron: O macron.top
Omacronacute: O macron.top acute.top
Omacrongrave: O macron.top grave.top
Oogonek: O ogonek.bottom
Oogonekmacron: O ogonek.bottom macron.top
Oslashacute: Oslash acute.top
Otilde: O tilde.top
Otildeacute: O tilde.top acute.top
Otildedieresis: O tilde.top dieresis.top
Otildemacron: O tilde.top macron.top
Pacute: P acute.top
Pdotaccent: P dotaccent.top
Racute: R acute.top
Rcaron: R caron.top
Rcedilla: R cedilla.bottom
Rcommaaccent: R commaaccent.bottom
Rdblgrave: R dblgrave.top
Rdotaccent: R dotaccent.top
Rdotaccentmacron: R dotaccent.top macron.top
Sacute: S acute.top
Sacutedotaccent: S acute.top dotaccent.top
Scaron: S caron.top
Scarondotaccent: S caron.top dotaccent.top
Scedilla: S cedilla.bottom
Scircumflex: S circumflex.top
Scommaaccent: S commaaccent.bottom
Sdotaccent: S dotaccent.top
Tcaron: T caron.top
Tcedilla: T cedilla.bottom
Tcommaaccent: T commaaccent.bottom
Tdotaccent: T dotaccent.top
Uacute: U acute.top
Ubreve: U breve.top
Ucaron: U caron.top
Ucircumflex: U circumflex.top
Udblgrave: U dblgrave.top
Udieresis: U dieresis.top
Udieresisacute: U dieresis.top acute.top
Udieresiscaron: U dieresis.top caron.top
Udieresisgrave: U dieresis.top grave.top
Udieresismacron: U dieresis.top macron.top
Ugrave: U grave.top
Uhungarumlaut: U hungarumlaut.top
Umacron: U macron.top
Umacrondieresis: U macron.top dieresis.top
Uogonek: U ogonek.bottom
Uring: U ring.top
Utilde: U tilde.top
Utildeacute: U tilde.top acute.top
Vtilde: V tilde.top
Wacute: W acute.top
Wcircumflex: W circumflex.top
Wdieresis: W dieresis.top
Wdotaccent: W dotaccent.top
Wgrave: W grave.top
Xdieresis: X dieresis.top
Xdotaccent: X dotaccent.top
Yacute: Y acute.top
Ycircumflex: Y circumflex.top
Ydieresis: Y dieresis.top
Ydotaccent: Y dotaccent.top
Ygrave: Y grave.top
Ytilde: Y tilde.top
Zacute: Z acute.top
Zcaron: Z caron.top
Zcircumflex: Z circumflex.top
Zdotaccent: Z dotaccent.top
##: Lowercase
aacute: a acute.top
abreve: a breve.top
abreveacute: a breve.top acute.top
abrevedotaccent: a breve.top dotaccent.bottom
abrevegrave: a breve.top grave.top
abrevetilde: a breve.top tilde.top
acaron: a caron.top
acircumflex: a circumflex.top
acircumflexacute: a circumflex.top acute.top
acircumflexdotaccent: a circumflex.top dotaccent.bottom
acircumflexgrave: a circumflex.top grave.top
acircumflextilde: a circumflex.top tilde.top
adblgrave: a dblgrave.top
adieresis: a dieresis.top
adieresismacron: a dieresis.top macron.top
adotaccent: a dotaccent.top
adotaccentmacron: a dotaccent.top macron.top
aeacute: ae acute.top
aemacron: ae macron.top
agrave: a grave.top
amacron: a macron.top
aogonek: a ogonek.bottom
aring: a ring.top
aringacute: a ring.top acute.top
atilde: a tilde.top
bdotaccent: b dotaccent.top
cacute: c acute.top
ccaron: c caron.top
ccedilla: c cedilla.bottom
ccedillaacute: c cedilla.bottom acute.top
ccircumflex: c circumflex.top
cdotaccent: c dotaccent.top
dcaron: d commaaccent.right
dcedilla: d cedilla.bottom
ddotaccent: d dotaccent.top
dmacron: d macron.top
eacute: e acute.top
ebreve: e breve.top
ecaron: e caron.top
ecedilla: e cedilla.bottom
ecedillabreve: e cedilla.bottom breve.top
ecircumflex: e circumflex.top
ecircumflexacute: e circumflex.top acute.top
ecircumflexdotaccent: e circumflex.top dotaccent.bottom
ecircumflexgrave: e circumflex.top grave.top
ecircumflextilde: e circumflex.top tilde.top
edblgrave: e dblgrave.top
edieresis: e dieresis.top
edotaccent: e dotaccent.top
egrave: e grave.top
emacron: e macron.top
emacronacute: e macron.top acute.top
emacrongrave: e macron.top grave.top
eogonek: e ogonek.bottom
etilde: e tilde.top
fdotaccent: f dotaccent.top
gacute: g acute.top
gbreve: g breve.top
gcaron: g caron.top
gcedilla: g cedilla.top
gcircumflex: g circumflex.top
gcommaaccent: g commaaccent.top
gdotaccent: g dotaccent.top
gmacron: g macron.top
hcaron: h caron.top
hcedilla: h cedilla.bottom
hcircumflex: h circumflex.top
hdieresis: h dieresis.top
hdotaccent: h dotaccent.top
iacute: dotlessi acute.top
ibreve: dotlessi breve.top
icaron: dotlessi caron.top
icircumflex: dotlessi circumflex.top
idblgrave: dotlessi dblgrave.top
idieresis: dotlessi dieresis.top
idieresisacute: dotlessi dieresis.top acute.top
igrave: dotlessi grave.top
imacron: dotlessi macron.top
iogonek: i ogonek.bottom
itilde: dotlessi tilde.top
jcaron: dotlessj caron.top
jcircumflex: dotlessj circumflex.top
jacute: dotlessj acute.top
kacute: k acute.top
kcaron: k caron.top
kcedilla: k cedilla.bottom
kcommaaccent: k commaaccent.bottom
lacute: l acute.top
lcaron: l commaaccent.right
lcedilla: l cedilla.bottom
lcommaaccent: l commaaccent.bottom
ldot: l dot.right
ldotaccent: l dotaccent.bottom
ldotaccentmacron: l dotaccent.bottom macron.top
macute: m acute.top
mdotaccent: m dotaccent.top
nacute: n acute.top
ncaron: n caron.top
ncedilla: n cedilla.bottom
ncommaaccent: n commaaccent.bottom
ndotaccent: n dotaccent.top
ngrave: n grave.top
ntilde: n tilde.top
oacute: o acute.top
obreve: o breve.top
ocaron: o caron.top
ocircumflex: o circumflex.top
ocircumflexacute: o circumflex.top acute.top
ocircumflexdotaccent: o circumflex.top dotaccent.bottom
ocircumflexgrave: o circumflex.top grave.top
ocircumflextilde: o circumflex.top tilde.top
odblgrave: o dblgrave.top
odieresis: o dieresis.top
odieresismacron: o dieresis.top macron.top
ograve: o grave.top
ohungarumlaut: o hungarumlaut.top
omacron: o macron.top
omacronacute: o macron.top acute.top
omacrongrave: o macron.top grave.top
oogonek: o ogonek.bottom
oogonekmacron: o ogonek.bottom macron.top
oslashacute: oslash acute.top
otilde: o tilde.top
otildeacute: o tilde.top acute.top
otildedieresis: o tilde.top dieresis.top
otildemacron: o tilde.top macron.top
pacute: p acute.top
pdotaccent: p dotaccent.top
racute: r acute.top
rcaron: r caron.top
rcedilla: r cedilla.bottom
rcommaaccent: r commaaccent.bottom
rdblgrave: r dblgrave.top
rdotaccent: r dotaccent.top
rdotaccentmacron: r dotaccent.top macron.top
sacute: s acute.top
sacutedotaccent: s acute.top dotaccent.top
scaron: s caron.top
scarondotaccent: s caron.top dotaccent.top
scedilla: s cedilla.bottom
scircumflex: s circumflex.top
scommaaccent: s commaaccent.bottom
sdotaccent: s dotaccent.top
tcaron: t commaaccent.right
tcedilla: t cedilla.bottom
tcommaaccent: t commaaccent.bottom
tdieresis: t dieresis.top
tdotaccent: t dotaccent.top
uacute: u acute.top
ubreve: u breve.top
ucaron: u caron.top
ucircumflex: u circumflex.top
udblgrave: u dblgrave.top
udieresis: u dieresis.top
udieresisacute: u dieresis.top acute.top
udieresiscaron: u dieresis.top caron.top
udieresisgrave: u dieresis.top grave.top
udieresismacron: u dieresis.top macron.top
ugrave: u grave.top
uhungarumlaut: u hungarumlaut.top
umacron: u macron.top
umacrondieresis: u macron.top dieresis.top
uogonek: u ogonek.bottom
uring: u ring.top
utilde: u tilde.top
utildeacute: u tilde.top acute.top
vtilde: v tilde.top
wacute: w acute.top
wcircumflex: w circumflex.top
wdieresis: w dieresis.top
wdotaccent: w dotaccent.top
wgrave: w grave.top
wring: w ring.top
xdieresis: x dieresis.top
xdotaccent: x dotaccent.top
yacute: y acute.top
ycircumflex: y circumflex.top
ydieresis: y dieresis.top
ydotaccent: y dotaccent.top
ygrave: y grave.top
yring: y ring.top
ytilde: y tilde.top
zacute: z acute.top
zcaron: z caron.top
zcircumflex: z circumflex.top
zdotaccent: z dotaccent.top
##: Small: Caps
AEacute.sc: AE.sc acute.top
AEmacron.sc: AE.sc macron.top
Aacute.sc: A.sc acute.top
Abreve.sc: A.sc breve.top
Abreveacute.sc: A.sc breve.top acute.top
Abrevedotaccent.sc: A.sc breve.top dotaccent.bottom
Abrevegrave.sc: A.sc breve.top grave.top
Abrevetilde.sc: A.sc breve.top tilde.top
Acaron.sc: A.sc caron.top
Acircumflex.sc: A.sc circumflex.top
Acircumflexacute.sc: A.sc circumflex.top acute.top
Acircumflexdotaccent.sc: A.sc circumflex.top dotaccent.bottom
Acircumflexgrave.sc: A.sc circumflex.top grave.top
Acircumflextilde.sc: A.sc circumflex.top tilde.top
Adblgrave.sc: A.sc dblgrave.top
Adieresis.sc: A.sc dieresis.top
Adieresismacron.sc: A.sc dieresis.top macron.top
Adotaccent.sc: A.sc dotaccent.top
Adotaccentmacron.sc: A.sc dotaccent.top macron.top
Agrave.sc: A.sc grave.top
Amacron.sc: A.sc macron.top
Aogonek.sc: A.sc ogonek.bottom
Aring.sc: A.sc ring.top
Aringacute.sc: A.sc ring.top acute.top
Atilde.sc: A.sc tilde.top
Bdotaccent.sc: B.sc dotaccent.top
Cacute.sc: C.sc acute.top
Ccaron.sc: C.sc caron.top
Ccedilla.sc: C.sc cedilla.bottom
Ccedillaacute.sc: C.sc cedilla.bottom acute.top
Ccircumflex.sc: C.sc circumflex.top
Cdotaccent.sc: C.sc dotaccent.top
Dcaron.sc: D.sc caron.top
Dcedilla.sc: D.sc cedilla.bottom
Ddotaccent.sc: D.sc dotaccent.top
Eacute.sc: E.sc acute.top
Ebreve.sc: E.sc breve.top
Ecaron.sc: E.sc caron.top
Ecedilla.sc: E.sc cedilla.bottom
Ecedillabreve.sc: E.sc cedilla.bottom breve.top
Ecircumflex.sc: E.sc circumflex.top
Ecircumflexacute.sc: E.sc circumflex.top acute.top
Ecircumflexdotaccent.sc: E.sc circumflex.top dotaccent.bottom
Ecircumflexgrave.sc: E.sc circumflex.top grave.top
Ecircumflextilde.sc: E.sc circumflex.top tilde.top
Edblgrave.sc: E.sc dblgrave.top
Edieresis.sc: E.sc dieresis.top
Edotaccent.sc: E.sc dotaccent.top
Egrave.sc: E.sc grave.top
Emacron.sc: E.sc macron.top
Emacronacute.sc: E.sc macron.top acute.top
Emacrongrave.sc: E.sc macron.top grave.top
Eogonek.sc: E.sc ogonek.bottom
Etilde.sc: E.sc tilde.top
Fdotaccent.sc: F.sc dotaccent.top
Gacute.sc: G.sc acute.top
Gbreve.sc: G.sc breve.top
Gcaron.sc: G.sc caron.top
Gcedilla.sc: G.sc cedilla.bottom
Gcircumflex.sc: G.sc circumflex.top
Gcommaaccent.sc: G.sc commaaccent.bottom
Gdotaccent.sc: G.sc dotaccent.top
Gmacron.sc: G.sc macron.top
Hcaron.sc: H.sc caron.top
Hcedilla.sc: H.sc cedilla.top
Hcircumflex.sc: H.sc circumflex.top
Hdieresis.sc: H.sc dieresis.top
Hdotaccent.sc: H.sc dotaccent.top
Iacute.sc: I.sc acute.top
Ibreve.sc: I.sc breve.top
Icaron.sc: I.sc caron.top
Icircumflex.sc: I.sc circumflex.top
Idblgrave.sc: I.sc dblgrave.top
Idieresis.sc: I.sc dieresis.top
Idieresisacute.sc: I.sc dieresis.top acute.top
Idotaccent.sc: I.sc dotaccent.top
Igrave.sc: I.sc grave.top
Imacron.sc: I.sc macron.top
Iogonek.sc: I.sc ogonek.bottom
Itilde.sc: I.sc tilde.top
Jcircumflex.sc: J.sc circumflex.top
Kacute.sc: K.sc acute.top
Kcaron.sc: K.sc caron.top
Kcedilla.sc: K.sc cedilla.bottom
Kcommaaccent.sc: K.sc commaaccent.bottom
Lacute.sc: L.sc acute.top
Lcaron.sc: L.sc commaaccent.right
Lcedilla.sc: L.sc cedilla.bottom
Lcommaaccent.sc: L.sc commaaccent.bottom
Ldot.sc: L.sc dot.right
Ldotaccent.sc: L.sc dotaccent.bottom
Ldotaccentmacron.sc: L.sc dotaccent.bottom macron.top
Macute.sc: M.sc acute.top
Mdotaccent.sc: M.sc dotaccent.top
Nacute.sc: N.sc acute.top
Ncaron.sc: N.sc caron.top
Ncedilla.sc: N.sc cedilla.bottom
Ncommaaccent.sc: N.sc commaaccent.bottom
Ndotaccent.sc: N.sc dotaccent.top
Ngrave.sc: N.sc grave.top
Ntilde.sc: N.sc tilde.top
Oacute.sc: O.sc acute.top
Obreve.sc: O.sc breve.top
Ocaron.sc: O.sc caron.top
Ocircumflex.sc: O.sc circumflex.top
Ocircumflexacute.sc: O.sc circumflex.top acute.top
Ocircumflexdotaccent.sc: O.sc circumflex.top dotaccent.bottom
Ocircumflexgrave.sc: O.sc circumflex.top grave.top
Ocircumflextilde.sc: O.sc circumflex.top tilde.top
Odblgrave.sc: O.sc dblgrave.top
Odieresis.sc: O.sc dieresis.top
Odieresismacron.sc: O.sc dieresis.top macron.top
Ograve.sc: O.sc grave.top
Ohungarumlaut.sc: O.sc hungarumlaut.top
Omacron.sc: O.sc macron.top
Omacronacute.sc: O.sc macron.top acute.top
Omacrongrave.sc: O.sc macron.top grave.top
Oogonek.sc: O.sc ogonek.bottom
Oogonekmacron.sc: O.sc ogonek.bottom macron.top
Oslashacute.sc: Oslash.sc acute.top
Otilde.sc: O.sc tilde.top
Otildeacute.sc: O.sc tilde.top acute.top
Otildedieresis.sc: O.sc tilde.top dieresis.top
Otildemacron.sc: O.sc tilde.top macron.top
Pacute.sc: P.sc acute.top
Pdotaccent.sc: P.sc dotaccent.top
Racute.sc: R.sc acute.top
Rcaron.sc: R.sc caron.top
Rcedilla.sc: R.sc cedilla.bottom
Rcommaaccent.sc: R.sc commaaccent.bottom
Rdblgrave.sc: R.sc dblgrave.top
Rdotaccent.sc: R.sc dotaccent.top
Rdotaccentmacron.sc: R.sc dotaccent.top macron.top
Sacute.sc: S.sc acute.top
Sacutedotaccent.sc: S.sc acute.top dotaccent.top
Scaron.sc: S.sc caron.top
Scarondotaccent.sc: S.sc caron.top dotaccent.top
Scedilla.sc: S.sc cedilla.bottom
Scircumflex.sc: S.sc circumflex.top
Scommaaccent.sc: S.sc commaaccent.bottom
Sdotaccent.sc: S.sc dotaccent.top
Tcaron.sc: T.sc caron.top
Tcedilla.sc: T.sc cedilla.bottom
Tcommaaccent.sc: T.sc commaaccent.bottom
Tdotaccent.sc: T.sc dotaccent.top
Uacute.sc: U.sc acute.top
Ubreve.sc: U.sc breve.top
Ucaron.sc: U.sc caron.top
Ucircumflex.sc: U.sc circumflex.top
Udblgrave.sc: U.sc dblgrave.top
Udieresis.sc: U.sc dieresis.top
Udieresisacute.sc: U.sc dieresis.top acute.top
Udieresiscaron.sc: U.sc dieresis.top caron.top
Udieresisgrave.sc: U.sc dieresis.top grave.top
Udieresismacron.sc: U.sc dieresis.top macron.top
Ugrave.sc: U.sc grave.top
Uhungarumlaut.sc: U.sc hungarumlaut.top
Umacron.sc: U.sc macron.top
Umacrondieresis.sc: U.sc macron.top dieresis.top
Uogonek.sc: U.sc ogonek.bottom
Uring.sc: U.sc ring.top
Utilde.sc: U.sc tilde.top
Utildeacute.sc: U.sc tilde.top acute.top
Vtilde.sc: V.sc tilde.top
Wacute.sc: W.sc acute.top
Wcircumflex.sc: W.sc circumflex.top
Wdieresis.sc: W.sc dieresis.top
Wdotaccent.sc: W.sc dotaccent.top
Wgrave.sc: W.sc grave.top
Xdieresis.sc: X.sc dieresis.top
Xdotaccent.sc: X.sc dotaccent.top
Yacute.sc: Y.sc acute.top
Ycircumflex.sc: Y.sc circumflex.top
Ydieresis.sc: Y.sc dieresis.top
Ydotaccent.sc: Y.sc dotaccent.top
Ygrave.sc: Y.sc grave.top
Ytilde.sc: Y.sc tilde.top
Zacute.sc: Z.sc acute.top
Zcaron.sc: Z.sc caron.top
Zcircumflex.sc: Z.sc circumflex.top
Zdotaccent.sc: Z.sc dotaccent.top
"""

View File

@ -1,41 +0,0 @@
"""A separate module for glyphname to filename functions.
glyphNameToShortFileName() generates a non-clashing filename for systems with
filename-length limitations.
"""
MAXLEN = 31
def glyphNameToShortFileName(glyphName, glyphSet):
"""Alternative glyphname to filename function.
Features a garuanteed maximum filename for really long glyphnames, and clash testing.
- all non-ascii characters are converted to "_" (underscore), including "."
- all glyphnames which are too long are truncated and a hash is added at the end
- the hash is generated from the whole glyphname
- finally, the candidate glyphname is checked against the contents.plist
and a incrementing number is added at the end if there is a clash.
"""
import binascii, struct, string
ext = ".glif"
ok = string.ascii_letters + string.digits + " _"
h = binascii.hexlify(struct.pack(">l", binascii.crc32(glyphName)))
n = ''
for c in glyphName:
if c in ok:
if c != c.lower():
n += c + "_"
else:
n += c
else:
n += "_"
if len(n + ext) < MAXLEN:
return n + ext
count = 0
candidate = n[:MAXLEN - len(h + ext)] + h + ext
if glyphSet is not None:
names = glyphSet.getReverseContents()
while candidate.lower() in names:
candidate = n[:MAXLEN - len(h + ext + str(count))] + h + str(count) + ext
count += 1
return candidate

View File

@ -1,175 +0,0 @@
"""Remote control for MacOS FontLab.
initFontLabRemote() registers a callback for appleevents and
runFontLabRemote() sends the code from a different application,
such as a Mac Python IDE or Python interpreter.
"""
from robofab.world import world
if world.inFontLab and world.mac is not None:
from Carbon import AE as _AE
else:
import sys
from aetools import TalkTo
class FontLab(TalkTo):
pass
__all__ = ['initFontLabRemote', 'runFontLabRemote']
def _executePython(theAppleEvent, theReply):
import aetools
import cStringIO
import traceback
import sys
parms, attrs = aetools.unpackevent(theAppleEvent)
source = parms.get("----")
if source is None:
return
stdout = cStringIO.StringIO()
#print "<executing remote command>"
save = sys.stdout, sys.stderr
sys.stdout = sys.stderr = stdout
namespace = {}
try:
try:
exec source in namespace
except:
traceback.print_exc()
finally:
sys.stdout, sys.stderr = save
output = stdout.getvalue()
aetools.packevent(theReply, {"----": output})
_imported = False
def initFontLabRemote():
"""Call this in FontLab at startup of the application to switch on the remote."""
print "FontLabRemote is on."
_AE.AEInstallEventHandler("Rfab", "exec", _executePython)
if world.inFontLab and world.mac is not None:
initFontLabRemote()
def runFontLabRemote(code):
"""Call this in the MacOS Python IDE to make FontLab execute the code."""
fl = FontLab("FLab", start=1)
ae, parms, attrs = fl.send("Rfab", "exec", {"----": code})
output = parms.get("----")
return output
# GlyphTransmit
# Convert a glyph to a string using digestPen, transmit string, unpack string with pointpen.
#
def Glyph2String(glyph):
from robofab.pens.digestPen import DigestPointPen
import pickle
p = DigestPointPen(glyph)
glyph.drawPoints(p)
info = {}
info['name'] = glyph.name
info['width'] = glyph.width
info['points'] = p.getDigest()
return str(pickle.dumps(info))
def String2Glyph(gString, penClass, font):
import pickle
if gString is None:
return None
info = pickle.loads(gString)
name = info['name']
if not name in font.keys():
glyph = font.newGlyph(name)
else:
glyph = font[name]
pen = penClass(glyph)
for p in info['points']:
if p == "beginPath":
pen.beginPath()
elif p == "endPath":
pen.endPath()
else:
pt, type = p
pen.addPoint(pt, type)
glyph.width = info['width']
glyph.update()
return glyph
_makeFLGlyph = """
from robofab.world import CurrentFont
from robofab.tools.remote import receiveGlyph
code = '''%s'''
receiveGlyph(code, CurrentFont())
"""
def transmitGlyph(glyph):
from robofab.world import world
if world.inFontLab and world.mac is not None:
# we're in fontlab, on a mac
print Glyph2String(glyph)
pass
else:
remoteProgram = _makeFLGlyph%Glyph2String(glyph)
print "remoteProgram", remoteProgram
return runFontLabRemote(remoteProgram)
def receiveGlyph(glyphString, font=None):
from robofab.world import world
if world.inFontLab and world.mac is not None:
# we're in fontlab, on a mac
from robofab.pens.flPen import FLPointPen
print String2Glyph(glyphString, FLPointPen, font)
pass
else:
from robofab.pens.rfUFOPen import RFUFOPointPen
print String2Glyph(glyphString, RFUFOPointPen, font)
#
# command to tell FontLab to open a UFO and save it as a vfb
def os9PathConvert(path):
"""Attempt to convert a unix style path to a Mac OS9 style path.
No support for relative paths!
"""
if path.find("/Volumes") == 0:
# it's on the volumes list, some sort of external volume
path = path[len("/Volumes")+1:]
elif path[0] == "/":
# a dir on the root volume
path = path[1:]
new = path.replace("/", ":")
return new
_remoteUFOImportProgram = """
from robofab.objects.objectsFL import NewFont
import os.path
destinationPathVFB = "%(destinationPathVFB)s"
font = NewFont()
font.readUFO("%(sourcePathUFO)s", doProgress=True)
font.update()
font.save(destinationPathVFB)
print font, "done"
font.close()
"""
def makeVFB(sourcePathUFO, destinationPathVFB=None):
"""FontLab convenience function to import a UFO and save it as a VFB"""
import os
fl = FontLab("FLab", start=1)
if destinationPathVFB is None:
destinationPathVFB = os.path.splitext(sourcePathUFO)[0]+".vfb"
src9 = os9PathConvert(sourcePathUFO)
dst9 = os9PathConvert(destinationPathVFB)
code = _remoteUFOImportProgram%{'sourcePathUFO': src9, 'destinationPathVFB':dst9}
ae, parms, attrs = fl.send("Rfab", "exec", {"----": code})
output = parms.get("----")
return output

View File

@ -1,142 +0,0 @@
"""A collection of non-environment specific tools"""
import sys
import os
from robofab.objects.objectsRF import RInfo
if sys.platform == "darwin" and sys.version_info[:3] == (2, 2, 0):
# the Mac support of Jaguar's Python 2.2 is broken
have_broken_macsupport = 1
else:
have_broken_macsupport = 0
def readGlyphConstructions():
"""read GlyphConstruction and turn it into a dict"""
from robofab.tools.glyphConstruction import _glyphConstruction
data = _glyphConstruction.split("\n")
glyphConstructions = {}
for i in data:
if len(i) == 0: continue
if i[0] != '#':
name = i.split(': ')[0]
construction = i.split(': ')[1].split(' ')
build = [construction[0]]
for c in construction[1:]:
accent = c.split('.')[0]
position = c.split('.')[1]
build.append((accent, position))
glyphConstructions[name] = build
return glyphConstructions
#
#
# glyph.unicode: ttFont["cmap"].getcmap(3, 1)
#
#
def guessFileType(fileName):
if not os.path.exists(fileName):
return None
base, ext = os.path.splitext(fileName)
ext = ext.lower()
if not have_broken_macsupport:
try:
import MacOS
except ImportError:
pass
else:
cr, tp = MacOS.GetCreatorAndType(fileName)
if tp in ("sfnt", "FFIL"):
return "TTF"
if tp == "LWFN":
return "Type 1"
if ext == ".dfont":
return "TTF"
if ext in (".otf", ".ttf"):
return "TTF"
if ext in (".pfb", ".pfa"):
return "Type 1"
return None
def extractTTFFontInfo(font):
# UFO.info attribute name / index.
# name table entries index according to http://www.microsoft.com/typography/otspec/name.htm
attrs = [
('copyright', 0),
('familyName', 1),
('fontStyle', 1),
('postscriptFullName', 4),
('trademark', 7),
('openTypeNameDesigner', 9),
('openTypeNameLicenseURL', 14),
('openTypeNameDesignerURL', 12),
]
info = RInfo()
names = font['name']
info.ascender = font['hhea'].ascent
info.descender = font['hhea'].descent
info.unitsPerEm = font['head'].unitsPerEm
for name, index in attrs:
entry = font["name"].getName(index, 3, 1)
if entry is not None:
try:
setattr(info, name, unicode(entry.string, "utf16"))
except:
print "Error importing value %s: %s"%(str(name), str(info))
return info
def extractT1FontInfo(font):
info = RInfo()
src = font.font['FontInfo']
factor = font.font['FontMatrix'][0]
assert factor > 0
info.unitsPerEm = int(round(1/factor, 0))
# assume something for ascender descender
info.ascender = (info.unitsPerEm / 5) * 4
info.descender = info.ascender - info.unitsPerEm
info.versionMajor = font.font['FontInfo']['version']
info.fullName = font.font['FontInfo']['FullName']
info.familyName = font.font['FontInfo']['FullName'].split("-")[0]
info.notice = unicode(font.font['FontInfo']['Notice'], "macroman")
info.italicAngle = font.font['FontInfo']['ItalicAngle']
info.uniqueID = font['UniqueID']
return info
def fontToUFO(src, dst, fileType=None):
from robofab.ufoLib import UFOWriter
from robofab.pens.adapterPens import SegmentToPointPen
if fileType is None:
fileType = guessFileType(src)
if fileType is None:
raise ValueError, "Can't determine input file type"
ufoWriter = UFOWriter(dst)
if fileType == "TTF":
from fontTools.ttLib import TTFont
font = TTFont(src, 0)
elif fileType == "Type 1":
from fontTools.t1Lib import T1Font
font = T1Font(src)
else:
assert 0, "unknown file type: %r" % fileType
inGlyphSet = font.getGlyphSet()
outGlyphSet = ufoWriter.getGlyphSet()
for glyphName in inGlyphSet.keys():
print "-", glyphName
glyph = inGlyphSet[glyphName]
def drawPoints(pen):
pen = SegmentToPointPen(pen)
glyph.draw(pen)
outGlyphSet.writeGlyph(glyphName, glyph, drawPoints)
outGlyphSet.writeContents()
if fileType == "TTF":
info = extractTTFFontInfo(font)
elif fileType == "Type 1":
info = extractT1FontInfo(font)
ufoWriter.writeInfo(info)
if __name__ == "__main__":
print readGlyphConstructions()

View File

@ -1,339 +0,0 @@
"""
T.O.O.L.S.: Things Other Objects Lack (Sometimes)
-assorted raw tools.
This is an assorted colection of raw tools that do
things inside of FontLab. Many of these functions
form the bedrock of objectsFL. In short, use these
tools only if you need the raw functions and they are
not supported by the objects.
Object model:
Most of these tools were written before
objectsFL. Some of these tools are used by
objectsFL. That means that if you want to
use functions from robofab.tools you can always
feed them FontLab objects (like Font, Glyps,
etc.). If the functions also accept Robjects from
robofab.objects it is usually mentioned in the
doc string.
This is a simple way to convert a robofab Font
object back to a FL Font object. Even if you don't
know which particular faith an object belongs to
you can use this:
font = unwrapFont(font)
"""
from FL import *
from warnings import warn
try:
from fl_cmd import *
except ImportError:
print "The fl_cmd module is not available here. toolsFL.py"
import os
from robofab import RoboFabError
# local encoding
if os.name == "mac":
LOCAL_ENCODING = "macroman"
else:
LOCAL_ENCODING = "latin-1"
#
#
#
# stuff for fontlab app
#
#
#
def AppFolderRenamer():
"""This function will rename the folder that contains the
FontLab application to a more specific name that includes
the version of the application
Warning: it messes with the paths of your app, if you have
items that hardwired to this path you'd be in trouble.
"""
if fl.count > 0:
warn("Close all fonts before running AppFolderRenamer")
return
old = fl.path[:-1]
root = os.path.dirname(old)
new = "FontLab " + fl.version.replace('/', '_')
path = os.path.join(root, new)
if path != old:
try:
os.rename(old, path)
except OSError:
pass
warn("Please quit and restart FontLab")
#
#
#
# stuff for fonts
#
#
#
def GetFont(full_name):
"""Return fontobjects which match full_name.
Note: result is a list.
Returns: a list of FL Font objects
"""
found = []
for f in AllFonts():
if f.full_name == full_name:
found.append(f)
return found
def AllFonts():
"""Collect a list of all open fonts.
Returns: a list of FL Font objects.
"""
fontcount = len(fl)
af = []
for i in range(fontcount):
af.append(fl[i])
return af
def FontIndex(font):
"""return the index of a specified FL Font"""
font = unwrapFont(font)
a = AllFonts()
p = []
for f in a:
p.append(f.file_name)
if font.file_name in p:
return p.index(font.file_name)
else:
return None
def unwrapFont(font):
"""Unwrap the font if it happens to be a RoboFab Font"""
if hasattr(font, 'isRobofab'):
return font.naked()
return font
def MakeTempFont(font, dupemark=None, removeOverlap=True, decompose=True):
"""Save the current FL Font,
- close the file,
- duplicate the file in the finder (icon looks weird, but it works)
- open the duplicate
- decompose the glyphs
- remove overlaps
- return the fontobject
font is either a FL Font or RF RFont object.
Problems: doesn't check if the filename is getting too long.
Note: it will overwrite older files with the same name.
"""
import string
f = unwrapFont(font)
if not dupemark or dupemark == "":
dupemark = "_tmp_"
path = f.file_name
a = f.file_name.split('.')
a.insert(len(a)-1, dupemark)
newpath = string.join(a, '.')
f.Save(path)
fl.Close(FontIndex(f))
file = open(path, 'rb')
data = file.read()
file.close()
file = open(newpath, 'wb')
file.write(data)
file.close()
fl.Open(newpath, 1)
nf = fl.font
if nf is None:
print 'uh oh, sup?'
return None
else:
for g in nf.glyphs:
if decompose:
g.Decompose()
if removeOverlap:
g.RemoveOverlap()
return nf
def makePSFontName(name):
"""Create a postscript filename out of a regular postscript fontname,
using the old fashioned macintosh 5:3:3 convention.
"""
import string
parts = []
current = []
final = []
notAllowed = '-_+=,-'
index = 0
for c in name:
if c in notAllowed:
continue
if c in string.uppercase or index == 0:
c = string.upper(c)
if current:
parts.append("".join(current))
current = [c]
else:
current.append(c)
index = index + 1
if current:
parts.append("".join(current))
final.append(parts[0][:5])
for p in parts[1:]:
final.append(p[:3])
return "".join(final)
#
#
#
# stuff for glyphs
#
#
#
def NewGlyph(font, glyphName, clear=False, updateFont=True):
"""Make a new glyph if it doesn't already exist, return the glyph.
font is either a FL Font or RF RFont object. If updateFont is True
the (very slow) fl.UpdateFont function will be called.
"""
font = unwrapFont(font)
if isinstance(glyphName, unicode):
glyphName = glyphName.encode(LOCAL_ENCODING)
glyph = font[glyphName]
if glyph is None:
new = Glyph()
new.name = glyphName
font.glyphs.append(new)
if updateFont:
fl.UpdateFont(FontIndex(font))
glyph = font[glyphName]
elif clear:
glyph.Clear()
glyph.anchors.clean()
glyph.components.clean()
glyph.note = ""
return glyph
def AddToAlias(additions, sep='+'):
"""additions is a dict with glyphnames as keys
and glyphConstruction as values. In order to make
a bunch of additions in one go rather than open
and close the file for each name. Add a glyph
to the alias.dat file if it doesn't already exist.
additions = {'Gcircumflex': ['G','circumflex'], }
Returns a list of only the added glyphnames."""
import string
glyphs = {}
data = []
new = []
path = os.path.join(fl.path, 'Mapping', 'alias.dat')
if os.path.exists(path):
file = open(path, 'r')
data = file.read().split('\n')
file.close()
for i in data:
if len(i) == 0: continue
if i[0] != '%':
glyphs[i.split(' ')[0]] = i.split(' ')[1]
for glyphName, glyphConstruction in additions.items():
if glyphName not in glyphs.keys():
new.append(glyphName)
glyphs[glyphName] = string.join(glyphConstruction, sep)
newNames = ['%%FONTLAB ALIASES']
l = glyphs.keys()
l.sort()
for i in l:
newNames.append(string.join([i, glyphs[i]], ' '))
file = open(path, 'w')
file.write(string.join(newNames, '\n'))
file.close()
return new
def GlyphIndexTable(font):
"""Make a glyph index table for font"""
font = unwrapFont(font)
idx = {}
for i in range(len(font)):
g = font.glyphs[i]
idx[g.name] = i
return idx
def MakeReverseCompoMapping(font):
"""Return a dict that maps glyph names to lists containing tuples
of the form:
(clientGlyphName, componentIndex)
"""
font = unwrapFont(font)
reverseCompoMapping = {}
for g in font.glyphs:
for i, c in zip(range(len(g.components)), g.components):
base = font[c.index].name
if not base in reverseCompoMapping:
reverseCompoMapping[base] = []
reverseCompoMapping[base].append((g.name, i))
return reverseCompoMapping
#
#
#
# stuff for text files
#
#
#
def textPrinter(text, name=None, path=None):
"""Write a string to a text file. If no name is given it becomes
Untitled_hour_minute_second.txt . If no path is given it goes
into the FontLab/RoboFab Data directory."""
if not name:
import time
tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst = time.localtime()
now = '_'.join((`tm_hour`, `tm_min`, `tm_sec`))
name = 'Untitled_%s.txt'%now
if not path:
path = os.path.join(makeDataFolder(), name)
f = open(path, 'wb')
f.write(text)
f.close()
def makeDataFolder():
"""Make the RoboFab data folder"""
folderPath = os.path.join(fl.path, "RoboFab Data")
if not os.path.exists(folderPath):
try:
os.makedirs(folderPath)
except:
pass
return folderPath
def Log(text=None):
"""Make an entry in the default log file."""
now = str(time.asctime(time.localtime(time.time())))
if not text:
text = "-"
entry = "%s: %s\r"%(now, text)
path = os.path.join(os.getcwd(), "Logs")
new = 0
if not os.path.exists(path):
os.makedirs(path)
new = 1
log = os.path.join(path, "log.txt")
f = open(log, 'a')
if new:
f.write("# log file for FL\r")
f.write(entry)
f.close()

View File

@ -1,108 +0,0 @@
import os, sys
from robofab import RoboFabError, version, numberVersion
class RFWorld:
"""All parameters about platforms, versions and environments included in one object."""
def __init__(self):
self.mac = None
self.pc = None
self.platform = sys.platform
self.applicationName = None # name of the application we're running in
self.name = os.name
self.version = version # the robofab version
self.numberVersion = numberVersion
self.run = True
# get some platform information
if self.name == 'mac' or self.name == 'posix':
if self.platform == "darwin":
self.mac = "X"
else:
self.mac = "pre-X"
elif self.name == 'nt':
# if you know more about PC & win stuff, add it here!
self.pc = True
else:
raise RoboFabError, "We're running on an unknown platform."
# collect versions
self.pyVersion = sys.version[:3]
self.inPython = False
self.inFontLab = False
self.flVersion = None
self.inGlyphs = False
self.glyphsVersion = None
self.inRoboFont = False
self.roboFontVersion = None
# are we in FontLab?
try:
from FL import fl
self.applicationName = fl.filename
self.inFontLab = True
self.flVersion = fl.version
except ImportError: pass
# are we in Glyphs?
try:
import objectsGS
from AppKit import NSBundle
bundle = NSBundle.mainBundle()
self.applicationName = bundle.infoDictionary()["CFBundleName"]
self.inGlyphs = True
self.glyphsVersion = bundle.infoDictionary()["CFBundleVersion"]
except ImportError: pass
# are we in RoboFont
try:
import mojo
from AppKit import NSBundle
bundle = NSBundle.mainBundle()
self.applicationName = bundle.infoDictionary()["CFBundleName"]
self.inRoboFont = True
self.roboFontVersion = bundle.infoDictionary()["CFBundleVersion"]
except ImportError: pass
# we are in NoneLab
if not self.inFontLab:
self.inPython = True
# see if we have DialogKit
self.supportsDialogKit = False
def __repr__(self):
s = [
"Robofab is running on %s" % self.platform,
"Python version: %s" % self.pyVersion,
"Mac stuff: %s" % self.mac,
"PC stuff: %s" % self.pc,
"FontLab stuff: %s" % self.inFontLab,
"FLversion: %s" % self.flVersion,
"Glyphs stuff: %s" % self.inGlyphs,
"Glyphs version: %s" % self.glyphsVersion,
"RoboFont stuff: %s" %self.inRoboFont,
"RoboFont version: %s" %self.roboFontVersion,
]
return ", ".join(s)
world = RFWorld()
lineBreak = os.linesep
if world.inFontLab:
from robofab.interface.all.dialogs import SelectFont, SelectGlyph
from robofab.objects.objectsFL import CurrentFont, CurrentGlyph, RFont, RGlyph, OpenFont, NewFont, AllFonts
lineBreak = "\n"
elif world.inRoboFont:
from mojo.roboFont import CurrentFont, CurrentGlyph, RFont, RGlyph, OpenFont, NewFont, AllFonts
elif world.inGlyphs:
from objectsGS import CurrentFont, CurrentGlyph, RFont, RGlyph, OpenFont, NewFont, AllFonts
elif world.inPython:
from robofab.objects.objectsRF import CurrentFont, CurrentGlyph, RFont, RGlyph, OpenFont, NewFont, AllFonts
if __name__ == "__main__":
f = RFWorld()
print f

View File

@ -30,7 +30,7 @@ are available for external use. These are
validateFontInfoVersion2ValueForAttribute
validateFontInfoVersion3ValueForAttribute
Value conversion functions are availble for converting
Value conversion functions are available for converting
fontinfo.plist values between the possible format versions.
convertFontInfoValueForAttributeFromVersion1ToVersion2
convertFontInfoValueForAttributeFromVersion2ToVersion1

View File

@ -1,5 +1,6 @@
from __future__ import absolute_import
"""Small helper module to parse Plist-formatted data from trees as created
"""
Small helper module to parse Plist-formatted data from trees as created
by xmlTreeBuilder.
"""
@ -8,7 +9,8 @@ __all__ = "readPlistFromTree"
from ufoLib.plistlib import PlistParser
def readPlistFromTree(tree):
"""Given a (sub)tree created by xmlTreeBuilder, interpret it
"""
Given a (sub)tree created by xmlTreeBuilder, interpret it
as Plist-formatted data, and return the root object.
"""
parser = PlistTreeParser()

View File

@ -1,6 +1,7 @@
"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
"""
plistlib.py -- a tool to generate and parse MacOSX .plist files.
The PropertList (.plist) file format is a simple XML pickle supporting
The PropertyList (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings.
Usually the top level object is a dictionary.
@ -50,7 +51,6 @@ Parse Plist example:
print pl["aKey"]
"""
__all__ = [
"readPlist", "writePlist", "readPlistFromString", "writePlistToString",
"readPlistFromResource", "writePlistToResource",

View File

@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
"""FontLab Tokenize
Tokenize FontLabs preview/metrics text into single characters
respecting escaped glyph names (eg. /A.smcp) and providing a
lossless reverse function. Sample usage (and actual test suite):
>>> tokenize('/A/B/C')
['/A', '/B', '/C']
>>> tokenize('abcde/B/C')
['a', 'b', 'c', 'd', 'e', '/B', '/C']
>>> tokenize('foo/A.smcp/B.smcp abc')
['f', 'o', 'o', '/A.smcp', '/B.smcp', 'a', 'b', 'c']
>>> p = ['f', 'o', 'o', '/A.smcp', '/B.smcp', 'a', 'b', 'c']
>>> serialize(p)
'foo/A.smcp/B.smcp abc'
>>> tokenize('/a /b /c')
['/a', '/b', '/c']
>>> tokenize('/a/b c')
['/a', '/b', 'c']
>>> tokenize('@a@b@')
['@', 'a', '@', 'b', '@']
>>> tokenize('abc def ghi ')
['a', 'b', 'c', ' ', 'd', 'e', 'f', ' ', 'g', 'h', 'i', ' ']
>>> p = ['a', 'b', 'c', ' ', 'd', 'e', 'f', ' ', 'g', 'h', 'i', ' ']
>>> serialize(p)
'abc def ghi '
>>> serialize(['/a', 'b', '/c', 'd'])
'/a b/c d'
"""
__author__ = 'Antonio Cavedoni <http://cavedoni.com/>'
__version__ = '0.1'
__svnid__ = '$Id$'
__license__ = 'Python'
def tokenize(input):
tokens = []
escaped = []
for i in range(len(input)):
x = input[i]
if x != '/' and not escaped:
tokens.append(x)
else:
if x == '/' and not escaped:
# append the slash so the escaped list is no longer
# false: starts capturing elements
escaped.append(x)
elif x != '/' and escaped:
if i == (len(input) - 1):
escaped.append(x)
tokens.append("".join(escaped))
else:
if x == ' ':
tokens.append("".join(escaped))
escaped = []
else:
escaped.append(x)
elif x == '/' and escaped:
# starts a new sequence so, flush the escaped buffer
# and start anew
tokens.append("".join(escaped))
escaped = [x]
return tokens
def serialize(tokens):
series = []
for i in range(len(tokens)):
t = tokens[i]
if t.startswith('/') and i != (len(tokens) - 1):
if not tokens[i+1].startswith('/'):
series.append(t + ' ')
else:
series.append(t)
else:
series.append(t)
return "".join(series)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,28 +0,0 @@
The code in the Contributions folder is covered, like the rest of RoboFab,
by the BSD license, unless stated otherwise in the code itself.
RoboFab License Agreement
Copyright (c) 2005-2012, The RoboFab Developers:
Erik van Blokland
Tal Leming
Just van Rossum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the The RoboFab Developers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Up to date info on RoboFab:
http://robofab.com/
This is the BSD license:
http://www.opensource.org/licenses/BSD-3-Clause

View File

@ -1,3 +0,0 @@
RoboFab Contributed Scripts
This is a folder with contributed scripts.

View File

@ -1,3 +0,0 @@
RoboFab Scripts
These are some folders with scripts that can be run in FontLab.

View File

@ -1,72 +0,0 @@
#FLM: Align Two Nodes
"""Align bPoints horizontally, vertically or both."""
from robofab.world import CurrentGlyph
from robofab.interface.all.dialogs import TwoChecks
glyph = CurrentGlyph()
sel = []
#gather selected bPoints
for contour in glyph.contours:
if contour.selected:
for bPoint in contour.bPoints:
if bPoint.selected:
sel.append(bPoint)
if len(sel) != 0:
xL = []
yL = []
#store up all coordinates for use later
for bPoint in sel:
x, y = bPoint.anchor
xL.append(x)
yL.append(y)
if len(xL) > 1:
w = TwoChecks("Horizontal", "Vertical", 0, 0)
if w == None or w == 0:
#the user doesn't want to align anything
pass
else:
#find the center among all those bPoints
minX = min(xL)
maxX = max(xL)
minY = min(yL)
maxY = max(yL)
cX = int(round((minX + maxX)/2))
cY = int(round((minY + maxY)/2))
#set the undo
fl.SetUndo()
#determine what the user wants to do
noY = False
noX = False
if w == 1:
#the user wants to align y
noX = True
elif w == 2:
#the user wants to align x
noY = True
elif w == 3:
#the user wants to align x and y
pass
for bPoint in sel:
#get the move value for the bPoint
aX, aY = bPoint.anchor
mX = cX - aX
mY = cY - aY
if noY:
#don't move the y
mY = 0
if noX:
#don't move the x
mX = 0
bPoint.move((mX, mY))
glyph.update()

View File

@ -1,14 +0,0 @@
"""Correct contour direction for all glyphs in the font"""
from robofab.world import OpenFont
from robofab.interface.all.dialogs import ProgressBar
font = OpenFont()
bar = ProgressBar('Correcting contour direction...', len(font))
for glyph in font:
bar.label(glyph.name)
glyph.correctDirection()
glyph.update()
bar.tick()
font.update()
bar.close()

View File

@ -1,27 +0,0 @@
#
#
# make a list of which glyphs in this font could theoretically
# interpolate with each other.
#
#
from robofab.world import CurrentFont
from robofab.pens.digestPen import DigestPointPen, DigestPointStructurePen
compatibles = {}
f = CurrentFont()
for c in f:
p = DigestPointStructurePen()
c.drawPoints(p)
d = p.getDigest()
if not compatibles.has_key(d):
compatibles[d] = []
compatibles[d].append(c.name)
print
print 'In %s, these glyphs could interpolate:'%(f.info.postscriptFullName)
for d, names in compatibles.items():
if len(names) > 1:
print ", ".join(names)

View File

@ -1,22 +0,0 @@
#FLM: Glyph Appender
"""Add a glyph to the current glyph"""
from robofab.world import CurrentFont, CurrentGlyph
from robofab.interface.all.dialogs import SelectGlyph
glyph = CurrentGlyph()
font = CurrentFont()
# select a glyph to add
selected = SelectGlyph(font)
# make sure that we are not trying add the current glyph to itself
if selected.name != glyph.name:
# preserve the current state
fl.SetUndo()
# add the selected glyph to the current glyph
glyph.appendGlyph(selected)
# always update the glyph!
glyph.update()
# and, just to be safe, update the font...
font.update()

View File

@ -1,24 +0,0 @@
#FLM: Fun with GlyphMath
# this example is meant to run with the RoboFab Demo Font
# as the Current Font. So, if you're doing this in FontLab
# import the Demo Font UFO first.
from robofab.world import CurrentFont
from random import random
f = CurrentFont()
condensedLight = f["a#condensed_light"]
wideLight = f["a#wide_light"]
wideBold = f["a#wide_bold"]
diff = wideLight - condensedLight
destination = f.newGlyph("a#deltaexperiment")
destination.clear()
x = wideBold + (condensedLight-wideLight)*random()
destination.appendGlyph( x)
destination.width = x.width
destination.update()
f.update()

View File

@ -1,50 +0,0 @@
#FLM: Interpol Preview
"""This script draws all incremental interpolations
between 1% and 99% of a selected glyph into a new font.
It requires two open source fonts in FontLab."""
from robofab.interface.all.dialogs import SelectFont, OneList, ProgressBar
from robofab.world import NewFont
src1 = SelectFont('Select source font one:')
if src1:
src2 = SelectFont('Select source font two:')
if src2:
# collect a list of all compatible glyphs
common = []
for glyphName in src1.keys():
if src2.has_key(glyphName):
if src1[glyphName].isCompatible(src2[glyphName]):
common.append(glyphName)
common.sort()
selName = OneList(common, 'Select a glyph:')
if selName:
dest = NewFont()
g1 = src1[selName]
g2 = src2[selName]
count = 1
bar = ProgressBar('Interpolating...', 100)
# add the sourec one glyph for reference
dest.newGlyph(selName + '_000')
dest[selName + '_000'].width = src1[selName].width
dest[selName + '_000'].appendGlyph(src1[selName])
dest[selName + '_000'].mark = 1
dest[selName + '_000'].update()
# add a new glyph and interpolate it
while count != 100:
factor = count * .01
newName = selName + '_' + `count`.zfill(3)
gD = dest.newGlyph(newName)
gD.interpolate(factor, g1, g2)
gD.update()
bar.tick()
count = count + 1
# add the source two glyph for reference
dest.newGlyph(selName + '_100')
dest[selName + '_100'].width = src2[selName].width
dest[selName + '_100'].appendGlyph(src2[selName])
dest[selName + '_100'].mark = 1
dest[selName + '_100'].update()
dest.update()
bar.close()

View File

@ -1,16 +0,0 @@
#FLM: Invert Selection
"""Invert the selected segments in the current glyph"""
from robofab.world import CurrentGlyph
glyph = CurrentGlyph()
for contour in glyph.contours:
notSelected = []
for segment in contour.segments:
if not segment.selected:
notSelected.append(segment.index)
contour.selected = False
for index in notSelected:
contour[index].selected = True
glyph.update()

View File

@ -1,50 +0,0 @@
# FLM: Make Cameo Font
"""Make a cameo font. Pretty simple."""
from robofab.world import CurrentFont
from robofab.interface.all.dialogs import Message
buffer = 30
scaleValue = .9
f = CurrentFont()
# clear all kerning
f.kerning.clear()
#determine top and bottom of the box
t = f.info.unitsPerEm + f.info.descender + buffer
b = f.info.descender - buffer
#first decompose any components
for g in f:
g.decompose()
#then proceed with the cameo operation
for g in f:
#catch negative sidebearings
if g.leftMargin < 0:
g.leftMargin = 0
if g.rightMargin < 0:
g.rightMargin = 0
#scale the glyph and sidebearings
leftMargin = int(round((g.rightMargin * scaleValue) + buffer))
rightMargin = int(round((g.rightMargin * scaleValue) + buffer))
g.scale((scaleValue, scaleValue), (int(round(g.width/2)), 0))
g.leftMargin = leftMargin
g.rightMargin = rightMargin
#determine the left and the right of the box
l = 0
r = g.width
#draw the box using flPen
p = g.getPen()
p.moveTo((l, b))
p.lineTo((l, t))
p.lineTo((r, t))
p.lineTo((r, b))
p.closePath()
#correct path direction
g.correctDirection()
#update the glyph
g.update()
#update the font
f.update()
#tell me when it is over
Message('The highly complex "Cameo Operation" is now complete. Please examine the results and be thankful that RoboFab is on your side.')

View File

@ -1,12 +0,0 @@
#FLM: Kerning Counter
"""print kerning counts for glyphs selected in the font window"""
from robofab.world import CurrentFont
font = CurrentFont()
selectedGlyphs = font.selection
kerning = font.kerning
counts = kerning.occurrenceCount(selectedGlyphs)
for glyphName in selectedGlyphs:
print "%s: %s pairs"%(glyphName, counts[glyphName])

View File

@ -1,29 +0,0 @@
#FLM: Print Measurments
"""print the distance and angle between two selected points"""
from robofab.world import CurrentGlyph
import math
glyph = CurrentGlyph()
selectedPoints = []
for contour in glyph.contours:
if contour.selected:
for segment in contour.segments:
if segment.selected:
onCurve = segment.onCurve
point = (onCurve.x, onCurve.y)
if point not in selectedPoints:
selectedPoints.append(point)
if len(selectedPoints) == 2:
xList = [x for x, y in selectedPoints]
yList = [y for x, y in selectedPoints]
xList.sort()
yList.sort()
xDiff = xList[1] - xList[0]
yDiff = yList[1] - yList[0]
ang = round(math.atan2(yDiff, xDiff)*180/math.pi, 3)
print "x:%s y:%s a:%s"%(xDiff, yDiff, ang)

View File

@ -1,14 +0,0 @@
"""round all kerning values to increments of a specified value"""
value = 100
from robofab.world import CurrentFont
font = CurrentFont()
kerning = font.kerning
startCount = len(kerning)
kerning.round(value)
font.update()
print 'finished rounding kerning by %s.'%value
print 'you started with %s kerning pairs.'%startCount
print 'you now have %s kerning pairs.'%len(kerning)

View File

@ -1,31 +0,0 @@
#FLM: UFO Remove Overlap
"""
Remove overlap on all glyphs in a .ufo font.
This script sis more than a little silly, but it
demonstrates how objectsRF and objectsFL can
work hand in hand.
"""
from robofab.objects.objectsRF import OpenFont
from robofab.objects.objectsFL import NewFont
from robofab.interface.all.dialogs import ProgressBar
ufoFont = OpenFont(note="Select a .ufo")
if ufoFont:
bar = ProgressBar('Removing Overlap...', len(ufoFont))
flFont = NewFont()
flGlyph = flFont.newGlyph('OverlapRemover')
for ufoGlyph in ufoFont:
flPen = flGlyph.getPointPen()
ufoGlyph.drawPoints(flPen)
flGlyph.removeOverlap()
ufoPen = ufoGlyph.getPointPen()
ufoGlyph.clear()
flGlyph.drawPoints(ufoPen)
flGlyph.clear()
bar.tick()
flFont.close(save=0)
bar.close()
ufoFont.save(doProgress=True)

View File

@ -1,32 +0,0 @@
#FLM: 010 FontLab to RoboFab and Back
# In which an adventurous glyph of your choice
# makes a trip into RoboFab land,
# and returns safely home after various inspections
# and modifications.
from robofab.world import CurrentGlyph, CurrentFont
c = CurrentGlyph()
f = CurrentFont()
from robofab.objects.objectsRF import RGlyph
d = RGlyph()
# woa! d is now a rf version of a fl glyph!
d.appendGlyph(c)
d.width = 100
c.printDump()
d.printDump()
e = f.newGlyph('copyTest')
# dump the rf glyph back to a fl glyph!
e.appendGlyph(d)
# see, it still takes its own kind as well
e.appendGlyph(f['a'])
e.printDump()

View File

@ -1,60 +0,0 @@
#FLM: RoboFab Intro, Building Accented Glyphs
#
#
# demo building accented glyphs with robofab
#
#
# RoboFab sports very simple, yet very simple automatic
# accented glyph compiling. The accentBuilder module has
# several tools that handle these operations splendidly.
# Why don't we have a look?
# (you will need to have a font open in FontLab)
from robofab.world import CurrentFont
# accentBuilder lives in robofab.tools
from robofab.tools.accentBuilder import AccentTools
font = CurrentFont()
# The first thing that you need is a list of accented glyphs
# that you want to compile. This is a very short one for
# demonstration purposes. There are some more extensive
# lists located in robofab.gString
myList = ['aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde', 'aring']
# AccentTools is the class that contains the most important methods.
# It takes a font and the list of accented glyphs as arguments.
accentTool = AccentTools(font, myList)
# These accented glyphs are compiled using anchors. Anchors are
# simple reference points in the glyph. These anchors are used
# to align the various components when the accented glyph is compiled.
# AccentTools looks at the glyph names in your list of accented glyphs
# and references an internal glyph construction database to determine
# where the anchors need to be placed. So, we need to tell AccentTools
# to position the anchors appropriately. We have very flexible control
# of how the anchors are positioned in relation to the glyph.
# So, build the needed anchors!
accentTool.buildAnchors(ucXOffset=30, ucYOffset=70, lcXOffset=20, lcYOffset=50, doProgress=True)
# AccentTools may encounter some problems, for example if some
# necessary glyphs are not present, when adding the anchors.
# We can print these errors by calling:
accentTool.printAnchorErrors()
# Now that we have the anchors in place, we can compile the glyphs.
accentTool.buildAccents(doProgress=True)
# It may also run into some problems, for example if necessary
# anchors are not prest. You can print these errors out as well.
accentTool.printAccentErrors()
# That's it! Now, update the font.
font.update()
# See how easy that is? And, this is just the tip of the iceburg
# with AccentTools. Read the source!

View File

@ -1,67 +0,0 @@
#FLM: RoboFab Intro, Contours, Segments and Points
#
#
# demo of RoboFab contour, segments and points
#
#
# In RoboFab, glyphs are constructed mainly from a list
# of contours. These contours are constructed from lists
# of segments, which are construted from a list of points.
# Kind of makes sense, doesn't it? This demo will briefly
# run through printing some info about the contours in
# a glyph and it will show how to do a few things to contours.
# You should really check out the documentation for a complete
# list of everything that can be done with and to contours,
# segments and points. There are far too many things to list
# in this intro. Ok. Here we go!
from robofab.world import CurrentGlyph
# (you will need t have a font open in FontLab for this demo)
glyph = CurrentGlyph()
# Before we get into changing any of these objects,
# let's print a little info about them.
print "This glyph has %s contours"%len(glyph.contours)
# a glyph has a list of contours
for contour in glyph.contours:
# every contour has an index,
print "-contour index %s"%contour.index
# a direction,
print "--clockwise: %s"%contour.clockwise
# and a list of segments
print "--%s segments"%len(contour.segments)
for segment in contour.segments:
# every segment has an index,
print "---segment %s"%segment.index
# a type,
print "---type: %s"%segment.type
# a list of points,
print "---%s points"%len(segment.points)
# which includes one on curve point,
onCurve = segment.onCurve
print "---onCurve point at: (%s, %s)"%(onCurve.x, onCurve.y)
# and possibly some off curve points
print "---%s offCurve points"%len(segment.offCurve)
# Now, contours, segments and points all have
# unique methods of their own, but for now let's
# just look at contour methods.
for contour in glyph.contours:
# you can move the contour
contour.move((314, 159))
# you can scale the contour
# and you can even set the center
# of the scale
contour.scale((2, .5), center=(100, 100))
# you can reverse the direction
contour.reverseContour()
# and there are many more!
# as always, update the glyph
glyph.update()

View File

@ -1,94 +0,0 @@
#FLM: RoboFab Intro, Font and Glyph Lib
#
#
# demo of font.lib and glyph.lib
#
#
from robofab.world import CurrentFont, CurrentGlyph
# font.lib and glyph.lib are an idea inherited from RoboFog.
# They are spaces to store custom data. font.lib is for
# font-level custom data, glyph.lib is for glyph-level data.
# They're both Python dictionaries.
#
# Now let's talk about your slice of the lib pie.
# Everyone has their own little address space in the lib object.
# This is done by advising to use a naming convention for the
# top level keys in the lib dictionary.
# Your address is defined by inversing your domain name.
# For example, keys used by RoboFab start with "org.robofab",
# LettError keys start with "com.letterror".
# It's pretty simple, and best of all it avoids the problems
# associated with maintaining a registry. This approach
# to naming was pioneered by Java and has been adopted
# by industry luminaries like Apple and RoboFab.
# What you store in your section is completely up to you.
# Enough of that, let's take a look at how to use the thing.
# Make sure you have a font open.
font = CurrentFont()
# Essentially the lib is a dict object, and it has all properties
# of a dict. So, following the naming scheme above, this is how
# you access your section of the lib (but use your address!):
font.lib['org.robofab'] = 'our address in the lib'
font.lib['org.robofab.keepout'] = 'this also belongs to us'
# Like a dict, you can simply store a string in the dict
font.lib['org.robofab'] = 'Hello World'
print font.lib['org.robofab']
# But that is really boring! Let's store a bunch of stuff.
# Here we can go in two directions: we can either store a
# dict right at our address:
font.lib['org.robofab'] = {'An Int':1, 'A Str':'Howdy!', 'A List':['X', 'Y', 'Z'], 'A Dict':{'Robo':'Fab'}}
# Now because we have a nested dict, and we can access
# it like any other dict let's print the stuff we just stored:
print font.lib['org.robofab']['An Int']
print font.lib['org.robofab']['A Str']
print font.lib['org.robofab']['A List']
print font.lib['org.robofab']['A Dict']
# ...or we can avoid deeper nesting, and use our address as a
# key prefix:
font.lib['org.robofab.A'] = "A"
font.lib['org.robofab.B'] = "B"
font.lib['org.robofab.aList'] = [1, 2, 3]
print font.lib['org.robofab.A']
print font.lib['org.robofab.B']
print font.lib['org.robofab.aList']
# It is all sooo easy!
# Every glyph has it's very own lib as well
# and it works just like the font lib
glyph = CurrentGlyph()
glyph.lib['org.robofab'] = {'My glyph is totally':'Awesome'}
print glyph.lib['org.robofab']['My glyph is totally']
# The type of data that can be stored in lib dictionaries is
# limited. Dictionary keys must be strings (or unicode strings),
# values may be dicts, lists, ints, floats, (unicode) strings and
# booleans. There is also a special type to store arbitrary binary
# data: robofab.plistlib.Data. Don't use plain strings for that!
# So, as you can see, this can be a very powerful thing
# to use and you are only limited by your imagination.
# Have fun!
# A Technical Note:
# You "under the hood" type folks out there may be
# wondering where this data is being stored in the
# FontLab file. Well, in all environments, it's stored
# as a Property List* (.plist), which is a simple XML
# format defined by Apple.
# In FontLab, libs are stored in the font.customdata
# and glyph.customdata fields and it is parsed whenever
# the lib is requested. So, don't try to put anything
# else in those fields, otherwise the lib won't work.
# When using UFO/GLIF, font.lib is stored in a file called
# lib.plist in the .ufo folder, glyph.lib is stored as
# part of the .glif file.

View File

@ -1,43 +0,0 @@
#FLM: RoboFab Intro, The Font Object
#
#
# demo of the RoboFab font object
#
#
import robofab
# Let's talk to some of the font objects.
# CurrentFont and CurrentGlyph are similar to
# the RoboFog functions. They return a font
# or Glyph object respectively. It will be the
# front most font or the front most glyph.
from robofab.world import CurrentFont, CurrentGlyph
# This is a brief intro into Robofabs all singing and
# dancing dialog class. It will produce simple
# dialogs in almost any environment, FontLab, Python IDE, W.
from robofab.interface.all.dialogs import Message
# (make sure you have a font opened in FontLab)
f = CurrentFont()
# so now f is the name of a font object for the current font.
if f == None:
# let's see what dialog can do, a warning
Message("You should open a font first, there's nothing to look at now!")
else:
# and another dialog.
Message("The current font is %s"%(f.info.postscriptFullName))
# let's have a look at some of the attributes a RoboFab Font object has
print "the number of glyphs:", len(f)
# some of the attributes map straight to the FontLab Font class
# We just straightened the camelCase here and there
print "full name of this font:", f.info.postscriptFullName
print "list of glyph names:", f.keys()
print 'ascender:', f.info.ascender
print 'descender:', f.info.descender

View File

@ -1,75 +0,0 @@
#FLM: RoboFab Intro, FoundrySettings.plist
#
#
# demo FoundrySettings.plist
#
#
# Setting all the data strings in the FontLab font header can be a repetitive and
# tedious exercise. RoboFab to the rescue! RoboFab features some nifty tools
# that help automate this process. These tools read a .plist file that you are free
# to edit to include your own standard settings. Currently, the .plist file contains
# these bits of data. We reserve the right to add more in the future.
# -copyright
# -trademark
# -license
# -licenseurl
# -notice
# -ttvendor
# -vendorurl
# -designer
# -designerurl
#
# The foundry settings tools parse this .plist file into a python dictionary that
# can be used to apply the data to fonts. It's really easy to use. Let's check it out!
from robofab.world import CurrentFont
from robofab.tools.toolsFL import makeDataFolder
# all the foundry settings tools live here:
from robofab.tools.toolsAll import readFoundrySettings, getFoundrySetting, setFoundrySetting
import time
import os
# You will need a font open in fontlab for this demo
font = CurrentFont()
# We need to know where the .plist file lives. In the FontLab environment
# it can live in the "RoboFab Data" folder with its friends. makeDataFolder()
# will make the data folder if it doesn't exist and it will return the path
settingsPath = os.path.join(makeDataFolder(), 'FoundrySettings.plist')
# Now, let's load those settings up
# readFoundrySettings(path) will return the data from the .plist as dictionary
mySettings = readFoundrySettings(settingsPath)
# Let's get the current year so that the year string is always up to date
font.info.year = time.gmtime(time.time())[0]
# Apply those settings that we just loaded
font.info.copyright = mySettings['copyright']
font.info.trademark = mySettings['trademark']
font.info.openTypeNameLicense = mySettings['license']
font.info.openTypeNameLicenseURL = mySettings['licenseurl']
font.info.openTypeNameDescription = mySettings['notice']
font.info.openTypeOS2VendorID = mySettings['ttvendor']
font.info.openTypeNameManufacturerURL = mySettings['vendorurl']
font.info.openTypeNameDesigner = mySettings['designer']
font.info.openTypeNameDesignerURL = mySettings['designerurl']
# and call the update method
font.update()
# But, Prof. RoboFab, what if I want to change the settings in the .plist file?
# Good question. You can always edit the .plist data by hand, or you can
# do it via a script. It would go a little something like this:
setFoundrySetting('trademark', 'This font is a trademark of Me, Myself and I', settingsPath)
# If you are on OSX, and you have the Apple developers tools installed, you
# can also edit it with /Developer/Applications/Property List Editor.
# And to read only only one setting from the file you can use this handly little method.
font.info.trademark = getFoundrySetting('trademark', settingsPath)
font.update()
# It's that easy!

View File

@ -1,55 +0,0 @@
#FLM: RoboFab Intro, Generating Fonts
#
#
# demo generating fonts with robofab
#
#
# Generating fonts with RoboFab is super easy! Let's have a look.
# (you will need to have a font open in FontLab)
from robofab.world import CurrentFont
import os
# A little function for making folders. we'll need it later.
def makeFolder(path):
#if the path doesn't exist, make it!
if not os.path.exists(path):
os.makedirs(path)
# We need to have a font open for this demo to work
font = CurrentFont()
# This will tell us what folder the font is in
fontPath = os.path.dirname(font.path)
# We'll put the fonts into a folder called "FabFonts" next the .vfb file
macPath = os.path.join(fontPath, 'FabFonts', 'ForMac')
pcPath = os.path.join(fontPath, 'FabFonts', 'ForPC')
bothPath = os.path.join(fontPath, 'FabFonts', 'ForBoth')
# Now, we'll use that little function we made earlier to make the folders
makeFolder(macPath)
makeFolder(pcPath)
makeFolder(bothPath)
# A dict of all the font types we want to output
fontTypes = { 'mac' : ['mactype1', 'macttf', 'macttdfont'],
'pc' : ['pctype1', 'pcmm'],
'both' : ['otfcff', 'otfttf']
}
# Finally, let's generate the fonts!
for macType in fontTypes['mac']:
print "generating %s..."%macType
font.generate(macType, macPath)
for pcType in fontTypes['pc']:
print "generating %s..."%pcType
font.generate(pcType, pcPath)
for bothType in fontTypes['both']:
print "generating %s..."%bothType
font.generate(bothType, bothPath)
print 'Done!'
# Wow! Could it be any easier than that?

View File

@ -1,34 +0,0 @@
#FLM: RoboFab Intro, The Glyph Object
#
#
# demo of the RoboFab glyph object
#
#
import robofab
from robofab.world import CurrentFont, CurrentGlyph
from robofab.interface.all.dialogs import Message
# (make sure you have a font opened in FontLab)
# this code starts out the same as intro_FontObject
f = CurrentFont()
if f == None:
Message("You should open a font first, there's nothing to look at now!")
else:
for g in f:
print "glyphname:", g.name, ", glyph width:", g.width
# so now g is a RoboFab Glyph object
print "this glyph has %d contours" % len(g.contours)
print "this glyph has %d components" % len(g.components)
print "this glyph has %d anchors" % len(g.anchors)
print
# easy huh?
# There are many more attributes and methods.
# Most of these can be used to edit the font data.
# Which makes them not suited for a simple intro as this
# because we don't want to mess up your font.

View File

@ -1,24 +0,0 @@
#FLM: RoboFab Intro, Font and Glyph Lib
#
#
# demo of glyph properties
#
#
from robofab.world import OpenFont
# This is a specific test of some features which are probably
# still only supported in one glyph in the DemoFont.ufo,
# which you can find the robofab/Data/ folder. Here is goes.
f = OpenFont(None, "")
for c in f:
if not c.properties.isEmpty():
print c.properties.dump()
# This prints the available GlyphProperties objects.
# Not very impressive at the moment, but it means
# that at least they're getting stored with the glyphs
# and that they can be read and interpreted.

Some files were not shown because too many files have changed in this diff Show More