<?xml version="1.0" encoding="UTF-8"?><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>opengl &#8211; CS@Worcester</title>
	<atom:link href="https://cs.worcester.edu/category/opengl/feed/" rel="self" type="application/rss+xml" />
	<link>https://cs.worcester.edu</link>
	<description>Worcester State University Computer Science Department</description>
	<lastBuildDate>Sat, 17 Aug 2013 20:00:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
<site xmlns="com-wordpress:feed-additions:1">236835116</site>	<item>
		<title>Font Rendering in OpenGL with Pango and Cairo</title>
		<link>http://dthompson.us/font-rendering-in-opengl-with-pango-and-cairo.html</link>
		
		<dc:creator><![CDATA[David Thompson]]></dc:creator>
		<pubDate>Sat, 17 Aug 2013 20:00:00 +0000</pubDate>
				<category><![CDATA[cairo]]></category>
		<category><![CDATA[font]]></category>
		<category><![CDATA[opengl]]></category>
		<category><![CDATA[pango]]></category>
		<category><![CDATA[WSU]]></category>
		<guid isPermaLink="false">http://cs.worcester.edu/blog/?guid=15361c35259035b9db080926c8381f37</guid>

					<description><![CDATA[<p>I am working towards a 0.1 release of my game development framework
for GNU Guile, <a href="https://github.com/davexunit/guile-2d">guile-2d</a>. One of the few remaining blockers on my
to-do list is font rendering. A reddit user, <a href="http://www.reddit.com/user/Madsy9">Madsy9</a>, pointed me in
the right direction with this <a href="http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk">comment</a>. There are two libraries needed
to perform nice font rendering with proper internationalization
support: <a href="http://www.pango.org/">Pango</a>, "a library for laying out and rendering of text, with
an emphasis on internationalization," and <a href="http://cairographics.org/">Cairo</a>, "a 2D graphics
library with support for multiple output devices."</p>
<p>It took me awhile to put together all of the pieces and build a
working sample program. The goal of this post is to help others that
may be trying to accomplish a similar task that have no prior
knowledge of Pango and Cairo. I will assume basic knowledge of C, SDL, and
OpenGL throughout this post.</p>
<p>Let's get the basic SDL and OpenGL initialization out of the way:</p>
<div><pre><span>#include &#60;pango/pangocairo.h&#62;</span>
<span>#include &#60;SDL.h&#62;</span>
<span>#include &#60;SDL_opengl.h&#62;</span>

<span>#define WINDOW_WIDTH 800</span>
<span>#define WINDOW_HEIGHT 600</span>
<span>#define FONT "Sans Bold 18"</span>
<span>#define TEXT "The quick brown fox is so &#12363;&#12431;&#12356;&#12356;!"</span>

<span>void</span>
<span>init_sdl</span> <span>()</span>
<span>{</span>
    <span>SDL_Init</span> <span>(</span><span>SDL_INIT_EVERYTHING</span><span>);</span>
    <span>SDL_SetVideoMode</span> <span>(</span><span>WINDOW_WIDTH</span><span>,</span> <span>WINDOW_HEIGHT</span><span>,</span> <span>0</span><span>,</span> <span>SDL_OPENGL</span><span>);</span>
<span>}</span>

<span>void</span>
<span>init_gl</span> <span>()</span>
<span>{</span>
    <span>glClearColor</span> <span>(</span><span>0.0f</span><span>,</span> <span>0.0f</span><span>,</span> <span>0.0f</span><span>,</span> <span>0.0f</span><span>);</span>
    <span>glDisable</span> <span>(</span><span>GL_DEPTH_TEST</span><span>);</span>
    <span>glEnable</span> <span>(</span><span>GL_BLEND</span><span>);</span>
    <span>glBlendFunc</span> <span>(</span><span>GL_SRC_ALPHA</span><span>,</span> <span>GL_ONE_MINUS_SRC_ALPHA</span><span>);</span>
    <span>glEnable</span> <span>(</span><span>GL_TEXTURE_2D</span><span>);</span>
    <span>glViewport</span> <span>(</span><span>0</span><span>,</span> <span>0</span><span>,</span> <span>WINDOW_WIDTH</span><span>,</span> <span>WINDOW_HEIGHT</span><span>);</span>
    <span>glMatrixMode</span> <span>(</span><span>GL_PROJECTION</span><span>);</span>
    <span>glLoadIdentity</span> <span>();</span>
    <span>glOrtho</span> <span>(</span><span>0</span><span>,</span> <span>WINDOW_WIDTH</span><span>,</span> <span>WINDOW_HEIGHT</span><span>,</span> <span>0</span><span>,</span> <span>-</span><span>1</span><span>,</span> <span>1</span><span>);</span>
    <span>glMatrixMode</span> <span>(</span><span>GL_MODELVIEW</span><span>);</span>
    <span>glLoadIdentity</span> <span>();</span>
<span>}</span>
</pre></div>
<p><tt>create_texture</tt> simply creates an OpenGL texture given an array of
pixel data and the texture dimensions. Our Cairo surface will use BGRA
color.</p>
<div><pre><span>unsigned</span> <span>int</span>
<span>create_texture</span> <span>(</span><span>unsigned</span> <span>int</span> <span>width</span><span>,</span>
                <span>unsigned</span> <span>int</span> <span>height</span><span>,</span>
                <span>unsigned</span> <span>char</span> <span>*</span><span>pixels</span><span>)</span>
<span>{</span>
    <span>unsigned</span> <span>int</span> <span>texture_id</span><span>;</span>

    <span>glGenTextures</span> <span>(</span><span>1</span><span>,</span> <span>&#38;</span><span>texture_id</span><span>);</span>
    <span>glBindTexture</span> <span>(</span><span>GL_TEXTURE_2D</span><span>,</span> <span>texture_id</span><span>);</span>
    <span>glTexParameteri</span> <span>(</span><span>GL_TEXTURE_2D</span><span>,</span> <span>GL_TEXTURE_MIN_FILTER</span><span>,</span> <span>GL_LINEAR</span><span>);</span>
    <span>glTexParameteri</span> <span>(</span><span>GL_TEXTURE_2D</span><span>,</span> <span>GL_TEXTURE_MAG_FILTER</span><span>,</span> <span>GL_LINEAR</span><span>);</span>
    <span>glTexImage2D</span> <span>(</span><span>GL_TEXTURE_2D</span><span>,</span>
                  <span>0</span><span>,</span>
                  <span>GL_RGBA</span><span>,</span>
                  <span>width</span><span>,</span>
                  <span>height</span><span>,</span>
                  <span>0</span><span>,</span>
                  <span>GL_BGRA</span><span>,</span>
                  <span>GL_UNSIGNED_BYTE</span><span>,</span>
                  <span>pixels</span><span>);</span>

    <span>return</span> <span>texture_id</span><span>;</span>
<span>}</span>
</pre></div>
<p><tt>draw_texture</tt> clears the screen, renders a simple textured quad
using OpenGL's immediate mode, and then swaps buffers.</p>
<div><pre><span>void</span>
<span>draw_texture</span> <span>(</span><span>int</span> <span>width</span><span>,</span>
              <span>int</span> <span>height</span><span>,</span>
              <span>unsigned</span> <span>int</span> <span>texture_id</span><span>)</span>
<span>{</span>
    <span>/* Render a texture in immediate mode. */</span>
    <span>glMatrixMode</span> <span>(</span><span>GL_MODELVIEW</span><span>);</span>
    <span>glLoadIdentity</span> <span>();</span>
    <span>glClear</span> <span>(</span><span>GL_COLOR_BUFFER_BIT</span><span>);</span>
    <span>glPushMatrix</span> <span>();</span>
    <span>glBindTexture</span> <span>(</span><span>GL_TEXTURE_2D</span><span>,</span> <span>texture_id</span><span>);</span>
    <span>glColor3f</span> <span>(</span><span>1.f</span><span>,</span> <span>1.0f</span><span>,</span> <span>1.0f</span><span>);</span>

    <span>glBegin</span> <span>(</span><span>GL_QUADS</span><span>);</span>
    <span>glTexCoord2f</span> <span>(</span><span>0.0f</span><span>,</span> <span>0.0f</span><span>);</span>
    <span>glVertex2f</span> <span>(</span><span>0.0f</span><span>,</span> <span>0.0f</span><span>);</span>
    <span>glTexCoord2f</span> <span>(</span><span>1.0f</span><span>,</span> <span>0.0f</span><span>);</span>
    <span>glVertex2f</span> <span>(</span><span>width</span><span>,</span> <span>0.0f</span><span>);</span>
    <span>glTexCoord2f</span> <span>(</span><span>1.0f</span><span>,</span> <span>1.0f</span><span>);</span>
    <span>glVertex2f</span> <span>(</span><span>width</span> <span>,</span> <span>height</span><span>);</span>
    <span>glTexCoord2f</span> <span>(</span><span>0.0f</span><span>,</span> <span>1.0f</span><span>);</span>
    <span>glVertex2f</span> <span>(</span><span>0.0f</span><span>,</span> <span>height</span><span>);</span>
    <span>glEnd</span> <span>();</span>

    <span>glPopMatrix</span> <span>();</span>
    <span>SDL_GL_SwapBuffers</span><span>();</span>
<span>}</span>
</pre></div>
<p><tt>create_cairo_context</tt> is used to make a new Cairo context that
draws to a raw data surface.  The return value, a <tt>cairo_t</tt>, is the
main object in Cairo. All drawing is done via a <tt>cairo_t</tt> object.  A
context needs a surface to draw on.
<tt>cairo_image_surface_create_for_data</tt> creates a raw data surface for
us. We will be translating the surface into a texture later on.</p>
<div><pre><span>cairo_t</span><span>*</span>
<span>create_cairo_context</span> <span>(</span><span>int</span> <span>width</span><span>,</span>
                      <span>int</span> <span>height</span><span>,</span>
                      <span>int</span> <span>channels</span><span>,</span>
                      <span>cairo_surface_t</span><span>**</span> <span>surf</span><span>,</span>
                      <span>unsigned</span> <span>char</span><span>**</span> <span>buffer</span><span>)</span>
<span>{</span>
    <span>*</span><span>buffer</span> <span>=</span> <span>calloc</span> <span>(</span><span>channels</span> <span>*</span> <span>width</span> <span>*</span> <span>height</span><span>,</span> <span>sizeof</span> <span>(</span><span>unsigned</span> <span>char</span><span>));</span>
    <span>*</span><span>surf</span> <span>=</span> <span>cairo_image_surface_create_for_data</span> <span>(</span><span>*</span><span>buffer</span><span>,</span>
                                                 <span>CAIRO_FORMAT_ARGB32</span><span>,</span>
                                                 <span>width</span><span>,</span>
                                                 <span>height</span><span>,</span>
                                                 <span>channels</span> <span>*</span> <span>width</span><span>);</span>
    <span>return</span> <span>cairo_create</span> <span>(</span><span>*</span><span>surf</span><span>);</span>
<span>}</span>
</pre></div>
<p><tt>create_layout_context</tt> also makes a new Cairo context, but this
context is for PangoLayout objects.  In Pango, a layout describes the
style of a paragraph of text.  The layout needs a context in order to
function. We use <tt>cairo_image_surface_create</tt> with dimensions of 0x0
because we won't actually be rendering to this surface. Instead, we
will layout our text and use <tt>create_cairo_context</tt> to build a
context with a surface that is the size of the rendered text.  Cairo
uses reference counting for dynamically allocated objects, so we need
to call <tt>cairo_surface_destroy</tt> when we're done with the temporary
surface.  The context still maintains a reference to the surface, so
the memory for the surface will not be freed until the context is.</p>
<div><pre><span>cairo_t</span><span>*</span>
<span>create_layout_context</span> <span>()</span>
<span>{</span>
    <span>cairo_surface_t</span> <span>*</span><span>temp_surface</span><span>;</span>
    <span>cairo_t</span> <span>*</span><span>context</span><span>;</span>

    <span>temp_surface</span> <span>=</span> <span>cairo_image_surface_create</span> <span>(</span><span>CAIRO_FORMAT_ARGB32</span><span>,</span> <span>0</span><span>,</span> <span>0</span><span>);</span>
    <span>context</span> <span>=</span> <span>cairo_create</span> <span>(</span><span>temp_surface</span><span>);</span>
    <span>cairo_surface_destroy</span> <span>(</span><span>temp_surface</span><span>);</span>

    <span>return</span> <span>context</span><span>;</span>
<span>}</span>
</pre></div>
<p><tt>get_text_size</tt> tells us the size of the text that's in the layout,
in pixels.  Pango's units are not in pixels, so we must divide by
<tt>PANGO_SCALE</tt> in order to get pixel units.</p>
<div><pre><span>void</span>
<span>get_text_size</span> <span>(</span><span>PangoLayout</span> <span>*</span><span>layout</span><span>,</span>
                <span>unsigned</span> <span>int</span> <span>*</span><span>width</span><span>,</span>
                <span>unsigned</span> <span>int</span> <span>*</span><span>height</span><span>)</span>
<span>{</span>
    <span>pango_layout_get_size</span> <span>(</span><span>layout</span><span>,</span> <span>width</span><span>,</span> <span>height</span><span>);</span>
    <span>/* Divide by pango scale to get dimensions in pixels. */</span>
    <span>*</span><span>width</span> <span>/=</span> <span>PANGO_SCALE</span><span>;</span>
    <span>*</span><span>height</span> <span>/=</span> <span>PANGO_SCALE</span><span>;</span>
<span>}</span>
</pre></div>
<p><tt>render_text</tt> is where all of the magic happens.  First, we create a
layout with a layout context and set the text that we will render with
this layout.  <tt>TEXT</tt> is defined earlier in the program as "The quick
brown fox is so &#12363;&#12431;&#12356;&#12356;!"</p>
<p>Then we create a <tt>PangoFontDescription</tt> object.  This object
represents the font that we want to render.  Earlier in the program,
<tt>FONT</tt> is defined as "Sans Bold 18".  Pango is able to figure out
how to load a font from a string in this format.  Your system must be
able to recognize the font family and font face, though.  I haven't
yet figured out how to have Pango render an arbitrary font from a
<tt>*.ttf</tt> file.</p>
<p>Next, we create a rendering context by getting the layout's size and
creating a context with a surface big enough to show all of the
rendered text.</p>
<p>Finally, we set the font color to white, render the text to the
surface with <tt>pango_cairo_show_layout</tt>, and create an OpenGL texture
from the surface. We also clean up all the objects that we no longer
need before returning.</p>
<div><pre><span>unsigned</span> <span>int</span>
<span>render_text</span> <span>(</span><span>const</span> <span>char</span> <span>*</span><span>text</span><span>,</span>
             <span>unsigned</span> <span>int</span> <span>*</span><span>text_width</span><span>,</span>
             <span>unsigned</span> <span>int</span> <span>*</span><span>text_height</span><span>,</span>
             <span>unsigned</span> <span>int</span> <span>*</span><span>texture_id</span><span>)</span>
<span>{</span>
    <span>cairo_t</span> <span>*</span><span>layout_context</span><span>;</span>
    <span>cairo_t</span> <span>*</span><span>render_context</span><span>;</span>
    <span>cairo_surface_t</span> <span>*</span><span>temp_surface</span><span>;</span>
    <span>cairo_surface_t</span> <span>*</span><span>surface</span><span>;</span>
    <span>unsigned</span> <span>char</span><span>*</span> <span>surface_data</span> <span>=</span> <span>NULL</span><span>;</span>
    <span>PangoFontDescription</span> <span>*</span><span>desc</span><span>;</span>
    <span>PangoLayout</span> <span>*</span><span>layout</span><span>;</span>

    <span>layout_context</span> <span>=</span> <span>create_layout_context</span> <span>();</span>

    <span>/* Create a PangoLayout, set the font and text */</span>
    <span>layout</span> <span>=</span> <span>pango_cairo_create_layout</span> <span>(</span><span>layout_context</span><span>);</span>
    <span>pango_layout_set_text</span> <span>(</span><span>layout</span><span>,</span> <span>text</span><span>,</span> <span>-</span><span>1</span><span>);</span>

    <span>/* Load the font */</span>
    <span>desc</span> <span>=</span> <span>pango_font_description_from_string</span> <span>(</span><span>FONT</span><span>);</span>
    <span>pango_layout_set_font_description</span> <span>(</span><span>layout</span><span>,</span> <span>desc</span><span>);</span>
    <span>pango_font_description_free</span> <span>(</span><span>desc</span><span>);</span>

    <span>/* Get text dimensions and create a context to render to */</span>
    <span>get_text_size</span> <span>(</span><span>layout</span><span>,</span> <span>text_width</span><span>,</span> <span>text_height</span><span>);</span>
    <span>render_context</span> <span>=</span> <span>create_cairo_context</span> <span>(</span><span>*</span><span>text_width</span><span>,</span>
                                           <span>*</span><span>text_height</span><span>,</span>
                                           <span>4</span><span>,</span>
                                           <span>&#38;</span><span>surface</span><span>,</span>
                                           <span>&#38;</span><span>surface_data</span><span>);</span>

    <span>/* Render */</span>
    <span>cairo_set_source_rgba</span> <span>(</span><span>render_context</span><span>,</span> <span>1</span><span>,</span> <span>1</span><span>,</span> <span>1</span><span>,</span> <span>1</span><span>);</span>
    <span>pango_cairo_show_layout</span> <span>(</span><span>render_context</span><span>,</span> <span>layout</span><span>);</span>
    <span>*</span><span>texture_id</span> <span>=</span> <span>create_texture</span><span>(</span><span>*</span><span>text_width</span><span>,</span> <span>*</span><span>text_height</span><span>,</span> <span>surface_data</span><span>);</span>

    <span>/* Clean up */</span>
    <span>free</span> <span>(</span><span>surface_data</span><span>);</span>
    <span>g_object_unref</span> <span>(</span><span>layout</span><span>);</span>
    <span>cairo_destroy</span> <span>(</span><span>layout_context</span><span>);</span>
    <span>cairo_destroy</span> <span>(</span><span>render_context</span><span>);</span>
    <span>cairo_surface_destroy</span> <span>(</span><span>surface</span><span>);</span>
<span>}</span>
</pre></div>
<p><tt>main</tt> is pretty simple.  We initialize SDL and OpenGL, render text
to a texture, and enter the rendering loop.  The program will run
until you click the close button, press "enter", or press "q".</p>
<div><pre><span>int</span> <span>main</span> <span>(</span><span>int</span> <span>argc</span><span>,</span> <span>char</span> <span>**</span><span>argv</span><span>)</span>
<span>{</span>
    <span>SDL_Event</span> <span>event</span><span>;</span>
    <span>int</span> <span>keep_running</span> <span>=</span> <span>1</span><span>;</span>
    <span>unsigned</span> <span>int</span> <span>texture_id</span><span>;</span>
    <span>unsigned</span> <span>int</span> <span>text_width</span> <span>=</span> <span>0</span><span>;</span>
    <span>unsigned</span> <span>int</span> <span>text_height</span> <span>=</span> <span>0</span><span>;</span>

    <span>init_sdl</span> <span>();</span>
    <span>init_gl</span> <span>();</span>
    <span>render_text</span><span>(</span><span>TEXT</span><span>,</span>
                <span>&#38;</span><span>texture_id</span><span>,</span>
                <span>&#38;</span><span>text_width</span><span>,</span>
                <span>&#38;</span><span>text_height</span><span>);</span>

    <span>/* Update/render loop */</span>
    <span>while</span> <span>(</span><span>keep_running</span><span>)</span> <span>{</span>
        <span>SDL_PollEvent</span> <span>(</span><span>&#38;</span><span>event</span><span>);</span>

        <span>switch</span> <span>(</span><span>event</span><span>.</span><span>type</span><span>)</span> <span>{</span>
        <span>case</span> <span>SDL_QUIT</span> :
            <span>keep_running</span> <span>=</span> <span>0</span><span>;</span>
            <span>break</span><span>;</span>

        <span>case</span> <span>SDL_KEYDOWN</span> :
            <span>if</span> <span>(</span><span>event</span><span>.</span><span>key</span><span>.</span><span>keysym</span><span>.</span><span>sym</span> <span>==</span> <span>SDLK_ESCAPE</span><span>)</span>
                <span>keep_running</span> <span>=</span> <span>0</span><span>;</span>
            <span>if</span> <span>(</span><span>event</span><span>.</span><span>key</span><span>.</span><span>keysym</span><span>.</span><span>sym</span> <span>==</span> <span>SDLK_q</span><span>)</span>
                <span>keep_running</span> <span>=</span> <span>0</span><span>;</span>
            <span>break</span><span>;</span>
        <span>}</span>

        <span>draw_texture</span> <span>(</span><span>texture_id</span><span>,</span> <span>text_width</span><span>,</span> <span>text_height</span><span>);</span>
        <span>SDL_Delay</span> <span>(</span><span>16</span><span>);</span>
    <span>}</span>

    <span>/* Clean up */</span>
    <span>glDeleteTextures</span> <span>(</span><span>1</span><span>,</span> <span>&#38;</span><span>texture_id</span><span>);</span>

    <span>SDL_Quit</span><span>();</span>

    <span>return</span> <span>0</span><span>;</span>
<span>}</span>
</pre></div>
<p>And we're done! You should now be able to render some text in an
OpenGL context. I hope this brief tutorial was helpful. Font rendering
isn't easy, and it's not really my area of interest. I'm glad that
Pango exists to do all of the real work for me so that I can more
quickly move on to the parts of graphics programming that I actually
enjoy.</p>
<p>You can download the full source code <a href="http://dthompson.us/src/pangocairo.tar.gz">here</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>I am working towards a 0.1 release of my game development framework<br />
for GNU Guile, <a class="reference external" href="https://github.com/davexunit/guile-2d">guile-2d</a>. One of the few remaining blockers on my<br />
to-do list is font rendering. A reddit user, <a class="reference external" href="http://www.reddit.com/user/Madsy9">Madsy9</a>, pointed me in<br />
the right direction with this <a class="reference external" href="http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk">comment</a>. There are two libraries needed<br />
to perform nice font rendering with proper internationalization<br />
support: <a class="reference external" href="http://www.pango.org/">Pango</a>, &quot;a library for laying out and rendering of text, with<br />
an emphasis on internationalization,&quot; and <a class="reference external" href="http://cairographics.org/">Cairo</a>, &quot;a 2D graphics<br />
library with support for multiple output devices.&quot;</p>
<p>It took me awhile to put together all of the pieces and build a<br />
working sample program. The goal of this post is to help others that<br />
may be trying to accomplish a similar task that have no prior<br />
knowledge of Pango and Cairo. I will assume basic knowledge of C, SDL, and<br />
OpenGL throughout this post.</p>
<p>Let&#8217;s get the basic SDL and OpenGL initialization out of the way:</p>
<div class="highlight">
<pre><span class="cp">#include &lt;pango/pangocairo.h&gt;</span>
<span class="cp">#include &lt;SDL.h&gt;</span>
<span class="cp">#include &lt;SDL_opengl.h&gt;</span>

<span class="cp">#define WINDOW_WIDTH 800</span>
<span class="cp">#define WINDOW_HEIGHT 600</span>
<span class="cp">#define FONT &quot;Sans Bold 18&quot;</span>
<span class="cp">#define TEXT &quot;The quick brown fox is so かわいい!&quot;</span>

<span class="kt">void</span>
<span class="nf">init_sdl</span> <span class="p">()</span>
<span class="p">{</span>
    <span class="n">SDL_Init</span> <span class="p">(</span><span class="n">SDL_INIT_EVERYTHING</span><span class="p">);</span>
    <span class="n">SDL_SetVideoMode</span> <span class="p">(</span><span class="n">WINDOW_WIDTH</span><span class="p">,</span> <span class="n">WINDOW_HEIGHT</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SDL_OPENGL</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span>
<span class="nf">init_gl</span> <span class="p">()</span>
<span class="p">{</span>
    <span class="n">glClearColor</span> <span class="p">(</span><span class="mf">0.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
    <span class="n">glDisable</span> <span class="p">(</span><span class="n">GL_DEPTH_TEST</span><span class="p">);</span>
    <span class="n">glEnable</span> <span class="p">(</span><span class="n">GL_BLEND</span><span class="p">);</span>
    <span class="n">glBlendFunc</span> <span class="p">(</span><span class="n">GL_SRC_ALPHA</span><span class="p">,</span> <span class="n">GL_ONE_MINUS_SRC_ALPHA</span><span class="p">);</span>
    <span class="n">glEnable</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">);</span>
    <span class="n">glViewport</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">WINDOW_WIDTH</span><span class="p">,</span> <span class="n">WINDOW_HEIGHT</span><span class="p">);</span>
    <span class="n">glMatrixMode</span> <span class="p">(</span><span class="n">GL_PROJECTION</span><span class="p">);</span>
    <span class="n">glLoadIdentity</span> <span class="p">();</span>
    <span class="n">glOrtho</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">WINDOW_WIDTH</span><span class="p">,</span> <span class="n">WINDOW_HEIGHT</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">glMatrixMode</span> <span class="p">(</span><span class="n">GL_MODELVIEW</span><span class="p">);</span>
    <span class="n">glLoadIdentity</span> <span class="p">();</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">create_texture</tt> simply creates an OpenGL texture given an array of<br />
pixel data and the texture dimensions. Our Cairo surface will use BGRA<br />
color.</p>
<div class="highlight">
<pre><span class="kt">unsigned</span> <span class="kt">int</span>
<span class="nf">create_texture</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">width</span><span class="p">,</span>
                <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">height</span><span class="p">,</span>
                <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pixels</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">texture_id</span><span class="p">;</span>

    <span class="n">glGenTextures</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">texture_id</span><span class="p">);</span>
    <span class="n">glBindTexture</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="n">texture_id</span><span class="p">);</span>
    <span class="n">glTexParameteri</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="n">GL_TEXTURE_MIN_FILTER</span><span class="p">,</span> <span class="n">GL_LINEAR</span><span class="p">);</span>
    <span class="n">glTexParameteri</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="n">GL_TEXTURE_MAG_FILTER</span><span class="p">,</span> <span class="n">GL_LINEAR</span><span class="p">);</span>
    <span class="n">glTexImage2D</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span>
                  <span class="mi">0</span><span class="p">,</span>
                  <span class="n">GL_RGBA</span><span class="p">,</span>
                  <span class="n">width</span><span class="p">,</span>
                  <span class="n">height</span><span class="p">,</span>
                  <span class="mi">0</span><span class="p">,</span>
                  <span class="n">GL_BGRA</span><span class="p">,</span>
                  <span class="n">GL_UNSIGNED_BYTE</span><span class="p">,</span>
                  <span class="n">pixels</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">texture_id</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">draw_texture</tt> clears the screen, renders a simple textured quad<br />
using OpenGL&#8217;s immediate mode, and then swaps buffers.</p>
<div class="highlight">
<pre><span class="kt">void</span>
<span class="nf">draw_texture</span> <span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span>
              <span class="kt">int</span> <span class="n">height</span><span class="p">,</span>
              <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">texture_id</span><span class="p">)</span>
<span class="p">{</span>
    <span class="cm">/* Render a texture in immediate mode. */</span>
    <span class="n">glMatrixMode</span> <span class="p">(</span><span class="n">GL_MODELVIEW</span><span class="p">);</span>
    <span class="n">glLoadIdentity</span> <span class="p">();</span>
    <span class="n">glClear</span> <span class="p">(</span><span class="n">GL_COLOR_BUFFER_BIT</span><span class="p">);</span>
    <span class="n">glPushMatrix</span> <span class="p">();</span>
    <span class="n">glBindTexture</span> <span class="p">(</span><span class="n">GL_TEXTURE_2D</span><span class="p">,</span> <span class="n">texture_id</span><span class="p">);</span>
    <span class="n">glColor3f</span> <span class="p">(</span><span class="mf">1.f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">);</span>

    <span class="n">glBegin</span> <span class="p">(</span><span class="n">GL_QUADS</span><span class="p">);</span>
    <span class="n">glTexCoord2f</span> <span class="p">(</span><span class="mf">0.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
    <span class="n">glVertex2f</span> <span class="p">(</span><span class="mf">0.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
    <span class="n">glTexCoord2f</span> <span class="p">(</span><span class="mf">1.0f</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
    <span class="n">glVertex2f</span> <span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
    <span class="n">glTexCoord2f</span> <span class="p">(</span><span class="mf">1.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">);</span>
    <span class="n">glVertex2f</span> <span class="p">(</span><span class="n">width</span> <span class="p">,</span> <span class="n">height</span><span class="p">);</span>
    <span class="n">glTexCoord2f</span> <span class="p">(</span><span class="mf">0.0f</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">);</span>
    <span class="n">glVertex2f</span> <span class="p">(</span><span class="mf">0.0f</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
    <span class="n">glEnd</span> <span class="p">();</span>

    <span class="n">glPopMatrix</span> <span class="p">();</span>
    <span class="n">SDL_GL_SwapBuffers</span><span class="p">();</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">create_cairo_context</tt> is used to make a new Cairo context that<br />
draws to a raw data surface.  The return value, a <tt class="docutils literal">cairo_t</tt>, is the<br />
main object in Cairo. All drawing is done via a <tt class="docutils literal">cairo_t</tt> object.  A<br />
context needs a surface to draw on.<br />
<tt class="docutils literal">cairo_image_surface_create_for_data</tt> creates a raw data surface for<br />
us. We will be translating the surface into a texture later on.</p>
<div class="highlight">
<pre><span class="kt">cairo_t</span><span class="o">*</span>
<span class="nf">create_cairo_context</span> <span class="p">(</span><span class="kt">int</span> <span class="n">width</span><span class="p">,</span>
                      <span class="kt">int</span> <span class="n">height</span><span class="p">,</span>
                      <span class="kt">int</span> <span class="n">channels</span><span class="p">,</span>
                      <span class="kt">cairo_surface_t</span><span class="o">**</span> <span class="n">surf</span><span class="p">,</span>
                      <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">**</span> <span class="n">buffer</span><span class="p">)</span>
<span class="p">{</span>
    <span class="o">*</span><span class="n">buffer</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="n">channels</span> <span class="o">*</span> <span class="n">width</span> <span class="o">*</span> <span class="n">height</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">));</span>
    <span class="o">*</span><span class="n">surf</span> <span class="o">=</span> <span class="n">cairo_image_surface_create_for_data</span> <span class="p">(</span><span class="o">*</span><span class="n">buffer</span><span class="p">,</span>
                                                 <span class="n">CAIRO_FORMAT_ARGB32</span><span class="p">,</span>
                                                 <span class="n">width</span><span class="p">,</span>
                                                 <span class="n">height</span><span class="p">,</span>
                                                 <span class="n">channels</span> <span class="o">*</span> <span class="n">width</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">cairo_create</span> <span class="p">(</span><span class="o">*</span><span class="n">surf</span><span class="p">);</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">create_layout_context</tt> also makes a new Cairo context, but this<br />
context is for PangoLayout objects.  In Pango, a layout describes the<br />
style of a paragraph of text.  The layout needs a context in order to<br />
function. We use <tt class="docutils literal">cairo_image_surface_create</tt> with dimensions of 0x0<br />
because we won&#8217;t actually be rendering to this surface. Instead, we<br />
will layout our text and use <tt class="docutils literal">create_cairo_context</tt> to build a<br />
context with a surface that is the size of the rendered text.  Cairo<br />
uses reference counting for dynamically allocated objects, so we need<br />
to call <tt class="docutils literal">cairo_surface_destroy</tt> when we&#8217;re done with the temporary<br />
surface.  The context still maintains a reference to the surface, so<br />
the memory for the surface will not be freed until the context is.</p>
<div class="highlight">
<pre><span class="kt">cairo_t</span><span class="o">*</span>
<span class="nf">create_layout_context</span> <span class="p">()</span>
<span class="p">{</span>
    <span class="kt">cairo_surface_t</span> <span class="o">*</span><span class="n">temp_surface</span><span class="p">;</span>
    <span class="kt">cairo_t</span> <span class="o">*</span><span class="n">context</span><span class="p">;</span>

    <span class="n">temp_surface</span> <span class="o">=</span> <span class="n">cairo_image_surface_create</span> <span class="p">(</span><span class="n">CAIRO_FORMAT_ARGB32</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">context</span> <span class="o">=</span> <span class="n">cairo_create</span> <span class="p">(</span><span class="n">temp_surface</span><span class="p">);</span>
    <span class="n">cairo_surface_destroy</span> <span class="p">(</span><span class="n">temp_surface</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">context</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">get_text_size</tt> tells us the size of the text that&#8217;s in the layout,<br />
in pixels.  Pango&#8217;s units are not in pixels, so we must divide by<br />
<tt class="docutils literal">PANGO_SCALE</tt> in order to get pixel units.</p>
<div class="highlight">
<pre><span class="kt">void</span>
<span class="nf">get_text_size</span> <span class="p">(</span><span class="n">PangoLayout</span> <span class="o">*</span><span class="n">layout</span><span class="p">,</span>
                <span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">width</span><span class="p">,</span>
                <span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">height</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">pango_layout_get_size</span> <span class="p">(</span><span class="n">layout</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
    <span class="cm">/* Divide by pango scale to get dimensions in pixels. */</span>
    <span class="o">*</span><span class="n">width</span> <span class="o">/=</span> <span class="n">PANGO_SCALE</span><span class="p">;</span>
    <span class="o">*</span><span class="n">height</span> <span class="o">/=</span> <span class="n">PANGO_SCALE</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">render_text</tt> is where all of the magic happens.  First, we create a<br />
layout with a layout context and set the text that we will render with<br />
this layout.  <tt class="docutils literal">TEXT</tt> is defined earlier in the program as &quot;The quick<br />
brown fox is so かわいい!&quot;</p>
<p>Then we create a <tt class="docutils literal">PangoFontDescription</tt> object.  This object<br />
represents the font that we want to render.  Earlier in the program,<br />
<tt class="docutils literal">FONT</tt> is defined as &quot;Sans Bold 18&quot;.  Pango is able to figure out<br />
how to load a font from a string in this format.  Your system must be<br />
able to recognize the font family and font face, though.  I haven&#8217;t<br />
yet figured out how to have Pango render an arbitrary font from a<br />
<tt class="docutils literal">*.ttf</tt> file.</p>
<p>Next, we create a rendering context by getting the layout&#8217;s size and<br />
creating a context with a surface big enough to show all of the<br />
rendered text.</p>
<p>Finally, we set the font color to white, render the text to the<br />
surface with <tt class="docutils literal">pango_cairo_show_layout</tt>, and create an OpenGL texture<br />
from the surface. We also clean up all the objects that we no longer<br />
need before returning.</p>
<div class="highlight">
<pre><span class="kt">unsigned</span> <span class="kt">int</span>
<span class="nf">render_text</span> <span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">text</span><span class="p">,</span>
             <span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">text_width</span><span class="p">,</span>
             <span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">text_height</span><span class="p">,</span>
             <span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">texture_id</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">cairo_t</span> <span class="o">*</span><span class="n">layout_context</span><span class="p">;</span>
    <span class="kt">cairo_t</span> <span class="o">*</span><span class="n">render_context</span><span class="p">;</span>
    <span class="kt">cairo_surface_t</span> <span class="o">*</span><span class="n">temp_surface</span><span class="p">;</span>
    <span class="kt">cairo_surface_t</span> <span class="o">*</span><span class="n">surface</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">surface_data</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">PangoFontDescription</span> <span class="o">*</span><span class="n">desc</span><span class="p">;</span>
    <span class="n">PangoLayout</span> <span class="o">*</span><span class="n">layout</span><span class="p">;</span>

    <span class="n">layout_context</span> <span class="o">=</span> <span class="n">create_layout_context</span> <span class="p">();</span>

    <span class="cm">/* Create a PangoLayout, set the font and text */</span>
    <span class="n">layout</span> <span class="o">=</span> <span class="n">pango_cairo_create_layout</span> <span class="p">(</span><span class="n">layout_context</span><span class="p">);</span>
    <span class="n">pango_layout_set_text</span> <span class="p">(</span><span class="n">layout</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>

    <span class="cm">/* Load the font */</span>
    <span class="n">desc</span> <span class="o">=</span> <span class="n">pango_font_description_from_string</span> <span class="p">(</span><span class="n">FONT</span><span class="p">);</span>
    <span class="n">pango_layout_set_font_description</span> <span class="p">(</span><span class="n">layout</span><span class="p">,</span> <span class="n">desc</span><span class="p">);</span>
    <span class="n">pango_font_description_free</span> <span class="p">(</span><span class="n">desc</span><span class="p">);</span>

    <span class="cm">/* Get text dimensions and create a context to render to */</span>
    <span class="n">get_text_size</span> <span class="p">(</span><span class="n">layout</span><span class="p">,</span> <span class="n">text_width</span><span class="p">,</span> <span class="n">text_height</span><span class="p">);</span>
    <span class="n">render_context</span> <span class="o">=</span> <span class="n">create_cairo_context</span> <span class="p">(</span><span class="o">*</span><span class="n">text_width</span><span class="p">,</span>
                                           <span class="o">*</span><span class="n">text_height</span><span class="p">,</span>
                                           <span class="mi">4</span><span class="p">,</span>
                                           <span class="o">&amp;</span><span class="n">surface</span><span class="p">,</span>
                                           <span class="o">&amp;</span><span class="n">surface_data</span><span class="p">);</span>

    <span class="cm">/* Render */</span>
    <span class="n">cairo_set_source_rgba</span> <span class="p">(</span><span class="n">render_context</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">pango_cairo_show_layout</span> <span class="p">(</span><span class="n">render_context</span><span class="p">,</span> <span class="n">layout</span><span class="p">);</span>
    <span class="o">*</span><span class="n">texture_id</span> <span class="o">=</span> <span class="n">create_texture</span><span class="p">(</span><span class="o">*</span><span class="n">text_width</span><span class="p">,</span> <span class="o">*</span><span class="n">text_height</span><span class="p">,</span> <span class="n">surface_data</span><span class="p">);</span>

    <span class="cm">/* Clean up */</span>
    <span class="n">free</span> <span class="p">(</span><span class="n">surface_data</span><span class="p">);</span>
    <span class="n">g_object_unref</span> <span class="p">(</span><span class="n">layout</span><span class="p">);</span>
    <span class="n">cairo_destroy</span> <span class="p">(</span><span class="n">layout_context</span><span class="p">);</span>
    <span class="n">cairo_destroy</span> <span class="p">(</span><span class="n">render_context</span><span class="p">);</span>
    <span class="n">cairo_surface_destroy</span> <span class="p">(</span><span class="n">surface</span><span class="p">);</span>
<span class="p">}</span>
</pre>
</div>
<p><tt class="docutils literal">main</tt> is pretty simple.  We initialize SDL and OpenGL, render text<br />
to a texture, and enter the rendering loop.  The program will run<br />
until you click the close button, press &quot;enter&quot;, or press &quot;q&quot;.</p>
<div class="highlight">
<pre><span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">SDL_Event</span> <span class="n">event</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">keep_running</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">texture_id</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">text_width</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">text_height</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="n">init_sdl</span> <span class="p">();</span>
    <span class="n">init_gl</span> <span class="p">();</span>
    <span class="n">render_text</span><span class="p">(</span><span class="n">TEXT</span><span class="p">,</span>
                <span class="o">&amp;</span><span class="n">texture_id</span><span class="p">,</span>
                <span class="o">&amp;</span><span class="n">text_width</span><span class="p">,</span>
                <span class="o">&amp;</span><span class="n">text_height</span><span class="p">);</span>

    <span class="cm">/* Update/render loop */</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">keep_running</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">SDL_PollEvent</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">event</span><span class="p">);</span>

        <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">SDL_QUIT</span> :
            <span class="n">keep_running</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="k">case</span> <span class="n">SDL_KEYDOWN</span> :
            <span class="k">if</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">key</span><span class="p">.</span><span class="n">keysym</span><span class="p">.</span><span class="n">sym</span> <span class="o">==</span> <span class="n">SDLK_ESCAPE</span><span class="p">)</span>
                <span class="n">keep_running</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">key</span><span class="p">.</span><span class="n">keysym</span><span class="p">.</span><span class="n">sym</span> <span class="o">==</span> <span class="n">SDLK_q</span><span class="p">)</span>
                <span class="n">keep_running</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">draw_texture</span> <span class="p">(</span><span class="n">texture_id</span><span class="p">,</span> <span class="n">text_width</span><span class="p">,</span> <span class="n">text_height</span><span class="p">);</span>
        <span class="n">SDL_Delay</span> <span class="p">(</span><span class="mi">16</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/* Clean up */</span>
    <span class="n">glDeleteTextures</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">texture_id</span><span class="p">);</span>

    <span class="n">SDL_Quit</span><span class="p">();</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre>
</div>
<p>And we&#8217;re done! You should now be able to render some text in an<br />
OpenGL context. I hope this brief tutorial was helpful. Font rendering<br />
isn&#8217;t easy, and it&#8217;s not really my area of interest. I&#8217;m glad that<br />
Pango exists to do all of the real work for me so that I can more<br />
quickly move on to the parts of graphics programming that I actually<br />
enjoy.</p>
<p>You can download the full source code <a class="reference external" href="http://dthompson.us/src/pangocairo.tar.gz">here</a>.</p>

<p class="syndicated-attribution"><em>From the blog <a href="http://dthompson.us/">dthompson</a> by <a href="https://cs.worcester.edu/author/0/" title="Read other posts by David Thompson">David Thompson</a></em> and used with permission of the author. All other rights reserved by the author.</p>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">2688</post-id>	</item>
	</channel>
</rss>
