Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm currently trying to devise a way to kern bitmap fonts in XNA 4.0. The two easiest ways I've found are to use either BMFontGen or BMFont.

I chose to work with BMFontGen, as it was made specifically for XNA, although it's extremely old and finicky. So, my quest to update it took a while, but I eventually got it to compile and work on the PC. Then, when it didn't work on the 360, I got pretty angry.

I'd just spent all that time, and for naught!

So, then I did some Google-fu and found out that the XmlNodeList and XmlNode classes are no longer options for the 360 in 4.0. Then, I came here, got great replies, and set about reading. Which, leads me to this post.

Now, before I seek any more help, I've learned that the better question is this:

Is there an easier way to do whatever it is I'm doing?

Thus, I will ask:

Is there an easier, better way to have kerned bitmap fonts in XNA?

If the answer is no, then I'm afraid I need some more help.

For reference, here's the areas of my CustomFont class that work great on the PC, but not so great on the 360. It's pretty long, so if you want to help me and end up actually reading it I'll be very grateful for your time spent.

private void Init(ContentManager cm, string strFontFilename)   
    {   
        m_content = cm;   
        m_sb = null;   
        m_sbOverride = null;   

        m_dictBitmapID2BitmapInfo = new Dictionary<int, BitmapInfo>();   
        m_dictBitmapID2Texture = new Dictionary<int, Texture2D>();   

        m_dictUnicode2GlyphInfo = new Dictionary<char, GlyphInfo>();   
        m_dictKern = new Dictionary<char, Dictionary<char, sbyte>>();   

        XmlDocument xd = new XmlDocument();   

        // load the XML file from wherever it is - filesystem or embedded   
        if (System.IO.File.Exists(strFontFilename))   
        {   
            // all files mentioned in the font descriptor file are relative to the parent directory of the font file.   
            // record the path to this directory.   
            m_strPath = System.IO.Path.GetDirectoryName(strFontFilename);   
            if (m_strPath != "")   
                m_strPath += @"/";   
            m_strFilename = strFontFilename;    // relative path + filename   
            m_fLoadFromResource = false;   

            xd.Load(strFontFilename);   
        }   
        else  
        {   
            // look in the assembly resources   
            bool fFoundResource = false;   
            string strEmbeddedPath, strEmbeddedName;   
            ConvertFilePath2EmbeddedPath(strFontFilename, out strEmbeddedPath, out strEmbeddedName);   
            System.IO.Stream ios = Assembly.GetExecutingAssembly().GetManifestResourceStream(strEmbeddedPath + strEmbeddedName);   
            if (ios != null)   
            {   
                m_strPath = strEmbeddedPath;        // path to resource   
                m_strFilename = strEmbeddedName;    // resource name   
                m_fLoadFromResource = true;   
                fFoundResource = true;   

                xd.Load(ios);   
            }   
            if (!fFoundResource)   
                throw new System.Exception(String.Format("Unable to find font named '{0}'.", strFontFilename));   
        }   

        m_strName = "";   

        LoadFontXML(xd.ChildNodes);   

        // if the font doesn't define a name, create one from the filename   
        if (m_strName == "")   
            m_strName = System.IO.Path.GetFileNameWithoutExtension(strFontFilename);   

        // add this font to the list of active fonts   
        m_dictBitmapFonts.Add(m_strName, this);   
    }   

