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