1 // FreetypeGlyphsProvider.cpp: Freetype glyphs manager
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "gnashconfig.h"
24 #include "FreetypeGlyphsProvider.h"
27 #include <memory> // for auto_ptr
28 #include <boost/cstdint.hpp>
29 #include <boost/format.hpp>
31 #include "smart_ptr.h" // for intrusive_ptr
32 #include "GnashException.h"
33 #include "ShapeRecord.h"
35 #include "FillStyle.h"
38 # include <ft2build.h>
39 # include FT_OUTLINE_H
41 // Methods of FT_Outline_Funcs take a 'const FT_Vector*' in 2.2
42 // and a non-const one in 2.1, so we use an FT_CONST macro to
44 # if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 2
47 # define FT_CONST const
51 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
52 # define HAVE_FONTCONFIG 1
55 #ifdef HAVE_FONTCONFIG
56 # include <fontconfig/fontconfig.h>
57 # include <fontconfig/fcfreetype.h>
62 # include <FindDirectory.h>
65 // Define the following to make outline decomposition verbose
66 //#define DEBUG_OUTLINE_DECOMPOSITION 1
68 // Define the following to make device font handling verbose
69 //#define GNASH_DEBUG_DEVICEFONTS 1
75 /// Outline glyph walker/decomposer, for drawing an FT_Outline to DynamicShape
77 /// See FT_Outline_Decompose function of freetype2 lib
84 /// Create an outline walker drawing to the given DynamiShape
87 /// The DynamicShape to draw to. Externally owned.
90 /// The scale to apply to coordinates.
91 /// This is to match an arbitrary EM
93 OutlineWalker(SWF::ShapeRecord
& sh
, float scale
)
101 /// A default fill style is solid white.
102 FillStyle f
= SolidFill(rgba());
103 _shape
.addFillStyle(f
);
104 _shape
.addPath(Path(_x
, _y
, 1, 0, 0, true));
105 _currPath
= &_shape
.currentPath();
115 /// Callback function for the move_to member of FT_Outline_Funcs
117 walkMoveTo(FT_CONST FT_Vector
* to
, void* ptr
)
119 OutlineWalker
* walker
= static_cast<OutlineWalker
*>(ptr
);
120 return walker
->moveTo(to
);
123 /// Callback function for the line_to member of FT_Outline_Funcs
125 walkLineTo(FT_CONST FT_Vector
* to
, void* ptr
)
127 OutlineWalker
* walker
= static_cast<OutlineWalker
*>(ptr
);
128 return walker
->lineTo(to
);
131 /// Callback function for the conic_to member of FT_Outline_Funcs
133 walkConicTo(FT_CONST FT_Vector
* ctrl
, FT_CONST FT_Vector
* to
, void* ptr
)
135 OutlineWalker
* walker
= static_cast<OutlineWalker
*>(ptr
);
136 return walker
->conicTo(ctrl
, to
);
139 /// Callback function for the cubic_to member of FT_Outline_Funcs
141 /// Transform the cubic curve into a quadratic one an interpolated point
142 /// falling in the middle of the two control points.
145 walkCubicTo(FT_CONST FT_Vector
* ctrl1
, FT_CONST FT_Vector
* ctrl2
,
146 FT_CONST FT_Vector
* to
, void* ptr
)
148 OutlineWalker
* walker
= static_cast<OutlineWalker
*>(ptr
);
149 return walker
->cubicTo(ctrl1
, ctrl2
, to
);
154 int moveTo(const FT_Vector
* to
)
156 #ifdef DEBUG_OUTLINE_DECOMPOSITION
157 log_debug("moveTo: %ld,%ld", to
->x
, to
->y
);
159 _x
= static_cast<boost::int32_t>(to
->x
* _scale
);
160 _y
= - static_cast<boost::int32_t>(to
->y
* _scale
);
162 _shape
.addPath(Path(_x
, _y
, 1, 0, 0, false));
163 _currPath
= &_shape
.currentPath();
167 int lineTo(const FT_Vector
* to
)
169 #ifdef DEBUG_OUTLINE_DECOMPOSITION
170 log_debug("lineTo: %ld,%ld", to
->x
, to
->y
);
172 _x
= static_cast<boost::int32_t>(to
->x
* _scale
);
173 _y
= - static_cast<boost::int32_t>(to
->y
* _scale
);
174 _currPath
->drawLineTo(_x
, _y
);
175 expandBounds(_x
, _y
);
179 int conicTo(const FT_Vector
* ctrl
, const FT_Vector
* to
)
181 #ifdef DEBUG_OUTLINE_DECOMPOSITION
182 log_debug("conicTo: %ld,%ld %ld,%ld", ctrl
->x
, ctrl
->y
, to
->x
, to
->y
);
184 boost::int32_t x1
= static_cast<boost::int32_t>(ctrl
->x
* _scale
);
185 boost::int32_t y1
= static_cast<boost::int32_t>(ctrl
->y
* _scale
);
186 _x
= static_cast<boost::int32_t>(to
->x
* _scale
);
187 _y
= - static_cast<boost::int32_t>(to
->y
* _scale
);
188 _currPath
->drawCurveTo(x1
, -y1
, _x
, _y
);
189 expandBounds(x1
, -y1
, _x
, _y
);
194 cubicTo(const FT_Vector
* ctrl1
, const FT_Vector
* ctrl2
, const FT_Vector
* to
)
196 #ifdef DEBUG_OUTLINE_DECOMPOSITION
197 log_debug("cubicTo: %ld,%ld %ld,%ld %ld,%ld", ctrl1
->x
,
198 ctrl1
->y
, ctrl2
->x
, ctrl2
->y
, to
->x
, to
->y
);
200 float x
= ctrl1
->x
+ ( (ctrl2
->x
- ctrl1
->x
) * 0.5 );
201 float y
= ctrl1
->y
+ ( (ctrl2
->y
- ctrl1
->y
) * 0.5 );
202 boost::int32_t x1
= static_cast<boost::int32_t>(x
* _scale
);
203 boost::int32_t y1
= static_cast<boost::int32_t>(y
* _scale
);
204 _x
= static_cast<boost::int32_t>(to
->x
* _scale
);
205 _y
= - static_cast<boost::int32_t>(to
->y
* _scale
);
207 _currPath
->drawCurveTo(x1
, -y1
, _x
, _y
);
208 expandBounds(x1
, -y1
, _x
, _y
);
212 void expandBounds(int x
, int y
) {
213 SWFRect bounds
= _shape
.getBounds();
214 if (_currPath
->size() == 1) _currPath
->expandBounds(bounds
, 0, 6);
216 bounds
.expand_to_circle(x
, y
, 0);
218 _shape
.setBounds(bounds
);
221 void expandBounds(int ax
, int ay
, int cx
, int cy
) {
222 SWFRect bounds
= _shape
.getBounds();
223 if (_currPath
->size() == 1) _currPath
->expandBounds(bounds
, 0, 6);
225 bounds
.expand_to_circle(ax
, ay
, 0);
226 bounds
.expand_to_circle(cx
, cy
, 0);
228 _shape
.setBounds(bounds
);
231 SWF::ShapeRecord
& _shape
;
237 boost::int32_t _x
, _y
;
242 FT_Library
FreetypeGlyphsProvider::m_lib
= 0;
243 boost::mutex
FreetypeGlyphsProvider::m_lib_mutex
;
247 FreetypeGlyphsProvider::init()
249 boost::mutex::scoped_lock
lock(m_lib_mutex
);
253 const int error
= FT_Init_FreeType(&m_lib
);
255 boost::format err
= boost::format(_("Can't init FreeType! Error "
257 throw GnashException(err
.str());
263 FreetypeGlyphsProvider::close()
265 const int error
= FT_Done_FreeType(m_lib
);
267 log_error(_("Can't close FreeType! Error %d"), error
);
273 FreetypeGlyphsProvider::getFontFilename(const std::string
&name
,
274 bool bold
, bool italic
, std::string
& filename
)
278 // only returns a default
280 if (B_OK
!= find_directory(B_BEOS_FONTS_DIRECTORY
, &bp
)) {
281 log_error(_("Failed to find fonts directory, using hard-coded "
282 "font filename \"%s\""), DEFAULT_FONTFILE
);
283 filename
= DEFAULT_FONTFILE
;
287 bp
.Append("ttfonts/DejaVuSans.ttf");
288 filename
= bp
.Path();
292 #ifdef HAVE_FONTCONFIG
294 log_error(_("Can't init fontconfig library, using hard-"
295 "coded font filename \"%s\""), DEFAULT_FONTFILE
);
296 filename
= DEFAULT_FONTFILE
;
303 FcPattern
* pat
= FcNameParse((const FcChar8
*)name
.c_str());
305 FcConfigSubstitute (0, pat
, FcMatchPattern
);
308 FcPatternAddInteger (pat
, FC_SLANT
, FC_SLANT_ITALIC
);
312 FcPatternAddInteger (pat
, FC_WEIGHT
, FC_WEIGHT_BOLD
);
315 FcDefaultSubstitute (pat
);
318 match
= FcFontMatch (0, pat
, &result
);
319 FcPatternDestroy (pat
);
321 FcFontSet
* fs
= NULL
;
323 fs
= FcFontSetCreate ();
324 FcFontSetAdd (fs
, match
);
328 #ifdef GNASH_DEBUG_DEVICEFONTS
329 log_debug("Found %d fonts matching the family %s (using "
330 "first)", fs
->nfont
, name
);
333 for (int j
= 0; j
< fs
->nfont
; j
++) {
335 if (FcPatternGetString (fs
->fonts
[j
], FC_FILE
, 0, &file
) != FcResultMatch
) {
336 #ifdef GNASH_DEBUG_DEVICEFONTS
337 log_debug("Matching font %d has unknown filename, skipping", j
);
342 filename
= (char *)file
;
343 FcFontSetDestroy(fs
);
345 #ifdef GNASH_DEBUG_DEVICEFONTS
346 log_debug("Loading font from file %d", filename
);
352 FcFontSetDestroy(fs
);
355 log_error("No device font matches the name '%s', using hard-coded"
356 " font filename", name
);
357 filename
= DEFAULT_FONTFILE
;
360 log_error("Font filename matching not implemented (no fontconfig"
361 " support built-in), using hard-coded font filename",
363 filename
= DEFAULT_FONTFILE
;
368 #endif // USE_FREETYPE
372 std::auto_ptr
<FreetypeGlyphsProvider
>
373 FreetypeGlyphsProvider::createFace(const std::string
& name
, bool bold
, bool italic
)
376 std::auto_ptr
<FreetypeGlyphsProvider
> ret
;
379 ret
.reset(new FreetypeGlyphsProvider(name
, bold
, italic
));
381 catch (const GnashException
& ge
) {
382 log_error(ge
.what());
389 #else // ndef USE_FREETYPE
390 std::auto_ptr
<FreetypeGlyphsProvider
>
391 FreetypeGlyphsProvider::createFace(const std::string
&, bool, bool)
393 log_error("Freetype not supported");
394 return std::auto_ptr
<FreetypeGlyphsProvider
>(NULL
);
399 FreetypeGlyphsProvider::unitsPerEM() const
402 return _face
->units_per_EM
;
406 FreetypeGlyphsProvider::descent() const
409 return std::abs(_face
->descender
);
413 FreetypeGlyphsProvider::ascent() const
416 return _face
->ascender
;
420 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string
& name
,
421 bool bold
, bool italic
)
431 std::string filename
;
432 if (getFontFilename(name
, bold
, italic
, filename
) == false)
434 boost::format msg
= boost::format(_("Can't find font file "
435 "for font '%s'")) % name
;
436 throw GnashException(msg
.str());
439 int error
= FT_New_Face(m_lib
, filename
.c_str(), 0, &_face
);
445 case FT_Err_Unknown_File_Format
:
447 boost::format msg
= boost::format(_("Font file '%s' "
448 "has bad format")) % filename
;
449 throw GnashException(msg
.str());
455 // TODO: return a better error message !
456 boost::format msg
= boost::format(_("Some error "
457 "opening font '%s'"))
459 throw GnashException(msg
.str());
464 // We want an EM of unitsPerEM, so if units_per_EM is different
466 scale
= (float)unitsPerEM()/_face
->units_per_EM
;
468 #ifdef GNASH_DEBUG_DEVICEFONTS
469 log_debug("EM square for font '%s' is %d, scale is thus %g", name
, _face
->units_per_EM
, scale
);
472 #else // ndef(USE_FREETYPE)
473 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string
&, bool, bool)
475 abort(); // should never be called
477 #endif // ndef USE_FREETYPE
480 std::auto_ptr
<SWF::ShapeRecord
>
481 FreetypeGlyphsProvider::getGlyph(boost::uint16_t code
, float& advance
)
483 std::auto_ptr
<SWF::ShapeRecord
> glyph
;
485 FT_Error error
= FT_Load_Char(_face
, code
, FT_LOAD_NO_BITMAP
|
489 log_error("Error loading freetype outline glyph for char '%c' "
490 "(error: %d)", code
, error
);
494 // Scale advance by current scale, to match expected output coordinate space
495 advance
= _face
->glyph
->metrics
.horiAdvance
* scale
;
496 #ifdef GNASH_DEBUG_DEVICEFONTS
497 log_debug("Advance value for glyph '%c' is %g (horiAdvance:%ld, "
498 "scale:%g)", code
, advance
,
499 _face
->glyph
->metrics
.horiAdvance
, scale
);
502 if ( _face
->glyph
->format
!= FT_GLYPH_FORMAT_OUTLINE
)
504 unsigned long gf
= _face
->glyph
->format
;
505 log_unimpl("FT_Load_Char() returned a glyph format != "
506 "FT_GLYPH_FORMAT_OUTLINE (%c%c%c%c)",
507 static_cast<char>((gf
>>24)&0xff),
508 static_cast<char>((gf
>>16)&0xff),
509 static_cast<char>((gf
>>8)&0xff),
510 static_cast<char>(gf
&0xff));
514 FT_Outline
* outline
= &(_face
->glyph
->outline
);
516 FT_Outline_Funcs walk
;
517 walk
.move_to
= OutlineWalker::walkMoveTo
;
518 walk
.line_to
= OutlineWalker::walkLineTo
;
519 walk
.conic_to
= OutlineWalker::walkConicTo
;
520 walk
.cubic_to
= OutlineWalker::walkCubicTo
;
524 #ifdef DEBUG_OUTLINE_DECOMPOSITION
525 log_debug("Decomposing glyph outline for DisplayObject %u", code
);
528 glyph
.reset(new SWF::ShapeRecord
);
530 OutlineWalker
walker(*glyph
, scale
);
532 FT_Outline_Decompose(outline
, &walk
, &walker
);
533 #ifdef DEBUG_OUTLINE_DECOMPOSITION
535 // Don't use VM::get if this is to be re-enabled.
536 SWFRect bound
; sh
->compute_bound(&bound
, VM::get().getSWFVersion());
537 log_debug("Decomposed glyph for DisplayObject '%c' has bounds %s",
538 code
, bound
.toString());
546 #else // ndef(USE_FREETYPE)
548 std::auto_ptr
<SWF::ShapeRecord
>
549 FreetypeGlyphsProvider::getGlyph(boost::uint16_t, float& advance
)
551 abort(); // should never be called...
555 FreetypeGlyphsProvider::~FreetypeGlyphsProvider()
559 if (FT_Done_Face(_face
) != 0) {
560 log_error("Could not release FT face resources");