1789 lines
181 KiB
HTML
1789 lines
181 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>
|
||
RoboThon06: Print
|
||
</title>
|
||
<link href="../print.css" type="text/css" rel="stylesheet" />
|
||
</head><body>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="content">
|
||
|
||
|
||
<img src="robothon06_logo.gif" alt="robothon06 logo" width="369" height="372" />
|
||
|
||
|
||
|
||
|
||
<h1>Robothon06 Conference</h1>
|
||
<p>
|
||
This is a printed version of the notes of the Robothon 06 conference. Robothon 06, Februari 16 and 17 2006, The Hague. Hosted at the Royal Academy of Arts.
|
||
</p>
|
||
|
||
|
||
<h2>About the printed version</h2>
|
||
|
||
<p>
|
||
This version of the notes is specially formatted for printing from a browser. As much as such is possible at all, it will produce reasonable results. At the time of the conference, this documentation (or an updated version) is available from <strong>www.letterror.com/code/robothon06/index.html</strong>. Some of the material will be added to the robofab documentation which can be found at <strong>www.robofab.com</strong> but probably not immediately after the conference. The online version has working links and cross references to other sources of documentation.
|
||
</p>
|
||
|
||
|
||
<h2>Thanks:</h2>
|
||
<p class="small">
|
||
<a href="http://www.kabk.nl" target="new" title="Koninklijke Academie van Kunsten">KABK, Royal Academy of Arts: www.kabk.nl</a> for hosting and support.
|
||
<br /><a href="http://www.houseind.com" target="new" title="House Industries">House Industries: www.houseind.com</a>
|
||
<br /><a href="http://www.fontshop.com" target="new" title="FontShop International">FontShop International: www.fontshop.com</a>
|
||
<br /><a href="http://typesupply.com" target="new" title="Type Supply">Type Supply: typesupply.com</a>
|
||
<br /><a href="http://letterror.com" target="new" title="LettError">LettError: letterror.com</a>
|
||
<br /><a href="http://type-invaders.com/" target="new" title="Type-Invaders">Type-Invaders: type-invaders.com</a>
|
||
<br /><a href="http://xml.petr.com/" target="new" title="Buro Petr van Blokland + Claudia Mens">Buro Petr van Blokland: petr.com</a>
|
||
|
||
<br />The 2005-2006 class of Type]Media.
|
||
|
||
|
||
</p>
|
||
|
||
<h3>Version</h3>
|
||
|
||
<p>
|
||
This version was generated on Mon Nov 19 20:59:58 2007.
|
||
</p>
|
||
|
||
|
||
<h2>Contents</h2>
|
||
<p>
|
||
<ul>
|
||
|
||
<li>Editors
|
||
</li>
|
||
|
||
<li>Font and Info objects
|
||
</li>
|
||
|
||
<li>Glyph and Pen objects
|
||
</li>
|
||
|
||
<li>Kerning and glyph building
|
||
</li>
|
||
|
||
<li>Scripting and Interpolation
|
||
</li>
|
||
|
||
<li>Scripting and production
|
||
</li>
|
||
|
||
<li>Scripting and NoneLab
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</p>
|
||
|
||
|
||
|
||
<img src="robothon06_logo.gif" alt="robothon06 logo" width="369" height="372" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h1>
|
||
Python, RoboFab, FontLab, Editors
|
||
</h1>
|
||
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_17.gif" width="140" height="96" />
|
||
|
||
|
||
<h2>Scripting?</h2>
|
||
|
||
|
||
<p>
|
||
These talks are about scripting. The term <strong>scripting</strong> is loosely defined as "writing small programs" without making it sound as difficult or complicated as "programming". Scripting is programming of course, but you don't have to tell anyone. You're using a programming language, <strong>Python</strong>, to write a script (or whatever you want to call it), which is then acted out by the <strong>Python interpreter</strong>. This is a program which will try to follow your instructions, giving feedback when it runs into problems or finishing the job when it doesn't. Running into problems is not something to be afraid of, Python does it very politely with a <strong>traceback</strong>, Python's way of reporting what went wrong and roughly where it the code it happened. We'll see a lot of these and we'll learn how to read these reports and understand what went wrong.
|
||
</p>
|
||
|
||
<h2>Why?</h2>
|
||
|
||
<p>
|
||
Scripting is not type design. Or perhaps better: scripting is everything but typedesign. Scripts won't help you find ideas or draw a better curve. But it might help you make simple tools for things you need to do often. Just like anything, it will get better with some experience, but even a badly written, simple script can save you lots of time. You don't have to be (or become) a professional programmer to make it a useful skill.
|
||
|
||
</p>
|
||
|
||
|
||
|
||
<h2>Where</h2>
|
||
|
||
<p>
|
||
Python is a modern, open source, programming language which is relatively easy to work with. There are Pythons for all operating systems and it has many developers building many different tools for it. It is not just for fonts and glyphs, there are networking and scientific tools, games, web applications. Years ago <strong>RoboFog</strong> introduced Python to make tools for typedesign and the idea stuck. Now there is a range of type applications that use Python: FontLab, the Adobe FDK, MetricsMachine, RoboFab. Learning Python means you can use your skills in more than one application.
|
||
</p>
|
||
|
||
|
||
<p>
|
||
There are several places where you can write your code. Each with their own purpose and use. We'll look at some of them:
|
||
|
||
<ul>
|
||
|
||
<li><strong>FontLab's Macro panel</strong>: a simple code editor in FontLab where you can edit and run scripts. The advantage is that you're in FontLab and can start work immediately, manipulating fonts and glyphs which are open. But the panel is lacking features which are useful when writing a lot of code, which is why FontLab includes the:</li>
|
||
|
||
<li><strong>FontLab with "external editor"</strong>: in the FontLab preferences panel you can select another application as your python editor of choice. So when you hit the Macros button, this editor will pop up. This requires that your script is saved in a file somewhere on disk. You can use the editor to write the code, but FontLab will still run the program. FontLab doesn't actually execute the Python code, but uses a system installed interpreter.</li>
|
||
|
||
<li><strong>Python IDE, Win, Mac OSX:</strong> There are several IDE ("Integrated Development Environment") programs for Python on Mac and Windows. This means basically a Python code editor which can also run your code. Some of them offer debugging tools, module browsers or even complete interface toolkits. These IDE's are general programming tools and don't know much about type specific things. More and more code editors offer Python execution, for instance in <strong>BBEdit</strong> and <strong>Textmate</strong> on OSX you can edit your code, hit a cmd key and have the code run in the OSX Python interpreter. This is similar to the FontLab-with-external-editor option</li>
|
||
|
||
<li><strong>Command-line python interpreter:</strong> a form of Python where you write a line of code at a prompt and it is immediately executed. Useful to test simple problems, but not for anything over a couple of lines of code.</li>
|
||
|
||
<li><strong>Command-line python:</strong> use the installed command-line Python interpreter to execute files. The interpreter is called with the filename as an argument
|
||
|
||
<div class="pythonsource"><pre><span class="py_key">python</span> <span class="py_key">myCode</span>.<span class="py_key">py</span></pre></div>
|
||
<div class="pythonoutput"><pre>..<span class="py_key">does</span> <span class="py_key">stuf</span>..</pre></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
</p>
|
||
|
||
|
||
|
||
<h2>Objects</h2>
|
||
<p>
|
||
Perhaps the most useful invention in programming since the paper-punch card is called <strong>object oriented programming</strong>. The term is used to describe a way of programming in which the data you're working on, and the code which belongs to that data are kept together, much like a person holding a handful of balloons on strings, in a single <strong>object</strong>, the cluster of balloons. This may sound a bit abstract. But it's a way to keep all the code and data sorted in a useful way. Otherwise there will be too much stuff very quickly. More terminology:
|
||
|
||
<ul>
|
||
|
||
<li><strong>attributes</strong>: the things an object knows about, its data or value. An object's data is stored in its attributes.</li>
|
||
|
||
<li><strong>methods</strong>: the things an object can do. The code to manipulate an object, its functions.</li>
|
||
|
||
</ul>
|
||
</p>
|
||
|
||
<p>
|
||
Risking an example: an object of the class <strong>car</strong> has an attribute <strong>color</strong> (blue) and a method <strong>drive</strong> (slow). Big objects are usually split up into smaller, more specific objects. For instance, a <strong>Font</strong> object offers access to <strong>Glyph</strong> objects. The way the various objects relate, wich object contains what etc. — the way something is abstracted — is called an <strong>object model</strong>. A map of the object model used in RoboFab is <a href="http://www.robofab.com/objects/model.html" class="reference" target="new">in the Fab docs</a>. An object model is also called <strong>API</strong> for Application Programming Interface.
|
||
|
||
|
||
|
||
</p>
|
||
|
||
<img src="http://www.robofab.com/img/logo.gif" alt="" border="0" width="102" height="88" />
|
||
|
||
<p>
|
||
FontLab objects? RoboFab objects? Are there different flavors of objects? doesn't that confuse things? FontLab has its own object model. There are FontLab Font objects and FontLab Glyph objects. But these objects are relatively low-level, that means that while using these objects, you have to keep track of a lot of things yourself and understand some FontLab peculiarities. This makes it difficult to write code efficiently. RoboFab is a layer of objects built on top of the FontLab objects, making it a lot easier to work with the data, fewer things to memorise and that means faster development. FontLab now comes bundled with RoboFab. In this conference we focus mainly on the RoboFab objects, but for some things the FontLab objects are needed.
|
||
</p>
|
||
|
||
<p>
|
||
Back to Python. Objects, attributes and methods follow the <strong>dot separated syntax</strong> which is a handy way to clearly state what method or attribute you want to talk to. Other programming languages use dot syntax as well, for instance javascript or php.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># attribute<br><span class="py_key">someObject</span>.<span class="py_key">someAttribute</span><br><span class="py_key">someObject</span>.<span class="py_key">anotherAttribute</span><br><span class="py_key">font</span>.<span class="py_key">path</span><br><span class="py_key">glyph</span>.<span class="py_key">width</span><br> <br># method<br><span class="py_key">someObject</span>.<span class="py_key">someMethod</span>(<span class="py_key">aParameter</span>)<br><span class="py_key">someObject</span>.<span class="py_key">anotherMethod</span>()<br><span class="py_key">font</span>.<span class="py_key">generate</span>()<br><span class="py_key">glyph</span>.<span class="py_key">clear</span>()</pre></div>
|
||
|
||
<p>
|
||
See how the dot connects the names? But this can go deeper than one level as well. Sometimes objects contain other objects, which in turn can have.. etc. Dont' worry about getting lost, this is why there is documentation.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># attribute<br><span class="py_key">someObject</span>.<span class="py_key">someOtherObject</span>.<span class="py_key">theOtherObjectsAttribute</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fullName</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span><br> <br># method<br><span class="py_key">someObject</span>.<span class="py_key">someOtherObject</span>.<span class="py_key">theOtherObjectsMethod</span>(<span class="py_key">aParameter</span>)<br><span class="py_key">font</span>.<span class="py_key">kerning</span>.<span class="py_key">update</span>()<br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">autoNaming</span>()</pre></div>
|
||
|
||
<p>
|
||
Did you notice some lines has parentheses after them, and others don't? Writing <strong>( )</strong> means you want to use the method and execute it. In Python terms: the <strong>method</strong> is <strong>called</strong>.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># a method but not called, you're looking at<br># the python object which contains the method.<br><span class="py_key">font</span>.<span class="py_key">update</span><br> <br># but calling a method is more useful,<br># it means: take this code and run it.<br><span class="py_key">font</span>.<span class="py_key">update</span>()</pre></div>
|
||
|
||
|
||
<h2>Names of variables and methods</h2>
|
||
<p>
|
||
In Robofab we have a couple of conventions for naming classes, attributes, methods and functions. These make it easier to predict what something is called and that means fewer trips to the documentation. This is what we're talking about:
|
||
|
||
<ul>
|
||
<li><strong>camelCase</strong>: this means that when a name is made up from several words, each<strong>A</strong>dditional<strong>W</strong>ord<strong>S</strong>tarts<strong>W</strong>ith<strong>AC</strong>ap. Examples: glyphName, kernTable, groupList, fontTools.
|
||
|
||
</li>
|
||
|
||
<li><strong>class names</strong> always start with an uppercase, then camelCase. Examples: RFont(), RGlyph, RKerning().
|
||
</li>
|
||
|
||
<li><strong>attribute and method names</strong> always start with a lowercase, then camelCase. Examples: kerning.importAFM(), glyph.drawPoints()
|
||
</li>
|
||
|
||
</ul>
|
||
</p>
|
||
|
||
<p>Note: these are our conventions, we do it this way because we prefer it. But that does not mean that Python requires it, as long as your names are legal Python you can write whatever you want. It's just useful to stick to a predictable method one way or the other. Please look at the standard <a href="http://www.python.org/doc/" class="reference" target="docs">Python documentation</a> for a full description of the language. Some rules about legal Python names:
|
||
|
||
<ul>
|
||
<li>Names can be arbitrarily long.</li>
|
||
<li>Names can contain letters and numbers.</li>
|
||
<li>The first character has to be a letter.</li>
|
||
<li>Names can contain upper and lower case letters.</li>
|
||
<li>Upper and lower case letters are different.</li>
|
||
<li>bruce and Bruce are different variable names</li>
|
||
<li>The underscore character,_, is legal. my_name</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
</p>
|
||
|
||
|
||
<h2>Installing RoboFab</h2>
|
||
<p>
|
||
Installing RoboFab is usually straightforward. There are quite a few combinations of operating system, FontLab version and Python version possible, some with their own pecularities. There's not much we can do about that, you just have to get it sorted.
|
||
|
||
|
||
<ul>
|
||
<li>Some notes on installing RoboFab for FontLab Studio 5, on <a href="http://just.letterror.com/ltrwiki/RoboFab_2fFontLabStudio5" class="reference" target="docs">the LettError wiki.</a>
|
||
</li>
|
||
|
||
<li>
|
||
More <a href="http://letterror.com/code/robofab/install.html" class="reference" target="docs">installation notes</a> in the RoboFab documentation
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</p>
|
||
|
||
|
||
<h2>Documentation</h2>
|
||
|
||
<p>
|
||
|
||
There is a lot of documentation available on the internet. When writing code, <a href="http://www.google.com/search?rls=en&q=python+robofab&ie=UTF-8&oe=UTF-8" target="docs">google</a> is your best friend - there is almost always someone else who has dealt with or written about the same problem. Reading the documentation is always a good idea. Looking at code snippets is useful too because it will explain how other people have solved problems. Even if a particular bit of code doesn't do what you're looking for, it can give you ideas.
|
||
|
||
<ul>
|
||
<li><strong>RoboFab objects</strong>: <a href="http://letterror.com/code/robofab/objects/index.html" class="reference" target="docs">the RoboFab API and reference.</a></li>
|
||
|
||
<li><strong>FontLab objects</strong>: <a href="http://dev.fontlab.net/flpydoc/" class="reference" target="docs">the FontLab documentation</a>. API and reference for the FontLab objects.</li>
|
||
|
||
<li>
|
||
<strong>RoboFab introduction to Scripting</strong>: <a href="http://letterror.com/code/robofab/howto/scripting.html" class="reference" target="docs">RoboFab scripting intro</a>
|
||
</li>
|
||
|
||
<li><strong>How to think like a computer scientist</strong>: an introduction into <a href="http://www.ibiblio.org/obp/thinkCSpy/" class="reference" target="docs">learning Python</a>. Not specifically about typedesign, but general Python programming, lists, dicts, variables, stuff like that.
|
||
</li>
|
||
|
||
<li><strong>Emergency Python Basics</strong>: elsewhere on this site <a href="basic.html" class="reference" target="docs"> an short overview of some of Python's basic stuff.</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h1>
|
||
Font & Info objects
|
||
</h1>
|
||
<img src="http://letterror.com/code/robofab/img/offdrawmodel_07.gif" width="140" height="96" />
|
||
|
||
|
||
<h2>
|
||
Code!
|
||
</h2>
|
||
<p>
|
||
So now then, you should have your editor fired up. Your RoboFab installed. Locate the
|
||
<strong>
|
||
output
|
||
</strong>
|
||
window as well.
|
||
</p>
|
||
<div class="pythonsource"><pre><span class="py_res">import</span> <span class="py_key">robofab</span>.<span class="py_key">world</span></pre></div>
|
||
<p>
|
||
If that runs without problems you're good to go. If a new window pops up with a <strong>traceback</strong> like this it means there's something wrong with the installation.
|
||
</p>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">Traceback</span> (<span class="py_key">most</span> <span class="py_key">recent</span> <span class="py_key">call</span> <span class="py_key">last</span>):<br><span class="py_key">File</span> <span class="py_str">"<string>"</span>, <span class="py_key">line</span> <span class="py_num">1</span>, <span class="py_res">in</span><span class="py_com"> </span><span class="py_com">?</span><br><span class="py_key">ImportError</span>: <span class="py_key">No</span> <span class="py_key">module</span> <span class="py_key">named</span> <span class="py_key">robofab</span>.<span class="py_key">world</span></pre></div>
|
||
|
||
|
||
<p>
|
||
In this documentation, stuff in the output window is indicated with a tinted background. Whenever something is printed in Python code it will end up in the output window.
|
||
</p>
|
||
|
||
<h2>
|
||
Huh, import ?
|
||
</h2>
|
||
<p>
|
||
Python can do a lot of different things. Some of its functionality is always available (the
|
||
<strong>
|
||
built-in
|
||
</strong>
|
||
things) but most of it is stored in seperate
|
||
<strong>
|
||
modules</strong>. When you want to use code from a different module, you need to
|
||
<strong>
|
||
import
|
||
</strong>
|
||
it first so that Python knows it needs to look somewhere else for objects, functions and stuff. Most of the Robofab stuff is stored in the
|
||
<strong>
|
||
robofab.world
|
||
</strong>
|
||
module. Notice that dot there? The dot syntax also works for modules and modules within modules. If you want to import a module and Python can't find it, you will get a traceback with an <strong>ImportError</strong>. You can also import specific things from another module, then you write:
|
||
</p>
|
||
<div class="pythonsource"><pre><span class="py_res">from</span> <span class="py_key">someModule</span> <span class="py_res">import</span> <span class="py_key">oneSpecificThing</span><br><span class="py_res">from</span> <span class="py_key">someModule</span>.<span class="py_key">subModule</span> <span class="py_res">import</span> <span class="py_key">oneSpecificThing</span><br><span class="py_res">from</span> <span class="py_key">someModule</span> <span class="py_res">import</span> <span class="py_key">anotherSpecificThing</span>, <span class="py_key">andAnotherThing</span><br> <br># and these:<br><span class="py_res">import</span> <span class="py_key">someModule</span><br><span class="py_res">import</span> <span class="py_key">someModule</span>.<span class="py_key">subModule</span></pre></div>
|
||
|
||
|
||
|
||
<h2>CurrentFont()</h2>
|
||
<p>
|
||
So, suppose you have FontLab, and a font file open. Make sure it is a font you can trash if you have to, and not the single copy of the production master of your newest bestseller. How do you get started talking to that font in Python? Use <strong>CurrentFont()</strong>. This is a special function which will return an object for the font which is at the front. When there are no fonts it will return <strong>None</strong>.
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_res">print</span> <span class="py_key">CurrentFont</span>()</pre></div>
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RFont</span> <span class="py_key">font</span> <span class="py_res">for</span> <span class="py_key">DemoFont</span> <span class="py_key">Italic</span>></pre></div>
|
||
|
||
<p>
|
||
A <a href="http://letterror.com/code/robofab/objects/font.html" target="new" class="reference">Font</a> object! We'll be using CurrentFont and that font object shortly, but first let's have a look at CurrentFont's siblings: <strong>CurrentGlyph</strong> and <strong>AllFonts</strong>.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># open a glyph in FL first!<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentGlyph</span><br><span class="py_res">print</span> <span class="py_key">CurrentGlyph</span>()</pre></div>
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">DemoFont</span>.<span class="py_key">ograve</span>></pre></div>
|
||
|
||
<p>
|
||
<strong>CurrentGlyph()</strong> returns a <a href="http://letterror.com/code/robofab/objects/glyph.html" target="new" class="reference">Glyph</a> object for the glyph which is at the front. So this is a useful place to start if you want to write a script which manipulates a single glyph and you want an object for that glyph.
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># open a couple of fonts in FL first!<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">AllFonts</span><br><span class="py_res">print</span> <span class="py_key">AllFonts</span>()</pre></div>
|
||
|
||
<div class="pythonoutput"><pre>[<<span class="py_key">RFont</span> <span class="py_key">font</span> <span class="py_res">for</span> <span class="py_key">MyDemoFont</span>>,<br><<span class="py_key">RFont</span> <span class="py_key">font</span> <span class="py_res">for</span> <span class="py_key">AnotherFont</span> <span class="py_key">Plain</span>>,<br><<span class="py_key">RFont</span> <span class="py_key">font</span> <span class="py_res">for</span> <span class="py_key">AnotherFont</span> <span class="py_key">Italic</span>>]</pre></div>
|
||
|
||
<p>
|
||
<strong>AllFonts()</strong> returns a list with Font objects, one object for each open font. CurrentFont, CurrentGlyph and AllFonts are three very useful functions, and they all live in the robofab.world module. We'll be using them a lot.
|
||
</p>
|
||
|
||
|
||
|
||
<h2>Some Font attributes</h2>
|
||
<p>
|
||
So what are attributes of fonts objects? Let's have a look (at the documentation!).
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># open a couple of fonts in FL first!<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">path</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">kerning</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span></pre></div>
|
||
|
||
<div class="pythonoutput"><pre>/<span class="py_key">aFolder</span>/<span class="py_key">anotherFolder</span>/<span class="py_key">demoStuff</span>/<span class="py_key">myFont</span>.<span class="py_key">vfb</span><br><<span class="py_key">RKerning</span> <span class="py_res">for</span> <span class="py_key">MyFont</span> <span class="py_key">Plain</span>><br><<span class="py_key">RInfo</span> <span class="py_res">for</span> <span class="py_key">MyFont</span> <span class="py_key">Plain</span>></pre></div>
|
||
|
||
<p>
|
||
Hang on! that didn't print anything that looks like kerning, and what's that font.info thing? Remember that objects can contain objects? The object model splits all font related data into smaller, easier to manage pieces. So a <strong>Font</strong> object has one single <strong>Info</strong> object which in turn stores all of the naming and dimensions. Same for font.kerning, it's an object which represents all kerning data of the font. We'll get back to the <a href="http://letterror.com/code/robofab/objects/kerning.html" target="new" class="reference">kerning object</a> later.
|
||
</p>
|
||
|
||
<h2>Some Info attributes</h2>
|
||
|
||
<p>
|
||
The <strong>Info</strong> object stores all of the <a href="http://letterror.com/code/robofab/objects/info.html" target="new" class="reference">font's names, key dimensions</a> etc.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># getting data from the info object<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br># naming attributes<br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">styleName</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fullName</span><br># dimension attributes<br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">unitsPerEm</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">ascender</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">descender</span><br></pre></div><div class="pythonsourcetitle"><a href="examples/someFontInfoAttributes.py" target="new">download examples/someFontInfoAttributes.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">MyDemo</span><br><span class="py_key">Plain</span><br><span class="py_key">MyDemo</span> <span class="py_key">Plain</span><br><span class="py_num">1000</span><br><span class="py_num">720</span><br>-<span class="py_num">280</span></pre></div>
|
||
|
||
<p>
|
||
Almost all attributes can also be set to new values. This is when it starts getting interesting. But it also opens new ways of messing your font up.
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># setting data in the info object<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br># naming attributes<br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span> = <span class="py_str">"MyFamily"</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">styleName</span> = <span class="py_str">"Roman"</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">styleName</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fullName</span> = <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span> + <span class="py_str">'-'</span> + <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">styleName</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fullName</span><br># dimension attributes<br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">ascender</span> = <span class="py_num">600</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">ascender</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">descender</span> = -<span class="py_num">400</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">descender</span><br><span class="py_key">font</span>.<span class="py_key">update</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/someFontInfoAttributes2.py" target="new">download examples/someFontInfoAttributes2.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">MyFamily</span><br><span class="py_key">Roman</span><br><span class="py_key">MyFamily</span>-<span class="py_key">Roman</span><br><span class="py_num">600</span><br>-<span class="py_num">400</span></pre></div>
|
||
|
||
<p>
|
||
A useful method of the Info object is <strong>autoNaming()</strong>. It assumes you have entered correct data for <strong>familyName</strong> and <strong>styleName</strong>. Based on these 2 values, a bunch of variations and permutations are generated and stored in the appropriate fields. These are the basic names, no fancy OpenType stuff.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># get a particular glyph<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br> <br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">familyName</span> = <span class="py_str">"myFamilyName"</span><br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">styleName</span> = <span class="py_str">"myStyleName"</span><br> <br><span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">autoNaming</span>()<br> <br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fullName</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fontName</span><br><span class="py_res">print</span> <span class="py_key">font</span>.<span class="py_key">info</span>.<span class="py_key">fondName</span></pre></div><div class="pythonsourcetitle"><a href="examples/infoAutonaming.py" target="new">download examples/infoAutonaming.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">myFamilyName</span> <span class="py_key">myStyleName</span><br><span class="py_key">myFamilyName</span>-<span class="py_key">myStyleName</span><br><span class="py_key">myFamilyName</span></pre></div>
|
||
|
||
<h2>Getting to glyphs</h2>
|
||
<p>
|
||
We've seen <strong>CurrentGlyph</strong> and <strong>CurrentFont</strong>, but how do you we get to other glyphs in a font? A <strong>Font</strong> object contains glyphs and this is what you do to get to them:
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># get a particular glyph<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_res">print</span> <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_res">print</span> <span class="py_key">font</span>[<span class="py_str">'Adieresis'</span>]<br><span class="py_res">print</span> <span class="py_key">font</span>[<span class="py_str">'two'</span>]<br><span class="py_res">print</span> <span class="py_key">font</span>[<span class="py_str">'afii12934'</span>]<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/getSomeGlyphs.py" target="new">download examples/getSomeGlyphs.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">A</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Adieresis</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">two</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">afii12934</span>></pre></div>
|
||
|
||
<p>
|
||
The Font object in this case behaves like a Python dictionary object. Between the <strong>[</strong> square brackets <strong>]</strong> you can ask for a glyph by its (postscript) name. In Python speak:
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre><span class="py_key">value</span> = <span class="py_key">dictionary</span>[<span class="py_key">key</span>]</pre></div>
|
||
|
||
|
||
<p>
|
||
If you want to look at all glyphs in a font, one at a time, you can <strong>loop</strong> or <strong>iterate</strong> through the font. It's written like this:
|
||
</p>
|
||
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># iteration through glyphs in a font<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_res">print</span> <span class="py_str">"font has %d glyphs"</span> % <span class="py_key">len</span>(<span class="py_key">font</span>)<br><span class="py_res">for</span> <span class="py_key">glyph</span> <span class="py_res">in</span> <span class="py_key">font</span>:<br> <span class="py_res">print</span> <span class="py_key">glyph</span><br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/iterateFont.py" target="new">download examples/iterateFont.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">font</span> <span class="py_key">has</span> <span class="py_num">201</span> <span class="py_key">glyphs</span><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">aring</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">ordfeminine</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">less</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">ograve</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">V</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">dollar</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">circumflex</span>><br>..<span class="py_key">etc</span>..</pre></div>
|
||
|
||
<p>
|
||
A couple of things to look for in the example above:
|
||
<ul>
|
||
<li><strong>len(font)</strong> shows Python's built-in <strong>len()</strong> function, which will try to count the thing its given and it will return the number. Fonts like to be counted and they respond with the number of glyphs. In this case the font has 201 glyphs.</li>
|
||
|
||
<li>
|
||
All the glyphs are mixed up! there is no particular order! chaos! In Python dictionaries there is no standard order in which the keys appear. It will iterate through all the glyphs though.
|
||
</li>
|
||
|
||
<li>Notice the indentation at the beginning of the line under <strong>for glyph in font:</strong> This is Python's way of showing that all of the code that's indented belongs to the same loop. When the code is <strong>dedented</strong> again that's where Python will continue when it is done with the loop.
|
||
</li>
|
||
|
||
</ul>
|
||
</p>
|
||
|
||
|
||
<p>
|
||
When you want to be sure about the order in which the glyphs are looked at, you need to sort them first. Example:
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># iteration through alphabetically sorted glyphnames<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_res">print</span> <span class="py_str">"font has %d glyphs"</span> % <span class="py_key">len</span>(<span class="py_key">font</span>)<br> <br># names is now a list of strings, the names of the glyphs<br># not the glyphs themselves!<br><span class="py_key">names</span> = <span class="py_key">font</span>.<span class="py_key">keys</span>()<br> <br># the list of names is sorted<br><span class="py_key">names</span>.<span class="py_key">sort</span>()<br> <br># now we iterate through the list of names<br><span class="py_res">for</span> <span class="py_key">glyphName</span> <span class="py_res">in</span> <span class="py_key">names</span>:<br> # now we ask for the glyph with glyphName<br> <span class="py_res">print</span> <span class="py_key">font</span>[<span class="py_key">glyphName</span>]</pre></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">font</span> <span class="py_key">has</span> <span class="py_num">201</span> <span class="py_key">glyphs</span><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">A</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">AE</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Aacute</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Acircumflex</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Adieresis</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Agrave</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Aring</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">Atilde</span>><br><<span class="py_key">RGlyph</span> <span class="py_res">for</span> <span class="py_key">MyFamily</span>-<span class="py_key">Roman</span>.<span class="py_key">B</span>><br>..<span class="py_key">etc</span>..</pre></div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h1>
|
||
Glyph, Contour & Pen
|
||
</h1>
|
||
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_17.gif" width="140" height="96" />
|
||
|
||
|
||
<p>
|
||
|
||
Working with <a href="http://letterror.com/code/robofab/objects/glyph.html" target="new" class="reference">Glyph</a>, <a href="http://letterror.com/code/robofab/objects/contour.html" target="new" class="reference">Contour</a>, <a href="http://letterror.com/code/robofab/howto/usepens.html" target="new" class="reference">Pen</a> objects. Deep in the font live the glyphs. Inside the glyphs are contours, components. Inside a contour are segments and points!
|
||
</p>
|
||
|
||
|
||
<p>Previous: <a href="robofab_session1.html">fonts</a>, next: <a href="robofab_session3.html">kerning and metrics, building glyphs</a>
|
||
</p>
|
||
|
||
<h2>Some Glyph attributes</h2>
|
||
<p>
|
||
In the previous talk we saw ways of getting fonts and glyph objects. Now we're going to see what Glyph objects can do for us. Glyphs are perhaps the most interesting object in the whole API. Some basic attributes of a glyph object:
|
||
</p>
|
||
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># set basic attributes in a glyph<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_key">glyph</span>.<span class="py_key">width</span> = <span class="py_num">200</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">width</span><br><span class="py_key">glyph</span>.<span class="py_key">leftMargin</span> = <span class="py_num">50</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">leftMargin</span><br><span class="py_key">glyph</span>.<span class="py_key">rightMargin</span> = <span class="py_num">50</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">rightMargin</span><br> <br><span class="py_key">glyph</span>.<span class="py_key">unicode</span> = <span class="py_num">666</span><br><span class="py_key">glyph</span>.<span class="py_key">update</span>()</pre></div><div class="pythonsourcetitle"><a href="examples/someGlyphAttributes.py" target="new">download examples/someGlyphAttributes.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">A</span><br><span class="py_num">812</span><br><span class="py_num">0</span><br><span class="py_num">0</span><br>(<span class="py_num">0</span>, <span class="py_num">0</span>, <span class="py_num">812</span>, <span class="py_num">600</span>)<br><span class="py_num">65</span></pre></div>
|
||
|
||
<p>Some of these attributes can also be set, you can put new values in them. Why not all of them? For instance, the bounding box of a glyph is an attribute, it is useful information. But it depends entirely on the size of the contours in the glyph. There's no way in which you could set this attribute other than by changing the actual paths. Useful attributes you <strong>can</strong> set are things like the width, left and right margin:
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># set basic attributes in a glyph<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_key">glyph</span>.<span class="py_key">width</span> = <span class="py_num">200</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">width</span><br><span class="py_key">glyph</span>.<span class="py_key">leftMargin</span> = <span class="py_num">50</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">leftMargin</span><br><span class="py_key">glyph</span>.<span class="py_key">rightMargin</span> = <span class="py_num">50</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">rightMargin</span><br> <br><span class="py_key">glyph</span>.<span class="py_key">unicode</span> = <span class="py_num">666</span><br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">unicode</span><br> <br><span class="py_key">glyph</span>.<span class="py_key">update</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/someGlyphAttributes2.py" target="new">download examples/someGlyphAttributes2.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_num">200</span><br><span class="py_num">50</span><br><span class="py_num">50</span><br><span class="py_num">666</span></pre></div>
|
||
|
||
|
||
<p>
|
||
Notice the <strong>glyph.update()</strong> statement at the end. After setting the attributes to new values, the glyph data will be changed. But the FontLab interface will not have noticed your changes. For instance, the little preview in the font window might still show the old version. The <strong>update()</strong> method of the glyph object is a wakeup call for the application to refresh all glyph drawings with the new data. Note that it is just the <strong>representation</strong> that's being updated. The data itself was changed when the script was executed. Saving the file and opening it again will have the same effect in this respect.
|
||
</p>
|
||
|
||
<p>
|
||
Have a look at the RoboFab documentation on the <a href="http://letterror.com/code/robofab/objects/glyph.html" target="new" class="reference">Glyph</a> object for more attributes and methods.
|
||
</p>
|
||
|
||
|
||
<h2>Some Glyph methods</h2>
|
||
|
||
<p>
|
||
|
||
|
||
</p>
|
||
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># use some methods to transform a glyph<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br> <br># ask a font for a glyph by name<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br> <br># now you have a glyph object<br># make it do stuff by calling some of its methods<br><span class="py_key">glyph</span>.<span class="py_key">move</span>((<span class="py_num">100</span>, <span class="py_num">75</span>))<br><span class="py_key">glyph</span>.<span class="py_key">scale</span>((<span class="py_num">.5</span>, <span class="py_num">1.5</span>))<br><span class="py_key">glyph</span>.<span class="py_key">appendGlyph</span>(<span class="py_key">font</span>[<span class="py_str">'B'</span>])<br><span class="py_key">glyph</span>.<span class="py_key">removeOverlap</span>()<br><span class="py_key">glyph</span>.<span class="py_key">correctDirection</span>()<br><span class="py_key">glyph</span>.<span class="py_key">update</span>()<br> <br><br></pre></div><div class="pythonsourcetitle"><a href="examples/transformGlyph.py" target="new">download examples/transformGlyph.py</a></div>
|
||
|
||
|
||
<p>
|
||
Note: these will really screw up your glyph if you don't want it to be moved 100 units horizontally and 75 units vertically, then scaled 50% horizontally and 150% vertically. Then the glyph <strong>B</strong> is added, the overlap is removed, the path directions are corrected and finally the app is told something was changed.
|
||
</p>
|
||
|
||
<h2>getParent()</h2>
|
||
<p>
|
||
The Glyph object has a <strong>glyph.getParent()</strong> method. When called it will return the <strong>Font</strong> object the glyph belongs to. If it has one: "orphan" glyphs are objects which don't belong to any particular font like the result of a <strong>GlyphMath</strong> operation or glyph level interpolation. When there is no parent, <strong>getParent()</strong> returns <strong>None</strong>. You need to test for this result.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># iterate through a glyph's contours<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br> <br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">"A"</span>]<br><span class="py_res">print</span> <span class="py_key">glyph</span>.<span class="py_key">getParent</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/aGlyphParent.py" target="new">download examples/aGlyphParent.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RFont</span> <span class="py_key">font</span> <span class="py_res">for</span> <span class="py_key">SomeFont</span>></pre></div>
|
||
|
||
|
||
|
||
<h2>The stuff glyphs are made of</h2>
|
||
|
||
<p>
|
||
A glyph consists of contours, which in turn consist of points, right? If only it were that easy. There are all sorts of points conspiring to become all sorts of shapes. Bezier curves, Quadratic curves, open, closed, components. Sometimes it is just the point of view that's different. The <a href="http://letterror.com/code/robofab/howto/understandcontours.html" target="docs" class="reference">Understanding Contours and Segments</a> section of the RoboFab documentation has a couple of diagrams of the different ways of looking at the vectors in a glyph. Let's have a look at <a href="http://letterror.com/code/robofab/objects/contour.html" target="new" class="reference">Contour</a> objects first.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># iterate through a glyph's contours<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_res">print</span> <span class="py_str">"glyph has %d contours"</span> % <span class="py_key">len</span>(<span class="py_key">glyph</span>)<br><span class="py_res">for</span> <span class="py_key">contour</span> <span class="py_res">in</span> <span class="py_key">glyph</span>.<span class="py_key">contours</span>:<br> <span class="py_res">print</span> <span class="py_key">contour</span><br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/iterateGlyph.py" target="new">download examples/iterateGlyph.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">glyph</span> <span class="py_key">has</span> <span class="py_num">2</span> <span class="py_key">contours</span><br><<span class="py_key">RContour</span> <span class="py_res">for</span> <span class="py_key">MyDemo</span> <span class="py_key">Italic</span>.<span class="py_key">A</span>[<span class="py_num">0</span>]><br><<span class="py_key">RContour</span> <span class="py_res">for</span> <span class="py_key">MyDemo</span> <span class="py_key">Italic</span>.<span class="py_key">A</span>[<span class="py_num">1</span>]></pre></div>
|
||
|
||
<p>So a <strong>Glyph</strong> object contains zero, one or more <strong>Contour</strong> objects. Let's see what secrets a Contour has:
|
||
</p>
|
||
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># get a specific contour and view it<br>#through point, segment and bPoint structures<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_key">contour</span> = <span class="py_key">glyph</span>[<span class="py_num">0</span>]<br><span class="py_res">print</span> <span class="py_key">contour</span>.<span class="py_key">points</span><br><span class="py_res">print</span> <span class="py_key">countours</span>.<span class="py_key">segments</span><br><span class="py_res">print</span> <span class="py_key">contour</span>.<span class="py_key">bPoints</span><br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/iterateContour.py" target="new">download examples/iterateContour.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre>[<<span class="py_key">RPoint</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">0</span>]>,<br><<span class="py_key">RPoint</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">1</span>]>,<br>...<br><<span class="py_key">RPoint</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">4</span>]>]<br> <br>[<<span class="py_key">RSegment</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">0</span>]>,<br><<span class="py_key">RSegment</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">1</span>]>,<br>...<br><<span class="py_key">RSegment</span> <span class="py_res">for</span> <span class="py_key">Plinc01</span> <span class="py_key">Plinc01</span>.<span class="py_key">A</span>[<span class="py_num">0</span>][<span class="py_num">4</span>]>]<br> <br>[<<span class="py_key">RBPoint</span> <span class="py_res">for</span> <span class="py_key">unnamed_font</span>.<span class="py_key">unnamed_glyph</span>[<span class="py_num">7</span>][<span class="py_num">0</span>][<span class="py_num">0</span>]>,<br><<span class="py_key">RBPoint</span> <span class="py_res">for</span> <span class="py_key">unnamed_font</span>.<span class="py_key">unnamed_glyph</span>[<span class="py_num">7</span>][<span class="py_num">0</span>][<span class="py_num">1</span>]>,<br>...<br><<span class="py_key">RBPoint</span> <span class="py_res">for</span> <span class="py_key">unnamed_font</span>.<span class="py_key">unnamed_glyph</span>[<span class="py_num">7</span>][<span class="py_num">0</span>][<span class="py_num">4</span>]>]</pre></div>
|
||
|
||
<p>
|
||
Let's have a look at these different attributes. In the end, they're all describing the same shape, the same actual points. It's just sorted in different ways for different purposes.
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<strong>contour.points:</strong> This is a list of all points on a contour. off-curve and on-curve points are presented as <strong>RPoint</strong>objects. Each RPoint has x, y, type and smooth attributes. On-cuve and off-curve points are treated as equals.
|
||
</li>
|
||
|
||
<li>
|
||
<strong>contour.segments:</strong> A segment is an object which represents a series of off-curve points and finally one on-curve point. Though kind of abstract, segments are needed to deal with quadratic curves which can have several off-curves on a row.
|
||
</li>
|
||
|
||
<li>
|
||
<strong>contour.bPoints:</strong> bPoints are objects which look more or less like the curve points you know from editing Beziers. One main on-curve point and two satellite off-curve points or <strong>bcps</strong>: an incoming one and an outgoing one. This is more or less the kind of point that was used in <strong>RoboFog</strong> code.
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
|
||
|
||
</p>
|
||
|
||
<p>
|
||
|
||
Again, please refer to the <a href="http://letterror.com/code/robofab/howto/understandcontours.html" target="docs" class="reference">Understanding Contours and Segments</a> to make sense of these things visually.
|
||
</p>
|
||
|
||
<p>
|
||
Finally, when you iterate through the contour.points, you get to see <a href="http://letterror.com/code/robofab/objects/point.html" target="new" class="reference">RPoint</a> objects with familiar things like x, y, and type attributes.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># iterate through points<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_res">for</span> <span class="py_key">p</span> <span class="py_res">in</span> <span class="py_key">glyph</span>[<span class="py_num">0</span>].<span class="py_key">points</span>:<br> <span class="py_res">print</span> <span class="py_key">p</span>.<span class="py_key">x</span>, <span class="py_key">p</span>.<span class="py_key">y</span>, <span class="py_key">p</span>.<span class="py_key">type</span><br></pre></div><div class="pythonsourcetitle"><a href="examples/iteratePoints.py" target="new">download examples/iteratePoints.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_num">37</span> <span class="py_num">509</span> <span class="py_key">move</span><br><span class="py_num">37</span> <span class="py_num">407</span> <span class="py_key">offcurve</span><br><span class="py_num">119</span> <span class="py_num">325</span> <span class="py_key">offcurve</span><br><span class="py_num">221</span> <span class="py_num">325</span> <span class="py_key">curve</span><br><span class="py_num">323</span> <span class="py_num">325</span> <span class="py_key">offcurve</span><br><span class="py_num">405</span> <span class="py_num">407</span> <span class="py_key">offcurve</span><br><span class="py_num">405</span> <span class="py_num">509</span> <span class="py_key">curve</span><br><span class="py_num">405</span> <span class="py_num">611</span> <span class="py_key">offcurve</span><br><span class="py_num">323</span> <span class="py_num">693</span> <span class="py_key">offcurve</span><br><span class="py_num">221</span> <span class="py_num">693</span> <span class="py_key">curve</span><br><span class="py_num">119</span> <span class="py_num">693</span> <span class="py_key">offcurve</span><br><span class="py_num">37</span> <span class="py_num">611</span> <span class="py_key">offcurve</span><br><span class="py_num">37</span> <span class="py_num">509</span> <span class="py_key">curve</span></pre></div>
|
||
|
||
<p>
|
||
That looks interesting! Finally we're dealing with the stuff that letters are made of. You can now find the coordinates of every single point in the font, and change them if you want. But it looks very cumbersome to actually add shapes like this, all these contour objects, point objects. That's going to hurt, no? Yes. So, if you want to draw new forms there is a solution.
|
||
</p>
|
||
|
||
<h2>The Pen and Draw model</h2>
|
||
|
||
|
||
<p>
|
||
The pen/draw model is a very powerful concept, but it might seem a bit abstract to begin with. If you're interested in building filters, shape manipulating scripts, or programmatically draw glyphs, this is good to study. Otherwise, if you're here for the first time, just remember that there are ways to get paths into glyphs and that this is where you saw the examples for later reference.
|
||
</p>
|
||
|
||
<p>
|
||
Glyph objects have a
|
||
<strong>
|
||
draw()
|
||
</strong>
|
||
method which takes a
|
||
<strong>
|
||
Pen
|
||
</strong>
|
||
object as a parameter. All glyphs know how to draw and all pen objects have the same methods (see below). When draw() is called, the glyph instructs the pen to recreate the shapes using <strong>moveTo()</strong>, <strong>lineTo()</strong>, <strong>curveTo()</strong> commands. The pen/draw() model is a standardised way of getting to the point data in a glyph. It is also a standardised way of getting data into a glyph. The glyph doesn't have to know what the pen is doing with its information and the pen doesn't have to know where the glyph gets its data from. That's how we like things in the object oriented world.
|
||
</p>
|
||
|
||
|
||
<p>
|
||
There is a wide range of pen objects available for different things. Some pens just print the coordinates of a glyph. Some pens draw contours in a glyph, some manipulate the data, others can draw on screen under certain conditions.
|
||
</p>
|
||
|
||
|
||
<p>
|
||
How to get a pen object? A bunch of them are stored in the <strong>robofab/pens</strong> module, have a look in the source! But one of the first things you'll want to use a pen for is to construct paths in a glyph. A Glyph object has a <strong>glyph.getPen()</strong> method which will return the right Pen object for drawing in that glyph. Example:
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># get a pen and draw something in the current glyph<br># what will it draw? ha! run the script and find out!<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentGlyph</span><br><span class="py_key">g</span> = <span class="py_key">CurrentGlyph</span>()<br><span class="py_key">myPen</span> = <span class="py_key">g</span>.<span class="py_key">getPen</span>()<br> <br># myPen is a pen object of a type meant for<br># constructing paths in a glyph.<br># So rather than use this pen with the glyph's<br># own draw() method, we're going to tell it <br># to do things ourselves. (Just like DrawBot!)<br><span class="py_res">print</span> <span class="py_key">myPen</span><br> <br><span class="py_key">myPen</span>.<span class="py_key">moveTo</span>((<span class="py_num">344</span>, <span class="py_num">645</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">647</span>, <span class="py_num">261</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">662</span>, -<span class="py_num">32</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">648</span>, -<span class="py_num">61</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">619</span>, -<span class="py_num">61</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">352</span>, <span class="py_num">54</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">72</span>, <span class="py_num">446</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">117</span>, <span class="py_num">590</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">228</span>, <span class="py_num">665</span>))<br><span class="py_key">myPen</span>.<span class="py_key">closePath</span>()<br><span class="py_key">myPen</span>.<span class="py_key">moveTo</span>((<span class="py_num">99</span>, <span class="py_num">451</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">365</span>, <span class="py_num">74</span>))<br><span class="py_key">myPen</span>.<span class="py_key">curveTo</span>((<span class="py_num">359</span>, <span class="py_num">122</span>), (<span class="py_num">376</span>, <span class="py_num">178</span>), (<span class="py_num">420</span>, <span class="py_num">206</span>))<br><span class="py_key">myPen</span>.<span class="py_key">curveTo</span>((<span class="py_num">422</span>, <span class="py_num">203</span>), (<span class="py_num">142</span>, <span class="py_num">579</span>), (<span class="py_num">142</span>, <span class="py_num">579</span>))<br><span class="py_key">myPen</span>.<span class="py_key">closePath</span>()<br><span class="py_key">myPen</span>.<span class="py_key">moveTo</span>((<span class="py_num">631</span>, -<span class="py_num">32</span>))<br><span class="py_key">myPen</span>.<span class="py_key">lineTo</span>((<span class="py_num">629</span>, <span class="py_num">103</span>))<br><span class="py_key">myPen</span>.<span class="py_key">curveTo</span>((<span class="py_num">556</span>, <span class="py_num">111</span>), (<span class="py_num">524</span>, <span class="py_num">71</span>), (<span class="py_num">508</span>, <span class="py_num">20</span>))<br><span class="py_key">myPen</span>.<span class="py_key">closePath</span>()<br> <br><span class="py_key">g</span>.<span class="py_key">update</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/drawStuffWithPen.py" target="new">download examples/drawStuffWithPen.py</a></div>
|
||
|
||
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">robofab</span>.<span class="py_key">pens</span>.<span class="py_key">adapterPens</span>.<span class="py_key">SegmentToPointPen</span> <span class="py_key">instance</span> <span class="py_key">at</span> <span class="py_num">0x65d2558</span>></pre></div>
|
||
|
||
|
||
<p>
|
||
The next example will show the use of a Pen object which doesn't construct any path, but rather it prints the instructions the glyph is calling to draw itself. Note: the stuff printed out by <strong>PrintingSegmentPen</strong> is python code too. You can use this snippet to convert a shape into python code, if for whatever reason you want to draw stuff programmatically. That's how the previous example was created: draw a shape in a glyph, print the draw instructions, write the rest of the code.
|
||
</p>
|
||
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># get a pen and use it to print the coordinates<br># to the output window. This is actually almost-python<br># code which you can use it other scripts!<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">pens</span>.<span class="py_key">pointPen</span> <span class="py_res">import</span> <span class="py_key">PrintingSegmentPen</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br> <br># PrintingSegmentPen won't actually draw anything<br># just print the coordinates to the output:<br><span class="py_key">pen</span> = <span class="py_key">PrintingSegmentPen</span>()<br><span class="py_key">glyph</span>.<span class="py_key">draw</span>(<span class="py_key">pen</span>)<br></pre></div><div class="pythonsourcetitle"><a href="examples/printingPen.py" target="new">download examples/printingPen.py</a></div>
|
||
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">pen</span>.<span class="py_key">moveTo</span>((<span class="py_num">37</span>, <span class="py_num">509</span>))<br><span class="py_key">pen</span>.<span class="py_key">curveTo</span>((<span class="py_num">37</span>, <span class="py_num">407</span>), (<span class="py_num">119</span>, <span class="py_num">325</span>), (<span class="py_num">221</span>, <span class="py_num">325</span>))<br><span class="py_key">pen</span>.<span class="py_key">curveTo</span>((<span class="py_num">323</span>, <span class="py_num">325</span>), (<span class="py_num">405</span>, <span class="py_num">407</span>), (<span class="py_num">405</span>, <span class="py_num">509</span>))<br><span class="py_key">pen</span>.<span class="py_key">curveTo</span>((<span class="py_num">405</span>, <span class="py_num">611</span>), (<span class="py_num">323</span>, <span class="py_num">693</span>), (<span class="py_num">221</span>, <span class="py_num">693</span>))<br><span class="py_key">pen</span>.<span class="py_key">curveTo</span>((<span class="py_num">119</span>, <span class="py_num">693</span>), (<span class="py_num">37</span>, <span class="py_num">611</span>), (<span class="py_num">37</span>, <span class="py_num">509</span>))<br><span class="py_key">pen</span>.<span class="py_key">closePath</span>()</pre></div>
|
||
|
||
|
||
|
||
<p>
|
||
The following example uses a Pen to draw boxes as a simple rasteriser. Perhaps a bit overboard, but it illustrates what you can do with Pens and creating paths in RoboFab.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># rasterise the shape in glyph "A"<br># and draw boxes in a new glyph named "A.silly"<br>#<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span>, <span class="py_key">CurrentGlyph</span><br> <br><span class="py_key">sourceGlyph</span> = <span class="py_str">"a"</span><br> <br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">source</span> = <span class="py_key">f</span>[<span class="py_key">sourceGlyph</span>]<br> <br># find out how big the shape is from the glyph.box attribute<br><span class="py_key">xMin</span>, <span class="py_key">yMin</span>, <span class="py_key">xMax</span>, <span class="py_key">yMax</span> = <span class="py_key">source</span>.<span class="py_key">box</span><br> <br># create a new glyph<br><span class="py_key">dest</span> = <span class="py_key">f</span>.<span class="py_key">newGlyph</span>(<span class="py_key">sourceGlyph</span>+<span class="py_str">".silly"</span>)<br><span class="py_key">dest</span>.<span class="py_key">width</span> = <span class="py_key">source</span>.<span class="py_key">width</span><br> <br># get a pen to draw in the new glyph<br><span class="py_key">myPen</span> = <span class="py_key">dest</span>.<span class="py_key">getPen</span>()<br> <br># a function which draws a rectangle at a specified place<br><span class="py_res">def</span> <span class="py_def">drawRect</span>(<span class="py_key">pen</span>, <span class="py_key">x</span>, <span class="py_key">y</span>, <span class="py_key">size</span>=<span class="py_num">50</span>):<br> <span class="py_key">pen</span>.<span class="py_key">moveTo</span>((<span class="py_key">x</span>-<span class="py_num">.5</span>*<span class="py_key">size</span>, <span class="py_key">y</span>-<span class="py_num">.5</span>*<span class="py_key">size</span>))<br> <span class="py_key">pen</span>.<span class="py_key">lineTo</span>((<span class="py_key">x</span>+<span class="py_num">.5</span>*<span class="py_key">size</span>, <span class="py_key">y</span>-<span class="py_num">.5</span>*<span class="py_key">size</span>))<br> <span class="py_key">pen</span>.<span class="py_key">lineTo</span>((<span class="py_key">x</span>+<span class="py_num">.5</span>*<span class="py_key">size</span>, <span class="py_key">y</span>+<span class="py_num">.5</span>*<span class="py_key">size</span>))<br> <span class="py_key">pen</span>.<span class="py_key">lineTo</span>((<span class="py_key">x</span>-<span class="py_num">.5</span>*<span class="py_key">size</span>, <span class="py_key">y</span>+<span class="py_num">.5</span>*<span class="py_key">size</span>))<br> <span class="py_key">pen</span>.<span class="py_key">closePath</span>()<br> <br># the size of the raster unit<br><span class="py_key">resolution</span> = <span class="py_num">30</span><br> <br># draw from top to bottom<br><span class="py_key">yValues</span> = <span class="py_key">range</span>(<span class="py_key">yMin</span>, <span class="py_key">yMax</span>, <span class="py_key">resolution</span>)<br><span class="py_key">yValues</span>.<span class="py_key">reverse</span>()<br> <br># go for it!<br><span class="py_res">for</span> <span class="py_key">y</span> <span class="py_res">in</span> <span class="py_key">yValues</span>:<br> <span class="py_res">for</span> <span class="py_key">x</span> <span class="py_res">in</span> <span class="py_key">range</span>(<span class="py_key">xMin</span>, <span class="py_key">xMax</span>, <span class="py_key">resolution</span>):<br> # check the source glyph is white or black at x,y<br> <span class="py_res">if</span> <span class="py_key">source</span>.<span class="py_key">pointInside</span>((<span class="py_key">x</span>, <span class="py_key">y</span>)):<br> <span class="py_key">drawRect</span>(<span class="py_key">myPen</span>, <span class="py_key">x</span>, <span class="py_key">y</span>, <span class="py_key">resolution</span>-<span class="py_num">5</span>)<br> # update for each line if you like the animation<br> # otherwise move the update() out of the loop<br> <span class="py_key">dest</span>.<span class="py_key">update</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/sillyRasteriser.py" target="new">download examples/sillyRasteriser.py</a></div>
|
||
|
||
|
||
<p>
|
||
|
||
<img src="rasteriser.gif" alt="rasteriser" width="400" height="193" />
|
||
<br />
|
||
This is what it looks like.
|
||
</p>
|
||
|
||
<p>
|
||
|
||
|
||
A more in-depth discussion of the Pen protocol at the <a href="http://just.letterror.com/ltrwiki/PenProtocol" class="reference" target="docs">LettError wiki</a>. More examples of using pens, and some samples using pens to filter letterforms in the <a href="http://letterror.com/code/robofab/howto/usepens.html" class="reference" target="new">RoboFab docs</a>.
|
||
|
||
</p>
|
||
|
||
<h2>Another kind of pen</h2>
|
||
|
||
<p>
|
||
Well, it wouldn't be Python if there weren't at least two ways of doing things. The pen/draw() model draws on-curve points and only accepts off-curves as part of the curveTo() and qCurveTo() methods, there's no room for additional information. For instance a <strong>smooth</strong> flag or a name for a point (and there are good reasons to want to do that). That's where <strong>glyph.drawPoints()</strong> comes in handy. This method is similar to <strong>glyph.draw()</strong> but it takes a different pen, a <strong>PointPen</strong>. All points are drawn one by one. PointPens are useful when you want to exactly address all information of each point in a shape. Compare the output of this example with the previous one. It's the same shape. Chances are you won't need pointPens for some time, but if you start working with pens it is good to know they exist.
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># use a point pen<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">pens</span>.<span class="py_key">pointPen</span> <span class="py_res">import</span> <span class="py_key">PrintingPointPen</span><br> <br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">glyph</span> = <span class="py_key">font</span>[<span class="py_str">'A'</span>]<br><span class="py_key">pen</span> = <span class="py_key">PrintingPointPen</span>()<br><span class="py_key">glyph</span>.<span class="py_key">drawPoints</span>(<span class="py_key">pen</span>)</pre></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">pen</span>.<span class="py_key">beginPath</span>()<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">37</span>, <span class="py_num">509</span>), <span class="py_key">segmentType</span>=<span class="py_str">'curve'</span>, <span class="py_key">smooth</span>=<span class="py_key">True</span>)<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">37</span>, <span class="py_num">407</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">119</span>, <span class="py_num">325</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">221</span>, <span class="py_num">325</span>), <span class="py_key">segmentType</span>=<span class="py_str">'curve'</span>, <span class="py_key">smooth</span>=<span class="py_key">True</span>, **{<span class="py_str">'selected'</span>: <span class="py_num">0</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">323</span>, <span class="py_num">325</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">405</span>, <span class="py_num">407</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">405</span>, <span class="py_num">509</span>), <span class="py_key">segmentType</span>=<span class="py_str">'curve'</span>, <span class="py_key">smooth</span>=<span class="py_key">True</span>, **{<span class="py_str">'selected'</span>: <span class="py_num">0</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">405</span>, <span class="py_num">611</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">323</span>, <span class="py_num">693</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">221</span>, <span class="py_num">693</span>), <span class="py_key">segmentType</span>=<span class="py_str">'curve'</span>, <span class="py_key">smooth</span>=<span class="py_key">True</span>, **{<span class="py_str">'selected'</span>: <span class="py_num">0</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">119</span>, <span class="py_num">693</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">addPoint</span>((<span class="py_num">37</span>, <span class="py_num">611</span>), **{<span class="py_str">'selected'</span>: <span class="py_key">False</span>})<br><span class="py_key">pen</span>.<span class="py_key">endPath</span>()</pre></div>
|
||
|
||
<p>
|
||
Notice that a pointPen only has 3 methods: <strong>addPoint</strong>, <strong>beginPath</strong> and <strong>endPath</strong>.
|
||
</p>
|
||
|
||
|
||
<h2>Path manipulation</h2>
|
||
<p>
|
||
FontLab has path manipulation tools which let you remove overlap and add and subtract shapes. Remove overlap is available: <strong>glyph.removeOverlap()</strong>. Here is an example using the FontLab layer to subtract one shape from another to produce a third.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06
|
||
<br># Use FontLab pathfinder functionality to cut one glyph from another
|
||
<br>
|
||
<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span>
|
||
<br>
|
||
<br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()
|
||
<br>
|
||
<br><span class="py_key">base</span> = <span class="py_key">f</span>[<span class="py_str">"A"</span>]
|
||
<br><span class="py_key">cutter</span> = <span class="py_key">f</span>[<span class="py_str">"B"</span>]
|
||
<br><span class="py_key">dest</span> = <span class="py_key">f</span>[<span class="py_str">"C"</span>]
|
||
<br>
|
||
<br><span class="py_key">dest</span>.<span class="py_key">clear</span>()
|
||
<br><span class="py_key">dest</span>.<span class="py_key">appendGlyph</span>(<span class="py_key">base</span>)
|
||
<br><span class="py_key">dest</span>.<span class="py_key">width</span> = <span class="py_key">base</span>.<span class="py_key">width</span>
|
||
<br><span class="py_key">dest</span>.<span class="py_key">naked</span>().<span class="py_key">Bsubtract</span>(<span class="py_key">cutter</span>.<span class="py_key">naked</span>())
|
||
<br>
|
||
<br><span class="py_key">dest</span>.<span class="py_key">update</span>()
|
||
<br>
|
||
<br></pre></div><div class="pythonsourcetitle"><a href="examples/cookieCutter.py" target="new">download examples/cookieCutter.py</a></div>
|
||
|
||
<p>
|
||
This example also sneakily introduces the <strong>naked()</strong> method of the glyph object. This method returns FontLab's own object for the glyph. We'll look at this in a bit more detail later on.
|
||
</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h1>
|
||
Kerning and building Glyphs
|
||
</h1>
|
||
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_11.gif" />
|
||
|
||
|
||
|
||
<h2>Kerning</h2>
|
||
<p>
|
||
All kerning data of a font is represented by the <strong>Kerning</strong> object. This object behaves like a Python dictionary: the key is a tuple of two glyph or groupnames, the dictionary value is the kern distance.
|
||
</p>
|
||
|
||
|
||
<p>In FontLab, font.kerning should not be called repeatedly like a normal attribute. Nothing will go wrong if you do, it will just be slow. The reason for this is that font.kerning is an attribute which (under the hood) has to do a lot of work to collect the data from the underlying FontLab file. Kerning in FontLab is stored at the glyph level, so to pull it up to the RoboFab level a massive iteration must occur when the kerning object is created. This happens each time you ask for the font.kerning attribute. But there's a simple way to work with that efficiently: cache the kerning object. Like so:
|
||
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># work with kerning 1<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br> <br># now the kerning object is generated once<br><span class="py_key">kerning</span> = <span class="py_key">font</span>.<span class="py_key">kerning</span><br> <br># and ready for your instructions.<br><span class="py_res">print</span> <span class="py_key">kerning</span><br><span class="py_res">print</span> <span class="py_key">len</span>(<span class="py_key">kerning</span>)<br><span class="py_res">print</span> <span class="py_key">kerning</span>.<span class="py_key">keys</span>()<br> <br># proceed to work with the myKerning object<br># this happens in the following examples too.</pre></div><div class="pythonsourcetitle"><a href="examples/workWithKerning1.py" target="new">download examples/workWithKerning1.py</a></div>
|
||
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RKerning</span> <span class="py_res">for</span> <span class="py_key">MyFont</span> <span class="py_key">Normal</span>><br><span class="py_num">3141</span><br>[(<span class="py_str">'F'</span>, <span class="py_str">'L'</span>), (<span class="py_str">'D'</span>, <span class="py_str">'G'</span>), (<span class="py_str">'N'</span>, <span class="py_str">'Eacute'</span>), ..<span class="py_key">etc</span>.. ]</pre></div>
|
||
|
||
<p>
|
||
The Kerning object has some useful methods to transform and analyse the data.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># work with kerning 2<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">kerning</span> = <span class="py_key">font</span>.<span class="py_key">kerning</span><br> <br># calculate the average offset<br><span class="py_res">print</span> <span class="py_key">kerning</span>.<span class="py_key">getAverage</span>()<br> <br># count pairs with these glyphs<br><span class="py_res">print</span> <span class="py_key">kerning</span>.<span class="py_key">occurrenceCount</span>([<span class="py_str">"a"</span>, <span class="py_str">"b"</span>])<br> <br># get the maximum values<br><span class="py_res">print</span> <span class="py_key">kerning</span>.<span class="py_key">getExtremes</span>()<br> <br># count the pars<br><span class="py_res">print</span> <span class="py_str">"font has %d kerning pairs"</span> % <span class="py_key">len</span>(<span class="py_key">kerning</span>)<br> <br># this prints all the pairs<br><span class="py_res">for</span> (<span class="py_key">left</span>, <span class="py_key">right</span>), <span class="py_key">value</span> <span class="py_res">in</span> <span class="py_key">kerning</span>.<span class="py_key">items</span>():<br> <span class="py_res">print</span> (<span class="py_key">left</span>, <span class="py_key">right</span>), <span class="py_key">value</span><br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/workWithKerning2.py" target="new">download examples/workWithKerning2.py</a></div>
|
||
|
||
|
||
|
||
<div class="pythonoutput"><pre><<span class="py_key">RKerning</span> <span class="py_res">for</span> <span class="py_key">MyFont</span> <span class="py_key">Normal</span>><br>-<span class="py_num">20.5953517988</span><br>{<span class="py_str">'a'</span>: <span class="py_num">82</span>, <span class="py_str">'b'</span>: <span class="py_num">52</span>}<br>(-<span class="py_num">191</span>, <span class="py_num">184</span>)<br><span class="py_key">font</span> <span class="py_key">has</span> <span class="py_num">3141</span> <span class="py_key">kerning</span> <span class="py_key">pairs</span><br>(<span class="py_str">'F'</span>, <span class="py_str">'L'</span>) -<span class="py_num">7</span><br>(<span class="py_str">'D'</span>, <span class="py_str">'G'</span>) <span class="py_num">31</span><br>(<span class="py_str">'N'</span>, <span class="py_str">'Eacute'</span>) -<span class="py_num">34</span><br>(<span class="py_str">'agrave.sc'</span>, <span class="py_str">'z'</span>) -<span class="py_num">7</span><br>(<span class="py_str">'K'</span>, <span class="py_str">'v'</span>) -<span class="py_num">111</span><br>(<span class="py_str">'Z'</span>, <span class="py_str">'N'</span>) -<span class="py_num">15</span><br>..<span class="py_key">etc</span>..</pre></div>
|
||
|
||
|
||
|
||
<p>
|
||
In the example above you see the Kerning object as attribute of a font object, then it is cached by storing it in a new variable. <strong>len(kerning)</strong> gives you the length of the kerning dictionary, the number of kern pairs. Have a look at the attributes and methods of the <a href="http://letterror.com/code/robofab/objects/kerning.html" target="new" class="reference">Kerning object here.</a> It has some very useful methods for interpolating, sorting, combining and splitting kern tables. Back to the example, did you note that the kern pairs appear in random order? It's that Python dictionary thing again: keys of a dictionary have no particular order. Just like the example of the glyphnames in a font object.
|
||
</p>
|
||
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># work with kerning 3<br># print a specific set of pairs<br> <br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">kerning</span> = <span class="py_key">font</span>.<span class="py_key">kerning</span><br><span class="py_res">for</span> <span class="py_key">left</span>, <span class="py_key">right</span> <span class="py_res">in</span> <span class="py_key">kerning</span>.<span class="py_key">keys</span>():<br> <span class="py_res">if</span> <span class="py_key">kerning</span>[(<span class="py_key">left</span>, <span class="py_key">right</span>)] < -<span class="py_num">100</span>:<br> <span class="py_res">print</span> <span class="py_key">left</span>, <span class="py_key">right</span>, <span class="py_key">kerning</span>[(<span class="py_key">left</span>, <span class="py_key">right</span>)]<br></pre></div><div class="pythonsourcetitle"><a href="examples/workWithKerning3.py" target="new">download examples/workWithKerning3.py</a></div>
|
||
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">K</span> <span class="py_key">v</span> -<span class="py_num">111</span><br><span class="py_key">N</span> <span class="py_key">Atilde</span> -<span class="py_num">114</span><br><span class="py_key">W</span> <span class="py_key">o</span> -<span class="py_num">118</span><br><span class="py_key">W</span> <span class="py_key">odieresis</span> -<span class="py_num">118</span><br><span class="py_key">Acircumflex</span> <span class="py_key">Y</span> -<span class="py_num">103</span><br><span class="py_key">T</span> <span class="py_key">e</span> -<span class="py_num">153</span><br><span class="py_key">T</span> <span class="py_key">adieresis</span> -<span class="py_num">126</span><br><span class="py_key">T</span> <span class="py_key">odieresis</span> -<span class="py_num">133</span><br><span class="py_key">T</span> <span class="py_key">aacute</span> -<span class="py_num">126</span><br><span class="py_key">W</span> <span class="py_key">eacute</span> -<span class="py_num">141</span><br>..<span class="py_key">etc</span>..</pre></div>
|
||
|
||
<p>
|
||
Another example of iterating through the kerning dictionary. This time each kern is tested if the value is less than -100, and only when this is the case the pair is printed. This shows you how you can write code which responds to particular kinds of kerns.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># work with kerning 4<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">kerning</span> = <span class="py_key">font</span>.<span class="py_key">kerning</span><br><span class="py_res">for</span> <span class="py_key">left</span>, <span class="py_key">right</span> <span class="py_res">in</span> <span class="py_key">kerning</span>.<span class="py_key">keys</span>():<br> <span class="py_res">if</span> <span class="py_key">left</span> == <span class="py_str">"acircumflex"</span>:<br> <span class="py_res">print</span> <span class="py_key">left</span>, <span class="py_key">right</span>, <span class="py_key">kerning</span>[(<span class="py_key">left</span>, <span class="py_key">right</span>)]<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/workWithKerning4.py" target="new">download examples/workWithKerning4.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">acircumflex</span> <span class="py_key">k</span> -<span class="py_num">7</span><br><span class="py_key">acircumflex</span> <span class="py_key">v</span> -<span class="py_num">38</span><br><span class="py_key">acircumflex</span> <span class="py_key">r</span> -<span class="py_num">4</span><br><span class="py_key">acircumflex</span> <span class="py_key">u</span> -<span class="py_num">4</span><br><span class="py_key">acircumflex</span> <span class="py_key">y</span> -<span class="py_num">31</span><br><span class="py_key">acircumflex</span> <span class="py_key">j</span> -<span class="py_num">26</span><br>..<span class="py_key">etc</span>..</pre></div>
|
||
|
||
<p>
|
||
This script prints all kerns with <strong>acircumflex</strong> as first glyph.
|
||
</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h3>Building glyphs</h3>
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_20.gif" />
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_26.gif" />
|
||
|
||
<p>
|
||
A particularly interesting topic of scripting is building glyphs out of component parts. If a font already has all the parts, a script can, in many cases, assemble the accented versions. An overview of <a href="http://letterror.com/code/robofab/howto/buildingaccents.html" class="reference" target="docs">glyph building options is in the how-to section of the Robofab docs</a>. The first example takes a look at all necessary ingredients: making a new glyph, adding parts and finishing it. Then we'll look at more efficient ways.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># building a glyph from parts<br># the hard way<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br> <br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()<br> <br># make a new glyph<br><span class="py_key">f</span>.<span class="py_key">newGlyph</span>(<span class="py_str">"aacute"</span>)<br> <br># add the component for the base glyph, a<br><span class="py_key">f</span>[<span class="py_str">"aacute"</span>].<span class="py_key">appendComponent</span>(<span class="py_str">"a"</span>)<br> <br># add the component for the accent, acute<br># note it has an offset<br><span class="py_key">f</span>[<span class="py_str">"aacute"</span>].<span class="py_key">appendComponent</span>(<span class="py_str">"acute"</span>, (<span class="py_num">200</span>, <span class="py_num">0</span>))<br> <br># set the width too<br><span class="py_key">f</span>[<span class="py_str">"aacute"</span>].<span class="py_key">width</span> = <span class="py_key">f</span>[<span class="py_str">"a"</span>].<span class="py_key">width</span><br><span class="py_key">f</span>.<span class="py_key">update</span>()<br> <br></pre></div><div class="pythonsourcetitle"><a href="examples/buildingAccents1.py" target="new">download examples/buildingAccents1.py</a></div>
|
||
|
||
<p>
|
||
Let's have a look at that line by line. <strong>f.newGlyph("aacute")</strong>. The <strong>newGlyph()</strong> of the <strong>Font</strong> object creates a new glyph and names it <strong>"aacute"</strong>. Then we can get to the new glyph by asking the font. The <strong>Glyph</strong> object has a <strong>appendComponent()</strong> method, which takes a glyphName of the glyph you want to add as a component and optionally an offset coordinate. This you can see in the line where the <strong>acute</strong> glyph is added. Then the width of the new glyph is set to the width of the original glyph. Finally FontLab is told to update.
|
||
</p>
|
||
|
||
<p>
|
||
Well, that's going to be an awful lot of code if you have to write 4 lines of code for each new letter. There are other ways of going about this, using FontLab's <strong>Anchor</strong> points. </p>
|
||
|
||
<h3>glyph.generateGlyph()</h3>
|
||
|
||
<p>
|
||
RoboFab has a database of glyph constructions based on the Adobe Glyph List. Have a look in your RoboFab code folder, <strong>robofab/Data/GlyphConstruction.txt</strong>. The RoboFab list contains information about where components should be connected and what the anchor points are called.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre><span class="py_key">Acircumflexdotaccent</span>: <span class="py_key">A</span> <span class="py_key">circumflex</span>.<span class="py_key">top</span> <span class="py_key">dotaccent</span>.<span class="py_key">bottom</span></pre></div>
|
||
|
||
<p>
|
||
This entry shows that Acircumflexdotaccent is constructed with components from A, a circumflex using the <strong>top</strong> anchor, and dotaccent using the <strong>bottom</strong> anchor. In order to make this work, you need to add anchor points to your glyphs and accents. Check the FontLab manual for instructions. For instance the "a" has an anchor point named "top", the "acute" glyph has one named "_top".
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># building a glyph from parts<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">font</span>.<span class="py_key">generateGlyph</span>(<span class="py_str">"aacute"</span>)</pre></div>
|
||
|
||
<p>
|
||
This creates a new glyph at "aacute", it puts all the components in the right place and sets the width.
|
||
</p>
|
||
|
||
<h3>glyph.compileGlyph()</h3>
|
||
<p>
|
||
Suppose you want to create glyphs using anchor points, but the glyphs don't have entries in Robofab's <strong>GlyphConstruction.txt</strong> list. What to do? Editing GlyphConstruction.txt is not recommended because you will loose your changes when you install a new version of RoboFab. Glyph has another method: <strong>compileGlyph()</strong>. This method, like generateGlyph, builds a new glyph with components, but you get to provide the list and tell which anchor points you want to use. CompileGlyph takes a list of accents and anchors. It will follow the list and allow "stacking" of accents.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06
|
||
<br># Compile a new glyph from a list of accents and required anchors
|
||
<br># Demo of multiple accents chaining together, or "stacking".
|
||
<br># For this example you need to set up a couple of things
|
||
<br># in your test font:
|
||
<br># - base glyph "a", with anchor "top" and anchor "bottom"
|
||
<br># - glyph "dieresis" with anchor "_top" and anchor "top"
|
||
<br># - glyph "acture" with anchor "_top"
|
||
<br># - glyph "cedilla" with anchor "_bottom"
|
||
<br>#
|
||
<br>
|
||
<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span>
|
||
<br><span class="py_key">font</span> = <span class="py_key">CurrentFont</span>()
|
||
<br>
|
||
<br># this is a list of tuples
|
||
<br># each tuple has the name of the accent as first element
|
||
<br># and the name of the anchor which to use as the second element
|
||
<br><span class="py_key">accentList</span> = [(<span class="py_str">"dieresis"</span>, <span class="py_str">"top"</span>),
|
||
<br> (<span class="py_str">"acute"</span>, <span class="py_str">"top"</span>),
|
||
<br> (<span class="py_str">"cedilla"</span>, <span class="py_str">"bottom"</span>)]
|
||
<br>
|
||
<br># The accents are compiled in this order, so first
|
||
<br># "dieresis" connects to "a" using "top" anchor
|
||
<br># "acute" connects to dieresis, using the next "top" anchor
|
||
<br>
|
||
<br><span class="py_key">font</span>.<span class="py_key">compileGlyph</span>(<span class="py_str">"myCompiledGlyph"</span>, <span class="py_str">"a"</span>, <span class="py_key">accentList</span>)
|
||
<br><span class="py_key">font</span>.<span class="py_key">update</span>()
|
||
<br>
|
||
<br></pre></div><div class="pythonsourcetitle"><a href="examples/compileGlyph.py" target="new">download examples/compileGlyph.py</a></div>
|
||
|
||
|
||
|
||
<h2>Next</h2>
|
||
<p>
|
||
The Robofab stuff will continue with:
|
||
<a href="robofab_session5.html">Scripting for interpolation</a>
|
||
</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h1>
|
||
Scripting for interpolation
|
||
</h1>
|
||
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_17.gif" width="140" height="96" />
|
||
|
||
<img src="http://letterror.com/code/robofab/img/ondrawmodel_07.gif" />
|
||
|
||
|
||
<h2>Interpolation</h2>
|
||
<p>
|
||
|
||
In this session we're going to look at scripting and interpolation. Building an interpolation system has 2 phases: <strong>preparing</strong> it and <strong>using</strong> it. Though this isn't necessarily a design / production difference. In the first phase you need to make sure all the data is set up properly, the compatible glyphs in each master, matching contours in each glyph, path directions, start point locations etc. This can be a lot of work, but RoboFab can assist by reporting the problems. The second phase, using the interpolation comes when everything works and rather than look for problems (there shouldn't be any) you just want to generate all the weights as fast as possible and get on with it proofing and other things.
|
||
</p>
|
||
|
||
<h2>Terminology</h2>
|
||
<p>
|
||
The glyphs (or whole fonts) that are used in an interpolation system are usually called <strong>masters</strong>. The <strong>interpolation factor</strong> is a number between 0 and 1. With factor = 0 the result will be identical to the first master (in this case glyph "A"), and as you can probably guess, with factor = 1 the result will match glyph "B". Factor =0.5 means the resulting shape will be exactly between the two masters. The factor can actually also be outside the 0, 1 range - in that case we speak of <strong>extrapolation</strong>. The particular change in a glyph when interpolating from one master to another is called an <strong>axis</strong>.
|
||
</p>
|
||
|
||
|
||
<h2>Why use RoboFab interpolation?</h2>
|
||
<p>
|
||
All the alternatives come with strings attached. FontLab's <strong>Blend</strong> tool forces points in contours which don't match. This is handy if you're looking for a quick fix, but it can more or less destroy your masters. Alternatively you can use the Multiple Master tools, but this requires all of your masters to be in the same file and drawing this way can be tricky. Since interpolation is a useful process in typedesign, and a good candidate for scripting we decided to include solid interpolation support in Robofab.
|
||
</p>
|
||
|
||
<h2>Interpolating glyphs</h2>
|
||
<p>
|
||
In the first example we're going to interpolate two glyphs in the same font. This can be useful in the design stage, for sketching or testing. The result is stored in a third glyph. Note: if you want to run this example in FontLab, open a new, empty font. Use the circle tool to draw one circle in "A", and again in "B". Make sure these are different enough to tell what's going on.
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># interpolate two glyphs in the same font<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_key">factor</span> = <span class="py_num">0.5</span><br><span class="py_key">f</span>[<span class="py_str">"C"</span>].<span class="py_key">interpolate</span>(<span class="py_key">factor</span>, <span class="py_key">f</span>[<span class="py_str">"A"</span>], <span class="py_key">f</span>[<span class="py_str">"B"</span>])<br><span class="py_key">f</span>[<span class="py_str">"C"</span>].<span class="py_key">update</span>()<br></pre></div><div class="pythonsourcetitle"><a href="examples/interpolateGlyphs.py" target="new">download examples/interpolateGlyphs.py</a></div>
|
||
|
||
<p>
|
||
You see there are 3 glyphs involved: the two masters (<strong>"A"</strong> and <strong>"B"</strong>) and a new glyph which is used to store the results. In this case the new glyph is stored as <strong>"C"</strong>, but it can be helpful to give it a more descriptive name. This is not very PostScript-like, but chances are these glyphs won't make it to production anyway, so it's not a problem. Notice that <strong>interpolate()</strong> is a method of the <strong>Glyph</strong> object. The <strong>Font</strong> object has an interpolate method as well, more about that later. The <a href="http://letterror.com/code/robofab/howto/interpolate.html" class="reference" target="docs">obligatory link</a> to the relevant RoboFab documentation.
|
||
</p>
|
||
|
||
<p>
|
||
Here's the same script, but now it generates a range of interpolated glyphs, each with a better name which tells you which interpolation factor was used:
|
||
</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06<br># interpolate two glyphs in the same font a bunch of times<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span><br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()<br><span class="py_res">for</span> <span class="py_key">i</span> <span class="py_res">in</span> <span class="py_key">range</span>(<span class="py_num">0</span>, <span class="py_num">10</span>):<br> <span class="py_key">factor</span> = <span class="py_key">i</span>*<span class="py_num">.1</span><br> <span class="py_key">name</span> = <span class="py_str">"result_%f"</span>%<span class="py_key">factor</span><br> <span class="py_res">print</span> <span class="py_str">"interpolating"</span>, <span class="py_key">name</span><br> <span class="py_key">f</span>[<span class="py_key">name</span>].<span class="py_key">interpolate</span>(<span class="py_key">factor</span>, <span class="py_key">f</span>[<span class="py_str">"A"</span>], <span class="py_key">f</span>[<span class="py_str">"B"</span>])<br><span class="py_key">f</span>.<span class="py_key">update</span>()</pre></div><div class="pythonsourcetitle"><a href="examples/interpolateSeries.py" target="new">download examples/interpolateSeries.py</a></div>
|
||
|
||
<div class="pythonoutput"><pre><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.000000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.100000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.200000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.300000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.400000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.500000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.600000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.700000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.800000</span><br><span class="py_key">interpolating</span> <span class="py_key">result_0</span><span class="py_num">.900000</span></pre></div>
|
||
|
||
<img src="glyphinterpol.gif" alt="" width="493" height="65" />
|
||
|
||
<p>
|
||
Here you see a range of resulting glyphs as they could be generated by the script above.
|
||
</p>
|
||
|
||
<h2>Rounding errors</h2>
|
||
<p>
|
||
When you interpolate in FontLab you need to take into account that the results will be rounded off. Coordinates can only consist of whole numbers, i,e, "101" or "102" but not "101.3290" There is a nice solution for working with floating point precision glyphs using RoboFab. Here's a brief introduction:</p>
|
||
|
||
<div class="pythonsource"><pre># robothon06
|
||
<br>
|
||
<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">world</span> <span class="py_res">import</span> <span class="py_key">CurrentFont</span>
|
||
<br># We need to import a class with a different
|
||
<br># implementation for the glyph object.
|
||
<br># It looks a bit odd, but this is how it is done
|
||
<br><span class="py_res">from</span> <span class="py_key">robofab</span>.<span class="py_key">objects</span>.<span class="py_key">objectsRF</span> <span class="py_res">import</span> <span class="py_key">RGlyph</span> <span class="py_key">as</span> <span class="py_key">_RGlyph</span>
|
||
<br>
|
||
<br><span class="py_key">f</span> = <span class="py_key">CurrentFont</span>()
|
||
<br>
|
||
<br># pick two compatible glyphs as masters
|
||
<br><span class="py_key">m1</span> = <span class="py_key">f</span>[<span class="py_str">"A"</span>]
|
||
<br><span class="py_key">m2</span> = <span class="py_key">f</span>[<span class="py_str">"B"</span>]
|
||
<br>
|
||
<br># make a new glyph object from this other glyph class
|
||
<br><span class="py_key">g</span> = <span class="py_key">_RGlyph</span>()
|
||
<br>
|
||
<br># interpolation factor which is bound to make floats
|
||
<br><span class="py_key">oddFactor</span> = <span class="py_num">0.2382345</span>
|
||
<br>
|
||
<br># go!
|
||
<br><span class="py_key">g</span>.<span class="py_key">interpolate</span>(<span class="py_key">oddFactor</span>, <span class="py_key">m1</span>, <span class="py_key">m2</span>)
|
||
<br>
|
||
<br># let's have a look at the raw results
|
||
<br><span class="py_res">for</span> <span class="py_key">contour</span> <span class="py_res">in</span> <span class="py_key">g</span>:
|
||
<br> <span class="py_res">for</span> <span class="py_key">pt</span> <span class="py_res">in</span> <span class="py_key">contour</span>.<span class="py_key">points</span>:
|
||
<br> <span class="py_res">print</span> <span class="py_str">"float"</span>, <span class="py_key">pt</span>.<span class="py_key">x</span>, <span class="py_key">pt</span>.<span class="py_key">y</span>
|
||
<br>
|
||
<br># a glyph can round itself off:
|
||
<br><span class="py_key">g</span>.<span class="py_key">round</span>()
|
||
<br>
|
||
<br># and then it looks like integers again
|
||
<br><span class="py_res">for</span> <span class="py_key">contour</span> <span class="py_res">in</span> <span class="py_key">g</span>:
|
||
<br> <span class="py_res">for</span> <span class="py_key">pt</span> <span class="py_res">in</span> <span class="py_key">contour</span>.<span class="py_key">points</span>:
|
||
<br> <span class="py_res">print</span> <span class="py_str">"integer"</span>, <span class="py_key">pt</span>.<span class="py_key">x</span>, <span class="py_key">pt</span>.<span class="py_key">y</span>
|
||
<br><span class="py_com"> |