openframeworks / openFrameworks

openFrameworks is a community-developed cross platform toolkit for creative coding in C++.
http://openframeworks.cc
Other
9.97k stars 2.56k forks source link

ofTrueTypeFont feature discussion #1131

Open ofTheo opened 12 years ago

ofTheo commented 12 years ago

This is a feature discussion issue for what should be added / changed to ofTTF. Would be great to get some eyes on this from @vtron @ofzach @arturoc and anyone with thoughts on the matter.

TextAreas:

Alignment:

Spacing:

Loading:

Drawing:

Formatting:

Info:

Update: -Underline -Render rect behind text ( a la what @kylemcdonald added to ofDrawBitmapString) -Scale type to fit a rect + keep aspect ratio -Crazy: text along a path :)

ofZach commented 12 years ago

maybe something that scaled type to fit a rect? (we also could use a max rect in rect function that preserves aspect ratio if we don't have it... I write that constantly).

a draw from center, top left corner or baseline left mode - like OF_RECTMODE_CENTER. For doing rotated type, this could be helpful.

simple layout for mutiple words (ie, wrapping, justification).

ofTheo commented 12 years ago

@ofZach the align stuff we have in there. I think also the wrapping justification is in there or is that something else? I added the scale type to fix rect.

@vtron would be great if you could take a look at this too.

bilderbuchi commented 12 years ago

sry, I don't know why I recommended reserving the second post. was thinking of something else w.r.t. forums, I now realize. must've been some mental blackout. :-P

roymacdonald commented 12 years ago

when using ofTrueTypeFont::drawString(stringstring s, float x, float y) the x and y coordinates correspond to the LOWER Left corner of the text. When rendering a single line it might be ok but when rendering a string with several lines it becomes awkward as this coordinates correspond to the baseline left of the first line. Besides this being awkward I think it would be much more consistent with the rest of OF if the top left corner is used as the coordinate at which is drawn. Maybe having the option to switch which mode to use could be good. i.e, OF_DRAW_STRING_LEFT_TOP, OF_DRAW_STRING_LEFT_BASELINE, and extending it to OF_DRAW_STRING_CENTER_TOP, OF_DRAW_STRING_RIGHT _TOP....

vtron commented 12 years ago

Yes this is definitely an issue...I think that having a choice between the two is the optimal solution. The biggest issue with top left is that it will change based on the height of different characters (type starts from the baseline with ascenders and descenders in free type) so we might ahve to set a static height.

I have a lot of this in the works/done already, hoping to have something to put up soon that should give us a good jumping off point...have had a lot of stuff going on so have been quiet but should have some stuff to add shortly.

One of the main things I would also like to get in there would be texture atlases vs textures for each glyph, could save a lot of memory on iOS.

ofZach commented 12 years ago

@vtron -- good to see you in the discussion! I was thinking top left = baseline + line height, in order to not have it shift as you say if the string is xyz or lkj... ie, the top left corner would be on the baseline of the above line if you were looking...

for example, here: http://imgur.com/dL3aY the top left of "one two three" is where the box is, on the baseline of the above line.

roymacdonald commented 12 years ago

there is already a lineHeight variable that is set when the font is loaded, so changing from top to baseline is just as simple as adding or not this var to the possition to be drawn. i can make this changes if it's ok with you.

roymacdonald commented 12 years ago

@ofZach take a look to a branch in my OF fork called "feature-TTFDrawingPos". the two font examples in examples/graphics are modified so to switch between the two different string drawing modes (TOP and BASELINE).

kylemcdonald commented 12 years ago

here's a link to the commit @roymacdonald is talking about https://github.com/roymacdonald/openFrameworks/commit/146bf2436b7ce5aee16a52f3edeb115e36deee79

roymacdonald commented 12 years ago

Thanks kyle! I should have posted the link to my commit at the begininng.

Roy Macdonald 8248-8478

On 03-04-2012, at 15:40, Kyle McDonald reply@reply.github.com wrote:

here's a link to the commit @roymacdonald is talking about https://github.com/roymacdonald/openFrameworks/commit/146bf2436b7ce5aee16a52f3edeb115e36deee79


Reply to this email directly or view it on GitHub: https://github.com/openframeworks/openFrameworks/issues/1131#issuecomment-4919738

bakercp commented 12 years ago

Hi Theo, I added kerning (at least as much as you can get from freetype -- no GPOS), and many of the items you noted in your in your initial post. I'll make sure all of my changes are linked here later this evening.

bakercp commented 12 years ago

Here is a handful of ofFont related stuff I have been working on. It is not complete, but you can tell where I'm headed. https://github.com/bakercp/openFrameworks/commit/0ba93595a50d1dada83fd32caf87a37744eeba68

The remaining thing I need to figure out is a data structure that is something like a "text block". The goal of this data structure would be that you could feed a string and a bounding rectangle (or other arbitrary convex closed ofPath shape) and it would give you all of the lines broken appropriately. The text block would allow access to individual glyph positions for easier animation and and could be rendered quickly without reformatting each time. Returning a block like this would prevent the draw methods from doing line breaking, etc every time it is rendered (which it does now).

Regarding justification vertical and horizontal -- it's all pretty straight forward once you have lines broken with the exception of full justification. Full justification (that looks somewhat decent) is not trivial (i.e. if you want more than a typical look of expanding the inter word spaces to make the line fit). I was looking into a Knuth-Plask style algorithm for line breaking, but I'm not sure if it's worth the effort for really nice full justification (which may be why Processing has also avoided it for so long). It's still an open question in my mind. My current line breaking algorithm works pretty well.

Kerning is also in there, a new approach to font Settings (inspired by ofFbo, etc) that enables much more flexible glyph generation. One thing to note is that freetype only accesses the normal kerning tables and not the GPOS tables (http://www.freetype.org/freetype2/docs/tutorial/step2.html). Many modern fonts almost exclusively use GPOS for kerning and really can only be used well in a with a pretty sophisticated layout engine (like Pango / ICU), which is probably outside of the scope for the core ofFont class.

Anyway, I'm very happy to continue work on this or combine efforts with others. I am working on some text layout projects now and needed some of this functionality and hoped to share it at some point when it is a bit more polished.

Cheers, Christopher

kylemcdonald commented 12 years ago

@bakercp this looks incredible, it really does address a ton of the issues theo brings up!

@vtron can you spend some time looking at @bakercp's updates, testing all the features rigorously and preparing to get them into the core?

@ofTheo and @ofZach it would also be awesome, if you have a chance, to look over the commit above from @bakercp and see if you have any suggestions for different ways of accomplishing this, as far as the interface goes. personally, the ofFbo::Settings approach seems right on to me!

bakercp commented 12 years ago

Hi all, I'm continuing to polish this -- I'll post any changes here. Basically my immediate todo list is:

So, don't spend too much time hacking on the commit above just yet as I still have a bunch of cleanup. More soon.

ofTheo commented 12 years ago

honestly I think ofTTF needs a complete re-write :)

I like the idea of ofFont @bakercp, I think like ofSoundPlayer etc ofFont could be a more general class and we could have a new ofTTF as a swappable engine of sorts ( like ofFmodSoundPlayer, etc ), which ofFont would use to extract the font information.

Might be interesting to bounce around some pseduo code via pastbin or gist on how we could structure ofFont.

bakercp commented 12 years ago

I've got an almost complete rewrite that moves in this direction to propose here shortly. I'll be posting it soon.

bakercp commented 12 years ago

It's got unicode support, selectable character sets, kerning, more sophisticated layout (even inside concave polygons!) text on a line, etc. Anyway, more soon.

@ofTheo would this complete rewrite still use freetype as its engine? or would you propose engaging the platform specific font managers?

ofTheo commented 12 years ago

ahh interesting - I hadn't thought of that. I think for now maybe FT but with the possibility of using different font apis in the future.

very excited to see your re-write! :)

bakercp commented 12 years ago

Just wanted to ping here. Getting very close to finishing this.

ofTheo commented 12 years ago

great - looking forward to it!! :)

kylemcdonald commented 12 years ago

ok, now it's starting to get crazy -- here's another rendition: https://github.com/sosolimited/ofxSoso/blob/master/src/ofxSosoTrueTypeFont.h

bakercp commented 12 years ago

@ofTheo So, a few things to check out:

https://github.com/bakercp/ofxUnicode

This package is core to all of my packages. Basically, it provides simple method for dealing with Unicode. It currently combines a lot of POCO::Unicode support with the better UTF8 iteration provided by UTF8CPP (http://utfcpp.sourceforge.net/). POCO is able to iterate through UTF8 text via POCO::TextIterator, but its iterator is unidirectional, which is insufficient for things like hyphenators. Liblinebreak below has its own unidirectional utf8 iterator in the library.

Just by way of review, I wrote myself a long comment about what this library is actually doing here https://github.com/bakercp/ofxUnicode/blob/master/src/ofTextConverter.cpp It's very rough, but may be a good addition to the wikipedia articles on the topic and this page http://www.joelonsoftware.com/articles/Unicode.html which is a must read.

I have been bumping into some major limitations with this current fusion of UTF8CPP when it comes to layout. Initially I avoided the massive (and extremely comprehensive) ICU library (http://site.icu-project.org/). But, I just got it running in a different branch of ofxUnicode and the static code footprint is only about 16MB, and you get a ton of features with that -- probably the most comprehensive in the world. ICU also includes linebreakers, bidi (bidirectional text) algorithms and a rudimentary (but decent) text layout engine that can tap into our existing freetype system. More on that shortly.

https://github.com/bakercp/ofxFont

https://github.com/bakercp/ofxHyphenator

This is simply layout candy. It implements the classic hyphenation algorithm invented ages ago.

http://www.tug.org/docs/liang/ and http://en.wikipedia.org/wiki/Hyphenation_algorithm

The main work here was to free the existing libhyphenate (the only c/c++ implementation I could find) it from glib (it used glib for UTF8 iteration) and add static/object support for the required language files. I did this to prevent oF users from having to drop the data files in a data directory. It's kind of the same situation as a haar tracker -- it likes to load its text files. There is an example project that generates the static language headers and rolls them up into a base64 encoded-stringified strings. It's fancy.

https://github.com/bakercp/ofxLinebreaker

ofxLinebreaker is an implementation of Unicode's own linebreaking standard. This is important for text wrapping, filling boxes, shapes, etc. In most linebreakers (like Processing, etc) lines are simply broken at spaces, line feeds, and in a worse case of a box being too small, words are just hacked in half. Of course, there are ideal places to break lines, "ok" places to break lines and places were breaking a line will destroy the meaning of a word/phrase line. Thus, the Unicode standard attends to the international pitfalls of linebreaking.

One point of note -- the ICU library also implements this on its own. So, if ofxUnicode moves to ICU, then that this would be dropped in order to reduce the numer of external libs.

// and some path tools that are not directly related, but somewhat https://github.com/bakercp/ofxClipper

This doesn't include much about my current layout research, but I'll post on that shortly or share it @ the mtg.

bakercp commented 12 years ago

So a few things regarding layout.

First international text compatible layout happens on a number of different levels. Usually the tools look like this:

There are some other issues that I have not come to terms with quite yet -- including the ideas of an ofFontManager to manage fonts, create and cache glyphs on the fly (currently, like Processing, my current implementation rasterizes a limited character set via https://github.com/bakercp/ofxFont/blob/master/src/ofCharacterSet.h but ultimately, I think it should just happen on the fly, automatically cache and uncache the data, etc. Harfbuzz actually takes care of a lot of this stuff via its (already existing) Freetype bindings.

Anyway ... that's my story for the moment. I'm trying to fix up the ofxFont demo and it should be up in a minute.

bakercp commented 12 years ago

If you just can't get enough of this stuff, here's an article by one of the big players in the FLOSS text internationalization / layout unicode effort http://behdad.org/text/. The actual harfbuzz library is being actively updated now and compiles and works just fine.

bakercp commented 12 years ago

Just fixed a little ofFont example the demos the framesetter and "line" creation.

https://github.com/bakercp/ofxFont

Just for full disclosure, I'm modeling the layout engine (i.e. framesetters, typesetters, etc) after Apple's coretext. Not using their code, but their approach seems to be pretty common across typesetting solutions (libcinder basically glues each platform's text layout stuff together and uses object divisions similar to apple's coretext).

More on the purpose of objects like:

ofFormattedText, ofFramesetter, ofTextFrame, ofTextLayout, ofTextLine, ofTextRun, ofTypesetter, etc.

http://en.wikipedia.org/wiki/Core_Text

Nice pictures are here too:

https://developer.apple.com/library/mac/#documentation/StringsTextFonts/Conceptual/CoreText_Programming/Overview/Overview.html