BigFont.cs

6 minutes read

BigFont has been updated! Read about the changes here.

To support an NLog wrapper I’ve been working on, I needed a way to print text to the console and to a log file in a more conspicuous way. To achieve this goal I’ve created BigFont.cs for C#.

Note that this post is written for those learning c#. If your skill level is above that of a beginner you’ll probably want to skip directly to the code on GitHub here.

The class is extremely simple, consisting of about 6 lines of actual code:

/// <summary>
/// Generates a large string of characters corresponding to the input phrase.
/// </summary>
/// <remarks>
/// Any character that doesn't exist in the alphabet will be replaced with a question mark.
/// </remarks>
/// <param name="phrase">The phrase to generate.</param>
/// <param name="size">The size of the font to use.</param>
/// <returns>A string array containing the generated output.</returns>
public static string[] Generate(string phrase, FontSize size = FontSize.Large)
{
    string[] r = new string[(int)size];

    foreach (char c in phrase.ToUpper())
        for (int i = 0; i < (int)size; i++)
            r[i] += Alphabet[new Tuple<char, FontSize>((Alphabet.ContainsKey(new Tuple<char, FontSize>(c, size)) ? c : '?'), size)][i];

    return r;
}

The “Alphabet”

The “Alphabet” for the class is stored in a Dictionary containing a Tuple and a string array:

/// <summary>
/// The alphabet.
/// </summary>
public static Dictionary<Tuple<char, FontSize>, string[]> Alphabet { get; private set; }

This allows for the storage of individual characters for each of the FontSize enumerations, with the “character” being a string array containing an ASCII representation of the character. For example, here is the “A” character for FontSize.Large:

Alphabet.Add(new Tuple<char, FontSize>('A', FontSize.Large), new string[]
{
    "  ▄████████  ",
    "  ███    ███ ",
    "  ███    ███ ",
    "  ███    ███ ",
    "▀███████████ ",
    "  ███    ███ ",
    "  ███    ███ ",
    "  ███    █▀  "
});

A Tuple is used for the Dictionary key so that we can support multiple font sizes. If only one size (and thus one set of letters) was needed, the key would be a single character.

The Generate() Method

The Generate() method is the sole method in the class. It accepts a string containing the text to generate and a FontSize enumeration which can be FontSize.Large, FontSize.Medium, or FontSize.Small. Large is the default and generates the input text using characters 8 lines in height, while medium produces 6 line characters and small produces 4. The integer values for the enumeration correspond to the height:

/// <summary>
/// Font size enumeration; the integer value indicates the height in lines
/// </summary>
public enum FontSize
{
    Large = 8,
    Medium = 6,
    Small = 4
}

The first statement creates a string array r with a length corresponding to the height of the alphabet.

string[] r = new string[(int)size];

The size enumeration is cast to an integer to retrieve the size of the font specified in the enumeration. Next, a foreach loop is used to iterate over each character in the phrase.

foreach (char c in phrase.ToUpper())

The ToUpper() extension method is used to return the input phrase in all uppercase letters. This allows us to have one “a” in the alphabet. If desired, this could be removed and lowercase characters could be added to the alphabet.

Next, a for loop is used to iterate over each line in the r array. This allows the method to build the output string array from the top down for each character.

for (int i = 0; i < (int)size; i++)

Finally, within the innermost loop, we have the statement:

r[i] += Alphabet[new Tuple<char, FontSize>((Alphabet.ContainsKey(new Tuple<char, FontSize>(c, size)) ? c : '?'), size)][i];

There’s a lot going on in this line so we’ll break it down into parts. First, we have to handle the possibility that the character we are looking at isn’t in the alphabet. If this happens we want to replace that character with a question mark. To do this we use a conditional operator.

The conditional operator uses the following format:

(if-then-else condition ? expression if true : expression if false)

