STLL  0.0
Simple Text Layouting Library
Tutorial

To layout text you basically have two options:

  • use the paragraph layouter
  • use the XHTML layouter

Using the Paragraph Layouter and the SDL output

The paragraph layouter is a basic layouter for one or more paragraphs of text. Its input is a unicode string, some settings and a set of properties for each and every character in the string to format.

That way it is possible to change a single character to underlined, or red or make is using a different font

To use the paragraph layouter you need to use code like the following:

// includes for the layouter and the SDL output functions, if you want to
// output the text using a different graphics library, you need a different
// include here
#include <stll/layouter.h>
// all STLL symbols are in the namespace STLL
using namespace STLL;
int main()
{
// create a font cache to get fonts. the font cache will take care of
// opening font files and providing it to the application. you need to
// keep the object alive until you are done with the layouting and the output
// usually you will have one font cache per application
// setup the attributes for the text you want to output
// we use a font from the fontcache and the color white
// we also use the English language
// keep in mind that the font size is given in units of 1/64th, so
// multiply by 64
attr.font = fc.getFont(FontResource_c("tests/FreeSans.ttf"), 20*64);
attr.c = Color_c(255, 255, 255);
attr.lang = "en";
// setup the attribute index. This index assigns attributes to
// all the characters in your text, the way we create it here
// will setup the index in such a way, that _all_ characters will
// have the given attribute
AttributeIndex_c ai(attr);
// just to show how: make the 6th glyph (the W) red
attr.c = Color_c(255, 0, 0);
ai.set(6, attr);
// layouting properties, the default is good enough for us right now
// now layout the text "Hello World" with the given attributes, put
// it into a rectangle of width 200 pixel and use our layouting properties
// the text needs to be utf-32 encoded for this function
auto layout = layoutParagraph(U"Hello World", ai, RectangleShape_c(200*64), prop);
// that is it, now you can output the layout, using one of the Output
// driver functions, e.g. using the SDL driver you can output the text
// at position 20, 20 on the given surface (also in units of 1/64th)
SDL_Surface *screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
// create the output object, with glyph cache and all local storage, keep this
// object around for the lifetime of your program
showSDL<> show;
show.showLayout(layout, 20*64, 20*64, screen);
SDL_Flip(screen);
sleep(10);
// now you can get rid of the cache, if you want
}

Using the XHTML Layouter and the OpenGL output

The XHTML layouter takes a subsection of XHTML as a string and parses it and uses the paragraph layouter to layout the text. Here you specify the format using html and css-like formatting information.

The XHTML Layouter comes with 2 entry points. One takes a string, parses that string into a DOM-tree and works with that tree.

The other choice is to directly input a DOM-tree on your own.

For the XHTML layouter the flow is slightly different:

// you have to define the xml library you want to use for the xml
// parser. STLL needs to be compiled with this library
#define USE_PUGI_XML
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// include your OpenGL stuff _before_ this header
using namespace STLL;
int main()
{
// setup a stylesheet, this will automatically creata a font cache
// for you. typically you will have one or only a few stylesheets in
// your application, if you have more than one you should share the
// font cache between them
TextStyleSheet_c styleSheet;
// add the fonts that are required
// we create one font family named "sans" with 2 members, a normal and a bold one
styleSheet.addFont("sans", FontResource_c("tests/FreeSans.ttf"));
styleSheet.addFont("sans", FontResource_c("tests/FreeSansBold.ttf"), "normal", "normal", "bold");
// add the CSS rules you need, this should be familiar to everybody knowing CSS
styleSheet.addRule("body", "color", "#ffffff");
styleSheet.addRule("body", "font-size", "20px");
styleSheet.addRule("body", "text-align", "justify");
styleSheet.addRule("body", "padding", "10px");
styleSheet.addRule("h1", "font-weight", "bold");
styleSheet.addRule("h1", "font-size", "60px");
styleSheet.addRule("h1", "text-align", "center");
styleSheet.addRule("h1", "background-color", "#FF8080");
// The XHTML code we want to format, it needs to be utf-8 encoded
std::string text = u8"<html><body><h1>Title</h1><p>Some text</p></body></html>";
// layout the XHTML code, in a 200 pixel wide rectangle
auto layout = layoutXHTMLPugi(text, styleSheet, RectangleShape_c(200*64));
// output the layout using OpenGL... this is a bit more complicated than SDL
// the example uses glfw for the context creation and glew for extensions
// initialite glfw, setup window hints
if (!glfwInit()) return 1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// this hint is important for sRGB correct output
glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
// create the context and activate it
GLFWwindow* screen = glfwCreateWindow(800, 600, "OpenGL example", NULL, NULL);
if(!screen) return 1;
glfwMakeContextCurrent(screen);
// glew needs to be initialized _after_ the context has been created, also
// this strange experimental flag has to be active... otherwise it will not
// work, at least for the current version
glewExperimental = true;
if (glewInit() != GLEW_OK) return 1;
// create out output object using OpenGL 3 features, use the default values for
// the texture cache... which is too big here, but should be good for normal usage
showOpenGL<3> openGL;
// this sets up the projection matrix, or the shader equivalents for them, you
// need to call it only once for OpenGL3, except when you change the viewport
// for OpenGL 1 you need to call it whenever you start outputting as it sets up
// modelview and projection matricies
openGL.setupMatrixes(800, 600);
// this is the drawing cache, we don't need it for this example, as we only draw once
// but to show how it is supposed to work...
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// don't forget to actually enable the sRGB framebuffer...
glEnable(GL_FRAMEBUFFER_SRGB);
// this is the actual draw call for the layout, if you don't want a cache, simply
// give a nullptr to the function instead of &dc
// after this call the cache is "bound" to the layout. You may only use it with this
// layout, everything else will not work
openGL.showLayout(layout, 20*64, 20*64, SUBP_RGB, nullptr, &dc);
glfwSwapBuffers(screen);
// wait until user closed the window
while (!glfwWindowShouldClose(screen)) glfwPollEvents();
glfwDestroyWindow(screen);
glfwTerminate();
return 0;
}

Using hyphenation dictionaries

The following example shows how to use hyphenation. There are only 2 things to do:

  • register the dictionaries required for your language
  • provide a language information, either as an attribute in the XHTML code, or to the member variable lang in the CodepointAttributes_c of you text
#define USE_PUGI_XML
#include <stll/hyphenationdictionaries/hyph_en_US.h>
#include <stll/hyphenationdictionaries/hyph_de_DE_gz.h>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
using namespace STLL;
int main()
{
// all the stuff you already know to setup the style sheet
TextStyleSheet_c styleSheet;
styleSheet.addFont("sans", FontResource_c("tests/FreeSans.ttf"));
styleSheet.addFont("sans", FontResource_c("tests/FreeSansBold.ttf"), "normal", "normal", "bold");
styleSheet.addRule("body", "color", "#ffffff");
styleSheet.addRule("body", "font-size", "20px");
styleSheet.addRule("body", "text-align", "justify");
styleSheet.addRule("body", "padding", "10px");
styleSheet.addRule("h1", "font-weight", "bold");
styleSheet.addRule("h1", "font-size", "60px");
styleSheet.addRule("h1", "text-align", "center");
styleSheet.addRule("h1", "background-color", "#FF8080");
// The following lines demonstrate 2 possible ways to register hyphenation
// dictionaries.
// both use the provided hyphenation dictionaries that come with STLL
// to use them you need to include the headers containing the dictionaries.
// the included dictionaries are provided in 2 different forms: as a string
// that contains the whole dictionary
// and as n byte array containing the dictionary compressed using zlib.
// if you already have to ling against zlib anyway that might be useful
// you only pay for the dictionaries you are actually using as the
// dictionaries are only within the header.
// It is your responsibility to only include the headers once or your program
// will be unnecessarily big.So a good practice for your program would be to
// have a module that sets up all the required dictionaries.
// us this line is the probably best way to load the uncompressed dictionary
// you can provide multiple languages to register the dictionary for, but make
// sure to not load the same dictionary twice as that would waste your memory
// also you can use country code.
STLL::addHyphenDictionary({"en", "en-us"}, std::istringstream((const char*)hyph_en_US));
// for zlib there is no one right way. The following lines demonstrate using boosts
// iostream library, but there are surely others out there
{
boost::iostreams::filtering_istream in;
in.push(boost::iostreams::gzip_decompressor());
boost::iostreams::array_source src((const char*)hyph_de_DE_gz, hyph_de_DE_gz_len);
in.push(src);
}
// The XHTML code we want to format, you will need to supply language
// tags or otherwise nothing will be hyphenated. Texts without language
// tag are never hyphenated
// when the code is looking for a dictionary to use, it will first look for
// the most specific, but then it will start stripping away elements from the specification
// until is finds a dictionary, so it will first look for de-de, then for a dictionary
// for the language de
std::string text = u8"<html><body>"
"<p lang='en'>Text in English language</p>"
"<p lang='de-de'>Text in deutscher Sprache</p></body></html>";
// use a 100 pixel wide rectangle so that we actually see hyphenation
auto layout = layoutXHTMLPugi(text, styleSheet, RectangleShape_c(100*64));
// output using SDL as already known
SDL_Surface *screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
showSDL<> show;
show.showLayout(layout, 20*64, 0*64, screen, SUBP_RGB);
SDL_Flip(screen);
sleep(10);
return 0;
}