Randomized drawing of individual glyphs

I have been thinking about this question for weeks now, and finally I think I came really close to a result you may also like. I have even tried to use Processing to solve this problem, which resulted in a nice animation as a byproduct, but it didn't lead me closer to the solution. But back to the point…

Unfortunately the solution I'm posting, which is my best and only shot, does not support drawing the distorted glyphs as text but as drawings. Also there is some work to be done outside the context of LaTeX, but most of it is done in LaTeX (LuaTeX + TikZ).

The picture above shows an undistorted glyph (character “a” on the left in the line at the top), a distorted glyph (character “a” on the right in the line at the top), a word consisting of distorted glyphs (middle line), and a special character (Omega), all these can be found in the code at the end of the answer.

Now I will describe the process I have followed to achieve these distortions. I mentioned that there is some work to be done outside of LaTeX, that is to convert a font file into SVG using FontForge. I found the solution how to do this in an answer to the question: Can we extract the points making the character from the font file?

Copy the following into a file named font2svg.pe into your “project” folder.

#!/usr/bin/env fontforge
Open($1)
Generate($1:t:r + ".svg")

And make a SVG file from the font you want to use (I chose cmr10) with the following command.

fontforge font2svg.pe /usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb

Note that the location of the font on the filesystem may vary based on your LaTeX installation and operating system you use, but this will generate an SVG file into your project folder. All is left to process the generated SVG file which contains the data (name, unicode code, width, and outline) of the glyphs, which I will describe below.

The function function read_font_data(file) takes a file name as an argument (the generated SVG file), and extracts the data of the glyphs into an associative array which can be addressed with the unicode code and contains the width and outline data of the specific character. Note that not all glyphs have width or outline data, some basic error checking is done but the code is not foolproof.

The function random_in_interval(lower_boundary, upper_boundary) takes two float arguments, and will return a random float between them. The more the boundaries converge to 1 the smaller the randomization will be. This will be used when the time comes to randomize the outline of a glyph.

The function scale_and_randomize(glyph, scale_factor, lower_boundary, upper_boundary) will take a glyph, a scale factor, a lower and upper boundary, the latter two will be used for the randomization. Scaling is needed because the default measurement unit of TikZ is centimeters (I think) and the outline data of a glyph may contain large values, which TikZ interprets as centimeters. Note that the scale factor may vary depending the font you use, and size you want.

The functions print_glyph(glyph, scale_factor, lower_boundary, upper_boundary) and return_glyph (the latter takes the same arguments) only differ in that print_glyph will pass the TikZ drawing command (using svg.path library) used to print the glyph to LaTeX, while return_glyph only returns the drawing command as a string which can be further used in Lua before passing it to LaTeX.

The remaining functions only use the previously described print_glyph and return_glyph functions to print the picture above.

That's it. I hope this would fit your needs.

\documentclass[10pt, a4paper]{article}

\usepackage[T1]{fontenc}

\usepackage{luacode}

\usepackage{tikz}
\usetikzlibrary{svg.path, positioning}

\pagestyle{empty}

\tikzset{%
    glyph node/.style={%
        inner sep=0pt,%
        outer sep=0pt%
    },%
    glyph outline/.style={%
        line width=0pt%
    }%
}

