<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/rss.xsl" type="text/xsl"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
	<title>Sacha Chua - tag - typography</title>
	<atom:link href="https://sachachua.com/blog/tag/typography/feed/index.xml" rel="self" type="application/rss+xml" />
	<atom:link href="https://sachachua.com/blog/tag/typography" rel="alternate" type="text/html" />
	<link>https://sachachua.com/blog/tag/typography/feed/index.xml</link>
	<description>Emacs, sketches, and life</description>
	<lastBuildDate>Tue, 22 Oct 2024 12:03:51 GMT</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>11ty</generator>
  <item>
		<title>Having fun kerning using Org Mode and FontForge</title>
		<link>https://sachachua.com/blog/2020/06/having-fun-kerning-using-org-mode-and-fontforge/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Mon, 08 Jun 2020 03:01:59 GMT</pubDate>
    <category>emacs</category>
<category>org</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=29577</guid>
		<description><![CDATA[
<p>It turns out that working with font bearings and kerning tables using Org Mode makes lots of things so much easier. </p>



<figure class="wp-block-image size-large"><img loading="lazy" width="1366" height="726" src="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_00-01-05.png" alt="" class="wp-image-29578" srcset="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_00-01-05.png 1366w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_00-01-05-640x340.png 640w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_00-01-05-280x149.png 280w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_00-01-05-768x408.png 768w" sizes="(max-width: 1366px) 100vw, 1366px"><figcaption>Bearings in the top left, kerning matrix in the top right</figcaption></figure>



<p>
While trying to figure out kerning, I came across this issue that described how you sometimes need a <a href="https://www.dafont.com/forum/read/405813/the-kerning-is-set-in-a-way-that-doesn-t-work-at-dafont-we-use-the-gd-library-of-php">character-pair kern table instead of just class-based kerning</a>. Since I had figured out character-based kerning before I figured out class-based kerning, it was easy to restore my Python code that takes the same kerning matrix and generates character pairs. Here's what that code looks like.
</p>



<div class="org-src-container">
<pre class="src src-python" id="orge5e9368"><code><span class="org-keyword">def</span> <span class="org-function-name">kern_by_char</span>(font, kerning_matrix):</code>
<code>  <span class="org-comment-delimiter"># </span><span class="org-comment">Add kerning by character as backup</span></code>
<code>  font.addLookupSubtable(<span class="org-string">"kern"</span>, <span class="org-string">"kern-2"</span>)</code>
<code>  <span class="org-variable-name">offsets</span> = np.asarray(kerning_matrix)</code>
<code>  <span class="org-variable-name">classes_right</span> = [<span class="org-constant">None</span> <span class="org-keyword">if</span> (x == <span class="org-string">""</span> <span class="org-keyword">or</span> x == <span class="org-string">"None"</span>) <span class="org-keyword">else</span> x.split(<span class="org-string">","</span>) <span class="org-keyword">for</span> x <span class="org-keyword">in</span> offsets[0,1:]]</code>
<code>  <span class="org-variable-name">classes_left</span> = [<span class="org-constant">None</span> <span class="org-keyword">if</span> (x == <span class="org-string">""</span> <span class="org-keyword">or</span> x == <span class="org-string">"None"</span>) <span class="org-keyword">else</span> x.split(<span class="org-string">','</span>) <span class="org-keyword">for</span> x <span class="org-keyword">in</span> offsets[1:,0]]</code>
<code>  <span class="org-keyword">for</span> r, row <span class="org-keyword">in</span> <span class="org-builtin">enumerate</span>(classes_left):</code>
<code>    <span class="org-keyword">if</span> row <span class="org-keyword">is</span> <span class="org-constant">None</span>: <span class="org-keyword">continue</span></code>
<code>    <span class="org-keyword">for</span> first_letter <span class="org-keyword">in</span> row:</code>
<code>      <span class="org-variable-name">g</span> = font.createMappedChar(first_letter)</code>
<code>      <span class="org-keyword">for</span> c, column <span class="org-keyword">in</span> <span class="org-builtin">enumerate</span>(classes_right):</code>
<code>        <span class="org-keyword">if</span> column <span class="org-keyword">is</span> <span class="org-constant">None</span>: <span class="org-keyword">continue</span></code>
<code>        <span class="org-keyword">for</span> second_letter <span class="org-keyword">in</span> column:</code>
<code>          <span class="org-keyword">if</span> kerning_matrix[r + 1][c + 1]:</code>
<code>            g.addPosSub(<span class="org-string">"kern-2"</span>, second_letter, 0, 0, kerning_matrix[r + 1][c + 1], 0, 0, 0, 0, 0)</code>
<code>  <span class="org-keyword">return</span> font</code>
</pre>
</div>



<p>I wanted to be able to easily compare different versions of my font: my original glyphs versus my tweaked glyphs, simple spacing versus kerned. This was a hassle with FontForge, since I had to open different font files in different Metrics windows. If I execute a little bit of source code in my Org Mode, though, I can use my test web page to view all the different versions. By arranging my Emacs windows a certain way and adding <code>:eval no</code> to the Org Babel blocks I'm not currently using, I can easily change the relevant table entries and evaluate the whole buffer to regenerate the font versions, including exports to OTF and WOFF. Here's the code for that: </p>



<div class="org-src-container">
<pre class="src src-python"><code>&lt;&lt;params&gt;&gt;</code>
<code>&lt;&lt;def_import_glyphs&gt;&gt;</code>
<code>&lt;&lt;def_set_bearings&gt;&gt;</code>
<code>&lt;&lt;def_kern_classes&gt;&gt;</code>
<code>&lt;&lt;def_kern_by_char&gt;&gt;</code>
<code><span class="org-variable-name">font</span> = fontforge.font()</code>
<code><span class="org-variable-name">font</span> = import_glyphs(font, params)</code>
<code><span class="org-variable-name">font</span> = set_bearings(font, bearings)</code>
<code>save_font(font, {**params, <span class="org-string">"new_otf"</span>: <span class="org-string">"sachacHandRaw.otf"</span>})</code>
<code><span class="org-variable-name">font</span> = kern_classes(font, kerning_matrix)</code>
<code><span class="org-variable-name">font</span> = kern_by_char(font, kerning_matrix)</code>
<code>save_font(font, {**params, <span class="org-string">"new_otf"</span>: <span class="org-string">"sachacHandRawKerned.otf"</span>})</code>
<code><span class="org-variable-name">font</span> = load_font(<span class="org-string">'SachaHandEdited.sfd'</span>)</code>
<code><span class="org-variable-name">font</span> = set_bearings(font, bearings)</code>
<code>font.removeLookup(<span class="org-string">'kern'</span>)</code>
<code>save_font(font, {**params, <span class="org-string">"new_otf"</span>: <span class="org-string">"sachacHandEdited.otf"</span>})</code>
<code><span class="org-variable-name">font</span> = kern_classes(font, kerning_matrix)</code>
<code><span class="org-variable-name">font</span> = kern_by_char(font, kerning_matrix)</code>
<code>save_font(font, {**params, <span class="org-string">"new_otf"</span>: <span class="org-string">"sachacHand.otf"</span>})</code>
</pre>
</div>



<p>I also like the way it's pretty easy to update multiple kerning values without clicking around. I sometimes use FontForge to get the number to set it to and then copy that into my table, but I also sometimes just tweak the number in Org Mode directly. </p>



<p>To see the results, I can generate a test HTML that shows me text with different versions of my font. I can also look at lots of kerning pairs at the same time. Here are the components of that test page: </p>



<div class="org-src-container">
<pre class="src src-python" id="orgde60347"><code><span class="org-keyword">def</span> <span class="org-function-name">test_css</span>(fonts):</code>
<code>  <span class="org-variable-name">doc</span>, <span class="org-variable-name">tag</span>, <span class="org-variable-name">text</span>, <span class="org-variable-name">line</span> = Doc().ttl()</code>
<code>  <span class="org-keyword">with</span> tag(<span class="org-string">'style'</span>):</code>
<code>    <span class="org-keyword">for</span> f <span class="org-keyword">in</span> fonts:</code>
<code>      text(<span class="org-string">"@font-face { font-family: '%s'; src: url('%s'); }"</span> % (f[0], f[1]))</code>
<code>      text(<span class="org-string">".%s { font-family: '%s'; }"</span> % (f[0], f[0]))</code>
<code>    text(<span class="org-string">"table { font-size: inherit; font-weight: inherit }"</span>)</code>
<code>    text(<span class="org-string">"td { text-align: left }"</span>)</code>
<code>    text(<span class="org-string">".blog-heading { font-weight: bold; font-size: 32px }"</span>)</code>
<code>    text(<span class="org-string">".default { color: gray }"</span>)</code>
<code>    text(<span class="org-string">"body { font-family: woff, Arial, sans-serif; font-size: 32px; padding: 10px }"</span>)</code>
<code>  <span class="org-keyword">return</span> doc.getvalue()</code>
<code><span class="org-keyword">def</span> <span class="org-function-name">test_html</span>(strings):</code>
<code>  <span class="org-variable-name">doc</span>, <span class="org-variable-name">tag</span>, <span class="org-variable-name">text</span>, <span class="org-variable-name">line</span> = Doc().ttl()</code>
<code>  <span class="org-keyword">with</span> doc.tag(<span class="org-string">'table'</span>, style=<span class="org-string">'border-bottom: 1px solid gray; width: 100%; border-collapse: collapse'</span>):</code>
<code>    <span class="org-keyword">for</span> s <span class="org-keyword">in</span> strings:</code>
<code>      <span class="org-keyword">for</span> i, f <span class="org-keyword">in</span> <span class="org-builtin">enumerate</span>(fonts):</code>
<code>        <span class="org-variable-name">style</span> = <span class="org-string">'border-top: 1px solid gray'</span> <span class="org-keyword">if</span> (i == 0) <span class="org-keyword">else</span> <span class="org-string">""</span></code>
<code>        <span class="org-keyword">with</span> tag(<span class="org-string">'tr'</span>, klass=f[0], style=style):</code>
<code>          line(<span class="org-string">'td'</span>, f[0])</code>
<code>          line(<span class="org-string">'td'</span>, s)</code>
<code>  <span class="org-keyword">return</span> doc.getvalue()</code>
<code><span class="org-keyword">def</span> <span class="org-function-name">test_kerning_matrix</span>(kerning_matrix):</code>
<code>  <span class="org-variable-name">doc</span>, <span class="org-variable-name">tag</span>, <span class="org-variable-name">text</span>, <span class="org-variable-name">line</span> = Doc().ttl()</code>
<code>  <span class="org-keyword">with</span> tag(<span class="org-string">'table'</span>):</code>
<code>    <span class="org-keyword">for</span> r, row <span class="org-keyword">in</span> <span class="org-builtin">enumerate</span>(classes_left):</code>
<code>      <span class="org-keyword">if</span> row <span class="org-keyword">is</span> <span class="org-constant">None</span>: <span class="org-keyword">continue</span></code>
<code>      <span class="org-keyword">for</span> first_letter <span class="org-keyword">in</span> row:</code>
<code>        <span class="org-keyword">with</span> tag(<span class="org-string">'tr'</span>):</code>
<code>          line(<span class="org-string">'td'</span>, first_letter)</code>
<code>          <span class="org-keyword">for</span> c, column <span class="org-keyword">in</span> <span class="org-builtin">enumerate</span>(classes_right):</code>
<code>            <span class="org-keyword">if</span> column <span class="org-keyword">is</span> <span class="org-constant">None</span>: <span class="org-keyword">continue</span></code>
<code>            <span class="org-keyword">for</span> second_letter <span class="org-keyword">in</span> column:</code>
<code>              <span class="org-variable-name">klass</span> = <span class="org-string">"kerned"</span> <span class="org-keyword">if</span> kerning_matrix[r + 1][c + 1] <span class="org-keyword">else</span> <span class="org-string">"default"</span></code>
<code>              line(<span class="org-string">'td'</span>, aglfn.to_glyph(first_letter) + aglfn.to_glyph(second_letter), klass=klass)</code>
<code>  <span class="org-keyword">return</span> doc.getvalue()</code>
</pre>
</div>



<p>
This code actually generates the test file:
</p>



<div class="org-src-container">
<pre class="src src-python"><code><span class="org-keyword">from</span> yattag <span class="org-keyword">import</span> Doc</code>
<code><span class="org-keyword">import</span> numpy <span class="org-keyword">as</span> np</code>
<code><span class="org-keyword">import</span> aglfn</code>
<code>&lt;&lt;def_test_html&gt;&gt;</code>
<code><span class="org-variable-name">doc</span>, <span class="org-variable-name">tag</span>, <span class="org-variable-name">text</span>, <span class="org-variable-name">line</span> = Doc().ttl()</code>
<code><span class="org-variable-name">fonts</span> = [[<span class="org-string">'raw'</span>, <span class="org-string">'sachacHandRaw.otf'</span>],</code>
<code>         [<span class="org-string">'raw-kerned'</span>, <span class="org-string">'sachacHandRawKerned.otf'</span>],</code>
<code>         [<span class="org-string">'edited'</span>, <span class="org-string">'sachacHandEdited.otf'</span>],</code>
<code>         [<span class="org-string">'woff'</span>, <span class="org-string">'sachacHand.woff'</span>]]</code>
<code><span class="org-variable-name">strings</span> = [<span class="org-string">"Python+FontForge+Org: I made a font based on my handwriting!"</span>,</code>
<code>           <span class="org-string">"Monthly review: May 2020"</span>,</code>
<code>           <span class="org-string">"Emacs News 2020-06-01"</span>]</code>
<code><span class="org-variable-name">offsets</span> = np.asarray(kerning_matrix)</code>
<code><span class="org-variable-name">classes_left</span> = [<span class="org-constant">None</span> <span class="org-keyword">if</span> (x == <span class="org-string">""</span> <span class="org-keyword">or</span> x == <span class="org-string">"None"</span>) <span class="org-keyword">else</span> x.split(<span class="org-string">','</span>) <span class="org-keyword">for</span> x <span class="org-keyword">in</span> offsets[1:,0]]</code>
<code><span class="org-variable-name">classes_right</span> = [<span class="org-constant">None</span> <span class="org-keyword">if</span> (x == <span class="org-string">""</span> <span class="org-keyword">or</span> x == <span class="org-string">"None"</span>) <span class="org-keyword">else</span> x.split(<span class="org-string">","</span>) <span class="org-keyword">for</span> x <span class="org-keyword">in</span> offsets[0,1:]]</code>
<code><span class="org-keyword">with</span> tag(<span class="org-string">'html'</span>):</code>
<code>  <span class="org-keyword">with</span> tag(<span class="org-string">'head'</span>): </code>
<code>    doc.asis(test_css(fonts))</code>
<code>  <span class="org-keyword">with</span> tag(<span class="org-string">'body'</span>):</code>
<code>    line(<span class="org-string">'h1'</span>, <span class="org-string">'Test headings'</span>)</code>
<code>    <span class="org-keyword">with</span> tag(<span class="org-string">'div'</span>, klass=<span class="org-string">"blog-heading"</span>):</code>
<code>      doc.asis(test_html(strings))</code>
<code>    line(<span class="org-string">'h1'</span>, <span class="org-string">'Kerning matrix'</span>)</code>
<code>    doc.asis(test_kerning_matrix(kerning_matrix))</code>
<code><span class="org-keyword">return</span> doc.getvalue()</code>
</pre>
</div>



<p> And here's what that <a href="https://pages.sachachua.com/sachac-hand/files/test.html">test.html</a> looks like: </p>



<figure class="wp-block-image size-large"><img loading="lazy" width="878" height="576" src="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-12.png" alt="" class="wp-image-29579" srcset="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-12.png 878w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-12-640x420.png 640w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-12-280x184.png 280w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-12-768x504.png 768w" sizes="(max-width: 878px) 100vw, 878px"><figcaption>Testing the font with a few blog headings</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" width="560" height="266" src="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-23.png" alt="" class="wp-image-29580" srcset="https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-23.png 560w, https://sachachua.com/blog/wp-content/uploads/2020/06/Screenshot_2020-06-07_22-37-23-280x133.png 280w" sizes="(max-width: 560px) 100vw, 560px"><figcaption>Kerning pairs: black for specified pairs, gray for defaults</figcaption></figure>



<p> Not bad… Now Emacs is my font editor! The code is at <a href="https://github.com/sachac/sachac-hand">https://github.com/sachac/sachac-hand</a> .</p>
]]></description>
		</item><item>
		<title>The unexpected lightness of learning</title>
		<link>https://sachachua.com/blog/2009/01/the-unexpected-lightness-of-learning/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Fri, 23 Jan 2009 02:13:49 GMT</pubDate>
    <category>learning</category>
		<guid isPermaLink="false">https://sachachua.com/blog/?p=5660</guid>
		<description><![CDATA[<p>I had been invited to participate in the usability studies for <a href="http://www.ibm.com/developerworks/web/library/wa-piabeta/?S_TACT=106AH21W&amp;S_CMP=HP">Pass It Along</a>, an IBM peer-to-peer learning system. The project team was planning a revamp of the site, and Amanda had prepared a visual design for our feedback.</p>
<p>The logo she used was simple: pass<strong>it</strong>along, lowercase, with &#8220;it&#8221; shaded in a different color. I couldn&#8217;t help but comment on how wonderfully symmetric it was, with the two descenders (the bottom parts of p and g) at both ends of the word. No, really, look at it.</p>
<p></p><div style="font-size: 3em; line-height: 5em">pass<strong>it</strong>along</div>
<p></p>
<p>It&#8217;s prettier than &#8220;Pass It Along.&#8221; I&#8217;d never noticed things like that before I started to learn about type, and now exposure and awareness lets me appreciate new things.</p>
<p>When I commented on the pleasing symmetry of the descenders, Amanda stopped and laughed. She said, &#8220;You know about descenders?! You always surprise me!&#8221;</p>
<p>So I told her about <a href="http://twitter.com/fivetwelve">@fivetwelve</a>&#8216;s braindump of cool font resources (The Elements of Typographic Style (Bringhurst), <a title="ilovetypography.org" href="http://ilovetypography.org">ilovetypography.org</a>) after he saw (on Twitter) how I enjoyed the Helvetica documentary.</p>
<p>Jargon is the secret handshake of different professions, a shibboleth that distinguishes between people inside and outside. It&#8217;s fun crossing boundaries and learning about people&#8217;s fields, and it&#8217;s fun being able to see things in a new light. =)</p>
]]></description>
		</item>
	</channel>
</rss>