Very quick test text implementation.

This commit is contained in:
H. Utku Maden 2022-08-19 16:13:19 +03:00
parent a0473d9e83
commit bf47915491
12 changed files with 441 additions and 8 deletions

@ -1,4 +1,6 @@
namespace Quik using Quik.Typography;
namespace Quik
{ {
/// <summary> /// <summary>
/// An object which QUIK commands may be issued to. /// An object which QUIK commands may be issued to.
@ -10,11 +12,13 @@
/// </summary> /// </summary>
public QuikDraw Draw { get; } = new QuikDraw(); public QuikDraw Draw { get; } = new QuikDraw();
public QuikStrokeStyle DefaultStroke { get; set; } = new QuikStrokeStyle(new QuikColor(0x000000FF), 4); public QuikStrokeStyle DefaultStroke { get; set; } = new QuikStrokeStyle(new QuikColor(0xaaaaaaff), 4);
public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle() public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle()
{ {
Color = new QuikColor(0xA0A0A0FF) Color = new QuikColor(0xeeeeeeff)
}; };
public QuikFont DefaultFont { get; set; }
} }
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Compression;
using System.Linq; using System.Linq;
namespace Quik namespace Quik
@ -33,5 +34,8 @@ namespace Quik
public void StencilClear() => Commands.Enqueue(new QuikCommandStencilClear()); public void StencilClear() => Commands.Enqueue(new QuikCommandStencilClear());
public void StencilBegin() => Commands.Enqueue(new QuikCommandStencilBegin()); public void StencilBegin() => Commands.Enqueue(new QuikCommandStencilBegin());
public void StencilEnd() => Commands.Enqueue(new QuikCommandStencilEnd()); public void StencilEnd() => Commands.Enqueue(new QuikCommandStencilEnd());
public void PutChar(char chr, QuikVec2 position) => Commands.Enqueue(new QuikCommandPutChar(chr, position));
public void PutText(string text, QuikVec2 position) => Commands.Enqueue(new QuikCommandPutText(text, position));
public void FlowText(string text, QuikRectangle bounds) => Commands.Enqueue(new QuikCommandFlowText(text, bounds));
} }
} }

@ -0,0 +1,14 @@
namespace Quik.Typography
{
public abstract class QuikFont
{
public abstract QuikFontStyle Style { get; }
public string FontFamily => Style.Family;
public float FontSize => Style.Size;
public QuikFontStyle FontStyle => Style;
public abstract bool HasCharacter(int character);
public abstract void GetCharacter(int character, out int texture, out QuikGlyph glyph);
}
}

@ -0,0 +1,23 @@
namespace Quik.Typography
{
public class QuikFontStyle
{
public string Family { get; }
public QuikFontType Type { get; }
public float Size { get; }
public QuikFontStyle(string family, float size, QuikFontType type = QuikFontType.Normal)
{
Family = family;
Size = size;
Type = type;
}
}
public enum QuikFontType
{
Normal,
Italic,
Bold
}
}

