GG
Font.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /* GG is a GUI for SDL and OpenGL.
3  Copyright (C) 2003-2008 T. Zachary Laine
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public License
7  as published by the Free Software Foundation; either version 2.1
8  of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free
17  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  02111-1307 USA
19 
20  If you do not wish to comply with the terms of the LGPL please
21  contact the author as other terms are available for a fee.
22 
23  Zach Laine
24  whatwasthataddress@gmail.com */
25 
32 #ifndef _GG_Font_h_
33 #define _GG_Font_h_
34 
35 #include <GG/AlignmentFlags.h>
36 #include <GG/FontFwd.h>
37 #include <GG/Texture.h>
38 #include <GG/UnicodeCharsets.h>
39 
40 #include <set>
41 #include <stack>
42 
43 #include <boost/unordered_map.hpp>
44 
45 
46 struct FT_FaceRec_;
47 typedef struct FT_FaceRec_* FT_Face;
48 typedef int FT_Error;
49 
50 namespace GG {
51 
54 GG_API std::string RgbaTag(const Clr& c);
55 
56 
122 class GG_API Font
123 {
124 public:
127  class GG_API Substring
128  {
129  public:
130  typedef std::pair<std::string::const_iterator, std::string::const_iterator> IterPair;
131 
133  Substring();
134 
136  Substring(const std::string& str_,
137  std::string::const_iterator first_,
138  std::string::const_iterator second_);
139 
142  Substring(const std::string& str_, const IterPair& pair);
143 
145  std::string::const_iterator begin() const;
146 
148  std::string::const_iterator end() const;
149 
151  bool empty() const;
152 
154  std::size_t size() const;
155 
157  operator std::string() const;
158 
160  bool operator==(const std::string& rhs) const;
161 
163  bool operator!=(const std::string& rhs) const;
164 
168  Substring& operator+=(const IterPair& rhs);
169 
170  private:
171  const std::string* str;
172  std::ptrdiff_t first;
173  std::ptrdiff_t second;
174 
175  static const std::string EMPTY_STRING;
176  };
177 
180  struct GG_API TextElement
181  {
189 
194  NEWLINE
195  };
196 
199  TextElement(bool ws, bool nl);
200 
201  virtual ~TextElement();
202 
204  virtual TextElementType Type() const;
205 
207  X Width() const;
208 
209  /* Returns the number of characters in the original string that the
210  element represents. */
211  StrSize StringSize() const;
212 
215  CPSize CodePointSize() const;
216 
219 
220  std::vector<X> widths;
221  const bool whitespace;
222  const bool newline;
223 
224  protected:
225  TextElement();
226 
227  private:
228  mutable X cached_width;
229  };
230 
233  struct GG_API FormattingTag : TextElement
234  {
237  FormattingTag(bool close);
238 
239  virtual TextElementType Type() const;
240 
243  std::vector<Substring> params;
244 
247 
249  const bool close_tag;
250 
251  private:
252  FormattingTag();
253  };
254 
260  struct GG_API LineData
261  {
262  LineData();
263 
267  struct GG_API CharData
268  {
270  CharData();
271 
273  CharData(X extent_, StrSize str_index, StrSize str_size, CPSize cp_index,
274  const std::vector<boost::shared_ptr<TextElement> >& tags_);
275 
279 
283 
287 
290 
293  std::vector<boost::shared_ptr<FormattingTag> > tags;
294  };
295 
296  X Width() const;
297  bool Empty() const;
298 
300  std::vector<CharData> char_data;
301 
304  Alignment justification;
305  };
306 
312  struct GG_API RenderState
313  {
314  RenderState();
315 
317  std::size_t use_italics;
318 
320  std::size_t draw_underline;
321 
323  std::stack<Clr> colors;
324  };
325 
327 
330  Font(const std::string& font_filename, unsigned int pts);
331 
336  Font(const std::string& font_filename, unsigned int pts,
337  const std::vector<unsigned char>& file_contents);
338 
343  template <class CharSetIter>
344  Font(const std::string& font_filename, unsigned int pts,
345  CharSetIter first, CharSetIter last);
346 
352  template <class CharSetIter>
353  Font(const std::string& font_filename, unsigned int pts,
354  const std::vector<unsigned char>& file_contents,
355  CharSetIter first, CharSetIter last);
356 
357  ~Font();
358 
359 
361 
362  const std::string& FontName() const;
363 
366  unsigned int PointSize() const;
367 
369  const std::vector<UnicodeCharset>& UnicodeCharsets() const;
370 
372  Y Ascent() const;
373 
375  Y Descent() const;
376 
378  Y Height() const;
379 
382  Y Lineskip() const;
383 
385  X SpaceWidth() const;
386 
390  X RenderGlyph(const Pt& pt, char c) const;
391 
393  X RenderGlyph(const Pt& pt, boost::uint32_t c) const;
394 
397  X RenderText(const Pt& pt, const std::string& text) const;
398 
400  void RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format,
401  const std::vector<LineData>* line_data = 0, RenderState* render_state = 0) const;
402 
407  void RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format,
408  const std::vector<LineData>& line_data, RenderState& render_state,
409  std::size_t begin_line, CPSize begin_char,
410  std::size_t end_line, CPSize end_char) const;
411 
414  void ProcessTagsBefore(const std::vector<LineData>& line_data, RenderState& render_state,
415  std::size_t begin_line, CPSize begin_char) const;
416 
419  Pt DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
420  std::vector<LineData>& line_data) const;
421 
425  Pt DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
426  std::vector<LineData>& line_data,
427  std::vector<boost::shared_ptr<TextElement> >& text_elements) const;
428 
434  Pt DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
435  const std::vector<boost::shared_ptr<TextElement> >& text_elements,
436  std::vector<LineData>& line_data) const;
437 
441  Pt TextExtent(const std::string& text, Flags<TextFormat> format = FORMAT_NONE,
442  X box_width = X0) const;
443 
445  Pt TextExtent(const std::string& text, const std::vector<LineData>& line_data) const;
447 
451  static void RegisterKnownTag(const std::string& tag);
452 
455  static void RemoveKnownTag(const std::string& tag);
456 
459  static void ClearKnownTags();
460 
462 
463  GG_ABSTRACT_EXCEPTION(Exception);
464 
466  GG_CONCRETE_EXCEPTION(BadFile, GG::Font, Exception);
467 
469  GG_CONCRETE_EXCEPTION(InvalidPointSize, GG::Font, Exception);
470 
473  GG_CONCRETE_EXCEPTION(UnscalableFont, GG::Font, Exception);
474 
477  GG_CONCRETE_EXCEPTION(BadFace, GG::Font, Exception);
478 
481  GG_CONCRETE_EXCEPTION(BadPointSize, GG::Font, Exception);
482 
485  GG_CONCRETE_EXCEPTION(BadGlyph, GG::Font, Exception);
487 
492  static void ThrowBadGlyph(const std::string& format_str, boost::uint32_t c);
493 
494 protected:
496  Font();
497 
498 
499 private:
502  struct Glyph
503  {
504  Glyph();
505  Glyph(const boost::shared_ptr<Texture>& texture, const Pt& ul, const Pt& lr,
506  X lb, X adv);
507 
508  SubTexture sub_texture;
509  X left_bearing;
510  X advance;
511  X width;
512  };
513 
514  typedef boost::unordered_map<boost::uint32_t, Glyph> GlyphMap;
515 
516  Pt DetermineLinesImpl(const std::string& text,
517  Flags<TextFormat>& format,
518  X box_width,
519  std::vector<LineData>& line_data,
520  std::vector<boost::shared_ptr<TextElement> >* text_elements_ptr) const;
521 
522  FT_Error GetFace(FT_Face& face);
523  FT_Error GetFace(const std::vector<unsigned char>& file_contents, FT_Face& face);
524  void CheckFace(FT_Face font, FT_Error error);
525  void Init(FT_Face& font);
526  bool GenerateGlyph(FT_Face font, boost::uint32_t ch);
527  void ValidateFormat(Flags<TextFormat>& format) const;
528  inline X RenderGlyph(const Pt& pt, const Glyph& glyph,
529  const RenderState* render_state) const;
530  void HandleTag(const boost::shared_ptr<FormattingTag>& tag, double* orig_color,
531  RenderState& render_state) const;
532  bool IsDefaultFont();
533  boost::shared_ptr<Font>
534  GetDefaultFont(unsigned int pts);
535 
536  std::string m_font_filename;
537  unsigned int m_pt_sz;
538  std::vector<UnicodeCharset>
539  m_charsets;
540  Y m_ascent;
541  Y m_descent;
542  Y m_height;
543  Y m_lineskip;
544  double m_underline_offset;
545  double m_underline_height;
546  double m_italics_offset;
547  X m_space_width;
548  GlyphMap m_glyphs;
549  std::vector<boost::shared_ptr<Texture> >
550  m_textures;
551 
552  static std::set<std::string> s_action_tags;
553  static std::set<std::string> s_known_tags;
554 };
555 
557 GG_API std::ostream& operator<<(std::ostream& os, const Font::Substring& substr);
558 
563 GG_API CPSize CodePointIndexOf(std::size_t line, CPSize index,
564  const std::vector<Font::LineData>& line_data);
565 
570 GG_API StrSize StringIndexOf(std::size_t line, CPSize index,
571  const std::vector<Font::LineData>& line_data);
572 
577 GG_API std::pair<std::size_t, CPSize>
578 LinePositionOf(CPSize index, const std::vector<Font::LineData>& line_data);
579 
580 
597 class GG_API FontManager
598 {
599 private:
602  struct GG_API FontKey
603  {
604  FontKey(const std::string& str, unsigned int pts);
605  bool operator<(const FontKey& rhs) const;
606 
607  std::string filename;
608  unsigned int points;
609  };
610 
611 public:
613 
615  bool HasFont(const std::string& font_filename, unsigned int pts) const;
616 
619  template <class CharSetIter>
620  bool HasFont(const std::string& font_filename, unsigned int pts,
621  CharSetIter first, CharSetIter last) const;
623 
625 
628  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts);
629 
633  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
634  const std::vector<unsigned char>& file_contents);
635 
639  template <class CharSetIter>
640  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
641  CharSetIter first, CharSetIter last);
642 
647  template <class CharSetIter>
648  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
649  const std::vector<unsigned char>& file_contents,
650  CharSetIter first, CharSetIter last);
651 
654  void FreeFont(const std::string& font_filename, unsigned int pts);
656 
657 private:
658  FontManager();
659  template <class CharSetIter>
660  boost::shared_ptr<Font> GetFontImpl(const std::string& font_filename, unsigned int pts,
661  const std::vector<unsigned char>* file_contents,
662  CharSetIter first, CharSetIter last);
663 
664  std::map<FontKey, boost::shared_ptr<Font> > m_rendered_fonts;
665 
666  static const boost::shared_ptr<Font> EMPTY_FONT;
667 
668  friend GG_API FontManager& GetFontManager();
669 };
670 
672 GG_API FontManager& GetFontManager();
673 
675 GG_EXCEPTION(FailedFTLibraryInit);
676 
677 namespace detail {
678  template <class CharT, bool CharIsSigned = boost::is_signed<CharT>::value>
679  struct ValidUTFChar;
680 
681  template <class CharT>
682  struct ValidUTFChar<CharT, true>
683  {
684  bool operator()(CharT c)
685  { return 0x0 <= c; }
686  };
687 
688  template <class CharT>
689  struct ValidUTFChar<CharT, false>
690  {
691  bool operator()(CharT c)
692  { return c <= 0x7f; }
693  };
694 
695  struct GG_API FTFaceWrapper
696  {
697  FTFaceWrapper();
698  ~FTFaceWrapper();
699  FT_Face m_face;
700  };
701 }
702 
703 } // namespace GG
704 
705 
706 // template implementations
707 template <class CharSetIter>
708 GG::Font::Font(const std::string& font_filename, unsigned int pts,
709  CharSetIter first, CharSetIter last) :
710  m_font_filename(font_filename),
711  m_pt_sz(pts),
712  m_charsets(first, last),
713  m_ascent(0),
714  m_descent(0),
715  m_height(0),
716  m_lineskip(0),
717  m_underline_offset(0.0),
718  m_underline_height(0.0),
719  m_italics_offset(0.0),
720  m_space_width(0)
721 {
722  if (m_font_filename != "") {
723  detail::FTFaceWrapper wrapper;
724  FT_Error error = GetFace(wrapper.m_face);
725  CheckFace(wrapper.m_face, error);
726  Init(wrapper.m_face);
727  }
728 }
729 
730 template <class CharSetIter>
731 GG::Font::Font(const std::string& font_filename, unsigned int pts,
732  const std::vector<unsigned char>& file_contents,
733  CharSetIter first, CharSetIter last) :
734  m_font_filename(font_filename),
735  m_pt_sz(pts),
736  m_charsets(first, last),
737  m_ascent(0),
738  m_descent(0),
739  m_height(0),
740  m_lineskip(0),
741  m_underline_offset(0.0),
742  m_underline_height(0.0),
743  m_italics_offset(0.0),
744  m_space_width(0)
745 {
746  assert(!file_contents.empty());
747  detail::FTFaceWrapper wrapper;
748  FT_Error error = GetFace(file_contents, wrapper.m_face);
749  CheckFace(wrapper.m_face, error);
750  Init(wrapper.m_face);
751 }
752 
753 template <class CharSetIter>
754 bool GG::FontManager::HasFont(const std::string& font_filename, unsigned int pts,
755  CharSetIter first, CharSetIter last) const
756 {
757  bool retval = false;
758  FontKey key(font_filename, pts);
759  std::map<FontKey, boost::shared_ptr<Font> >::const_iterator it = m_rendered_fonts.find(key);
760  if (it != m_rendered_fonts.end()) {
761  std::set<UnicodeCharset> requested_charsets(first, last);
762  std::set<UnicodeCharset> found_charsets(it->second->UnicodeCharsets().begin(),
763  it->second->UnicodeCharsets().end());
764  retval = requested_charsets == found_charsets;
765  }
766  return retval;
767 }
768 
769 template <class CharSetIter>
770 boost::shared_ptr<GG::Font>
771 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
772  CharSetIter first, CharSetIter last)
773 { return GetFontImpl(font_filename, pts, 0, first, last); }
774 
775 template <class CharSetIter>
776 boost::shared_ptr<GG::Font>
777 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
778  const std::vector<unsigned char>& file_contents,
779  CharSetIter first, CharSetIter last)
780 { return GetFontImpl(font_filename, pts, &file_contents, first, last); }
781 
782 
783 template <class CharSetIter>
784 boost::shared_ptr<GG::Font>
785 GG::FontManager::GetFontImpl(const std::string& font_filename, unsigned int pts,
786  const std::vector<unsigned char>* file_contents,
787  CharSetIter first, CharSetIter last)
788 {
789  FontKey key(font_filename, pts);
790  std::map<FontKey, boost::shared_ptr<Font> >::iterator it = m_rendered_fonts.find(key);
791  if (it == m_rendered_fonts.end()) { // if no such font has been created, create it now
792  if (font_filename == "") {
793  // keeps this function from throwing; "" is the only invalid font
794  // filename that shouldn't throw
795  return EMPTY_FONT;
796  } else {
797  boost::shared_ptr<Font> font(
798  file_contents ?
799  new Font(font_filename, pts, *file_contents, first, last) :
800  new Font(font_filename, pts, first, last)
801  );
802  m_rendered_fonts[key] = font;
803  return m_rendered_fonts[key];
804  }
805  // if a font like this has been created, but it doesn't have all the right
806  // glyphs, release it and create a new one
807  } else {
808  std::set<UnicodeCharset> requested_charsets(first, last);
809  std::set<UnicodeCharset> found_charsets(it->second->UnicodeCharsets().begin(),
810  it->second->UnicodeCharsets().end());
811  if (requested_charsets != found_charsets) {
812  std::vector<UnicodeCharset> united_charsets;
813  std::set_union(requested_charsets.begin(), requested_charsets.end(),
814  found_charsets.begin(), found_charsets.end(),
815  std::back_inserter(united_charsets));
816  m_rendered_fonts.erase(it);
817  boost::shared_ptr<Font> font(
818  file_contents ?
819  new Font(font_filename, pts, *file_contents,
820  united_charsets.begin(), united_charsets.end()) :
821  new Font(font_filename, pts,
822  united_charsets.begin(), united_charsets.end())
823  );
824  m_rendered_fonts[key] = font;
825  return m_rendered_fonts[key];
826  } else { // otherwise, the font we found works, so just return it
827  return it->second;
828  }
829  }
830 }
831 
832 #endif