gFont.cpp

Engine/source/gfx/gFont.cpp

More...

Classes:

class

Used for repacking in GFont::importStrip.

Public Functions

DefineEngineFunction(dumpFontCacheStatus , void , () , "Dumps <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> full description of all cached fonts, along with " "info on the codepoints each <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">contains.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(duplicateCachedFont , void , (const char *oldFontName, S32 oldFontSize, const char *newFontName) , "Copy the specified old font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> name. The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> copy will not have <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "platform font backing it, and so will never have characters added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> it. " "But this is useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> making copies of fonts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> add postprocessing effects " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">exportCachedFont.\n</a>" " @param oldFontName The name of the font face <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">copy.\n</a>" " @param oldFontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">copy.\n</a>" " @param newFontName The name of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(exportCachedFont , void , (const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning) , "Export specified font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified filename as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> PNG. The " "image can then be processed in Photoshop or another tool and " "reimported using importCachedFont. Characters in the font are " "exported as one long <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">strip.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param fileName The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> name and path <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the output <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param padding The padding between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@param kerning The kerning between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(importCachedFont , void , (const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning) , "Import an image strip from exportCachedFont. Call with the " "same parameters you called <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">exportCachedFont.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param fileName The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> name and path <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the input <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param padding The padding between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@param kerning The kerning between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(populateAllFontCacheRange , void , (U32 rangeStart, U32 rangeEnd) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all fonts with Unicode code points in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">range.\n</a>" "@param rangeStart The start Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param rangeEnd The end Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@note We only support BMP- 0, so code points range from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 65535.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(populateAllFontCacheString , void , (const char *string) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all fonts with characters from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">string.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(populateFontCacheRange , void , (const char *faceName, S32 fontSize, U32 rangeStart, U32 rangeEnd) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified font with Unicode code points in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">range.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param rangeStart The start Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param rangeEnd The end Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@note We only support BMP- 0, so code points range from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 65535.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(populateFontCacheString , void , (const char *faceName, S32 fontSize, const char *string) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified font with characters from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">string.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param string The string <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">populate.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )
DefineEngineFunction(writeFontCache , void , () , "Force all cached fonts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> serialize themselves <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">cache.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

Detailed Description

Public Functions

DefineEngineFunction(dumpFontCacheStatus , void , () , "Dumps <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> full description of all cached fonts, along with " "info on the codepoints each <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">contains.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(duplicateCachedFont , void , (const char *oldFontName, S32 oldFontSize, const char *newFontName) , "Copy the specified old font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> name. The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> copy will not have <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "platform font backing it, and so will never have characters added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> it. " "But this is useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> making copies of fonts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> add postprocessing effects " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">exportCachedFont.\n</a>" " @param oldFontName The name of the font face <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">copy.\n</a>" " @param oldFontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">copy.\n</a>" " @param newFontName The name of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(exportCachedFont , void , (const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning) , "Export specified font <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified filename as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> PNG. The " "image can then be processed in Photoshop or another tool and " "reimported using importCachedFont. Characters in the font are " "exported as one long <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">strip.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param fileName The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> name and path <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the output <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param padding The padding between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@param kerning The kerning between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(importCachedFont , void , (const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning) , "Import an image strip from exportCachedFont. Call with the " "same parameters you called <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">exportCachedFont.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param fileName The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> name and path <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the input <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PNG.\n</a>" "@param padding The padding between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@param kerning The kerning between <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">characters.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(populateAllFontCacheRange , void , (U32 rangeStart, U32 rangeEnd) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all fonts with Unicode code points in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">range.\n</a>" "@param rangeStart The start Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param rangeEnd The end Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@note We only support BMP- 0, so code points range from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 65535.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(populateAllFontCacheString , void , (const char *string) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all fonts with characters from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">string.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(populateFontCacheRange , void , (const char *faceName, S32 fontSize, U32 rangeStart, U32 rangeEnd) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified font with Unicode code points in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">range.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param rangeStart The start Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param rangeEnd The end Unicode <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@note We only support BMP- 0, so code points range from 0 <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 65535.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(populateFontCacheString , void , (const char *faceName, S32 fontSize, const char *string) , "Populate the font cache <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified font with characters from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">string.\n</a>" "@param faceName The name of the font <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">face.\n</a>" "@param fontSize The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> of the font in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pixels.\n</a>" "@param string The string <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">populate.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

DefineEngineFunction(writeFontCache , void , () , "Force all cached fonts <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> serialize themselves <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">cache.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Font\n</a>" )

GFX_ImplementTextureProfile(GFXFontTextureProfile , GFXTextureProfile::DiffuseMap , GFXTextureProfile::PreserveSize</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da23d780ef2bcc57521a4945415ce5207f">GFXTextureProfile::Static</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82daa6bb823fc92adbec55ba4c34cd586062">GFXTextureProfile::KeepBitmap</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82daf55b13e32bd1a1746406b68ead73ba05">GFXTextureProfile::NoMipmap , GFXTextureProfile::NONE )

GlyphMapCompare(const void * a, const void * b)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "gfx/gFont.h"
  26
  27#include "core/resourceManager.h"
  28#include "core/stream/fileStream.h"
  29#include "core/strings/unicode.h"
  30#include "core/strings/findMatch.h"
  31#include "core/strings/stringFunctions.h"
  32#include "core/util/endian.h"
  33#include "core/util/safeDelete.h"
  34#include "console/console.h"
  35#include "console/engineAPI.h"
  36#include "platform/threads/mutex.h"
  37#include "zlib/zlib.h"
  38
  39
  40GFX_ImplementTextureProfile(GFXFontTextureProfile,
  41                            GFXTextureProfile::DiffuseMap, 
  42                            GFXTextureProfile::PreserveSize |
  43                            GFXTextureProfile::Static |
  44                            GFXTextureProfile::KeepBitmap |
  45                            GFXTextureProfile::NoMipmap, 
  46                            GFXTextureProfile::NONE);
  47
  48template<> void *Resource<GFont>::create(const Torque::Path &path)
  49{
  50#ifdef TORQUE_DEBUG_RES_MANAGER
  51   Con::printf( "Resource<GFont>::create - [%s]", path.getFullPath().c_str() );
  52#endif
  53
  54   return GFont::load( path );
  55}
  56
  57template<> ResourceBase::Signature  Resource<GFont>::signature()
  58{
  59   return MakeFourCC('f','o','n','t');
  60}
  61
  62/// Used for repacking in GFont::importStrip.
  63struct GlyphMap
  64{
  65   U32 charId;
  66   GBitmap *bitmap;
  67};
  68
  69static S32 QSORT_CALLBACK GlyphMapCompare(const void *a, const void *b)
  70{
  71   S32 ha = ((GlyphMap *) a)->bitmap->getHeight();
  72   S32 hb = ((GlyphMap *) b)->bitmap->getHeight();
  73
  74   return hb - ha;
  75}
  76
  77
  78const U32 GFont::csm_fileVersion = 3;
  79
  80String GFont::getFontCacheFilename(const String &faceName, U32 size)
  81{
  82   return String::ToString("%s/%s %d (%s).uft",
  83      Con::getVariable("$GUI::fontCacheDirectory"), faceName.c_str(), size, getCharSetName(0));
  84}
  85
  86GFont* GFont::load( const Torque::Path& path )
  87{
  88   FileStream  stream;
  89
  90   stream.open( path.getFullPath(), Torque::FS::File::Read );
  91   if ( stream.getStatus() != Stream::Ok )
  92      return NULL;
  93
  94   GFont *ret = new GFont;
  95   ret->mGFTFile = path;
  96
  97   if(!ret->read(stream))
  98   {
  99      Con::errorf( "GFont::load - error reading '%s'", path.getFullPath().c_str() );
 100      SAFE_DELETE(ret);
 101   }
 102   else
 103   {
 104      PlatformFont   *platFont = createPlatformFont(ret->getFontFaceName(), ret->getFontSize(), ret->getFontCharSet());
 105
 106      if ( platFont == NULL )
 107      {
 108         Con::errorf( "GFont::load - error creating platform font for '%s'", path.getFullPath().c_str() );
 109         SAFE_DELETE(ret);
 110      }
 111      else
 112         ret->setPlatformFont(platFont);
 113   }
 114   
 115   return ret;
 116}
 117
 118Resource<GFont> GFont::create(const String &faceName, U32 size, const char *cacheDirectory, U32 charset /* = TGE_ANSI_CHARSET */)
 119{
 120   if( !cacheDirectory )
 121      cacheDirectory = Con::getVariable( "$GUI::fontCacheDirectory" );
 122      
 123   const Torque::Path   path( String::ToString("%s/%s %d (%s).uft",
 124      cacheDirectory, faceName.c_str(), size, getCharSetName(charset)) );
 125
 126   Resource<GFont> ret;
 127
 128   // If the file already exists attempt to load it
 129   if (Platform::isFile(path.getFullPath().c_str()))
 130   {
 131      ret = ResourceManager::get().load(path);
 132
 133      if (ret != NULL)
 134      {
 135         ret->mGFTFile = path;
 136         return ret;
 137      }
 138   }
 139
 140   // Otherwise attempt to have the platform generate a new font
 141   PlatformFont *platFont = createPlatformFont(faceName, size, charset);
 142   
 143   if (platFont == NULL)
 144   {
 145      String fontName;
 146
 147#ifdef _XBOX
 148      //AssertFatal( false, "Font creation is not supported on the Xbox platform. Create the font files (*.uft) using the Windows/MacOS build of the project." );
 149      if(!faceName.equal("arial", String::NoCase) || size != 14)
 150      {
 151         return create("Arial", 14, cacheDirectory, charset);
 152      }
 153      return ret;
 154#endif
 155
 156      // Couldn't load the requested font.  This probably will be common
 157      // since many unix boxes don't have arial or lucida console installed.
 158      // Attempt to map the font name into a font we're pretty sure exist
 159      // Lucida Console is a common code & console font on windows, and
 160      // Monaco is the recommended code & console font on mac.
 161      if (faceName.equal("arial", String::NoCase))
 162         fontName = "Helvetica";
 163      else if (faceName.equal("lucida console", String::NoCase))
 164         fontName = "Monaco";
 165      else if (faceName.equal("monaco", String::NoCase))
 166         fontName = "Courier";
 167      else
 168         return ret;
 169
 170      return create(fontName, size, cacheDirectory, charset);
 171   }
 172
 173   // Create the actual GFont and set some initial properties
 174   GFont *font = new GFont;
 175   font->mPlatformFont = platFont;
 176   font->mGFTFile = path;
 177   font->mFaceName = faceName;
 178   font->mSize = size;
 179   font->mCharSet = charset;
 180
 181   font->mHeight   = platFont->getFontHeight();
 182   font->mBaseline = platFont->getFontBaseLine();
 183   font->mAscent   = platFont->getFontBaseLine();
 184   font->mDescent  = platFont->getFontHeight() - platFont->getFontBaseLine();
 185
 186   // Flag it to save when we exit
 187   font->mNeedSave = true;
 188
 189   // Load the newly created font into the ResourceManager
 190   ret.setResource(ResourceManager::get().load(path), font);
 191
 192   return ret;
 193}
 194
 195//-------------------------------------------------------------------------
 196
 197GFont::GFont()
 198{
 199   VECTOR_SET_ASSOCIATION(mCharInfoList);
 200   VECTOR_SET_ASSOCIATION(mTextureSheets);
 201
 202   std::fill_n(mRemapTable, Font_Table_MAX,-1);
 203
 204   mCurX = mCurY = mCurSheet = -1;
 205
 206   mPlatformFont = NULL;
 207   mSize = 0;
 208   mCharSet = 0;
 209   mHeight = 0;
 210   mBaseline = 0;
 211   mAscent = 0;
 212   mDescent = 0;
 213   mNeedSave = false;
 214   
 215   mMutex = Mutex::createMutex();
 216}
 217
 218GFont::~GFont()
 219{
 220   if(mNeedSave)
 221   {
 222      AssertFatal( mGFTFile.getFullPath().isNotEmpty(), "GFont::~GFont - path not set" );
 223
 224      FileStream stream;
 225      stream.open(mGFTFile, Torque::FS::File::Write);
 226
 227      if(stream.getStatus() == Stream::Ok) 
 228         write(stream);
 229
 230      stream.close();
 231   }
 232   
 233   S32 i;
 234
 235   for(i = 0;i < mCharInfoList.size();i++)
 236   {
 237       SAFE_DELETE_ARRAY(mCharInfoList[i].bitmapData);
 238   }
 239
 240   for(i=0; i<mTextureSheets.size(); i++)
 241      mTextureSheets[i] = NULL;
 242
 243   SAFE_DELETE(mPlatformFont);
 244   
 245   Mutex::destroyMutex(mMutex);
 246}
 247
 248void GFont::dumpInfo() const
 249{
 250   // Number and extent of mapped characters?
 251   U32 mapCount = 0, mapBegin=0xFFFF, mapEnd=0;
 252   for(U32 i=0; i<0x10000; i++)
 253   {
 254      if(mRemapTable[i] != -1)
 255      {
 256         mapCount++;
 257         if(i<mapBegin) mapBegin = i;
 258         if(i>mapEnd)   mapEnd   = i;
 259      }
 260   }
 261
 262
 263   // Let's write out all the info we can on this font.
 264   Con::printf("   '%s' %dpt", mFaceName.c_str(), mSize);
 265   Con::printf("      - %d texture sheets, %d mapped characters.", mTextureSheets.size(), mapCount);
 266
 267   if(mapCount)
 268      Con::printf("      - Codepoints range from 0x%x to 0x%x.", mapBegin, mapEnd);
 269   else
 270      Con::printf("      - No mapped codepoints.");
 271   Con::printf("      - Platform font is %s.", (mPlatformFont ? "present" : "not present") );
 272}
 273
 274//-----------------------------------------------------------------------------
 275
 276bool GFont::loadCharInfo(const UTF16 ch)
 277{
 278   PROFILE_SCOPE(GFont_loadCharInfo);
 279
 280    if(mRemapTable[ch] != -1)
 281        return true;    // Not really an error
 282
 283    if(mPlatformFont && mPlatformFont->isValidChar(ch))
 284    {
 285        Mutex::lockMutex(mMutex); // the CharInfo returned by mPlatformFont is static data, must protect from changes.
 286        PlatformFont::CharInfo &ci = mPlatformFont->getCharInfo(ch);
 287        if(ci.bitmapData)
 288            addBitmap(ci);
 289
 290        mCharInfoList.push_back(ci);
 291        mRemapTable[ch] = mCharInfoList.size() - 1;
 292        
 293        mNeedSave = true;
 294        
 295        Mutex::unlockMutex(mMutex);
 296        return true;
 297    }
 298
 299    return false;
 300}
 301
 302void GFont::addBitmap(PlatformFont::CharInfo &charInfo)
 303{
 304   U32 nextCurX = U32(mCurX + charInfo.width ); /*7) & ~0x3;*/
 305   U32 nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
 306
 307   // These are here for postmortem debugging.
 308   bool routeA = false, routeB = false;
 309
 310   if(mCurSheet == -1 || nextCurY >= TextureSheetSize)
 311   {
 312      routeA = true;
 313      addSheet();
 314
 315      // Recalc our nexts.
 316      nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
 317      nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
 318   }
 319
 320   if( nextCurX >= TextureSheetSize)
 321   {
 322      routeB = true;
 323      mCurX = 0;
 324      mCurY = nextCurY;
 325
 326      // Recalc our nexts.
 327      nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
 328      nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
 329   }
 330
 331   // Check the Y once more - sometimes we advance to a new row and run off
 332   // the end.
 333   if(nextCurY >= TextureSheetSize)
 334   {
 335      routeA = true;
 336      addSheet();
 337
 338      // Recalc our nexts.
 339      nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
 340      nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
 341   }
 342
 343    charInfo.bitmapIndex = mCurSheet;
 344    charInfo.xOffset = mCurX;
 345    charInfo.yOffset = mCurY;
 346
 347   mCurX = nextCurX;
 348
 349   S32 x, y;
 350   GBitmap *bmp = mTextureSheets[mCurSheet].getBitmap();
 351
 352   AssertFatal(bmp->getFormat() == GFXFormatA8, "GFont::addBitmap - cannot added characters to non-greyscale textures!");
 353
 354   for(y = 0;y < charInfo.height;y++)
 355      for(x = 0;x < charInfo.width;x++)
 356         *bmp->getAddress(x + charInfo.xOffset, y + charInfo.yOffset) = charInfo.bitmapData[y * charInfo.width + x];
 357
 358   mTextureSheets[mCurSheet].refresh();
 359}
 360
 361void GFont::addSheet()
 362{
 363    GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, GFXFormatA8 );
 364
 365    // Set everything to transparent.
 366    U8 *bits = bitmap->getWritableBits();
 367    dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize);
 368
 369    GFXTexHandle handle = GFXTexHandle( bitmap, &GFXFontTextureProfile, true, avar("%s() - (line %d)", __FUNCTION__, __LINE__) );
 370    mTextureSheets.increment();
 371    mTextureSheets.last() = handle;
 372
 373    mCurX = 0;
 374    mCurY = 0;
 375    mCurSheet = mTextureSheets.size() - 1;
 376}
 377
 378//-----------------------------------------------------------------------------
 379
 380const PlatformFont::CharInfo &GFont::getCharInfo(const UTF16 in_charIndex)
 381{
 382    PROFILE_SCOPE(GFont_getCharInfo);
 383
 384    AssertFatal(in_charIndex, "GFont::getCharInfo - can't get info for char 0!");
 385
 386    if(mRemapTable[in_charIndex] == -1)
 387        loadCharInfo(in_charIndex);
 388
 389    AssertFatal(mRemapTable[in_charIndex] != -1, "No remap info for this character");
 390    
 391    return mCharInfoList[mRemapTable[in_charIndex]];
 392}
 393
 394const PlatformFont::CharInfo &GFont::getDefaultCharInfo()
 395{
 396   static PlatformFont::CharInfo c;
 397   // c is initialized by the CharInfo default constructor.
 398   return c;
 399}
 400
 401//-----------------------------------------------------------------------------
 402
 403U32 GFont::getStrWidth(const UTF8* in_pString)
 404{
 405   AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, height is undefined");
 406   // If we ain't running debug...
 407   if (in_pString == NULL || *in_pString == '\0')
 408      return 0;
 409
 410   return getStrNWidth(in_pString, dStrlen(in_pString));
 411}
 412
 413U32 GFont::getStrWidthPrecise(const UTF8* in_pString)
 414{
 415   AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, height is undefined");
 416   // If we ain't running debug...
 417   if (in_pString == NULL)
 418      return 0;
 419
 420   return getStrNWidthPrecise(in_pString, dStrlen(in_pString));
 421}
 422
 423//-----------------------------------------------------------------------------
 424U32 GFont::getStrNWidth(const UTF8 *str, U32 n)
 425{
 426   // UTF8 conversion is expensive. Avoid converting in a tight loop.
 427   FrameTemp<UTF16> str16(n + 1);
 428   convertUTF8toUTF16N(str, str16, n + 1);
 429   return getStrNWidth(str16, dStrlen(str16));
 430}
 431
 432U32 GFont::getStrNWidth(const UTF16 *str, U32 n)
 433{
 434   AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL");
 435
 436   if (str == NULL || str[0] == '\0' || n == 0)   
 437      return 0;
 438      
 439   U32 totWidth = 0;
 440   UTF16 curChar;
 441   U32 charCount;
 442   
 443   for(charCount = 0; charCount < n; charCount++)
 444   {
 445      curChar = str[charCount];
 446      if(curChar == '\0')
 447         break;
 448
 449      if(isValidChar(curChar))
 450      {
 451         const PlatformFont::CharInfo& rChar = getCharInfo(curChar);
 452         totWidth += rChar.xIncrement;
 453      }
 454      else if (curChar == dT('\t'))
 455      {
 456         const PlatformFont::CharInfo& rChar = getCharInfo(dT(' '));
 457         totWidth += rChar.xIncrement * TabWidthInSpaces;
 458      }
 459   }
 460
 461   return(totWidth);
 462}
 463
 464U32 GFont::getStrNWidthPrecise(const UTF8 *str, U32 n)
 465{
 466   FrameTemp<UTF16> str16(n + 1);
 467   convertUTF8toUTF16N(str, str16, n + 1);
 468   return getStrNWidthPrecise(str16, dStrlen(str16));
 469}
 470
 471U32 GFont::getStrNWidthPrecise(const UTF16 *str, U32 n)
 472{
 473   AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL");
 474
 475   if (str == NULL || str[0] == '\0' || n == 0)   
 476      return(0);
 477      
 478   U32 totWidth = 0;
 479   UTF16 curChar;
 480   U32 charCount = 0;
 481   
 482   for(charCount = 0; charCount < n; charCount++)
 483   {
 484      curChar = str[charCount];
 485      if(curChar == '\0')
 486         break;
 487         
 488      if(isValidChar(curChar))
 489      {
 490         const PlatformFont::CharInfo& rChar = getCharInfo(curChar);
 491         totWidth += rChar.xIncrement;
 492      }
 493      else if (curChar == dT('\t'))
 494      {
 495         const PlatformFont::CharInfo& rChar = getCharInfo(dT(' '));
 496         totWidth += rChar.xIncrement * TabWidthInSpaces;
 497      }
 498   }
 499
 500   UTF16 endChar = str[getMin(charCount,n-1)];
 501
 502   if (isValidChar(endChar))
 503   {
 504      const PlatformFont::CharInfo& rChar = getCharInfo(endChar);
 505      if (rChar.width != rChar.xIncrement)
 506         totWidth += (rChar.width - rChar.xIncrement);
 507   }
 508
 509   return(totWidth);
 510}
 511
 512U32 GFont::getBreakPos(const UTF16 *str16, U32 slen, U32 width, bool breakOnWhitespace)
 513{
 514   // Some early out cases.
 515   if(slen==0)
 516      return 0;
 517
 518   U32 ret = 0;
 519   U32 lastws = 0;
 520   UTF16 c;
 521   U32 charCount = 0;
 522
 523   for( charCount=0; charCount < slen; charCount++)
 524   {
 525      c = str16[charCount];
 526      if(c == '\0')
 527         break;
 528         
 529      if(c == dT('\t'))
 530         c = dT(' ');
 531      
 532      if(!isValidChar(c))
 533      {
 534         ret++;
 535         continue;
 536      }
 537      
 538      if(c == dT(' '))
 539         lastws = ret+1;
 540
 541      const PlatformFont::CharInfo& rChar = getCharInfo(c);
 542      if(rChar.width > width || rChar.xIncrement > width)
 543      {
 544         if(lastws && breakOnWhitespace)
 545            return lastws;
 546         return ret;
 547      }
 548
 549      width -= rChar.xIncrement;
 550      
 551      ret++;
 552   }
 553   return ret;
 554}
 555
 556void GFont::wrapString(const UTF8 *txt, U32 lineWidth, Vector<U32> &startLineOffset, Vector<U32> &lineLen)
 557{
 558   // TODO: Is this error still true?
 559   //Con::errorf("GFont::wrapString(): Not yet converted to be UTF-8 safe");
 560
 561   startLineOffset.clear();
 562   lineLen.clear();
 563
 564   if (!txt || !txt[0] || lineWidth < getCharWidth('W')) //make sure the line width is greater then a single character
 565      return;
 566
 567   U32 len = dStrlen(txt);
 568
 569   U32 startLine; 
 570
 571   for (U32 i = 0; i < len;)
 572   {
 573      U32 wide = 0; 
 574      startLine = i;
 575      startLineOffset.push_back(startLine);
 576
 577      // loop until the string is too large
 578      bool needsNewLine = false;
 579      U32 lineStrWidth = 0;
 580      for (; i < len; i++)
 581      {
 582         if( txt[ i ] == '\n' )
 583         {
 584            needsNewLine = true;
 585            break;
 586         }
 587         else if(isValidChar(txt[i]))
 588         {
 589            lineStrWidth += getCharInfo(txt[i]).xIncrement;
 590            if(txt[i] < 0) // symbols which code > 127
 591            {  
 592               wide++; i++;
 593            }
 594            if( lineStrWidth > lineWidth )
 595            {
 596               needsNewLine = true;
 597               break;      
 598            }
 599         }
 600      }
 601
 602      if (!needsNewLine)
 603      {
 604         // we are done!
 605         lineLen.push_back(i - startLine - wide);
 606         return;
 607      }
 608
 609      S32 j; 
 610
 611      // Did we hit a hardwrap (newline character) in the string.
 612      bool hardwrap = ( txt[i] == '\n' );
 613      
 614      if ( hardwrap )
 615      {
 616         j = i;
 617      }
 618      // determine where to put the newline
 619      // we need to backtrack until we find a space character 
 620      // we don't do this for hardwrap(s)
 621      else
 622      {        
 623         for (j = i - 1; j >= startLine; j--)
 624         {
 625            if ( dIsspace(txt[j]) || txt[i] == '\n' )
 626               break;
 627         }
 628
 629         if (j < startLine)
 630         {
 631            // the line consists of a single word!              
 632            // So, just break up the word
 633
 634            j = i - 1;
 635         }
 636      }
 637
 638      lineLen.push_back(j - startLine - wide);
 639      i = j;
 640
 641      // Now we need to increment through any space characters at the
 642      // beginning of the next line.
 643      // We don't skip spaces after a hardwrap because they were obviously intended.
 644      for (i++; i < len; i++)
 645      {         
 646         if ( txt[i] == '\n' )
 647            continue;
 648
 649         if ( dIsspace( txt[i] ) && !hardwrap )
 650            continue;
 651
 652         break;
 653      }
 654   }
 655}
 656
 657//-----------------------------------------------------------------------------
 658
 659bool GFont::read(Stream& io_rStream)
 660{
 661    // Handle versioning
 662    U32 version;
 663    io_rStream.read(&version);
 664    if(version != csm_fileVersion)
 665        return false;
 666
 667    char buf[256];
 668    io_rStream.readString(buf);
 669    mFaceName = buf;
 670
 671    io_rStream.read(&mSize);
 672    io_rStream.read(&mCharSet);
 673
 674    io_rStream.read(&mHeight);
 675    io_rStream.read(&mBaseline);
 676    io_rStream.read(&mAscent);
 677    io_rStream.read(&mDescent);
 678
 679    U32 size = 0;
 680    io_rStream.read(&size);
 681    mCharInfoList.setSize(size);
 682    U32 i;
 683    for(i = 0; i < size; i++)
 684    {
 685        PlatformFont::CharInfo *ci = &mCharInfoList[i];
 686        io_rStream.read(&ci->bitmapIndex);
 687        io_rStream.read(&ci->xOffset);
 688        io_rStream.read(&ci->yOffset);
 689        io_rStream.read(&ci->width);
 690        io_rStream.read(&ci->height);
 691        io_rStream.read(&ci->xOrigin);
 692        io_rStream.read(&ci->yOrigin);
 693        io_rStream.read(&ci->xIncrement);
 694        ci->bitmapData = NULL;
 695   }
 696
 697   U32 numSheets = 0;
 698   io_rStream.read(&numSheets);
 699   
 700   for(i = 0; i < numSheets; i++)
 701   {
 702       GBitmap *bmp = new GBitmap;
 703       if(!bmp->readBitmap("png", io_rStream))
 704       {
 705           delete bmp;
 706           return false;
 707       }
 708       GFXTexHandle handle = GFXTexHandle(bmp, &GFXFontTextureProfile, true, avar("%s() - Read Font Sheet for %s %d (line %d)", __FUNCTION__, mFaceName.c_str(), mSize, __LINE__));
 709       //handle.setFilterNearest();
 710       mTextureSheets.push_back(handle);
 711   }
 712   
 713   // Read last position info
 714   io_rStream.read(&mCurX);
 715   io_rStream.read(&mCurY);
 716   io_rStream.read(&mCurSheet);
 717
 718   // Read the remap table.
 719   U32 minGlyph, maxGlyph;
 720   io_rStream.read(&minGlyph);
 721   io_rStream.read(&maxGlyph);
 722
 723   if(maxGlyph >= minGlyph)
 724   {
 725      // Length of buffer..
 726      U32 buffLen;
 727      io_rStream.read(&buffLen);
 728
 729      // Read the buffer.
 730      FrameTemp<S32> inBuff(buffLen);
 731      io_rStream.read(buffLen, inBuff);
 732
 733      // Decompress.
 734      uLongf destLen = (maxGlyph-minGlyph+1)*sizeof(S32);
 735      uncompress((Bytef*)&mRemapTable[minGlyph], &destLen, (Bytef*)(S32*)inBuff, buffLen);
 736
 737      AssertISV(destLen == (maxGlyph-minGlyph+1)*sizeof(S32), "GFont::read - invalid remap table data!");
 738
 739      // Make sure we've got the right endianness.
 740      for(i = minGlyph; i <= maxGlyph; i++)
 741         mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
 742   }
 743   
 744   return (io_rStream.getStatus() == Stream::Ok);
 745}
 746
 747bool GFont::write(Stream& stream)
 748{
 749    // Handle versioning
 750    stream.write(csm_fileVersion);
 751
 752    // Write font info
 753    stream.write(mFaceName);
 754    stream.write(mSize);
 755    stream.write(mCharSet);
 756   
 757    stream.write(mHeight);
 758    stream.write(mBaseline);
 759    stream.write(mAscent);
 760    stream.write(mDescent);
 761
 762    // Write char info list
 763    stream.write(U32(mCharInfoList.size()));
 764    U32 i;
 765    for(i = 0; i < mCharInfoList.size(); i++)
 766    {
 767        const PlatformFont::CharInfo *ci = &mCharInfoList[i];
 768        stream.write(ci->bitmapIndex);
 769        stream.write(ci->xOffset);
 770        stream.write(ci->yOffset);
 771        stream.write(ci->width);
 772        stream.write(ci->height);
 773        stream.write(ci->xOrigin);
 774        stream.write(ci->yOrigin);
 775        stream.write(ci->xIncrement);
 776   }
 777
 778   stream.write(mTextureSheets.size());
 779   for(i = 0; i < mTextureSheets.size(); i++)
 780       mTextureSheets[i].getBitmap()->writeBitmap("png", stream);
 781
 782   stream.write(mCurX);
 783   stream.write(mCurY);
 784   stream.write(mCurSheet);
 785
 786   // Get the min/max we have values for, and only write that range out.
 787   S32 minGlyph = S32_MAX, maxGlyph = 0;
 788
 789   for(i = 0; i < 65536; i++)
 790   {
 791      if(mRemapTable[i] != -1)
 792      {
 793         if(i>maxGlyph) maxGlyph = i;
 794         if(i<minGlyph) minGlyph = i;
 795      }
 796   }
 797
 798   stream.write(minGlyph);
 799   stream.write(maxGlyph);
 800
 801   // Skip it if we don't have any glyphs to do...
 802   if(maxGlyph >= minGlyph)
 803   {
 804      // Put everything big endian, to be consistent. Do this inplace.
 805      for(i = minGlyph; i <= maxGlyph; i++)
 806         mRemapTable[i] = convertHostToBEndian(mRemapTable[i]);
 807
 808      {
 809         // Compress.
 810         const U32 buffSize = 128 * 1024;
 811         FrameTemp<S32> outBuff(buffSize);
 812         uLongf destLen = buffSize * sizeof(S32);
 813         compress2((Bytef*)(S32*)outBuff, &destLen, (Bytef*)(S32*)&mRemapTable[minGlyph], (maxGlyph-minGlyph+1)*sizeof(S32), 9);
 814
 815         // Write out.
 816         stream.write((U32)destLen);
 817         stream.write(destLen, outBuff);
 818      }
 819
 820      // Put us back to normal.
 821      for(i = minGlyph; i <= maxGlyph; i++)
 822         mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
 823   }
 824   
 825   return (stream.getStatus() == Stream::Ok);
 826}
 827
 828void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning)
 829{
 830   // Figure dimensions of our strip by iterating over all the char infos.
 831   U32 totalHeight = 0;
 832   U32 totalWidth = 0;
 833
 834   S32 heightMin=0, heightMax=0;
 835
 836   for(S32 i=0; i<mCharInfoList.size(); i++)
 837   {
 838      totalWidth += mCharInfoList[i].width + kerning + 2*padding;
 839      heightMin = getMin((S32)heightMin, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin);
 840      heightMax = getMax((S32)heightMax, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin + (S32)mCharInfoList[i].height);
 841   }
 842
 843   totalHeight = heightMax - heightMin + 2*padding;
 844
 845   // Make the bitmap.
 846   GBitmap gb(totalWidth, totalHeight, false, mTextureSheets[0].getBitmap()->getFormat());
 847
 848   dMemset(gb.getWritableBits(), 0, sizeof(U8) * totalHeight * totalWidth );
 849
 850   // Ok, copy some rects, taking into account padding, kerning, offset.
 851   U32 curWidth = kerning + padding;
 852
 853   for(S32 i=0; i<mCharInfoList.size(); i++)
 854   {
 855      // Skip invalid stuff.
 856      if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
 857         continue;
 858
 859      // Copy the rect.
 860      U32 bitmap = mCharInfoList[i].bitmapIndex;
 861
 862      RectI ri(mCharInfoList[i].xOffset, mCharInfoList[i].yOffset, mCharInfoList[i].width, mCharInfoList[i].height );
 863      Point2I outRi(curWidth, padding + getBaseline() - mCharInfoList[i].yOrigin);
 864      gb.copyRect(mTextureSheets[bitmap].getBitmap(), ri, outRi); 
 865
 866      // Advance.
 867      curWidth +=  mCharInfoList[i].width + kerning + 2*padding;
 868   }
 869
 870   // Write the image!
 871   FileStream fs;
 872   
 873   fs.open( fileName, Torque::FS::File::Write );
 874
 875   if(fs.getStatus() != Stream::Ok)
 876   {
 877      Con::errorf("GFont::exportStrip - failed to open '%s' for writing.", fileName);
 878      return;
 879   }
 880 
 881   // Done!
 882   gb.writeBitmap("png", fs);
 883}
 884
 885void  GFont::setPlatformFont(PlatformFont *inPlatformFont)
 886{
 887   AssertFatal( mPlatformFont == NULL, "Trying to set platform font which already exists");
 888
 889   mPlatformFont = inPlatformFont;
 890}
 891
 892void GFont::importStrip(const char *fileName, U32 padding, U32 kerning)
 893{
 894   // Wipe our texture sheets, and reload bitmap data from the specified file.
 895   // Also deal with kerning.
 896   // Also, we may have to load RGBA instead of RGB.
 897
 898   // Wipe our texture sheets.
 899   mCurSheet = mCurX = mCurY = 0;
 900   mTextureSheets.clear();
 901
 902   //  Now, load the font strip.
 903   Resource<GBitmap> strip = GBitmap::load(fileName);
 904
 905   if(!strip)
 906   {
 907      Con::errorf("GFont::importStrip - could not load file '%s'!", fileName);
 908      return;
 909   }
 910
 911   // And get parsing and copying - load up all the characters as separate
 912   // GBitmaps, sort, then pack. Not terribly efficient but this is basically
 913   // on offline task anyway.
 914
 915   // Ok, snag some glyphs.
 916   Vector<GlyphMap> glyphList;
 917   glyphList.reserve(mCharInfoList.size());
 918
 919   U32 curWidth = 0;
 920   for(S32 i=0; i<mCharInfoList.size(); i++)
 921   {
 922      // Skip invalid stuff.
 923      if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
 924         continue;
 925
 926      // Allocate a new bitmap for this glyph, taking into account kerning and padding.
 927      glyphList.increment();
 928      GlyphMap& lastGlyphMap = glyphList.last();
 929      lastGlyphMap.bitmap = new GBitmap(mCharInfoList[i].width + kerning + 2 * padding, mCharInfoList[i].height + 2 * padding, false, strip->getFormat());
 930      lastGlyphMap.charId = i;
 931
 932      // Copy the rect.
 933      RectI ri(curWidth, getBaseline() - mCharInfoList[i].yOrigin, lastGlyphMap.bitmap->getWidth(), lastGlyphMap.bitmap->getHeight());
 934      Point2I outRi(0,0);
 935      lastGlyphMap.bitmap->copyRect(strip, ri, outRi);
 936
 937      // Update glyph attributes.
 938      mCharInfoList[i].width = lastGlyphMap.bitmap->getWidth();
 939      mCharInfoList[i].height = lastGlyphMap.bitmap->getHeight();
 940      mCharInfoList[i].xOffset -= kerning + padding;
 941      mCharInfoList[i].xIncrement += kerning;
 942      mCharInfoList[i].yOffset -= padding;
 943
 944      // Advance.
 945      curWidth += ri.extent.x;
 946   }
 947
 948   // Ok, we have a big list of glyphmaps now. So let's sort them, then pack them.
 949   dQsort(glyphList.address(), glyphList.size(), sizeof(GlyphMap), GlyphMapCompare);
 950
 951   // They're sorted by height, so now we can do some sort of awesome packing.
 952   Point2I curSheetSize(256, 256);
 953   Vector<U32> sheetSizes;
 954
 955   S32 curY = 0;
 956   S32 curX = 0;
 957   S32 curLnHeight = 0;
 958   S32 maxHeight = 0;
 959   for(U32 i = 0; i < glyphList.size(); i++)
 960   {
 961      PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
 962      
 963      if(ci->height > maxHeight)
 964         maxHeight = ci->height;
 965
 966      if(curX + ci->width > curSheetSize.x)
 967      {
 968         curY += curLnHeight;
 969         curX = 0;
 970         curLnHeight = 0;
 971      }
 972
 973      if(curY + ci->height > curSheetSize.y)
 974      {
 975         sheetSizes.push_back(curSheetSize.y);
 976         curX = 0;
 977         curY = 0;
 978         curLnHeight = 0;
 979      }
 980
 981      if(ci->height > curLnHeight)
 982         curLnHeight = ci->height;
 983      
 984      ci->bitmapIndex = sheetSizes.size();
 985      ci->xOffset = curX;
 986      ci->yOffset = curY;
 987      curX += ci->width;
 988   }
 989
 990   // Terminate the packing loop calculations.
 991   curY += curLnHeight;
 992
 993   if(curY < 64)
 994      curSheetSize.y = 64;
 995   else if(curY < 128)
 996      curSheetSize.y = 128;
 997
 998   sheetSizes.push_back(curSheetSize.y);
 999
1000   if(getHeight() + padding * 2 > maxHeight)
1001      maxHeight = getHeight() + padding * 2;
1002
1003   // Allocate texture pages.
1004   for(S32 i=0; i<sheetSizes.size(); i++)
1005   {
1006      GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, strip->getFormat());
1007
1008      // Set everything to transparent.
1009      U8 *bits = bitmap->getWritableBits();
1010      dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize * strip->getBytesPerPixel());
1011
1012      GFXTexHandle handle = GFXTexHandle( bitmap, &GFXFontTextureProfile, true, avar("%s() - Font Sheet for %s (line %d)", __FUNCTION__, fileName, __LINE__) );
1013      mTextureSheets.increment();
1014      mTextureSheets.last() = handle;
1015   }
1016
1017   // Alright, we're ready to copy bits!
1018   for(S32 i=0; i<glyphList.size(); i++)
1019   {
1020      // Copy each glyph into the appropriate place.
1021      PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
1022      U32 bi = ci->bitmapIndex;
1023      mTextureSheets[bi]->getBitmap()->copyRect(glyphList[i].bitmap, RectI(0,0, glyphList[i].bitmap->getWidth(),glyphList[i].bitmap->getHeight()), Point2I(ci->xOffset, ci->yOffset));
1024   }
1025
1026   // Ok, all done! Just refresh some textures and we're set.
1027   for(S32 i=0; i<sheetSizes.size(); i++)
1028      mTextureSheets[i].refresh();
1029}
1030
1031DefineEngineFunction( populateFontCacheString, void, ( const char *faceName, S32 fontSize, const char *string ),,
1032    "Populate the font cache for the specified font with characters from the specified string.\n"
1033    "@param faceName The name of the font face.\n"
1034    "@param fontSize The size of the font in pixels.\n"
1035    "@param string The string to populate.\n"
1036    "@ingroup Font\n" )
1037{
1038   Resource<GFont> f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory"));
1039
1040   if(f == NULL)
1041   {
1042      Con::errorf("populateFontCacheString - could not load font '%s %d'", faceName, fontSize);
1043      return;
1044   }
1045
1046   if(!f->hasPlatformFont())
1047   {
1048      Con::errorf("populateFontCacheString - font '%s %d' has no platform font. Cannot generate more characters.", faceName, fontSize);
1049      return;
1050   }
1051
1052   // This has the side effect of generating character info, including the bitmaps.
1053   f->getStrWidthPrecise( string );
1054}
1055
1056DefineEngineFunction( populateFontCacheRange, void, ( const char *faceName, S32 fontSize, U32 rangeStart, U32 rangeEnd ),,
1057   "Populate the font cache for the specified font with Unicode code points in the specified range.\n"
1058   "@param faceName The name of the font face.\n"
1059   "@param fontSize The size of the font in pixels.\n"
1060   "@param rangeStart The start Unicode point.\n"
1061   "@param rangeEnd The end Unicode point.\n"
1062   "@note We only support BMP-0, so code points range from 0 to 65535.\n"
1063   "@ingroup Font\n" )
1064{
1065   Resource<GFont> f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory"));
1066
1067   if(f == NULL)
1068   {
1069      Con::errorf("populateFontCacheRange - could not load font '%s %d'", faceName, fontSize);
1070      return;
1071   }
1072
1073   if(rangeStart > rangeEnd)
1074   {
1075      Con::errorf("populateFontCacheRange - range start is after end");
1076      return;
1077   }
1078
1079   if(!f->hasPlatformFont())
1080   {
1081      Con::errorf("populateFontCacheRange - font '%s %d' has no platform font Cannot generate more characters.", faceName, fontSize);
1082      return;
1083   }
1084
1085   // This has the side effect of generating character info, including the bitmaps.
1086   for(U32 i=rangeStart; i<rangeEnd; i++)
1087   {
1088      if(f->isValidChar(i))
1089         f->getCharWidth(i);
1090      else
1091         Con::warnf("populateFontCacheRange - skipping invalid char 0x%x",  i);
1092   }
1093
1094   // All done!
1095}
1096
1097DefineEngineFunction( dumpFontCacheStatus, void, (),,
1098   "Dumps to the console a full description of all cached fonts, along with "
1099   "info on the codepoints each contains.\n"
1100   "@ingroup Font\n" )
1101{
1102   Resource<GFont>   theFont = ResourceManager::get().startResourceList( Resource<GFont>::signature() );
1103 
1104   Con::printf("--------------------------------------------------------------------------");
1105   Con::printf("   Font Cache Usage Report");
1106
1107   while( theFont != NULL )
1108   {
1109      theFont->dumpInfo();
1110
1111      theFont = ResourceManager::get().nextResource();
1112   }
1113}
1114
1115DefineEngineFunction( writeFontCache, void, (),, 
1116   "Force all cached fonts to serialize themselves to the cache.\n"
1117   "@ingroup Font\n" )
1118{
1119   Resource<GFont>   theFont = ResourceManager::get().startResourceList( Resource<GFont>::signature() );
1120
1121   Con::printf("--------------------------------------------------------------------------");
1122   Con::printf("   Writing font cache to disk");
1123
1124   while( theFont != NULL )
1125   {
1126      const String   fileName( theFont.getPath() );
1127
1128      FileStream stream;
1129      stream.open(fileName, Torque::FS::File::Write);
1130
1131      if(stream.getStatus() == Stream::Ok) 
1132      {
1133         Con::printf("      o Writing '%s' to disk...", fileName.c_str());
1134         theFont->write(stream);
1135      }
1136      else
1137      {
1138         Con::errorf("      o Could not open '%s' for write", fileName.c_str());
1139      }
1140
1141      theFont = ResourceManager::get().nextResource();
1142  }
1143}
1144
1145DefineEngineFunction( populateAllFontCacheString, void, ( const char *string ),,
1146   "Populate the font cache for all fonts with characters from the specified string.\n"
1147   "@ingroup Font\n" )
1148{
1149   Resource<GFont> theFont = ResourceManager::get().startResourceList( Resource<GFont>::signature() );
1150
1151   Con::printf("Populating font cache with string '%s'", string);
1152
1153   while( theFont != NULL )
1154   {
1155      if(theFont->hasPlatformFont())
1156      {
1157         // This has the side effect of generating character info, including the bitmaps.
1158         theFont->getStrWidthPrecise( string );
1159      }
1160      else
1161      {
1162         const String   fileName( theFont.getPath() );
1163         Con::errorf("populateAllFontCacheString - font '%s' has no platform font. Cannot generate more characters.", fileName.c_str());
1164      }
1165
1166      theFont = ResourceManager::get().nextResource();
1167   }
1168}
1169
1170DefineEngineFunction( populateAllFontCacheRange, void, ( U32 rangeStart, U32 rangeEnd ),,
1171   "Populate the font cache for all fonts with Unicode code points in the specified range.\n"
1172   "@param rangeStart The start Unicode point.\n"
1173   "@param rangeEnd The end Unicode point.\n"
1174   "@note We only support BMP-0, so code points range from 0 to 65535.\n"
1175   "@ingroup Font\n" )
1176{
1177   if(rangeStart > rangeEnd)
1178   {
1179      Con::errorf("populateAllFontCacheRange - range start is after end!");
1180      return;
1181   }
1182
1183   Resource<GFont>   theFont = ResourceManager::get().startResourceList( Resource<GFont>::signature() );
1184
1185   Con::printf("Populating font cache with range 0x%x to 0x%x", rangeStart, rangeEnd);
1186
1187   while( theFont != NULL )
1188   {
1189      const String   fileName( theFont.getPath() );
1190
1191      if(theFont->hasPlatformFont())
1192      {
1193         // This has the side effect of generating character info, including the bitmaps.
1194         Con::printf("   o Populating font '%s'", fileName.c_str());
1195         for(U32 i=rangeStart; i<rangeEnd; i++)
1196         {
1197            if(theFont->isValidChar(i))
1198               theFont->getCharWidth(i);
1199            else
1200               Con::warnf("populateAllFontCacheRange - skipping invalid char 0x%x",  i);
1201         }
1202      }
1203      else
1204      {
1205         Con::errorf("populateAllFontCacheRange - font '%s' has no platform font. Cannot generate more characters.", fileName.c_str());
1206      }
1207
1208      theFont = ResourceManager::get().nextResource();
1209   }
1210}
1211
1212DefineEngineFunction( exportCachedFont, void, 
1213   ( const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning ),,
1214   "Export specified font to the specified filename as a PNG. The "
1215   "image can then be processed in Photoshop or another tool and "
1216   "reimported using importCachedFont. Characters in the font are "
1217   "exported as one long strip.\n"
1218   "@param faceName The name of the font face.\n"
1219   "@param fontSize The size of the font in pixels.\n"
1220   "@param fileName The file name and path for the output PNG.\n"
1221   "@param padding The padding between characters.\n"   
1222   "@param kerning The kerning between characters.\n"   
1223   "@ingroup Font\n" )
1224{
1225   // Tell the font to export itself.
1226   Resource<GFont> f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory"));
1227
1228   if(f == NULL)
1229   {
1230      Con::errorf("exportCachedFont - could not load font '%s %d'", faceName, fontSize);
1231      return;
1232   }
1233
1234   f->exportStrip(fileName, padding, kerning);
1235}
1236
1237DefineEngineFunction( importCachedFont, void,
1238   ( const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning ),,
1239   "Import an image strip from exportCachedFont. Call with the "
1240   "same parameters you called exportCachedFont.\n"
1241   "@param faceName The name of the font face.\n"
1242   "@param fontSize The size of the font in pixels.\n"
1243   "@param fileName The file name and path for the input PNG.\n"
1244   "@param padding The padding between characters.\n"   
1245   "@param kerning The kerning between characters.\n"   
1246   "@ingroup Font\n" )
1247{
1248   // Tell the font to import itself.
1249   Resource<GFont> f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory"));
1250
1251   if(f == NULL)
1252   {
1253      Con::errorf("importCachedFont - could not load font '%s %d'", faceName, fontSize);
1254      return;
1255   }
1256
1257   f->importStrip(fileName, padding, kerning);
1258}
1259
1260DefineEngineFunction( duplicateCachedFont, void, 
1261   ( const char *oldFontName, S32 oldFontSize, const char *newFontName ),,
1262   "Copy the specified old font to a new name. The new copy will not have a "
1263   "platform font backing it, and so will never have characters added to it. "
1264   "But this is useful for making copies of fonts to add postprocessing effects "
1265   "to via exportCachedFont.\n"
1266   "@param oldFontName The name of the font face to copy.\n"
1267   "@param oldFontSize The size of the font to copy.\n"
1268   "@param newFontName The name of the new font face.\n"
1269   "@ingroup Font\n" )
1270{
1271   String newFontFile = GFont::getFontCacheFilename(newFontName, oldFontSize);
1272
1273   // Load the original font.
1274   Resource<GFont> font = GFont::create(oldFontName, oldFontSize, Con::getVariable("$GUI::fontCacheDirectory"));
1275
1276   // Deal with inexplicably missing or failed to load fonts.
1277   if (font == NULL)
1278   {
1279      Con::errorf(" o Couldn't find font : %s", oldFontName);
1280      return;
1281   }
1282
1283   // Ok, dump info!
1284   FileStream stream;
1285   stream.open( newFontFile, Torque::FS::File::Write );
1286   if(stream.getStatus() == Stream::Ok) 
1287   {
1288      Con::printf( "      o Writing duplicate font '%s' to disk...", newFontFile.c_str() );
1289      font->write(stream);
1290      stream.close();
1291   }
1292   else
1293   {
1294      Con::errorf( "      o Could not open '%s' for write", newFontFile.c_str() );
1295   }
1296}
1297
1298