@ -0,0 +1,54 @@
namespace Quik.Typography
{
/// <summary>
/// Glyph properties with metrics based on FreeType glyph metrics.
/// </summary>
public struct QuikGlyph
{
/// <summary>
/// The code point for the character.
/// </summary>
public int Character { get; }
/// <summary>
/// Location of the glyph on the atlas.
/// </summary>
public QuikRectangle Location { get; }
/// <summary>
/// Size of the glyph in units.
/// </summary>
public QuikVec2 Size { get; }
/// <summary>
/// Bearing vector for horizontal layout.
/// </summary>
public QuikVec2 HorizontalBearing { get; }
/// <summary>
/// Bearing vector for vertical layout.
/// </summary>
public QuikVec2 VerticalBearing { get; }
/// <summary>
/// Advance vector for vertical and horizontal layouts.
/// </summary>
public QuikVec2 Advance { get; }
public QuikGlyph(
int character,
QuikRectangle location,
QuikVec2 size,
QuikVec2 horizontalBearing,
QuikVec2 verticalBearing,
QuikVec2 advance)
{
Character = character;
Location = location;
Size = size;
HorizontalBearing = horizontalBearing;
VerticalBearing = verticalBearing;
Advance = advance;
}
}
}

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Quik.Typography;
namespace Quik.VertexGenerator namespace Quik.VertexGenerator
{ {
@ -223,9 +224,17 @@ namespace Quik.VertexGenerator
case QuikCommandType.Rectangles: case QuikCommandType.Rectangles:
RenderRectangles(command as QuikCommandRectangles); RenderRectangles(command as QuikCommandRectangles);
goto exit_with_call; goto exit_with_call;
case QuikCommandType.PutChar:
RenderCharacter(command as QuikCommandPutChar);
goto exit_with_call;
case QuikCommandType.PutText:
RenderTextPut(command as QuikCommandPutText);
goto exit_with_call;
default: default:
{ {
QuikDrawCall? subcall = null;
if (OnHandleCommand(this, command)) if (OnHandleCommand(this, command))
goto exit_with_call; goto exit_with_call;
break; break;
@ -1393,6 +1402,97 @@ namespace Quik.VertexGenerator
} }
#endregion #endregion
#region Text
private void RenderCharacter(QuikCommandPutChar chr)
{
Context.DefaultFont.GetCharacter(chr.Character, out int texture, out QuikGlyph metrics);
QuikVertex a, b, c, d;
a = b = c = d = new QuikVertex() {Color = new QuikColor(0xffffffff)};
a.Position = chr.Position + new QuikVec2(0, metrics.HorizontalBearing.Y - metrics.Size.Y);
a.TextureCoordinates = metrics.Location.Min;
b.Position = a.Position + new QuikVec2(metrics.Size.X, 0);
c.Position = a.Position + metrics.Size;
d.Position = a.Position + new QuikVec2(0, metrics.Size.Y);
b.TextureCoordinates = new QuikVec2(metrics.Location.Right, metrics.Location.Bottom);
c.TextureCoordinates = metrics.Location.Max;
d.TextureCoordinates = new QuikVec2(metrics.Location.Left, metrics.Location.Top);
short startVertex = (short)_vertexBufferPointer;
short startElement = (short) _elementBufferPointer;
AddVertex(a, b, c, d);
AddElement(startVertex, (short)(startVertex + 1), (short)(startVertex + 2), startVertex, (short)(startVertex + 2), (short)(startVertex + 3));
QuikDrawCall call = CallTemplate;
call.Texture = texture;
call.Offset = (short) (startElement * 2);
call.Count = 6;
DrawCalls.Add(call);
}
private void RenderTextPut(QuikCommandPutText text)
{
short startElement = (short)_elementBufferPointer;
QuikFont font = Context.DefaultFont;
QuikVertex vertex = new QuikVertex() {Color = new QuikColor(0xff7777ff)};
QuikVec2 pointer = text.Position;
int texture = -1;
for (int i = 0; i < text.Text.Length; i++)
{
int chr = text.Text[i];
QuikGlyph metrics;
int ntex;
font.GetCharacter(chr, out ntex, out metrics);
if (ntex != texture && texture != -1)
{
QuikDrawCall call = CallTemplate;
call.Texture = texture;
call.Offset = (short) (startElement * 2);
call.Count = (short)(_elementBufferPointer - startElement);
DrawCalls.Add(call);
startElement = (short) _elementBufferPointer;
}
texture = ntex;
QuikVertex a, b, c, d;
a = b = c = d = vertex;
a.Position = pointer + new QuikVec2(0, metrics.HorizontalBearing.Y - metrics.Size.Y);
a.TextureCoordinates = metrics.Location.Min;
b.Position = a.Position + new QuikVec2(metrics.Size.X, 0);
c.Position = a.Position + metrics.Size;
d.Position = a.Position + new QuikVec2(0, metrics.Size.Y);
b.TextureCoordinates = new QuikVec2(metrics.Location.Right, metrics.Location.Bottom);
c.TextureCoordinates = metrics.Location.Max;
d.TextureCoordinates = new QuikVec2(metrics.Location.Left, metrics.Location.Top);
pointer.X += metrics.Advance.X;
short startVertex = (short)_vertexBufferPointer;
AddVertex(a, b, c, d);
AddElement(startVertex, (short)(startVertex + 1), (short)(startVertex + 2), startVertex, (short)(startVertex + 2), (short)(startVertex + 3));
}
{
QuikDrawCall call = CallTemplate;
call.Texture = texture;
call.Offset = (short) (startElement * 2);
call.Count = (short)(_elementBufferPointer - startElement);
DrawCalls.Add(call);
}
}
#endregion
} }
public delegate void VertexGeneratorCommandHandler(QuikVertexGenerator generator, QuikCommand command, ref bool anyCalls); public delegate void VertexGeneratorCommandHandler(QuikVertexGenerator generator, QuikCommand command, ref bool anyCalls);
@ -1410,5 +1510,6 @@ namespace Quik.VertexGenerator
public short Count; public short Count;
public QuikRectangle Bounds; public QuikRectangle Bounds;
public bool ClearStencil; public bool ClearStencil;
public int Texture;
} }
} }

