1 use std::ffi::{CString, CStr};
2 use std::os::raw::{c_uint, c_int, c_long};
3 use std::path::Path;
4 use std::error;
5 use std::error::Error;
6 use std::ffi::NulError;
7 use std::fmt;
8 use std::marker::PhantomData;
9 use ::surface::Surface;
10 use sys;
11 use sys::SDL_Surface;
12 use ::get_error;
13 use ::pixels::Color;
14 use ::rwops::RWops;
15
16 // Absolute paths are a workaround for https://github.com/rust-lang-nursery/bitflags/issues/39 .
17 bitflags! {
18 /// The styling of a font.
19 pub struct FontStyle: i32 {
20 const NORMAL = sys::ttf::TTF_STYLE_NORMAL as i32;
21 const BOLD = sys::ttf::TTF_STYLE_BOLD as i32;
22 const ITALIC = sys::ttf::TTF_STYLE_ITALIC as i32;
23 const UNDERLINE = sys::ttf::TTF_STYLE_UNDERLINE as i32;
24 const STRIKETHROUGH = sys::ttf::TTF_STYLE_STRIKETHROUGH as i32;
25 }
26 }
27
28 /// Information about the hinting of a font.
29 /// See [wikipedia](https://en.wikipedia.org/wiki/Font_hinting)
30 #[repr(i32)]
31 #[derive(Debug, PartialEq, Clone)]
32 pub enum Hinting {
33 Normal = sys::ttf::TTF_HINTING_NORMAL as i32,
34 Light = sys::ttf::TTF_HINTING_LIGHT as i32,
35 Mono = sys::ttf::TTF_HINTING_MONO as i32,
36 None = sys::ttf::TTF_HINTING_NONE as i32,
37 }
38
39 /// Information about a specific glyph (character) in a font face.
40 #[derive(Debug, PartialEq, Clone)]
41 pub struct GlyphMetrics {
42 pub minx: i32,
43 pub maxx: i32,
44 pub miny: i32,
45 pub maxy: i32,
46 pub advance: i32
47 }
48
49 /// The result of an `SDL2_TTF` font operation.
50 pub type FontResult<T> = Result<T, FontError>;
51
52 /// A font-related error.
53 #[derive(Debug)]
54 pub enum FontError {
55 /// A Latin-1 encoded byte string is invalid.
56 InvalidLatin1Text(NulError),
57 /// A SDL2-related error occured.
58 SdlError(String),
59 }
60
61 impl error::Error for FontError {
description(&self) -> &str62 fn description(&self) -> &str {
63 match *self {
64 FontError::InvalidLatin1Text(ref error) => {
65 error.description()
66 },
67 FontError::SdlError(ref message) => {
68 message
69 },
70 }
71 }
72
cause(&self) -> Option<&error::Error>73 fn cause(&self) -> Option<&error::Error> {
74 match *self {
75 FontError::InvalidLatin1Text(ref error) => {
76 Some(error)
77 },
78 FontError::SdlError(_) => {
79 None
80 },
81 }
82 }
83 }
84
85 impl fmt::Display for FontError {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>86 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
87 match *self {
88 FontError::InvalidLatin1Text(ref err) => {
89 write!(f, "Invalid Latin-1 bytes: {}", err.description())
90 },
91 FontError::SdlError(ref msg) => {
92 write!(f, "SDL2 error: {}", msg)
93 },
94 }
95
96 }
97 }
98
99 /// A renderable piece of text in the UTF8 or Latin-1 format.
100 enum RenderableText<'a> {
101 Utf8(&'a str),
102 Latin1(&'a [u8]),
103 Char(String),
104 }
105 impl<'a> RenderableText<'a> {
106 /// Converts the given text to a c-style string if possible.
convert(&self) -> FontResult<CString>107 fn convert(&self) -> FontResult<CString> {
108 match *self {
109 RenderableText::Utf8(text) => {
110 Ok(CString::new(text).unwrap())
111 },
112 RenderableText::Latin1(bytes) => {
113 match CString::new(bytes) {
114 Err(err) => {
115 Err(FontError::InvalidLatin1Text(err))
116 },
117 Ok(cstring) => {
118 Ok(cstring)
119 }
120 }
121 },
122 RenderableText::Char(ref string) => {
123 Ok(CString::new(string.as_bytes()).unwrap())
124 }
125 }
126 }
127 }
128
129 /// A builder for a font rendering.
130 #[must_use]
131 pub struct PartialRendering<'f, 'text> {
132 text: RenderableText<'text>,
133 font: &'f Font<'f, 'f>,
134 }
135
136 /// Converts the given raw pointer to a surface.
convert_to_surface<'a>(raw: *mut SDL_Surface) -> FontResult<Surface<'a>>137 fn convert_to_surface<'a>(raw: *mut SDL_Surface) -> FontResult<Surface<'a>> {
138 if (raw as *mut ()).is_null() {
139 Err(FontError::SdlError(get_error()))
140 } else {
141 Ok(unsafe {
142 Surface::from_ll(raw)
143 })
144 }
145 }
146
147 impl<'f,'text> PartialRendering<'f,'text> {
148 /// Renders the text in *solid* mode.
149 /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42)
150 /// for an explanation.
solid<'b, T>(self, color: T ) -> FontResult<Surface<'b>> where T: Into<Color>151 pub fn solid<'b, T>(self, color: T )
152 -> FontResult<Surface<'b>> where T: Into<Color> {
153 let source = try!(self.text.convert());
154 let color = color.into().into();
155 let raw = unsafe {
156 match self.text {
157 RenderableText::Utf8(_) | RenderableText::Char(_) => {
158 sys::ttf::TTF_RenderUTF8_Solid(self.font.raw(),
159 source.as_ptr(), color)
160 },
161 RenderableText::Latin1(_) => {
162 sys::ttf::TTF_RenderText_Solid(self.font.raw(),
163 source.as_ptr(), color)
164 },
165 }
166 };
167 convert_to_surface(raw)
168 }
169
170 /// Renders the text in *shaded* mode.
171 /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42)
172 /// for an explanation.
shaded<'b, T>(self, color: T, background: T) -> FontResult<Surface<'b>> where T: Into<Color>173 pub fn shaded<'b, T>(self, color: T, background: T)
174 -> FontResult<Surface<'b>> where T: Into<Color> {
175 let source = try!(self.text.convert());
176 let foreground = color.into().into();
177 let background = background.into().into();
178 let raw = unsafe {
179 match self.text {
180 RenderableText::Utf8(_) | RenderableText::Char(_) => {
181 sys::ttf::TTF_RenderUTF8_Shaded(self.font.raw(),
182 source.as_ptr(), foreground, background)
183 },
184 RenderableText::Latin1(_) => {
185 sys::ttf::TTF_RenderText_Shaded(self.font.raw(),
186 source.as_ptr(), foreground, background)
187 },
188 }
189 };
190 convert_to_surface(raw)
191 }
192
193 /// Renders the text in *blended* mode.
194 /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42)
195 /// for an explanation.
blended<'b, T>(self, color: T) -> FontResult<Surface<'b>> where T: Into<Color>196 pub fn blended<'b, T>(self, color: T)
197 -> FontResult<Surface<'b>> where T: Into<Color> {
198 let source = try!(self.text.convert());
199 let color = color.into().into();
200 let raw = unsafe {
201 match self.text {
202 RenderableText::Utf8(_) | RenderableText::Char(_) => {
203 sys::ttf::TTF_RenderUTF8_Blended(self.font.raw(),
204 source.as_ptr(), color)
205 },
206 RenderableText::Latin1(_) => {
207 sys::ttf::TTF_RenderText_Blended(self.font.raw(),
208 source.as_ptr(), color)
209 },
210 }
211 };
212 convert_to_surface(raw)
213 }
214
215 /// Renders the text in *blended* mode but wrapping the words if the width
216 /// exceeds the given maximum width.
217 /// See [the SDL2_TTF docs](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html#SEC42)
218 /// for an explanation of the mode.
blended_wrapped<'b, T>(self, color: T, wrap_max_width: u32) -> FontResult<Surface<'b>> where T: Into<Color>219 pub fn blended_wrapped<'b, T>(self, color: T, wrap_max_width: u32)
220 -> FontResult<Surface<'b>> where T: Into<Color> {
221 let source = try!(self.text.convert());
222 let color = color.into().into();
223 let raw = unsafe {
224 match self.text {
225 RenderableText::Utf8(_) | RenderableText::Char(_) => {
226 sys::ttf::TTF_RenderUTF8_Blended_Wrapped(self.font.raw(),
227 source.as_ptr(), color, wrap_max_width)
228 },
229 RenderableText::Latin1(_) => {
230 sys::ttf::TTF_RenderText_Blended_Wrapped(self.font.raw(),
231 source.as_ptr(), color, wrap_max_width)
232 },
233 }
234 };
235 convert_to_surface(raw)
236 }
237 }
238
239 /// A loaded TTF font.
240 pub struct Font<'ttf_module,'rwops> {
241 raw: *mut sys::ttf::TTF_Font,
242 // RWops is only stored here because it must not outlive
243 // the Font struct, and this RWops should not be used by
244 // anything else
245 // None means that the RWops is handled by SDL itself,
246 // and Some(rwops) means that the RWops is handled by the Rust
247 // side
248 #[allow(dead_code)]
249 rwops: Option<RWops<'rwops>>,
250 #[allow(dead_code)]
251 _marker: PhantomData<&'ttf_module ()>,
252 }
253
254
255
256 impl<'ttf,'r> Drop for Font<'ttf,'r> {
drop(&mut self)257 fn drop(&mut self) {
258 unsafe {
259 // avoid close font after quit()
260 if sys::ttf::TTF_WasInit() == 1 {
261 sys::ttf::TTF_CloseFont(self.raw);
262 }
263 }
264 }
265 }
266
267 /// Internally used to load a font (for internal visibility).
internal_load_font<'ttf,P:AsRef<Path>>(path: P, ptsize: u16) -> Result<Font<'ttf,'static>, String>268 pub fn internal_load_font<'ttf,P:AsRef<Path>>(path: P, ptsize: u16) -> Result<Font<'ttf,'static>, String> {
269 unsafe {
270 let cstring = CString::new(path.as_ref().to_str().unwrap()).unwrap();
271 let raw = sys::ttf::TTF_OpenFont(cstring.as_ptr(), ptsize as c_int);
272 if raw.is_null() {
273 Err(get_error())
274 } else {
275 Ok(Font { raw: raw, rwops: None, _marker: PhantomData })
276 }
277 }
278 }
279
280 /// Internally used to load a font (for internal visibility).
internal_load_font_from_ll<'ttf,'r, R>(raw: *mut sys::ttf::TTF_Font, rwops: R) -> Font<'ttf,'r> where R: Into<Option<RWops<'r>>>281 pub fn internal_load_font_from_ll<'ttf,'r, R>(raw: *mut sys::ttf::TTF_Font, rwops: R)
282 -> Font<'ttf,'r>
283 where R: Into<Option<RWops<'r>>> {
284 Font { raw: raw, rwops: rwops.into(), _marker: PhantomData }
285 }
286
287 /// Internally used to load a font (for internal visibility).
internal_load_font_at_index<'ttf,P: AsRef<Path>>(path: P, index: u32, ptsize: u16) -> Result<Font<'ttf, 'static>, String>288 pub fn internal_load_font_at_index<'ttf,P: AsRef<Path>>(path: P, index: u32, ptsize: u16)
289 -> Result<Font<'ttf, 'static>, String> {
290 unsafe {
291 let cstring = CString::new(path.as_ref().to_str().unwrap().as_bytes())
292 .unwrap();
293 let raw = sys::ttf::TTF_OpenFontIndex(cstring.as_ptr(),
294 ptsize as c_int, index as c_long);
295 if raw.is_null() {
296 Err(get_error())
297 } else {
298 Ok(Font { raw: raw, rwops: None, _marker: PhantomData})
299 }
300 }
301 }
302
303 impl<'ttf,'r> Font<'ttf,'r> {
304 /// Returns the underlying C font object.
raw(&self) -> *mut sys::ttf::TTF_Font305 unsafe fn raw(&self) -> *mut sys::ttf::TTF_Font {
306 self.raw
307 }
308
309 /// Starts specifying a rendering of the given UTF-8-encoded text.
render<'a, 'b>(&'a self, text: &'b str) -> PartialRendering<'a, 'b>310 pub fn render<'a, 'b>(&'a self, text: &'b str) -> PartialRendering<'a, 'b> {
311 PartialRendering {
312 text: RenderableText::Utf8(text),
313 font: self,
314 }
315 }
316
317 /// Starts specifying a rendering of the given Latin-1-encoded text.
render_latin1<'a, 'b>(&'a self, text: &'b [u8]) -> PartialRendering<'a, 'b>318 pub fn render_latin1<'a, 'b>(&'a self, text: &'b [u8]) -> PartialRendering<'a, 'b> {
319 PartialRendering {
320 text: RenderableText::Latin1(text),
321 font: self,
322 }
323 }
324
325 /// Starts specifying a rendering of the given UTF-8-encoded character.
render_char<'a>(&'a self, ch: char) -> PartialRendering<'a, 'static>326 pub fn render_char<'a>(&'a self, ch: char) -> PartialRendering<'a, 'static> {
327 let mut s = String::new();
328 s.push(ch);
329 PartialRendering {
330 text: RenderableText::Char(s),
331 font: self,
332 }
333 }
334
335 /// Returns the width and height of the given text when rendered using this
336 /// font.
size_of(&self, text: &str) -> FontResult<(u32, u32)>337 pub fn size_of(&self, text: &str) -> FontResult<(u32, u32)> {
338 let c_string = try!(RenderableText::Utf8(text).convert());
339 let (res, size) = unsafe {
340 let mut w = 0; // mutated by C code
341 let mut h = 0; // mutated by C code
342 let ret = sys::ttf::TTF_SizeUTF8(self.raw, c_string.as_ptr(), &mut w, &mut h);
343 (ret, (w as u32, h as u32))
344 };
345 if res == 0 {
346 Ok(size)
347 } else {
348 Err(FontError::SdlError(get_error()))
349 }
350 }
351
352 /// Returns the width and height of the given text when rendered using this
353 /// font.
354 #[allow(unused_mut)]
size_of_latin1(&self, text: &[u8]) -> FontResult<(u32, u32)>355 pub fn size_of_latin1(&self, text: &[u8])
356 -> FontResult<(u32, u32)> {
357 let c_string = try!(RenderableText::Latin1(text).convert());
358 let (res, size) = unsafe {
359 let mut w : i32 = 0; // mutated by C code
360 let mut h : i32 = 0; // mutated by C code
361 let ret = sys::ttf::TTF_SizeText(self.raw, c_string.as_ptr(), &w as *const _ as *mut i32, &h as *const _ as *mut i32);
362 (ret, (w as u32, h as u32))
363 };
364 if res == 0 {
365 Ok(size)
366 } else {
367 Err(FontError::SdlError(get_error()))
368 }
369 }
370
371 /// Returns the width and height of the given text when rendered using this
372 /// font.
size_of_char(&self, ch: char) -> FontResult<(u32, u32)>373 pub fn size_of_char(&self, ch: char) -> FontResult<(u32, u32)> {
374 let mut s = String::new();
375 s.push(ch);
376 self.size_of(&s)
377 }
378
379 /// Returns the font's style flags.
get_style(&self) -> FontStyle380 pub fn get_style(&self) -> FontStyle {
381 unsafe {
382 let raw = sys::ttf::TTF_GetFontStyle(self.raw);
383 FontStyle::from_bits_truncate(raw as i32)
384 }
385 }
386
387 /// Sets the font's style flags.
set_style(&mut self, styles: FontStyle)388 pub fn set_style(&mut self, styles: FontStyle) {
389 unsafe {
390 sys::ttf::TTF_SetFontStyle(self.raw, styles.bits() as c_int)
391 }
392 }
393
394 /// Returns the width of the font's outline.
get_outline_width(&self) -> u16395 pub fn get_outline_width(&self) -> u16 {
396 unsafe {
397 sys::ttf::TTF_GetFontOutline(self.raw) as u16
398 }
399 }
400
401 /// Sets the width of the font's outline.
set_outline_width(&mut self, width: u16)402 pub fn set_outline_width(&mut self, width: u16) {
403 unsafe {
404 sys::ttf::TTF_SetFontOutline(self.raw, width as c_int)
405 }
406 }
407
408 /// Returns the font's freetype hints.
get_hinting(&self) -> Hinting409 pub fn get_hinting(&self) -> Hinting {
410 unsafe {
411 match sys::ttf::TTF_GetFontHinting(self.raw) as c_uint {
412 sys::ttf::TTF_HINTING_NORMAL => Hinting::Normal,
413 sys::ttf::TTF_HINTING_LIGHT => Hinting::Light,
414 sys::ttf::TTF_HINTING_MONO => Hinting::Mono,
415 sys::ttf::TTF_HINTING_NONE | _ => Hinting::None
416 }
417 }
418 }
419
420 /// Sets the font's freetype hints.
set_hinting(&mut self, hinting: Hinting)421 pub fn set_hinting(&mut self, hinting: Hinting) {
422 unsafe {
423 sys::ttf::TTF_SetFontHinting(self.raw, hinting as c_int)
424 }
425 }
426
427 /// Returns whether the font is kerning.
get_kerning(&self) -> bool428 pub fn get_kerning(&self) -> bool {
429 unsafe {
430 sys::ttf::TTF_GetFontKerning(self.raw) != 0
431 }
432 }
433
434 /// Sets whether the font should use kerning.
set_kerning(&mut self, kerning: bool)435 pub fn set_kerning(&mut self, kerning: bool) {
436 unsafe {
437 sys::ttf::TTF_SetFontKerning(self.raw, kerning as c_int)
438 }
439 }
440
height(&self) -> i32441 pub fn height(&self) -> i32 {
442 //! Get font maximum total height.
443 unsafe {
444 sys::ttf::TTF_FontHeight(self.raw) as i32
445 }
446 }
447
448 /// Returns the font's highest ascent (height above base).
ascent(&self) -> i32449 pub fn ascent(&self) -> i32 {
450 unsafe {
451 sys::ttf::TTF_FontAscent(self.raw) as i32
452 }
453 }
454
455 /// Returns the font's lowest descent (height below base).
456 /// This is a negative number.
descent(&self) -> i32457 pub fn descent(&self) -> i32 {
458 unsafe {
459 sys::ttf::TTF_FontDescent(self.raw) as i32
460 }
461 }
462
463 /// Returns the recommended line spacing for text rendered with this font.
recommended_line_spacing(&self) -> i32464 pub fn recommended_line_spacing(&self) -> i32 {
465 unsafe {
466 sys::ttf::TTF_FontLineSkip(self.raw) as i32
467 }
468 }
469
470 /// Returns the number of faces in this font.
face_count(&self) -> u16471 pub fn face_count(&self) -> u16 {
472 unsafe {
473 sys::ttf::TTF_FontFaces(self.raw) as u16
474 }
475 }
476
477 /// Returns whether the font is monospaced.
face_is_fixed_width(&self) -> bool478 pub fn face_is_fixed_width(&self) -> bool {
479 unsafe {
480 sys::ttf::TTF_FontFaceIsFixedWidth(self.raw) != 0
481 }
482 }
483
484 /// Returns the family name of the current font face.
face_family_name(&self) -> Option<String>485 pub fn face_family_name(&self) -> Option<String> {
486 unsafe {
487 // not owns buffer
488 let cname = sys::ttf::TTF_FontFaceFamilyName(self.raw);
489 if cname.is_null() {
490 None
491 } else {
492 Some(String::from_utf8_lossy(CStr::from_ptr(cname).to_bytes()).to_string())
493 }
494 }
495 }
496
497 /// Returns the name of the current font face.
face_style_name(&self) -> Option<String>498 pub fn face_style_name(&self) -> Option<String> {
499 unsafe {
500 let cname = sys::ttf::TTF_FontFaceStyleName(self.raw);
501 if cname.is_null() {
502 None
503 } else {
504 Some(String::from_utf8_lossy(CStr::from_ptr(cname).to_bytes()).to_string())
505 }
506 }
507 }
508
509 /// Returns the index of the given character in this font face.
find_glyph(&self, ch: char) -> Option<u16>510 pub fn find_glyph(&self, ch: char) -> Option<u16> {
511 unsafe {
512 let ret = sys::ttf::TTF_GlyphIsProvided(self.raw, ch as u16);
513 if ret == 0 {
514 None
515 } else {
516 Some(ret as u16)
517 }
518 }
519 }
520
521 /// Returns the glyph metrics of the given character in this font face.
find_glyph_metrics(&self, ch: char) -> Option<GlyphMetrics>522 pub fn find_glyph_metrics(&self, ch: char) -> Option<GlyphMetrics> {
523 let minx = 0;
524 let maxx = 0;
525 let miny = 0;
526 let maxy = 0;
527 let advance = 0;
528 let ret = unsafe {
529 sys::ttf::TTF_GlyphMetrics(
530 self.raw,
531 ch as u16,
532 &minx as *const _ as *mut _,
533 &maxx as *const _ as *mut _,
534 &miny as *const _ as *mut _,
535 &maxy as *const _ as *mut _,
536 &advance as *const _ as *mut _
537 )
538 };
539 if ret == 0 {
540 Some(GlyphMetrics {
541 minx: minx as i32, maxx: maxx as i32, miny: miny as i32,
542 maxy: maxy as i32, advance: advance as i32
543 } )
544 } else {
545 None
546 }
547 }
548 }
549