FontSerialiser


#1

Hi,

I don’t know if anyone else has any use for this yet, but I’ve made a small command-line utility to make use of JUCE::Typeface’ serialise method. Basically it turns any font installed on your system into a compressed binary file, which you can then load on any machine.

A cool trick is to then use Jules’ Binary Builder on the resultant file to compile it into your program (See my Pedal Board for an example).

Downloads:
Executable: http://www.niallmoody.com/downloads/FontSerialiser.zip
Source: http://www.niallmoody.com/downloads/FSSource.zip

Sorry they’re separate files, but my webhost thinks the zip’s a virus if they’re not separate for some reason :?.

  • Niall.

#2

Raw Material Software ltd accepts no responsibility for illegal distribution of fonts in this way.

But yes, it is quite handy, and cross-platform too.


#3

I hadn’t thought of that. For the record, the font included in my Pedal Board is a stupid one that I made myself, that was mean to look comic :?..

  • Niall.

#4

I’ve been fooling with this all afternoon, which leads me to a question: does this method actually work? I notice Nial has commented it out in his Pedal Board source, with a note to the effect that it doesn’t work.

If this doesn’t work, what is the proper methodology for embedding a font as a resource and using it globally? I can’t make heads nor tails of this whole thing.


#5

Yeah, I discovered it didn’t work when I accidentally wiped the harddrive of my old laptop (thus uninstalling the font I’d made - which I had cleverly forgotten to back up). I don’t know if it’s something wrong with my FontSerialiser app, or if it’s a JUCE thing. I should have probably asked Jules about it…

  • Niall.

#6

any chance to see why this doesn’t work ? i’ve tried the same (and debugged it), and discovered that Typeface::serialise just actually returns a couple of bytes, really skipping all glyph and kerning serialization…


#7

Yeah, Chris figured out why - you actually have to read in all the glyphs before you serialise it. Also, I found it doesn’t seem to handle things like bold and italic fonts properly - possibly because they’re separate fonts? - but I may have just done something wrong.

If you use the following code instead of my original FontSerialiser code, it should work right (I think - I’m not sure how many glyphs are actually in a standard font, but you get the idea):

[code]int main (int argc, char* argv[])
{
printf ("\n\n--------------------------------\n Font Serialiser by Niall Moody\n--------------------------------\n\n");

if (argc != 3)
{
    printf (" Usage: FontSerialiser <filename> <fontname>\n\n");
    printf (" FontSerialiser will turn a font into a compressed binary file.\n\n\n");

    return 0;
}

// because we're not using the proper application startup procedure, we need to call
// this explicitly here to initialise some of the time-related stuff..
SystemStats::initialiseStats();

const File destDirectory (File::getCurrentWorkingDirectory().getChildFile (argv[1]));

String FontName(argv[2]);

OutputStream *fontfile = destDirectory.createOutputStream();

if (fontfile == 0)
{
    String error ("\nError : Couldn't open ");
    error << destDirectory.getFullPathName() << " for writing.\n\n";
    printf ((const char*) error);
    return 0;
}

Typeface *font = 0;
font = new Typeface(FontName, false, false);
if(!font)
{
	String error ("\nError : Where's the font?\n\n");
    printf ((const char*) error);
    return 0;
}

//Here's the important part.
for(int i=0;i<256;++i)
	font->getGlyph(i);

String op("\nWrote font ");
op << FontName << " to file " << destDirectory.getFullPathName() << " successfully.\n\n";
printf((const char *)op);

font->serialise(*fontfile);

delete font;
delete fontfile;

printf("\n(You might want to use Binary Builder to turn this file into a c++ file now)\n\n ");

return 0;

}[/code]

I’m not sure I’ve actually tested that code, but another way would be to modify the JUCE demo code to add a button that takes the currently selected font (on the fonts page) and serialises it (as all the glyphs will have been read in by that point).

  • Niall.

#8

thanx now i understand why it shouldn’t work before, actually in console mode you are not initialising gliphs before any drawing operations… i’ll try it.


#9

yeah now is working correctly, really thanx Niall :wink:


#10

i’ve discovered that if i serialize a font with Typeface serialize method, and then load the typeface in the application, build a font pointer, then pass the created *fontPointer to some TextEditors with setFont(), then i get some leaks not in the first component, but instead after creating some of them dynamically:

juce_application.exe!wcscmp(const wchar_t * src=0x00ed2fc8, const wchar_t * dst=0x000001fc)  Line 52 + 0x9 bytes
juce_application.exe!juce::String::operator!=(const juce::String & other={...})  Line 783 + 0x23 bytes
juce_application.exe!juce::FontDCHolder::loadFont(const juce::String & fontName_={...}, const bool bold_=true, const bool italic_=true, const int size_=0)  Line 274 + 0xf bytes
juce_application.exe!juce::Typeface::findAndAddSystemGlyph(wchar_t character=L'i')  Line 539 + 0x29 bytes
juce_application.exe!juce::Typeface::getGlyph(const wchar_t character=L'i')  Line 303
juce_application.exe!juce::Typeface::getOutlineForGlyph(const wchar_t character=L'i')  Line 271 + 0xd bytes
juce_application.exe!juce::FontGlyphAlphaMap::generate(juce::Typeface * const face=0x00f96a80, const wchar_t character_=L'i', const float fontHeight=10.000000, const float fontHorizontalScale=1.0000000)  Line 175 + 0xd bytes
juce_application.exe!juce::GlyphCache::getGlyphFor(juce::Typeface * const typeface=0x00f96a80, const float fontHeight=10.000000, const float fontHorizontalScale=1.0000000, const wchar_t character=L'i')  Line 265
juce_application.exe!juce::PositionedGlyph::draw(juce::Graphics & g={...})  Line 328 + 0x35 bytes
juce_application.exe!juce::GlyphArrangement::draw(juce::Graphics & g={...})  Line 1148
juce_application.exe!juce::Graphics::drawSingleLineText(const juce::String & text={...}, const int startX=0, const int baselineY=8)  Line 221
juce_application.exe!juce::TextEditorIterator::draw(juce::Graphics & g={...}, const juce::UniformTextSection * & lastSection=0x010784a0)  Line 500 + 0x80 bytes
juce_application.exe!juce::TextEditor::drawContent(juce::Graphics & g={...})  Line 1471
juce_application.exe!juce::TextHolderComponent::paint(juce::Graphics & g={...})  Line 775
juce_application.exe!juce::Component::paintEntireComponent(juce::Graphics & originalContext={...})  Line 1613 + 0x13 bytes

some other i get, randomly selecting and opening a new TextEditor, assigning this font (but a previously created TextEditor is showing the text with the font correctly):

juce_application.exe!juce::Array<void *,juce::DummyCriticalSection>::getUnchecked(const int index=0) Line 250 + 0x8 bytes juce_application.exe!juce::Typeface::getGlyph(const wchar_t character=L'v') Line 288 + 0xf bytes juce_application.exe!juce::Typeface::getOutlineForGlyph(const wchar_t character=L'v') Line 271 + 0xd bytes juce_application.exe!juce::FontGlyphAlphaMap::generate(juce::Typeface * const face=0x00f96a80, const wchar_t character_=L'v', const float fontHeight=10.000000, const float fontHorizontalScale=1.0000000) Line 175 + 0xd bytes juce_application.exe!juce::GlyphCache::getGlyphFor(juce::Typeface * const typeface=0x00f96a80, const float fontHeight=10.000000, const float fontHorizontalScale=1.0000000, const wchar_t character=L'v') Line 265 juce_application.exe!juce::PositionedGlyph::draw(juce::Graphics & g={...}) Line 328 + 0x35 bytes juce_application.exe!juce::GlyphArrangement::draw(juce::Graphics & g={...}) Line 1148 juce_application.exe!juce::Graphics::drawSingleLineText(const juce::String & text={...}, const int startX=0, const int baselineY=8) Line 221 juce_application.exe!ejuce::TextEditorIterator::draw(juce::Graphics & g={...}, const ejuce::UniformTextSection * & lastSection=0x00fb9120) Line 492 + 0x80 bytes juce_application.exe!ejuce::TextEditor::drawContent(juce::Graphics & g={...}) Line 1463 juce_application.exe!ejuce::TextHolderComponent::paint(juce::Graphics & g={...}) Line 767 juce_application.exe!juce::Component::paintEntireComponent(juce::Graphics & originalContext={...}) Line 1613 + 0x13 bytes

the font is clearly visible with some text editors, opening some others, or changing text on the first then the leak sleaks into.

i’m doing nothing more than:

[code]// global singleton config constructor
MemoryInputStream fontStream (Resource::myfont,Resource::myfont_size, false);
Typeface* typeFace = new Typeface (fontStream);

editorFont = new Font (*typeFace);
editorFont->setHeight (10.0f);

// global singleton config destructor
if (editorFont)
delete editorFont;

// then when user selects some i do:
addAndMakeVisible(debugOutput = new TextEditor());
debugOutput->setColours (Colours::lightgreen, Colours::black, Colours::palegreen);
debugOutput->setMultiLine (true, false);
debugOutput->setCaretVisible (true);
debugOutput->setFont (*(GlobalConfig::getInstance()->editorFont));
debugOutput->setText (T(""));
[/code]

if i use the default Font, without creating one from typeface those crashes don’t occurr. i’m doing something silly ?


#11

I haven’t actually used the serialise stuff other than to check that it works (didn’t test for memory leaks though), but from the code you posted, could it be that you don’t delete typeFace?

  • Niall.

#12