@ -35,9 +35,11 @@ in vec2 ftexcoord;
in vec4 fcolor; in vec4 fcolor;
out vec4 outcolor; out vec4 outcolor;
uniform sampler2D texture0;
void main() void main()
{ {
outcolor = fcolor; outcolor = fcolor * texture(texture0, ftexcoord);
} }
"; ";
@ -96,7 +98,7 @@ void main()
QuikVertex.PositionOffset); QuikVertex.PositionOffset);
GL.EnableVertexAttribArray(loc); GL.EnableVertexAttribArray(loc);
GL.VertexAttribPointer( GL.VertexAttribPointer(
loc = GL.GetAttribLocation(sp, "texcoords"), loc = GL.GetAttribLocation(sp, "texcoord"),
2, 2,
VertexAttribPointerType.Float, VertexAttribPointerType.Float,
false, false,
@ -133,6 +135,31 @@ void main()
Color = new QuikColor(0xeeeeeeff) Color = new QuikColor(0xeeeeeeff)
}; };
int whiteTexture = GL.GenTexture();
uint[] whitePixel = {0xFFFFFFFF};
GL.BindTexture(TextureTarget.Texture2D, whiteTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1, 1, 0, PixelFormat.Rgba, PixelType.UnsignedByte, whitePixel);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
TestFont font = new TestFont();
font.TextureBase = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, font.TextureBase);
GL.TexImage2D(
TextureTarget.Texture2D,
0,
PixelInternalFormat.Rgb,
(int)TestFont.TextureSize.X, (int)TestFont.TextureSize.Y, 0,
PixelFormat.Rgba, PixelType.UnsignedByte,
TestFont.Texture);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
context.DefaultFont = font;
window.Context.SwapInterval = 0;
for (;!window.IsExiting;) for (;!window.IsExiting;)
{ {
NativeWindow.ProcessWindowEvents(false); NativeWindow.ProcessWindowEvents(false);
@ -144,9 +171,9 @@ void main()
Matrix4 matrix = Matrix4.CreateOrthographicOffCenter( Matrix4 matrix = Matrix4.CreateOrthographicOffCenter(
0, 0,
(float)window.Size.X / 2, (float)window.Size.X,
0, 0,
(float)window.Size.Y / 2, (float)window.Size.Y,
1, 1,
-1); -1);
GL.UniformMatrix4(loc, false, ref matrix); GL.UniformMatrix4(loc, false, ref matrix);
@ -198,6 +225,8 @@ void main()
FillStyle = fill, FillStyle = fill,
StrokeStyle = strokeBorder StrokeStyle = strokeBorder
}); });
context.Draw.PutText("Aaah! the cat turned into a cat!", new QuikVec2(25,30));
QuikCommand command; QuikCommand command;
while (context.Draw.Commands.TryDequeue(out command)) while (context.Draw.Commands.TryDequeue(out command))
@ -210,6 +239,7 @@ void main()
foreach (QuikDrawCall call in gen.DrawCalls) foreach (QuikDrawCall call in gen.DrawCalls)
{ {
GL.BindTexture(TextureTarget.Texture2D, call.Texture == 0 ? whiteTexture : call.Texture);
GL.DrawElements(BeginMode.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset); GL.DrawElements(BeginMode.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset);
} }