/// <summary>   
    /// Load the font data from an XML font descriptor file   
    /// </summary>   
    /// <param name="xnl">XML node list containing the entire font descriptor file</param>   
    private void LoadFontXML(XmlNodeList xnl)   
    {   
        foreach (XmlNode xn in xnl)   
        {   
            if (xn.Name == "font")   
            {   
                m_strName = GetXMLAttribute(xn, "name");   
                m_nBase = Int32.Parse(GetXMLAttribute(xn, "base"));   
                m_nHeight = Int32.Parse(GetXMLAttribute(xn, "height"));   

                LoadFontXML_font(xn.ChildNodes);   
            }   
        }   
    }   

    /// <summary>   
    /// Load the data from the "font" node   
    /// </summary>   
    /// <param name="xnl">XML node list containing the "font" node's children</param>   
    private void LoadFontXML_font(XmlNodeList xnl)   
    {   
        foreach (XmlNode xn in xnl)   
        {   
            if (xn.Name == "bitmaps")   
                LoadFontXML_bitmaps(xn.ChildNodes);   
            if (xn.Name == "glyphs")   
                LoadFontXML_glyphs(xn.ChildNodes);   
            if (xn.Name == "kernpairs")   
                LoadFontXML_kernpairs(xn.ChildNodes);   
        }   
    }   

    /// <summary>   
    /// Load the data from the "bitmaps" node   
    /// </summary>   
    /// <param name="xnl">XML node list containing the "bitmaps" node's children</param>   
    private void LoadFontXML_bitmaps(XmlNodeList xnl)   
    {   
        foreach (XmlNode xn in xnl)   
        {   
            if (xn.Name == "bitmap")   
            {   
                string strID = GetXMLAttribute(xn, "id");   
                string strFilename = GetXMLAttribute(xn, "name");   
                string strSize = GetXMLAttribute(xn, "size");   
                string[] aSize = strSize.Split('x');   

                // if the ContentManager is being used, then we may need to strip off the .png extension   
                // to generate the correct asset name.   
                if (m_content != null && strFilename.EndsWith(@".png"))   
                    strFilename = strFilename.Remove(strFilename.Length - 4, 4);   

                BitmapInfo bminfo;   
                bminfo.strFilename = strFilename;   
                bminfo.nX = Int32.Parse(aSize[0]);   
                bminfo.nY = Int32.Parse(aSize[1]);   

                m_dictBitmapID2BitmapInfo[Int32.Parse(strID)] = bminfo;   
            }   
        }   
    }   

    /// <summary>   
    /// Load the data from the "glyphs" node   
    /// </summary>   
    /// <param name="xnl">XML node list containing the "glyphs" node's children</param>   
    private void LoadFontXML_glyphs(XmlNodeList xnl)   
    {   
        foreach (XmlNode xn in xnl)   
        {   
            if (xn.Name == "glyph")   
            {   
                string strChar = GetXMLAttribute(xn, "ch");   
                string strBitmapID = GetXMLAttribute(xn, "bm");   
                string strLoc = GetXMLAttribute(xn, "loc");   
                string strSize = GetXMLAttribute(xn, "size");   
                string strAW = GetXMLAttribute(xn, "aw");   
                string strLSB = GetXMLAttribute(xn, "lsb");   
                string strForceWhite = GetXMLAttribute(xn, "forcewhite");   

                if (strLoc == "")   
                    strLoc = GetXMLAttribute(xn, "origin"); // obsolete - use loc instead   

                string[] aLoc = strLoc.Split(',');   
                string[] aSize = strSize.Split('x');   

                GlyphInfo ginfo = new GlyphInfo();   
                ginfo.nBitmapID = UInt16.Parse(strBitmapID);   
                ginfo.pxLocX = Byte.Parse(aLoc[0]);   
                ginfo.pxLocY = Byte.Parse(aLoc[1]);   
                ginfo.pxWidth = Byte.Parse(aSize[0]);   
                ginfo.pxHeight = Byte.Parse(aSize[1]);   
                ginfo.pxAdvanceWidth = Byte.Parse(strAW);   
                ginfo.pxLeftSideBearing = SByte.Parse(strLSB);   
                ginfo.nFlags = 0;   
                ginfo.nFlags |= (strForceWhite == "true" ? GlyphFlags.ForceWhite : GlyphFlags.None);   

                m_dictUnicode2GlyphInfo[strChar[0]] = ginfo;   
            }   
        }   
    }   

    /// <summary>   
    /// Load the data from the "kernpairs" node   
    /// </summary>   
    /// <param name="xnl">XML node list containing the "kernpairs" node's children</param>   
    private void LoadFontXML_kernpairs(XmlNodeList xnl)   
    {   
        foreach (XmlNode xn in xnl)   
        {   
            if (xn.Name == "kernpair")   
            {   
                string strLeft = GetXMLAttribute(xn, "left");   
                string strRight = GetXMLAttribute(xn, "right");   
                string strAdjust = GetXMLAttribute(xn, "adjust");   

                char chLeft = strLeft[0];   
                char chRight = strRight[0];   

                // create a kern dict for the left char if needed   
                if (!m_dictKern.ContainsKey(chLeft))   
                    m_dictKern[chLeft] = new Dictionary<char, sbyte>();   

                // add the right char to the left char's kern dict   
                Dictionary<char, sbyte> kern2 = m_dictKern[chLeft];   
                kern2[chRight] = SByte.Parse(strAdjust);   
            }   
        }   
    }   

    /// <summary>   
    /// Get the XML attribute value   
    /// </summary>   
    /// <param name="n">XML node</param>   
    /// <param name="strAttr">Attribute name</param>   
    /// <returns>Attribute value, or the empty string if the attribute doesn't exist</returns>   
    private static string GetXMLAttribute(XmlNode n, string strAttr)   
    {   
        XmlAttribute attr = n.Attributes.GetNamedItem(strAttr) as XmlAttribute;   
        if (attr != null)   
            return attr.Value;   
        return "";   
    }  

So, here's my question(s), in a smaller chunk for those that want that:

  1. Am I using the easiest way to kern bitmap fonts in XNA?
  2. If I am, can someone help me convert the posted code from using XML classes to LINQ classes?

If you require more information or background, don't hesitate to ask.

Thanks in advance for your time spent in assisting me.

share|improve this question

closed as off-topic by Josh Petrie Dec 8 '13 at 0:00

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Programming questions that aren't specific to game development are off-topic here, but can be asked on Stack Overflow. A good rule of thumb is to ask yourself "would a professional game developer give me a better/different/more specific answer to this question than other programmers?"" – Josh Petrie
If this question can be reworded to fit the rules in the help center, please edit the question.

Browse other questions tagged or ask your own question.