\begin{luacode*}
    function read_font_data(file)
        local glyphs = {}
        local fd = io.open(file, "r")
        local content = fd:read("*all")
        fd.close()

        for glyph in string.gmatch(content, "<glyph[^/>]*") do
            local glyph_tag = string.gsub(glyph, "\n", " ")
            local unicode = string.match(glyph_tag, "unicode=\"[^\"]*")
            local outline = string.match(glyph_tag, "d=\"[^\"]*")
            local width = string.match(glyph_tag, "horiz%-adv%-x=\"[^\"]*")

            if unicode ~= nil and #unicode >= 10 then
                unicode = string.sub(unicode, 10, #unicode)
            end

            if outline ~= nil and #outline > 4 then
                outline = string.sub(outline, 4, #outline)
            end

            if width ~= nil and #width >= 14 then
                width = string.sub(width, 14, #width)
            end

            if unicode ~= nil then
                glyphs[unicode] = {width, outline}
            end
        end

        return glyphs
    end

    -- returns a random float number between the specified boundaries (floats)
    function random_in_interval(lower_boundary, upper_boundary)
        return ((math.random() * (upper_boundary - lower_boundary)) + lower_boundary)
    end

    -- note: scaling is applied before randomization
    function scale_and_randomize(glyph, scale_factor, lower_boundary, upper_boundary)
        local width = glyph[1]
        local outline = glyph[2]

        local previous_was_number = false
        local processed_outline = ""
        local number = ""

        if width ~= nil then
            width = width * scale_factor
        end

        if outline ~= nil then
            for i = 1, #outline, 1 do
                local char = string.sub(outline, i, i)

                if previous_was_number then
                    if string.match(char, '%d') ~= nil or
                        char == "." then
                        number = number .. char
                    else
                        -- scale and randomize
                        number = number * scale_factor
                        number = number * random_in_interval(lower_boundary, upper_boundary)
                        number = string.format("%.3f", number)
                        processed_outline = processed_outline .. number .. char
                        number = ""
                        previous_was_number = false
                    end
                else
                    if string.match(char, '%d') ~= nil or
                        char == "-" then

                        number = number .. char
                        previous_was_number = true
                    else
                        processed_outline = processed_outline .. char
                        previous_was_number = false
                    end
                end
            end
        end

        return {width, processed_outline}
    end

    function print_glyph(glyph, scale_factor, lower_boundary, upper_boundary)
        local randomized_glyph = scale_and_randomize(glyph, scale_factor, lower_boundary, upper_boundary)
        local width = randomized_glyph[1]
        local outline = randomized_glyph[2]

        if outline ~= nil then
            tex.sprint("\\filldraw[glyph outline] svg \"" .. outline .. "\";")
        end
    end

    function return_glyph(glyph, scale_factor, lower_boundary, upper_boundary)
        local randomized_glyph = scale_and_randomize(glyph, scale_factor, lower_boundary, upper_boundary)
        local width = randomized_glyph[1]
        local outline = randomized_glyph[2]

        if outline ~= nil then
            return "\\filldraw[glyph outline] svg \"" .. outline .. "\";"
        else
            return ""
        end
    end

    function draw_sample_glyphs(glyphs)
        tex.sprint("\\begin{tikzpicture}")
        tex.sprint("\\node[glyph node, matrix, anchor=south west] (a1) {" ..
            return_glyph(glyphs["a"], 0.05, 1, 1) ..
            "\\\\};")
        tex.sprint("\\node[glyph node, matrix, anchor=south west, right=7.5mm of a1] (a2) {" ..
            return_glyph(glyphs["a"], 0.05, 0.8, 1.2) ..
            "\\\\};")
        tex.sprint("\\end{tikzpicture}")
    end

    function draw_sample_text(glyphs)
        local horizontal_space = "0.5mm"
        local vertical_space = "1.25mm"
        local scale = 0.05
        local lower_boundary = 0.9
        local upper_boundary = 1.1

        tex.sprint("\\begin{tikzpicture}")
        tex.sprint("\\node[glyph node, matrix] (m1) {" ..
            return_glyph(glyphs["m"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of m1] (a1) {" ..
            return_glyph(glyphs["a"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of a1] (t1) {" .. "\\raisebox{" .. vertical_space .. "}{" ..
            return_glyph(glyphs["t"], scale, lower_boundary, upper_boundary) ..
            "}" .. "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of t1] (h1) {" .. "\\raisebox{" .. vertical_space .. "}{" ..
            return_glyph(glyphs["h"], scale, lower_boundary, upper_boundary) ..
            "}" .. "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of h1] (e1) {" ..
            return_glyph(glyphs["e"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of e1] (m2) {" ..
            return_glyph(glyphs["m"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of m2] (a2) {" ..
            return_glyph(glyphs["a"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of a2] (t2) {" .. "\\raisebox{" .. vertical_space .. "}{" ..
            return_glyph(glyphs["t"], scale, lower_boundary, upper_boundary) ..
            "}" .. "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of t2] (i1) {" .. "\\raisebox{" .. vertical_space .. "}{" ..
            return_glyph(glyphs["i"], scale, lower_boundary, upper_boundary) ..
            "}" .. "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of i1] (c1) {" ..
            return_glyph(glyphs["c"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")

        tex.sprint("\\node[glyph node, matrix, right=" .. horizontal_space ..
            " of c1] (s1) {" ..
            return_glyph(glyphs["s"], scale, lower_boundary, upper_boundary) ..
            "\\\\};")
        tex.sprint("\\end{tikzpicture}")
    end

    function draw_sample_glyph(glyphs)
        tex.sprint("\\begin{tikzpicture}")
        print_glyph(glyphs["&#x3a9;"], 0.05, 0.95, 1.05)
        tex.sprint("\\end{tikzpicture}")
    end

    function main()
        local cmr10_glyphs = {}

        math.randomseed(os.time())

        cmr10_glyphs = read_font_data("cmr10.svg")

        tex.sprint("\\noindent")
        draw_sample_glyphs(cmr10_glyphs)
        tex.sprint("\\\\[2cm]")
        draw_sample_text(cmr10_glyphs)
        tex.sprint("\\\\[2cm]")
        draw_sample_glyph(cmr10_glyphs)
    end
\end{luacode*}

\begin{document}
\luadirect{main()}
\end{document}

You can randomize letters in ConTeXt easily with MetaFun

\startMPpage
picture p; p := outlinetext("mathematics") ;
for r = 0 upto 10 :
    for i within p :
        draw i randomized (r*.1pt) shifted (0,-r*10pt) ;
    endfor ;
endfor ;
\stopMPpage

You should also try out randomizedcontrols in place of randomized for a different effect.

enter image description here