if i delete typeface, then i get crashes on the “delete typeFace”. also if i delete it at shutdown i get memory leaks on the glyphs. from what i know the typeface you pass to a font is auto managed internally by the font using a reference conted pointer. if you copy the font with copy constructor the typeface is shared between the two and have refCount+1… then if i delete the first created font i can see no leaks at application exiting. i only get leaks when assigning that font to multiple TextEditors in their paint method… and every time this could happens at the 3rd created texteditor or the 9th one, dunnow why. jules can you help in here ?


#13

Seems a bit peculiar, I wouldn’t expect any problems with the reference counting. The correct way to do it would be to delete the typeface immediately after using it to initialise the font. You say that it crashes when you do that??


#14

i do nothing else than this:

MemoryInputStream fontStream (Resource::font,Resource::font_size, false);
Typeface* typeFace = new Typeface (fontStream);
		
sharedFont = new Font (*typeFace);
sharedFont->setHeight (10.0f);

delete typeFace;

TextEditor* editor = new TextEditor();
editor->setFont (*sharedFont);
editor->setText (T(""));

setContentComponent(editor);

but then if i write in the texteditor i see text white, and after 6 or 7 characters pressed i get: a leak in getGlyph.

>	juce::Typeface::getGlyph(character=L'r') Line 290	C++
 	juce::Typeface::getOutlineForGlyph(character=L'r') Line 271	C++
 	juce::FontGlyphAlphaMap::generate(face=0x00f97378, character_=L'r', fontHeight=10.000000, fontHorizontalScale=1.0000000) Line 175	C++
 	juce::GlyphCache::getGlyphFor(typeface=0x00f97378, fontHeight=10.000000, fontHorizontalScale=1.0000000, character=L'r') Line 265	C++
 	juce::PositionedGlyph::draw(g={...}) Line 328	C++
 	juce::GlyphArrangement::draw(g={...}) Line 1148	C++
 	juce::Graphics::drawSingleLineText(text={...}, startX=0, baselineY=8) Line 221	C++
 	juce::TextEditorIterator::draw(g={...}, lastSection=0x00fdb288) Line 500	C++
 	juce::TextEditor::drawContent(g={...}) Line 1471	C++
 	juce::TextHolderComponent::paint(g={...}) Line 775	C++
 	juce::Component::paintEntireComponent(originalContext={...}) Line 1613	C++
 	juce::Component::paintEntireComponent(originalContext={...}) Line 1635	C++
 	juce::Component::paintEntireComponent(originalContext={...}) Line 1635	C++
 	juce::Component::paintEntireComponent(originalContext={...}) Line 1635	C++
 	juce::Component::paintEntireComponent(originalContext={...}) Line 1635	C++
 	juce::RepaintManager::renderCacheAreasNeedingRepaint() Line 221	C++
 	juce::Win32ComponentPeer::handlePaintMessage() Line 1413	C++
 	juce::Win32ComponentPeer::peerWindowProc(h=0x000203ba, message=15, wParam=0, lParam=0) Line 1826	C++
 	juce::Win32ComponentPeer::windowProc(h=0x000203ba, message=15, wParam=0, lParam=0) Line 1803	C++

instead if i dont’ do “delete typeFace” i can write all 256 characters to the texteditor correctly. it only crashes sometimes when i use in a lot of texteditors…

ah the font is serialised as niall suggested some post before this


#15

Ok - I found a bug in Typeface, try replacing this method:

[code]void Typeface::addGlyphCopy (void* const glyphInfoToCopy)
{
if (glyphInfoToCopy != 0)
{
const TypefaceGlyphInfo& old = (const TypefaceGlyphInfo) glyphInfoToCopy;

    if (old.character > 0 && old.character < 128)
        lookupTable [old.character] = (short)glyphs.size();

    TypefaceGlyphInfo* newOne = new TypefaceGlyphInfo (old);
    newOne->typeface = this;
    glyphs.add (newOne);
}

}

[/code]


#16

l-value specifies const object in

newOne->typeface = this;

i remove the const ?


#17

Ah - you spotted that I’d not even tried to compile it then! Slightly better:

[code] TypefaceGlyphInfo* const newOne
= new TypefaceGlyphInfo (old.character,
old.path,
old.width,
this);

    newOne->kerningPairs = old.kerningPairs;
    glyphs.add (newOne);

[/code]


#18

eheh i see you know your conscious child very well :wink: if you try to make he do nasty thing he is crying out !


#19

yessssss ! well spotted… from the first checks is not throwing leaks inside the application with lots of text editors.

thanx a lot :wink:

now font embedding is 99.99999999999999999% working in juce (one can’t say 100% in computer programming… ehehe)

:cry: beautiful !


#20

Is there some special trick needed to get the Font Serialiser working? I tried it, but always get an output file that is only 50 bytes in size or something like that :frowning: