/// <summary>Loads the character array (<see cref="Css.TextRenderingProperty.Characters"/>) from the given text string.</summary>
        internal void LoadCharacters(string text, RenderableData renderable)
        {
            if (Characters != null && Visible)
            {
                // Make sure each char goes offscreen:
                NowOffScreen();

                // Clear visible:
                Visible = false;
            }

            // Get the computed style:
            ComputedStyle computedStyle = renderable.computedStyle;

            // No longer dirty:
            Dirty = false;

            char[] characters = text.ToCharArray();

            // Get text transform:
            int textTransform = computedStyle.ResolveInt(Css.Properties.TextTransform.GlobalProperty);

            if (textTransform != TextTransformMode.None && characters.Length > 0)
            {
                switch (textTransform)
                {
                case TextTransformMode.Capitalize:

                    // Uppercase the first character:
                    characters[0] = char.ToUpper(characters[0]);

                    break;

                case TextTransformMode.Lowercase:

                    // Lowercase the whole string:
                    for (int i = 0; i < characters.Length; i++)
                    {
                        characters[i] = char.ToLower(characters[i]);
                    }

                    break;

                case TextTransformMode.Uppercase:

                    // Uppercase the whole string:
                    for (int i = 0; i < characters.Length; i++)
                    {
                        characters[i] = char.ToUpper(characters[i]);
                    }

                    break;
                }
            }

            // Get the whitespace mode:
            int whiteSpaceMode = computedStyle.WhiteSpaceX;

            // Purge characters that we don't want:
            if ((whiteSpaceMode & WhiteSpaceMode.NormalOrNoWrap) != 0)
            {
                // Dump breaks and repeated whitespace.
                bool dumpWhiteSpace = false;

                for (int i = 0; i < characters.Length; i++)
                {
                    char character = characters[i];

                    if (character == '\t')
                    {
                        // Dump:
                        characters[i] = '\0';
                    }
                    else if (character == '\r' || character == '\n')
                    {
                        // Dump:
                        characters[i] = '\0';

                        // Dump whitespaces immediately after these:
                        dumpWhiteSpace = true;
                        continue;
                    }
                    else if (character == '\u00A0')
                    {
                        // NBSP:
                        characters[i] = ' ';
                    }
                    else if (character == ' ')
                    {
                        if (dumpWhiteSpace)
                        {
                            // Dump:
                            characters[i] = '\0';
                        }
                        else
                        {
                            // Dump any consecutive whitespace (except for &nbsp;):
                            dumpWhiteSpace = true;
                        }

                        continue;
                    }

                    dumpWhiteSpace = false;
                }
            }
            else if (whiteSpaceMode == WhiteSpaceMode.PreLine)
            {
                // Dump repeated whitespace (and \r) only.
                bool dumpWhiteSpace = false;

                for (int i = 0; i < characters.Length; i++)
                {
                    char character = characters[i];

                    if (character == ' ')
                    {
                        if (dumpWhiteSpace)
                        {
                            // Dump:
                            characters[i] = '\0';
                        }
                        else
                        {
                            dumpWhiteSpace = true;
                        }

                        continue;
                    }
                    else if (character == '\u00A0')
                    {
                        // NBSP:
                        characters[i] = ' ';
                    }
                    else if (character == '\r')
                    {
                        characters[i]  = '\0';
                        dumpWhiteSpace = true;
                        continue;
                    }
                    else if (character == '\n')
                    {
                        dumpWhiteSpace = true;
                        continue;
                    }

                    dumpWhiteSpace = false;
                }
            }
            else
            {
                // \r and nbsp only.

                for (int i = 0; i < characters.Length; i++)
                {
                    char character = characters[i];

                    if (character == '\r')
                    {
                        characters[i] = '\0';
                    }
                    else if (character == '\u00A0')
                    {
                        // NBSP:
                        characters[i] = ' ';
                    }
                }
            }

            // Create characters if they're needed:
            if (Characters == null || Characters.Length != characters.Length)
            {
                Characters = new Glyph[characters.Length];
                Kerning    = null;
            }

            // Considered all empty until shown otherwise.
            AllEmpty = true;

            // Next, for each character, find its dynamic character.
            // At the same time we want to find out what dimensions this word has so it can be located correctly.
            Glyph previous = null;

            for (int i = 0; i < characters.Length; i++)
            {
                char rawChar = characters[i];

                if (rawChar == '\0')
                {
                    // It got dumped.
                    continue;
                }

                Glyph character = null;

                // Is it a unicode high/low surrogate pair?
                if (char.IsHighSurrogate(rawChar) && i != characters.Length - 1)
                {
                    // Low surrogate follows:
                    char lowChar = characters[i + 1];

                    // Get the full charcode:
                    int charcode = char.ConvertToUtf32(rawChar, lowChar);

                    // Grab the surrogate pair char:
                    character = FontToDraw.GetGlyphOrEmoji(charcode);

                    // Make sure there is no char in the low surrogate spot:
                    Characters[i + 1] = null;
                    // Update this character:
                    Characters[i] = character;
                    // Skip the low surrogate:
                    i++;
                }
                else if (rawChar == '\n')
                {
                    // Special case for newlines (They don't show up in host fonts).
                    if (NEWLINE_GLYPH == null)
                    {
                        NEWLINE_GLYPH             = new Glyph();
                        NEWLINE_GLYPH.RawCharcode = (int)'\n';
                    }

                    Characters[i] = NEWLINE_GLYPH;
                }
                else
                {
                    character     = FontToDraw.GetGlyphOrEmoji((int)rawChar);
                    Characters[i] = character;
                }


                if (character == null)
                {
                    continue;
                }

                if (previous != null)
                {
                    // Look for a kern pair:
                    if (character.Kerning != null)
                    {
                        float offset;

                        if (character.Kerning.TryGetValue(previous, out offset))
                        {
                            // Got a kern!
                            if (Kerning == null)
                            {
                                Kerning = new float[characters.Length];
                            }

                            Kerning[i] = offset;
                        }
                    }
                }

                previous = character;
                AllEmpty = false;
            }
        }
        /// <summary>Runs before reflow.</summary>
        public override void UpdateCss(Renderman renderer)
        {
            // Clear the blocks:
            FirstBox = null;
            LastBox  = null;

            // Get the text renderer (or create it):
            Css.TextRenderingProperty text = RequireTextProperty();

            // Get computed style:
            ComputedStyle cs = computedStyle;

            // Get the first box as it contains the fontface/ size:
            LayoutBox box = cs.FirstBox;

            // Colour too:
            Color fontColour = cs.Resolve(Css.Properties.ColorProperty.GlobalProperty).GetColour(this, Css.Properties.ColorProperty.GlobalProperty);

            // Colour:
            text.BaseColour = fontColour;

            // Font size update:
            float fontSize = box.FontSize;

            text.FontSize = fontSize;

            // Spacing:
            float wordSpacing   = cs.ResolveDecimal(Css.Properties.WordSpacing.GlobalProperty);
            float letterSpacing = cs.ResolveDecimal(Css.Properties.LetterSpacing.GlobalProperty);

            // If word spacing is not 'normal', remove 1em from it (Note that letter spacing is always additive):
            if (wordSpacing == -1f)
            {
                wordSpacing = 0f;
            }
            else
            {
                wordSpacing -= fontSize;
            }

            text.WordSpacing   = wordSpacing;
            text.LetterSpacing = letterSpacing;

            // Decoration:
            int decoration = cs.ResolveInt(Css.Properties.TextDecorationLine.GlobalProperty);

            if (decoration == 0)
            {
                // Remove a line if we have one:
                text.TextLine = null;
            }
            else
            {
                // Got a line!
                if (text.TextLine == null)
                {
                    text.TextLine = new TextDecorationInfo(decoration);
                }

                // Get the colour:
                Css.Value lineColour = cs.Resolve(Css.Properties.TextDecorationColor.GlobalProperty);

                if (lineColour == null || lineColour.IsType(typeof(Css.Keywords.CurrentColor)))
                {
                    // No override:
                    text.TextLine.ColourOverride = false;
                }
                else
                {
                    // Set the colour:
                    text.TextLine.SetColour(lineColour.GetColour(this, Css.Properties.TextDecorationColor.GlobalProperty));
                }
            }

            // Get the font face:
            text.FontToDraw = box.FontFace;

            // Overflow-wrap mode (only active for 'break-word' which is just '1'):
            text.OverflowWrapActive = (cs.ResolveInt(Css.Properties.OverflowWrap.GlobalProperty) == 1);

            // Check if the text is 'dirty'.
            // If it is, that means we'll need to rebuild the TextRenderingProperty's Glyph array.

            if (text.Dirty)
            {
                // Setup text now:
                // (Resets text.Characters based on all the text related CSS properties like variant etc).
                text.LoadCharacters((Node as RenderableTextNode).characterData_, this);
            }

            if (text.Characters == null || text.AllEmpty)
            {
                text.FontSize = 0f;
                return;
            }
        }