1 // FreetypeGlyphsProvider.cpp: Freetype glyphs manager
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
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 unique_ptr
29 #include <boost/format.hpp>
31 #include "GnashException.h"
32 #include "ShapeRecord.h"
34 #include "FillStyle.h"
37 # include <ft2build.h>
38 # include FT_OUTLINE_H
40 // Methods of FT_Outline_Funcs take a 'const FT_Vector*' in 2.2
41 // and a non-const one in 2.1, so we use an FT_CONST macro to
43 # if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 2
46 # define FT_CONST const
50 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
51 # define HAVE_FONTCONFIG 1
54 #ifdef HAVE_FONTCONFIG
55 # include <fontconfig/fontconfig.h>
56 # include <fontconfig/fcfreetype.h>
61 # include <FindDirectory.h>
64 // Define the following to make outline decomposition verbose
65 //#define DEBUG_OUTLINE_DECOMPOSITION 1
67 // Define the following to make device font handling verbose
68 //#define GNASH_DEBUG_DEVICEFONTS 1
74 /// Outline glyph walker/decomposer, for drawing an FT_Outline to DynamicShape
76 /// See FT_Outline_Decompose function of freetype2 lib
83 /// Create an outline walker drawing to the given DynamiShape
86 /// The DynamicShape to draw to. Externally owned.
89 /// The scale to apply to coordinates.
90 /// This is to match an arbitrary EM
92 OutlineWalker(SWF::ShapeRecord
& sh
, float scale
)
100 /// A default fill style is solid white.
101 FillStyle f
= SolidFill(rgba());
102 _subshape
.addFillStyle(f
);
103 _subshape
.addPath(Path(_x
, _y
, 1, 0, 0));
104 _currPath
= &_subshape
.currentPath();
110 _shape
.addSubshape(_subshape
);
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<std::int32_t>(to
->x
* _scale
);
160 _y
= - static_cast<std::int32_t>(to
->y
* _scale
);
162 _subshape
.addPath(Path(_x
, _y
, 1, 0, 0));
163 _currPath
= &_subshape
.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<std::int32_t>(to
->x
* _scale
);
173 _y
= - static_cast<std::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 std::int32_t x1
= static_cast<std::int32_t>(ctrl
->x
* _scale
);
185 std::int32_t y1
= static_cast<std::int32_t>(ctrl
->y
* _scale
);
186 _x
= static_cast<std::int32_t>(to
->x
* _scale
);
187 _y
= - static_cast<std::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 std::int32_t x1
= static_cast<std::int32_t>(x
* _scale
);
203 std::int32_t y1
= static_cast<std::int32_t>(y
* _scale
);
204 _x
= static_cast<std::int32_t>(to
->x
* _scale
);
205 _y
= - static_cast<std::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::Subshape _subshape
;
232 SWF::ShapeRecord
& _shape
;
243 FT_Library
FreetypeGlyphsProvider::m_lib
= nullptr;
244 std::mutex
FreetypeGlyphsProvider::m_lib_mutex
;
248 FreetypeGlyphsProvider::init()
250 std::lock_guard
<std::mutex
> lock(m_lib_mutex
);
254 const int error
= FT_Init_FreeType(&m_lib
);
256 boost::format err
= boost::format(_("Can't init FreeType! Error "
258 throw GnashException(err
.str());
264 FreetypeGlyphsProvider::close()
266 const int error
= FT_Done_FreeType(m_lib
);
268 log_error(_("Can't close FreeType! Error = %d"), error
);
274 FreetypeGlyphsProvider::getFontFilename(const std::string
&name
,
275 bool bold
, bool italic
, std::string
& filename
)
279 // only returns a default
281 if (B_OK
!= find_directory(B_BEOS_FONTS_DIRECTORY
, &bp
)) {
282 log_error(_("Failed to find fonts directory, using hard-coded "
283 "font filename \"%s\""), DEFAULT_FONTFILE
);
284 filename
= DEFAULT_FONTFILE
;
288 bp
.Append("ttfonts/DejaVuSans.ttf");
289 filename
= bp
.Path();
293 #ifdef HAVE_FONTCONFIG
295 log_error(_("Can't init fontconfig library, using hard-"
296 "coded font filename \"%s\""), DEFAULT_FONTFILE
);
297 filename
= DEFAULT_FONTFILE
;
304 FcPattern
* pat
= FcNameParse((const FcChar8
*)name
.c_str());
306 FcConfigSubstitute (nullptr, pat
, FcMatchPattern
);
309 FcPatternAddInteger (pat
, FC_SLANT
, FC_SLANT_ITALIC
);
313 FcPatternAddInteger (pat
, FC_WEIGHT
, FC_WEIGHT_BOLD
);
316 FcDefaultSubstitute (pat
);
319 match
= FcFontMatch (nullptr, pat
, &result
);
320 FcPatternDestroy (pat
);
322 FcFontSet
* fs
= nullptr;
324 fs
= FcFontSetCreate ();
325 FcFontSetAdd (fs
, match
);
329 #ifdef GNASH_DEBUG_DEVICEFONTS
330 log_debug("Found %d fonts matching the family %s (using "
331 "first)", fs
->nfont
, name
);
334 for (int j
= 0; j
< fs
->nfont
; j
++) {
336 if (FcPatternGetString (fs
->fonts
[j
], FC_FILE
, 0, &file
) != FcResultMatch
) {
337 #ifdef GNASH_DEBUG_DEVICEFONTS
338 log_debug("Matching font %d has unknown filename, skipping",
344 filename
= (char *)file
;
345 FcFontSetDestroy(fs
);
347 #ifdef GNASH_DEBUG_DEVICEFONTS
348 log_debug("Loading font from file %d", filename
);
354 FcFontSetDestroy(fs
);
357 log_error(_("No device font matches the name '%s', using hard-coded"
358 " font filename"), name
);
359 filename
= DEFAULT_FONTFILE
;
362 log_error(_("Font filename matching not implemented (no fontconfig"
363 " support built-in), using hard-coded font filename"),
365 filename
= DEFAULT_FONTFILE
;
370 #endif // USE_FREETYPE
374 std::unique_ptr
<FreetypeGlyphsProvider
>
375 FreetypeGlyphsProvider::createFace(const std::string
& name
, bool bold
, bool italic
)
378 std::unique_ptr
<FreetypeGlyphsProvider
> ret
;
381 ret
.reset(new FreetypeGlyphsProvider(name
, bold
, italic
));
383 catch (const GnashException
& ge
) {
384 log_error(ge
.what());
391 #else // ndef USE_FREETYPE
392 std::unique_ptr
<FreetypeGlyphsProvider
>
393 FreetypeGlyphsProvider::createFace(const std::string
&, bool, bool)
395 log_error(_("Freetype not supported"));
396 return std::unique_ptr
<FreetypeGlyphsProvider
>(NULL
);
401 FreetypeGlyphsProvider::unitsPerEM() const
404 return _face
->units_per_EM
;
408 FreetypeGlyphsProvider::descent() const
411 return std::abs(_face
->descender
);
415 FreetypeGlyphsProvider::ascent() const
418 return _face
->ascender
;
422 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string
& name
,
423 bool bold
, bool italic
)
428 if (m_lib
== nullptr)
433 std::string filename
;
434 if (getFontFilename(name
, bold
, italic
, filename
) == false)
436 boost::format msg
= boost::format(_("Can't find font file "
437 "for font '%s'")) % name
;
438 throw GnashException(msg
.str());
441 int error
= FT_New_Face(m_lib
, filename
.c_str(), 0, &_face
);
447 case FT_Err_Unknown_File_Format
:
449 boost::format msg
= boost::format(_("Font file '%s' "
450 "has bad format")) % filename
;
451 throw GnashException(msg
.str());
457 // TODO: return a better error message !
458 boost::format msg
= boost::format(_("Some error "
459 "opening font '%s'"))
461 throw GnashException(msg
.str());
466 // We want an EM of unitsPerEM, so if units_per_EM is different
468 scale
= (float)unitsPerEM()/_face
->units_per_EM
;
470 #ifdef GNASH_DEBUG_DEVICEFONTS
471 log_debug("EM square for font '%s' is %d, scale is this %g",
472 name
, _face
->units_per_EM
, scale
);
475 #else // ndef(USE_FREETYPE)
476 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string
&, bool, bool)
478 abort(); // should never be called
480 #endif // ndef USE_FREETYPE
483 std::unique_ptr
<SWF::ShapeRecord
>
484 FreetypeGlyphsProvider::getGlyph(std::uint16_t code
, float& advance
)
486 std::unique_ptr
<SWF::ShapeRecord
> glyph
;
488 FT_Error error
= FT_Load_Char(_face
, code
, FT_LOAD_NO_BITMAP
|
492 log_error(_("Error loading freetype outline glyph for char '%c' "
493 "(error: %d)"), code
, error
);
497 // Scale advance by current scale, to match expected output coordinate space
498 advance
= _face
->glyph
->metrics
.horiAdvance
* scale
;
499 #ifdef GNASH_DEBUG_DEVICEFONTS
500 log_debug("Advance value for glyph '%c' is %g (horiAdvance:%ld, "
501 "scale:%g)", code
, advance
,
502 _face
->glyph
->metrics
.horiAdvance
, scale
);
505 if ( _face
->glyph
->format
!= FT_GLYPH_FORMAT_OUTLINE
)
507 unsigned long gf
= _face
->glyph
->format
;
508 log_unimpl(_("FT_Load_Char() returned a glyph format != "
509 "FT_GLYPH_FORMAT_OUTLINE (%c%c%c%c)"),
510 static_cast<char>((gf
>>24)&0xff),
511 static_cast<char>((gf
>>16)&0xff),
512 static_cast<char>((gf
>>8)&0xff),
513 static_cast<char>(gf
&0xff));
517 FT_Outline
* outline
= &(_face
->glyph
->outline
);
519 FT_Outline_Funcs walk
;
520 walk
.move_to
= OutlineWalker::walkMoveTo
;
521 walk
.line_to
= OutlineWalker::walkLineTo
;
522 walk
.conic_to
= OutlineWalker::walkConicTo
;
523 walk
.cubic_to
= OutlineWalker::walkCubicTo
;
527 #ifdef DEBUG_OUTLINE_DECOMPOSITION
528 log_debug("Decomposing glyph outline for DisplayObject %u", code
);
531 glyph
.reset(new SWF::ShapeRecord
);
533 OutlineWalker
walker(*glyph
, scale
);
535 FT_Outline_Decompose(outline
, &walk
, &walker
);
536 #ifdef DEBUG_OUTLINE_DECOMPOSITION
538 // Don't use VM::get if this is to be re-enabled.
539 SWFRect bound
; sh
->compute_bound(&bound
, VM::get().getSWFVersion());
540 log_debug("Decomposed glyph for DisplayObject '%c' has bounds %s",
541 code
, bound
.toString());
549 #else // ndef(USE_FREETYPE)
551 std::unique_ptr
<SWF::ShapeRecord
>
552 FreetypeGlyphsProvider::getGlyph(std::uint16_t, float& advance
)
554 abort(); // should never be called...
558 FreetypeGlyphsProvider::~FreetypeGlyphsProvider()
562 if (FT_Done_Face(_face
) != 0) {
563 log_error(_("Could not release FT face resources"));