The condition is evaluated in the same manner as a typical if condition. If the condition evaluates to true, the expression contained in the true position is evaluated and returned. If it evaluates to false, the false expression is evaluated and returned. Therefore:

(Alphabet.ContainsKey(new Tuple<char, FontSize>(c, size)) ? c : '?')

Our if-then-else condition is Alphabet.ContainsKey(new Tuple(char, FontSize), the true expression is c and the false expression is ?. We create a new Tuple here because the alphabet contains Tuples instead of characters, and we need to make sure we are searching for something that would exist in the dictionary.

To simplify the rest of the explanation let’s pretend that we don’t care if the character we want to print exist in the alphabet. If we remove the conditional operator that handles this check, our line is rewritten as such:

r[i] += Alphabet[new Tuple<char, FontSize>(c, size)][i];

This is now quite a bit easier to read. We are using the increment operator to append a new string to line i of our string array r. We are retrieving the string from our alphabet using the following statement:

Alphabet[new Tuple<char, FontSize>(c, size)][i]

First, this statement retrieves the string array for the character from the alphabet with the code Alphabet[new Tuple<char, FontSize>(c, size)] Again, we have to use Tuples to retrieve things from our alphabet because the key for the Dictionary is a Tuple, so we create a tuple with the font size and character we want to retrieve and use that to access the correct item in the alphabet. This code returns a string array containing the entire character, however, so we have to select which string we want with the indexing operator [i].

The return array r is updated with the retrieved line and the for loop moves on to the next line of the current character and eventually the foreach loop next character in the input phrase.

How it Works, Step by Step

Let’s imagine we invoke the Generate() method with the following code:

string[] bigfont = BigFont.Generate("AB", FontSize.Small);

The method will build the string “AB” using the alphabet entries corresponding to FontSize.Small. First, the string array “r” will be created with the size corresponding to FontSize.Small, which is 4. We’ll end up with an empty array like so:

[0]:
[1]:
[2]:
[3]:

Next, the code will iterate over both characters in the input string, starting with A. It will retrieve the string array from the alphabet corresponding to “A” and FontSize.Small, which is:

▄█████  
██   ██ 
███████ 
██   ██ 

And begin appending the retrieved lines to the output array “r”, like so:

// Step 1:

[0]: ▄█████  
[1]:
[2]:
[3]:

// Step 2:

[0]: ▄█████  
[1]: ██   ██ 
[2]:
[3]:

// Step 3:

[0]: ▄█████  
[1]: ██   ██ 
[2]: ███████ 
[3]:

// Step 4:

[0]: ▄█████  
[1]: ██   ██ 
[2]: ███████ 
[3]: ██   ██ 

Having completed the inner for loop, the foreach loop will advance to the next character, and the process will be repeated for “B”:

// Alphabet for FontSize.Small "B":
▀█████▄ 
 ██▄▄▄▀ 
 ██   █ 
▄█████▀ 

// Step 1:

[0]: ▄█████  ▀█████▄ 
[1]: ██   ██ 
[2]: ███████ 
[3]: ██   ██ 

// Step 2:

[0]: ▄█████  ▀█████▄ 
[1]: ██   ██  ██▄▄▄▀ 
[2]: ███████
[3]: ██   ██ 

// Step 3:

[0]: ▄█████  ▀█████▄ 
[1]: ██   ██  ██▄▄▄▀ 
[2]: ███████  ██   █ 
[3]: ██   ██ 

// Step 4:

[0]: ▄█████  ▀█████▄ 
[1]: ██   ██  ██▄▄▄▀ 
[2]: ███████  ██   █ 
[3]: ██   ██ ▄█████▀ 

The inner for loop will again complete and the outer foreach loop, having reached the end of the input phrase string, will also complete, which will cause the method to return “r” to the calling code.

Conclusion

The full BigFont.cs code, along with some usage examples and a “Hello World” for all three font sizes can be found on GitHub here.

Updated:

Leave a Comment