Fix test for bug #32625
[gnash.git] / libcore / FreetypeGlyphsProvider.cpp
blob77b87da2ecaef3fcec1c5437a30928e6c18781f6
1 // FreetypeGlyphsProvider.cpp: Freetype glyphs manager
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
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.
10 //
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.
15 //
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
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
24 #include "FreetypeGlyphsProvider.h"
26 #include <string>
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"
34 #include "log.h"
35 #include "FillStyle.h"
37 #ifdef USE_FREETYPE
38 # include <ft2build.h>
39 # include FT_OUTLINE_H
40 # include FT_BBOX_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
43 // support both
44 # if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 2
45 # define FT_CONST
46 # else
47 # define FT_CONST const
48 # endif
49 #endif
51 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
52 # define HAVE_FONTCONFIG 1
53 #endif
55 #ifdef HAVE_FONTCONFIG
56 # include <fontconfig/fontconfig.h>
57 # include <fontconfig/fcfreetype.h>
58 #endif
60 #ifdef HAIKU_HOST
61 # include <Path.h>
62 # include <FindDirectory.h>
63 #endif
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
71 namespace gnash {
73 #ifdef USE_FREETYPE
75 /// Outline glyph walker/decomposer, for drawing an FT_Outline to DynamicShape
77 /// See FT_Outline_Decompose function of freetype2 lib
78 ///
79 class OutlineWalker
82 public:
84 /// Create an outline walker drawing to the given DynamiShape
86 /// @param sh
87 /// The DynamicShape to draw to. Externally owned.
88 ///
89 /// @param scale
90 /// The scale to apply to coordinates.
91 /// This is to match an arbitrary EM
92 ///
93 OutlineWalker(SWF::ShapeRecord& sh, float scale)
95 _shape(sh),
96 _scale(scale),
97 _currPath(0),
98 _x(0),
99 _y(0)
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();
108 void finish()
110 _currPath->close();
113 ~OutlineWalker() {}
115 /// Callback function for the move_to member of FT_Outline_Funcs
116 static int
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
124 static int
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
132 static int
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.
144 static int
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);
152 private:
154 int moveTo(const FT_Vector* to)
156 #ifdef DEBUG_OUTLINE_DECOMPOSITION
157 log_debug("moveTo: %ld,%ld", to->x, to->y);
158 #endif
159 _x = static_cast<boost::int32_t>(to->x * _scale);
160 _y = - static_cast<boost::int32_t>(to->y * _scale);
161 _currPath->close();
162 _shape.addPath(Path(_x, _y, 1, 0, 0, false));
163 _currPath = &_shape.currentPath();
164 return 0;
167 int lineTo(const FT_Vector* to)
169 #ifdef DEBUG_OUTLINE_DECOMPOSITION
170 log_debug("lineTo: %ld,%ld", to->x, to->y);
171 #endif
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);
176 return 0;
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);
183 #endif
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);
190 return 0;
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);
199 #endif
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);
209 return 0;
212 void expandBounds(int x, int y) {
213 SWFRect bounds = _shape.getBounds();
214 if (_currPath->size() == 1) _currPath->expandBounds(bounds, 0, 6);
215 else {
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);
224 else {
225 bounds.expand_to_circle(ax, ay, 0);
226 bounds.expand_to_circle(cx, cy, 0);
228 _shape.setBounds(bounds);
231 SWF::ShapeRecord& _shape;
233 const float _scale;
235 Path* _currPath;
237 boost::int32_t _x, _y;
241 // statics
242 FT_Library FreetypeGlyphsProvider::m_lib = 0;
243 boost::mutex FreetypeGlyphsProvider::m_lib_mutex;
245 // static private
246 void
247 FreetypeGlyphsProvider::init()
249 boost::mutex::scoped_lock lock(m_lib_mutex);
251 if (m_lib) return;
253 const int error = FT_Init_FreeType(&m_lib);
254 if (error) {
255 boost::format err = boost::format(_("Can't init FreeType! Error "
256 "= %d")) % error;
257 throw GnashException(err.str());
261 // static private
262 void
263 FreetypeGlyphsProvider::close()
265 const int error = FT_Done_FreeType(m_lib);
266 if (error) {
267 log_error(_("Can't close FreeType! Error %d"), error);
271 // private
272 bool
273 FreetypeGlyphsProvider::getFontFilename(const std::string &name,
274 bool bold, bool italic, std::string& filename)
277 #ifdef HAIKU_HOST
278 // only returns a default
279 BPath bp;
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;
284 return true;
287 bp.Append("ttfonts/DejaVuSans.ttf");
288 filename = bp.Path();
289 return true;
290 #endif
292 #ifdef HAVE_FONTCONFIG
293 if (!FcInit ()) {
294 log_error(_("Can't init fontconfig library, using hard-"
295 "coded font filename \"%s\""), DEFAULT_FONTFILE);
296 filename = DEFAULT_FONTFILE;
297 return true;
298 //return false;
301 FcResult result;
303 FcPattern* pat = FcNameParse((const FcChar8*)name.c_str());
305 FcConfigSubstitute (0, pat, FcMatchPattern);
307 if (italic) {
308 FcPatternAddInteger (pat, FC_SLANT, FC_SLANT_ITALIC);
311 if (bold) {
312 FcPatternAddInteger (pat, FC_WEIGHT, FC_WEIGHT_BOLD);
315 FcDefaultSubstitute (pat);
317 FcPattern *match;
318 match = FcFontMatch (0, pat, &result);
319 FcPatternDestroy (pat);
321 FcFontSet* fs = NULL;
322 if (match) {
323 fs = FcFontSetCreate ();
324 FcFontSetAdd (fs, match);
327 if ( fs ) {
328 #ifdef GNASH_DEBUG_DEVICEFONTS
329 log_debug("Found %d fonts matching the family %s (using "
330 "first)", fs->nfont, name);
331 #endif
333 for (int j = 0; j < fs->nfont; j++) {
334 FcChar8 *file;
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);
338 #endif
339 continue;
342 filename = (char *)file;
343 FcFontSetDestroy(fs);
345 #ifdef GNASH_DEBUG_DEVICEFONTS
346 log_debug("Loading font from file %d", filename);
347 #endif
348 return true;
352 FcFontSetDestroy(fs);
355 log_error("No device font matches the name '%s', using hard-coded"
356 " font filename", name);
357 filename = DEFAULT_FONTFILE;
358 return true;
359 #else
360 log_error("Font filename matching not implemented (no fontconfig"
361 " support built-in), using hard-coded font filename",
362 DEFAULT_FONTFILE);
363 filename = DEFAULT_FONTFILE;
364 return true;
365 #endif
368 #endif // USE_FREETYPE
370 #ifdef USE_FREETYPE
371 // static
372 std::auto_ptr<FreetypeGlyphsProvider>
373 FreetypeGlyphsProvider::createFace(const std::string& name, bool bold, bool italic)
376 std::auto_ptr<FreetypeGlyphsProvider> ret;
378 try {
379 ret.reset(new FreetypeGlyphsProvider(name, bold, italic));
381 catch (const GnashException& ge) {
382 log_error(ge.what());
383 assert(! ret.get());
386 return ret;
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);
396 #endif
398 unsigned short
399 FreetypeGlyphsProvider::unitsPerEM() const
401 assert(_face);
402 return _face->units_per_EM;
405 float
406 FreetypeGlyphsProvider::descent() const
408 assert(_face);
409 return std::abs(_face->descender);
412 float
413 FreetypeGlyphsProvider::ascent() const
415 assert(_face);
416 return _face->ascender;
419 #ifdef USE_FREETYPE
420 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string& name,
421 bool bold, bool italic)
423 _face(NULL)
426 if (m_lib == NULL)
428 init();
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);
440 switch (error)
442 case 0:
443 break;
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());
450 break;
453 default:
455 // TODO: return a better error message !
456 boost::format msg = boost::format(_("Some error "
457 "opening font '%s'"))
458 % filename;
459 throw GnashException(msg.str());
460 break;
464 // We want an EM of unitsPerEM, so if units_per_EM is different
465 // we will scale
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);
470 #endif
472 #else // ndef(USE_FREETYPE)
473 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string&, bool, bool)
475 abort(); // should never be called
477 #endif // ndef USE_FREETYPE
479 #ifdef 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 |
486 FT_LOAD_NO_SCALE);
488 if (error) {
489 log_error("Error loading freetype outline glyph for char '%c' "
490 "(error: %d)", code, error);
491 return glyph;
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);
500 #endif
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));
511 return glyph;
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;
521 walk.shift = 0; // ?
522 walk.delta = 0; // ?
524 #ifdef DEBUG_OUTLINE_DECOMPOSITION
525 log_debug("Decomposing glyph outline for DisplayObject %u", code);
526 #endif
528 glyph.reset(new SWF::ShapeRecord);
530 OutlineWalker walker(*glyph, scale);
532 FT_Outline_Decompose(outline, &walk, &walker);
533 #ifdef DEBUG_OUTLINE_DECOMPOSITION
534 #if 0
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());
539 #endif
540 #endif
542 walker.finish();
544 return glyph;
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...
553 #endif
555 FreetypeGlyphsProvider::~FreetypeGlyphsProvider()
557 #ifdef USE_FREETYPE
558 if (_face) {
559 if (FT_Done_Face(_face) != 0) {
560 log_error("Could not release FT face resources");
563 #endif
566 } // namespace gnash