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, Y y_ofs,
506  X lb, X adv);
507 
508  SubTexture sub_texture;
509  Y y_offset;
510  X left_bearing;
511  X advance;
512  X width;
513  };
514 
515  typedef boost::unordered_map<boost::uint32_t, Glyph> GlyphMap;
516 
517  Pt DetermineLinesImpl(const std::string& text,
518  Flags<TextFormat>& format,
519  X box_width,
520  std::vector<LineData>& line_data,
521  std::vector<boost::shared_ptr<TextElement> >* text_elements_ptr) const;
522 
523  FT_Error GetFace(FT_Face& face);
524  FT_Error GetFace(const std::vector<unsigned char>& file_contents, FT_Face& face);
525  void CheckFace(FT_Face font, FT_Error error);
526  void Init(FT_Face& font);
527  bool GenerateGlyph(FT_Face font, boost::uint32_t ch);
528  void ValidateFormat(Flags<TextFormat>& format) const;
529  inline X RenderGlyph(const Pt& pt, const Glyph& glyph,
530  const RenderState* render_state) const;
531  void HandleTag(const boost::shared_ptr<FormattingTag>& tag, double* orig_color,
532  RenderState& render_state) const;
533  bool IsDefaultFont();
534  boost::shared_ptr<Font>
535  GetDefaultFont(unsigned int pts);
536 
537  std::string m_font_filename;
538  unsigned int m_pt_sz;
539  std::vector<UnicodeCharset>
540  m_charsets;
541  Y m_ascent;
542  Y m_descent;
543  Y m_height;
544  Y m_lineskip;
545  double m_underline_offset;
546  double m_underline_height;
547  double m_italics_offset;
548  X m_space_width;
549  GlyphMap m_glyphs;
550  std::vector<boost::shared_ptr<Texture> >
551  m_textures;
552 
553  static std::set<std::string> s_action_tags;
554  static std::set<std::string> s_known_tags;
555 };
556 
558 GG_API std::ostream& operator<<(std::ostream& os, const Font::Substring& substr);
559 
564 GG_API CPSize CodePointIndexOf(std::size_t line, CPSize index,
565  const std::vector<Font::LineData>& line_data);
566 
571 GG_API StrSize StringIndexOf(std::size_t line, CPSize index,
572  const std::vector<Font::LineData>& line_data);
573 
578 GG_API std::pair<std::size_t, CPSize>
579 LinePositionOf(CPSize index, const std::vector<Font::LineData>& line_data);
580 
581 
598 class GG_API FontManager
599 {
600 private:
603  struct GG_API FontKey
604  {
605  FontKey(const std::string& str, unsigned int pts);
606  bool operator<(const FontKey& rhs) const;
607 
608  std::string filename;
609  unsigned int points;
610  };
611 
612 public:
614 
616  bool HasFont(const std::string& font_filename, unsigned int pts) const;
617 
620  template <class CharSetIter>
621  bool HasFont(const std::string& font_filename, unsigned int pts,
622  CharSetIter first, CharSetIter last) const;
624 
626 
629  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts);
630 
634  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
635  const std::vector<unsigned char>& file_contents);
636 
640  template <class CharSetIter>
641  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
642  CharSetIter first, CharSetIter last);
643 
648  template <class CharSetIter>
649  boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
650  const std::vector<unsigned char>& file_contents,
651  CharSetIter first, CharSetIter last);
652 
655  void FreeFont(const std::string& font_filename, unsigned int pts);
657 
658 private:
659  FontManager();
660  template <class CharSetIter>
661  boost::shared_ptr<Font> GetFontImpl(const std::string& font_filename, unsigned int pts,
662  const std::vector<unsigned char>* file_contents,
663  CharSetIter first, CharSetIter last);
664 
665  std::map<FontKey, boost::shared_ptr<Font> > m_rendered_fonts;
666 
667  static const boost::shared_ptr<Font> EMPTY_FONT;
668 
669  friend GG_API FontManager& GetFontManager();
670 };
671 
673 GG_API FontManager& GetFontManager();
674 
676 GG_EXCEPTION(FailedFTLibraryInit);
677 
678 namespace detail {
679  template <class CharT, bool CharIsSigned = boost::is_signed<CharT>::value>
680  struct ValidUTFChar;
681 
682  template <class CharT>
683  struct ValidUTFChar<CharT, true>
684  {
685  bool operator()(CharT c)
686  { return 0x0 <= c; }
687  };
688 
689  template <class CharT>
690  struct ValidUTFChar<CharT, false>
691  {
692  bool operator()(CharT c)
693  { return c <= 0x7f; }
694  };
695 
696  struct GG_API FTFaceWrapper
697  {
698  FTFaceWrapper();
699  ~FTFaceWrapper();
700  FT_Face m_face;
701  };
702 }
703 
704 } // namespace GG
705 
706 
707 // template implementations
708 template <class CharSetIter>
709 GG::Font::Font(const std::string& font_filename, unsigned int pts,
710  CharSetIter first, CharSetIter last) :
711  m_font_filename(font_filename),
712  m_pt_sz(pts),
713  m_charsets(first, last),
714  m_ascent(0),
715  m_descent(0),
716  m_height(0),
717  m_lineskip(0),
718  m_underline_offset(0.0),
719  m_underline_height(0.0),
720  m_italics_offset(0.0),
721  m_space_width(0)
722 {
723  if (m_font_filename != "") {
724  detail::FTFaceWrapper wrapper;
725  FT_Error error = GetFace(wrapper.m_face);
726  CheckFace(wrapper.m_face, error);
727  Init(wrapper.m_face);
728  }
729 }
730 
731 template <class CharSetIter>
732 GG::Font::Font(const std::string& font_filename, unsigned int pts,
733  const std::vector<unsigned char>& file_contents,
734  CharSetIter first, CharSetIter last) :
735  m_font_filename(font_filename),
736  m_pt_sz(pts),
737  m_charsets(first, last),
738  m_ascent(0),
739  m_descent(0),
740  m_height(0),
741  m_lineskip(0),
742  m_underline_offset(0.0),
743  m_underline_height(0.0),
744  m_italics_offset(0.0),
745  m_space_width(0)
746 {
747  assert(!file_contents.empty());
748  detail::FTFaceWrapper wrapper;
749  FT_Error error = GetFace(file_contents, wrapper.m_face);
750  CheckFace(wrapper.m_face, error);
751  Init(wrapper.m_face);
752 }
753 
754 template <class CharSetIter>
755 bool GG::FontManager::HasFont(const std::string& font_filename, unsigned int pts,
756  CharSetIter first, CharSetIter last) const
757 {
758  bool retval = false;
759  FontKey key(font_filename, pts);
760  std::map<FontKey, boost::shared_ptr<Font> >::const_iterator it = m_rendered_fonts.find(key);
761  if (it != m_rendered_fonts.end()) {
762  std::set<UnicodeCharset> requested_charsets(first, last);
763  std::set<UnicodeCharset> found_charsets(it->second->UnicodeCharsets().begin(),
764  it->second->UnicodeCharsets().end());
765  retval = requested_charsets == found_charsets;
766  }
767  return retval;
768 }
769 
770 template <class CharSetIter>
771 boost::shared_ptr<GG::Font>
772 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
773  CharSetIter first, CharSetIter last)
774 { return GetFontImpl(font_filename, pts, 0, first, last); }
775 
776 template <class CharSetIter>
777 boost::shared_ptr<GG::Font>
778 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
779  const std::vector<unsigned char>& file_contents,
780  CharSetIter first, CharSetIter last)
781 { return GetFontImpl(font_filename, pts, &file_contents, first, last); }
782 
783 
784 template <class CharSetIter>
785 boost::shared_ptr<GG::Font>
786 GG::FontManager::GetFontImpl(const std::string& font_filename, unsigned int pts,
787  const std::vector<unsigned char>* file_contents,
788  CharSetIter first, CharSetIter last)
789 {
790  FontKey key(font_filename, pts);
791  std::map<FontKey, boost::shared_ptr<Font> >::iterator it = m_rendered_fonts.find(key);
792  if (it == m_rendered_fonts.end()) { // if no such font has been created, create it now
793  if (font_filename == "") {
794  // keeps this function from throwing; "" is the only invalid font
795  // filename that shouldn't throw
796  return EMPTY_FONT;
797  } else {
798  boost::shared_ptr<Font> font(
799  file_contents ?
800  new Font(font_filename, pts, *file_contents, first, last) :
801  new Font(font_filename, pts, first, last)
802  );
803  m_rendered_fonts[key] = font;
804  return m_rendered_fonts[key];
805  }
806  // if a font like this has been created, but it doesn't have all the right
807  // glyphs, release it and create a new one
808  } else {
809  std::set<UnicodeCharset> requested_charsets(first, last);
810  std::set<UnicodeCharset> found_charsets(it->second->UnicodeCharsets().begin(),
811  it->second->UnicodeCharsets().end());
812  if (requested_charsets != found_charsets) {
813  std::vector<UnicodeCharset> united_charsets;
814  std::set_union(requested_charsets.begin(), requested_charsets.end(),
815  found_charsets.begin(), found_charsets.end(),
816  std::back_inserter(united_charsets));
817  m_rendered_fonts.erase(it);
818  boost::shared_ptr<Font> font(
819  file_contents ?
820  new Font(font_filename, pts, *file_contents,
821  united_charsets.begin(), united_charsets.end()) :
822  new Font(font_filename, pts,
823  united_charsets.begin(), united_charsets.end())
824  );
825  m_rendered_fonts[key] = font;
826  return m_rendered_fonts[key];
827  } else { // otherwise, the font we found works, so just return it
828  return it->second;
829  }
830  }
831 }
832 
833 #endif