1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 #include "ft2_font.hpp"
27 #include "ft2_bitmap.hpp"
29 #include "../utils/ustring.hpp"
32 #include <fribidi/fribidi.h>
36 FT2Font::FT2Font( intf_thread_t
*pIntf
, const string
&rName
, int size
):
37 GenericFont( pIntf
), m_name( rName
), m_buffer( NULL
), m_size( size
),
38 m_lib( NULL
), m_face( NULL
)
45 GlyphMap_t::iterator iter
;
46 for( iter
= m_glyphCache
.begin(); iter
!= m_glyphCache
.end(); ++iter
)
47 FT_Done_Glyph( (*iter
).second
.m_glyph
);
48 if( m_face
) FT_Done_Face( m_face
);
49 if( m_lib
) FT_Done_FreeType( m_lib
);
58 if( err
= FT_Init_FreeType( &m_lib
) )
60 msg_Err( getIntf(), "failed to initialize freetype (%s)",
61 ft2_strerror( err
) );
65 FILE *file
= fopen( m_name
.c_str(), "rb" );
68 msg_Dbg( getIntf(), "failed to open font %s (%s)",
69 m_name
.c_str(), strerror(errno
) );
72 msg_Dbg( getIntf(), "loading font %s", m_name
.c_str() );
74 fseek( file
, 0, SEEK_END
);
75 long size
= ftell( file
);
80 msg_Dbg( getIntf(), "fseek loading font %s (%s)",
81 m_name
.c_str(), strerror(errno
) );
86 m_buffer
= new (std::nothrow
) char[size
];
93 fread( m_buffer
, size
, 1, file
);
97 err
= FT_New_Memory_Face( m_lib
, (const FT_Byte
*)m_buffer
, size
, 0,
99 if ( err
== FT_Err_Unknown_File_Format
)
101 msg_Err( getIntf(), "unsupported font format (%s)", m_name
.c_str() );
106 msg_Err( getIntf(), "error opening font %s (%s)",
107 m_name
.c_str(), ft2_strerror(err
) );
111 // Select the charset
112 if( err
= FT_Select_Charmap( m_face
, ft_encoding_unicode
) )
114 msg_Err( getIntf(), "font %s has no UNICODE table (%s)",
115 m_name
.c_str(), ft2_strerror(err
) );
119 // Set the pixel size
120 if( err
= FT_Set_Pixel_Sizes( m_face
, 0, m_size
) )
122 msg_Warn( getIntf(), "cannot set a pixel size of %d for %s (%s)",
123 m_size
, m_name
.c_str(), ft2_strerror(err
) );
126 // Get the font metrucs
127 m_height
= m_face
->size
->metrics
.height
>> 6;
128 m_ascender
= m_face
->size
->metrics
.ascender
>> 6;
129 m_descender
= m_face
->size
->metrics
.descender
>> 6;
135 GenericBitmap
*FT2Font::drawString( const UString
&rString
, uint32_t color
,
141 int width1
= 0, width2
= 0;
142 int yMin
= 0, yMax
= 0;
143 uint32_t *pString
= (uint32_t*)rString
.u_str();
145 // Check if freetype has been initialized
151 // Get the length of the string
152 int len
= rString
.length();
154 // Use fribidi if available
156 uint32_t *pFribidiString
= NULL
;
159 pFribidiString
= new uint32_t[len
+1];
160 FriBidiCharType baseDir
= FRIBIDI_TYPE_ON
;
161 fribidi_log2vis( (FriBidiChar
*)pString
, len
, &baseDir
,
162 (FriBidiChar
*)pFribidiString
, 0, 0, 0 );
163 pString
= pFribidiString
;
167 // Array of glyph bitmaps and position
168 FT_BitmapGlyphRec
**glyphs
= new FT_BitmapGlyphRec
*[len
];
169 int *pos
= new int[len
];
171 // Does the font support kerning ?
172 FT_Bool useKerning
= FT_HAS_KERNING( m_face
);
175 // Index of the last glyph when the text is truncated with trailing ...
177 // Position of the first trailing dot
179 /// Get the dot glyph
180 Glyph_t
&dotGlyph
= getGlyph( '.' );
182 // First, render all the glyphs
183 for( n
= 0; n
< len
; n
++ )
186 // Get the glyph for this character
187 Glyph_t
&glyph
= getGlyph( code
);
188 glyphs
[n
] = (FT_BitmapGlyphRec
*)(glyph
.m_glyph
);
190 // Retrieve kerning distance and move pen position
191 if( useKerning
&& previous
&& glyph
.m_index
)
194 FT_Get_Kerning( m_face
, previous
, glyph
.m_index
,
195 ft_kerning_default
, &delta
);
196 penX
+= delta
.x
>> 6;
200 width1
= penX
+ glyph
.m_size
.xMax
- glyph
.m_size
.xMin
;
201 yMin
= __MIN( yMin
, glyph
.m_size
.yMin
);
202 yMax
= __MAX( yMax
, glyph
.m_size
.yMax
);
205 penX
+= glyph
.m_advance
;
208 previous
= glyph
.m_index
;
212 // Check if the truncated text with the '...' fit in the maxWidth
217 FT_Get_Kerning( m_face
, glyph
.m_index
, dotGlyph
.m_index
,
218 ft_kerning_default
, &delta
);
219 curX
+= delta
.x
>> 6;
221 int dotWidth
= 2 * dotGlyph
.m_advance
+
222 dotGlyph
.m_size
.xMax
- dotGlyph
.m_size
.xMin
;
223 if( curX
+ dotWidth
< maxWidth
)
225 width2
= curX
+ dotWidth
;
237 // Stop here if the text is too large
238 if( maxWidth
!= -1 && width1
> maxWidth
)
247 delete[] pFribidiString
;
251 // Adjust the size for vertical padding
252 yMax
= __MAX( yMax
, m_ascender
);
253 yMin
= __MIN( yMin
, m_descender
);
256 FT2Bitmap
*pBmp
= new FT2Bitmap( getIntf(), __MIN( width1
, width2
),
259 // Draw the glyphs on the bitmap
260 for( n
= 0; n
< maxIndex
; n
++ )
262 FT_BitmapGlyphRec
*pBmpGlyph
= (FT_BitmapGlyphRec
*)glyphs
[n
];
263 // Draw the glyph on the bitmap
264 pBmp
->draw( pBmpGlyph
->bitmap
, pos
[n
], yMax
- pBmpGlyph
->top
, color
);
266 // Draw the trailing dots if the text is truncated
269 int penX
= firstDotX
;
270 FT_BitmapGlyphRec
*pBmpGlyph
= (FT_BitmapGlyphRec
*)dotGlyph
.m_glyph
;
271 for( n
= 0; n
< 3; n
++ )
273 // Draw the glyph on the bitmap
274 pBmp
->draw( pBmpGlyph
->bitmap
, penX
, yMax
- pBmpGlyph
->top
,
276 penX
+= dotGlyph
.m_advance
;
287 FT2Font::Glyph_t
&FT2Font::getGlyph( uint32_t code
) const
289 // Try to find the glyph in the cache
290 GlyphMap_t::iterator iter
= m_glyphCache
.find( code
);
291 if( iter
!= m_glyphCache
.end() )
293 return (*iter
).second
;
297 // Add a new glyph in the cache
298 Glyph_t
&glyph
= m_glyphCache
[code
];
300 // Load and render the glyph
301 glyph
.m_index
= FT_Get_Char_Index( m_face
, code
);
302 FT_Load_Glyph( m_face
, glyph
.m_index
, FT_LOAD_DEFAULT
);
303 FT_Get_Glyph( m_face
->glyph
, &glyph
.m_glyph
);
304 FT_Glyph_Get_CBox( glyph
.m_glyph
, ft_glyph_bbox_pixels
,
306 glyph
.m_advance
= m_face
->glyph
->advance
.x
>> 6;
307 FT_Glyph_To_Bitmap( &glyph
.m_glyph
, ft_render_mode_normal
, NULL
, 1 );