@ -9,4 +9,11 @@
<ProjectReference Include="..\Quik.OpenTK\Quik.OpenTK.csproj" /> <ProjectReference Include="..\Quik.OpenTK\Quik.OpenTK.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="font.dat" />
<EmbeddedResource Include="font.dat" />
<None Remove="font.xml" />
<EmbeddedResource Include="font.xml" />
</ItemGroup>
</Project> </Project>

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Quik;
using Quik.Typography;
namespace QuikTestApplication
{
public class TestFont : QuikFont
{
public override QuikFontStyle Style => _style;
private static QuikFontStyle _style;
private static HashSet<int> _characters = new HashSet<int>();
private static Dictionary<int, QuikGlyph> _glyphs = new Dictionary<int, QuikGlyph>();
public static byte[] Texture { get; private set; } = Array.Empty<byte>();
public static QuikVec2 TextureSize { get; private set; }
public int TextureBase { get; set; } = 1;
static TestFont()
{
using (Stream str = typeof(TestFont).Assembly.GetManifestResourceStream("QuikTestApplication.font.xml"))
{
XmlDocument document = new XmlDocument();
document.Load(str);
string family = document.DocumentElement.Attributes["name"].Value;
float fontSize = float.Parse(document.DocumentElement.Attributes["size"].Value);
QuikFontType style = QuikFontType.Normal;
if (document.DocumentElement.Attributes["bold"].Value == "true")
style = QuikFontType.Bold;
if (document.DocumentElement.Attributes["italic"].Value == "true")
style = QuikFontType.Italic;
_style = new QuikFontStyle(family, fontSize, style);
QuikVec2 atlasSize = default;
atlasSize.X = float.Parse(document.DocumentElement.Attributes["width"].Value);
atlasSize.Y = float.Parse(document.DocumentElement.Attributes["height"].Value);
TextureSize = atlasSize;
foreach (XmlElement element in document.SelectNodes("/font/character"))
{
QuikRectangle UVs;
QuikVec2 origin;
float advance;
int chr = element.Attributes["text"].Value[0];
QuikVec2 pos;
QuikVec2 size;
pos.X = float.Parse(element.Attributes["x"].Value);
pos.Y = float.Parse(element.Attributes["y"].Value);
size.X = float.Parse(element.Attributes["width"].Value);
size.Y = float.Parse(element.Attributes["height"].Value);
UVs = new QuikRectangle(
(pos.X + size.X)/atlasSize.X,
pos.Y/atlasSize.Y,
pos.X/atlasSize.X,
(size.Y + pos.Y)/atlasSize.Y);
origin.X = float.Parse(element.Attributes["origin-x"].Value);
origin.Y = float.Parse(element.Attributes["origin-y"].Value);
advance = float.Parse(element.Attributes["advance"].Value);
QuikGlyph glyph = new QuikGlyph(chr, UVs, size, origin, default, new QuikVec2(advance, 0));
_glyphs.Add(chr, glyph);
_characters.Add(chr);
}
}
using (Stream str = typeof(TestFont).Assembly.GetManifestResourceStream("QuikTestApplication.font.dat"))
{
Texture = new byte[(int)str.Length];
str.Read(Texture, 0, (int)str.Length);
}
}
public override bool HasCharacter(int character)
{
return _characters.Contains((char) character);
}
public override void GetCharacter(int character, out int texture, out QuikGlyph glyph)
{
if (!_glyphs.TryGetValue(character, out glyph))
{
glyph = _glyphs['?'];
}
texture = TextureBase;
}
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<font name="monospace" bold="false" italic="false" width="512" height="128" size="32">
<character text=" " x="69" y="84" width="3" height="3" origin-x="1" origin-y="1" advance="19"/>
<character text="!" x="116" y="59" width="6" height="25" origin-x="-7" origin-y="24" advance="19"/>
<character text="&quot;" x="487" y="59" width="11" height="11" origin-x="-4" origin-y="24" advance="19"/>
<character text="#" x="373" y="0" width="22" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="$" x="48" y="0" width="18" height="31" origin-x="-1" origin-y="25" advance="19"/>
<character text="%" x="417" y="0" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="&amp;" x="438" y="0" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="'" x="498" y="59" width="5" height="11" origin-x="-7" origin-y="24" advance="19"/>
<character text="(" x="66" y="0" width="10" height="31" origin-x="-5" origin-y="25" advance="19"/>
<character text=")" x="76" y="0" width="10" height="31" origin-x="-4" origin-y="25" advance="19"/>
<character text="*" x="423" y="59" width="17" height="17" origin-x="-1" origin-y="24" advance="19"/>
<character text="+" x="359" y="59" width="19" height="19" origin-x="0" origin-y="20" advance="19"/>
<character text="," x="440" y="59" width="8" height="12" origin-x="-5" origin-y="7" advance="19"/>
<character text="-" x="58" y="84" width="11" height="5" origin-x="-4" origin-y="12" advance="19"/>
<character text="." x="29" y="84" width="7" height="7" origin-x="-6" origin-y="6" advance="19"/>
<character text="/" x="145" y="0" width="18" height="29" origin-x="0" origin-y="24" advance="19"/>
<character text="0" x="198" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="1" x="360" y="34" width="17" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="2" x="377" y="34" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="3" x="394" y="34" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="4" x="103" y="34" width="19" height="25" origin-x="0" origin-y="24" advance="19"/>
<character text="5" x="411" y="34" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="6" x="216" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="7" x="428" y="34" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="8" x="234" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="9" x="252" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text=":" x="378" y="59" width="7" height="19" origin-x="-6" origin-y="18" advance="19"/>
<character text=";" x="122" y="59" width="8" height="23" origin-x="-5" origin-y="18" advance="19"/>
<character text="&lt;" x="385" y="59" width="19" height="18" origin-x="0" origin-y="19" advance="19"/>
<character text="=" x="468" y="59" width="19" height="11" origin-x="0" origin-y="16" advance="19"/>
<character text="&gt;" x="404" y="59" width="19" height="18" origin-x="0" origin-y="19" advance="19"/>
<character text="?" x="68" y="59" width="16" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="@" x="105" y="0" width="21" height="29" origin-x="1" origin-y="23" advance="19"/>
<character text="A" x="459" y="0" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="B" x="270" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="C" x="445" y="34" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="D" x="288" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="E" x="462" y="34" width="17" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="F" x="479" y="34" width="17" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="G" x="122" y="34" width="19" height="25" origin-x="0" origin-y="24" advance="19"/>
<character text="H" x="306" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="I" x="84" y="59" width="16" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="J" x="100" y="59" width="16" height="25" origin-x="0" origin-y="24" advance="19"/>
<character text="K" x="63" y="34" width="20" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="L" x="0" y="59" width="17" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="M" x="141" y="34" width="19" height="25" origin-x="0" origin-y="24" advance="19"/>
<character text="N" x="324" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="O" x="160" y="34" width="19" height="25" origin-x="0" origin-y="24" advance="19"/>
<character text="P" x="17" y="59" width="17" height="25" origin-x="-2" origin-y="24" advance="19"/>
<character text="Q" x="126" y="0" width="19" height="29" origin-x="0" origin-y="24" advance="19"/>
<character text="R" x="83" y="34" width="20" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="S" x="342" y="34" width="18" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="T" x="480" y="0" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="U" x="34" y="59" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="V" x="0" y="34" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="W" x="395" y="0" width="22" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="X" x="21" y="34" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="Y" x="42" y="34" width="21" height="25" origin-x="1" origin-y="24" advance="19"/>
<character text="Z" x="179" y="34" width="19" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="[" x="96" y="0" width="9" height="31" origin-x="-6" origin-y="25" advance="19"/>
<character text="\" x="163" y="0" width="18" height="29" origin-x="0" origin-y="24" advance="19"/>
<character text="]" x="86" y="0" width="10" height="31" origin-x="-4" origin-y="25" advance="19"/>
<character text="^" x="448" y="59" width="20" height="11" origin-x="0" origin-y="25" advance="19"/>
<character text="_" x="36" y="84" width="22" height="5" origin-x="1" origin-y="-4" advance="19"/>
<character text="`" x="0" y="84" width="10" height="8" origin-x="-3" origin-y="27" advance="19"/>
<character text="a" x="246" y="59" width="17" height="20" origin-x="-1" origin-y="19" advance="19"/>
<character text="b" x="307" y="0" width="17" height="26" origin-x="-2" origin-y="25" advance="19"/>
<character text="c" x="263" y="59" width="16" height="20" origin-x="-2" origin-y="19" advance="19"/>
<character text="d" x="253" y="0" width="18" height="26" origin-x="0" origin-y="25" advance="19"/>
<character text="e" x="152" y="59" width="19" height="20" origin-x="0" origin-y="19" advance="19"/>
<character text="f" x="341" y="0" width="16" height="26" origin-x="-2" origin-y="25" advance="19"/>
<character text="g" x="200" y="0" width="18" height="27" origin-x="0" origin-y="19" advance="19"/>
<character text="h" x="357" y="0" width="16" height="26" origin-x="-2" origin-y="25" advance="19"/>
<character text="i" x="271" y="0" width="18" height="26" origin-x="-1" origin-y="25" advance="19"/>
<character text="j" x="5" y="0" width="13" height="33" origin-x="-1" origin-y="25" advance="19"/>
<character text="k" x="289" y="0" width="18" height="26" origin-x="-2" origin-y="25" advance="19"/>
<character text="l" x="324" y="0" width="17" height="26" origin-x="-1" origin-y="25" advance="19"/>
<character text="m" x="171" y="59" width="19" height="20" origin-x="0" origin-y="19" advance="19"/>
<character text="n" x="279" y="59" width="16" height="20" origin-x="-2" origin-y="19" advance="19"/>
<character text="o" x="228" y="59" width="18" height="20" origin-x="-1" origin-y="19" advance="19"/>
<character text="p" x="218" y="0" width="18" height="27" origin-x="-1" origin-y="19" advance="19"/>
<character text="q" x="236" y="0" width="17" height="27" origin-x="-1" origin-y="19" advance="19"/>
<character text="r" x="295" y="59" width="16" height="20" origin-x="-4" origin-y="19" advance="19"/>
<character text="s" x="311" y="59" width="16" height="20" origin-x="-2" origin-y="19" advance="19"/>
<character text="t" x="51" y="59" width="17" height="25" origin-x="-1" origin-y="24" advance="19"/>
<character text="u" x="327" y="59" width="16" height="20" origin-x="-2" origin-y="19" advance="19"/>
<character text="v" x="190" y="59" width="19" height="20" origin-x="0" origin-y="19" advance="19"/>
<character text="w" x="130" y="59" width="22" height="20" origin-x="1" origin-y="19" advance="19"/>
<character text="x" x="209" y="59" width="19" height="20" origin-x="0" origin-y="19" advance="19"/>
<character text="y" x="181" y="0" width="19" height="27" origin-x="0" origin-y="19" advance="19"/>
<character text="z" x="343" y="59" width="16" height="20" origin-x="-2" origin-y="19" advance="19"/>
<character text="{" x="18" y="0" width="15" height="32" origin-x="-2" origin-y="25" advance="19"/>
<character text="|" x="0" y="0" width="5" height="34" origin-x="-7" origin-y="25" advance="19"/>
<character text="}" x="33" y="0" width="15" height="32" origin-x="-2" origin-y="25" advance="19"/>
<character text="~" x="10" y="84" width="19" height="7" origin-x="0" origin-y="14" advance="19"/>
</font>