GG

Font.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /* GG is a GUI for SDL and OpenGL.
00003    Copyright (C) 2003-2008 T. Zachary Laine
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public License
00007    as published by the Free Software Foundation; either version 2.1
00008    of the License, or (at your option) any later version.
00009    
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014     
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with this library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA
00019 
00020    If you do not wish to comply with the terms of the LGPL please
00021    contact the author as other terms are available for a fee.
00022     
00023    Zach Laine
00024    whatwasthataddress@gmail.com */
00025 
00032 #ifndef _GG_Font_h_
00033 #define _GG_Font_h_
00034 
00035 #include <GG/AlignmentFlags.h>
00036 #include <GG/FontFwd.h>
00037 #include <GG/Texture.h>
00038 #include <GG/UnicodeCharsets.h>
00039 
00040 #include <set>
00041 #include <stack>
00042 
00043 #include <boost/unordered_map.hpp>
00044 #include <boost/serialization/access.hpp>
00045 
00046 
00047 struct FT_FaceRec_;
00048 typedef struct FT_FaceRec_*  FT_Face;
00049 typedef int FT_Error;
00050 
00051 namespace GG {
00052 
00055 GG_API std::string RgbaTag(const Clr& c);
00056 
00057 
00123 class GG_API Font
00124 {
00125 public:
00131     class GG_API Substring
00132     {
00133     public:
00134         typedef std::pair<std::string::const_iterator, std::string::const_iterator> IterPair;
00135 
00137         Substring();
00138 
00140         Substring(const std::string& str_,
00141                   std::string::const_iterator first_,
00142                   std::string::const_iterator second_);
00143 
00146         Substring(const std::string& str_, const IterPair& pair);
00147 
00149         std::string::const_iterator begin() const;
00150 
00152         std::string::const_iterator end() const;
00153 
00155         bool empty() const;
00156 
00158         std::size_t size() const;
00159 
00161         operator std::string() const;
00162 
00164         bool operator==(const std::string& rhs) const;
00165 
00167         bool operator!=(const std::string& rhs) const;
00168 
00172         Substring& operator+=(const IterPair& rhs);
00173 
00174     private:
00175         const std::string* str;
00176         std::ptrdiff_t first;
00177         std::ptrdiff_t second;
00178 
00179         static const std::string EMPTY_STRING;
00180 
00181         friend class boost::serialization::access;
00182         template <class Archive>
00183         void serialize(Archive& ar, const unsigned int version);
00184     };
00185 
00188     struct GG_API TextElement
00189     {
00192         enum TextElementType {
00193             OPEN_TAG,   
00194             CLOSE_TAG,  
00195             TEXT,       
00196             WHITESPACE, 
00197 
00202             NEWLINE
00203         };
00204 
00207         TextElement(bool ws, bool nl);
00208 
00209         virtual ~TextElement(); 
00210 
00212         virtual TextElementType Type() const;
00213 
00215         X Width() const;
00216 
00217         /* Returns the number of characters in the original string that the
00218            element represents. */
00219         StrSize StringSize() const;
00220 
00223         CPSize CodePointSize() const;
00224 
00226         Substring text;
00227 
00228         std::vector<X> widths;     
00229         const bool     whitespace; 
00230         const bool     newline;    
00231 
00232     protected:
00233         TextElement();
00234 
00235     private:
00236         mutable X cached_width;
00237 
00238         friend class boost::serialization::access;
00239         template <class Archive>
00240         void serialize(Archive& ar, const unsigned int version);
00241     };
00242 
00245     struct GG_API FormattingTag : TextElement
00246     {
00249         FormattingTag(bool close);
00250 
00251         virtual TextElementType Type() const;
00252 
00255         std::vector<Substring> params;
00256 
00258         Substring tag_name;
00259 
00261         const bool close_tag;
00262 
00263     private:
00264         FormattingTag();
00265         friend class boost::serialization::access;
00266         template <class Archive>
00267         void serialize(Archive& ar, const unsigned int version);
00268     };
00269 
00275     struct GG_API LineData
00276     {
00277         LineData(); 
00278 
00282         struct GG_API CharData
00283         {
00285             CharData();
00286 
00288             CharData(X extent_, StrSize str_index, StrSize str_size, CPSize cp_index,
00289                      const std::vector<boost::shared_ptr<TextElement> >& tags_);
00290 
00293             X extent;
00294 
00297             StrSize string_index;
00298 
00301             StrSize string_size;
00302 
00304             CPSize code_point_index;
00305 
00308             std::vector<boost::shared_ptr<FormattingTag> > tags;
00309 
00310         private:
00311             friend class boost::serialization::access;
00312             template <class Archive>
00313             void serialize(Archive& ar, const unsigned int version);
00314         };
00315 
00316         X    Width() const; 
00317         bool Empty() const; 
00318 
00320         std::vector<CharData> char_data;
00321 
00324         Alignment justification;
00325 
00326     private:
00327         friend class boost::serialization::access;
00328         template <class Archive>
00329         void serialize(Archive& ar, const unsigned int version);
00330     };
00331 
00337     struct GG_API RenderState
00338     {
00339         RenderState(); 
00340 
00342         std::size_t     use_italics;
00343 
00345         std::size_t     draw_underline;
00346 
00348         std::stack<Clr> colors;
00349     };
00350  
00352 
00355     Font(const std::string& font_filename, unsigned int pts);
00356 
00361     Font(const std::string& font_filename, unsigned int pts,
00362          const std::vector<unsigned char>& file_contents);
00363 
00368     template <class CharSetIter>
00369     Font(const std::string& font_filename, unsigned int pts,
00370          CharSetIter first, CharSetIter last);
00371 
00377     template <class CharSetIter>
00378     Font(const std::string& font_filename, unsigned int pts,
00379          const std::vector<unsigned char>& file_contents,
00380          CharSetIter first, CharSetIter last);
00381 
00382     ~Font(); 
00383 
00384  
00386 
00387     const std::string& FontName() const;
00388 
00391     unsigned int PointSize() const;
00392 
00394     const std::vector<UnicodeCharset>& UnicodeCharsets() const;
00395 
00397     Y    Ascent() const;
00398 
00400     Y    Descent() const;
00401 
00403     Y    Height() const;
00404 
00407     Y    Lineskip() const;
00408 
00410     X    SpaceWidth() const;
00411 
00415     X    RenderGlyph(const Pt& pt, char c) const;
00416 
00418     X    RenderGlyph(const Pt& pt, boost::uint32_t c) const;
00419 
00422     X    RenderText(const Pt& pt, const std::string& text) const;
00423 
00425     void RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format,
00426                     const std::vector<LineData>* line_data = 0, RenderState* render_state = 0) const;
00427 
00432     void RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format,
00433                     const std::vector<LineData>& line_data, RenderState& render_state,
00434                     std::size_t begin_line, CPSize begin_char,
00435                     std::size_t end_line, CPSize end_char) const;
00436 
00439     void ProcessTagsBefore(const std::vector<LineData>& line_data, RenderState& render_state,
00440                            std::size_t begin_line, CPSize begin_char) const;
00441 
00444     Pt   DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
00445                         std::vector<LineData>& line_data) const;
00446 
00450     Pt   DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
00451                         std::vector<LineData>& line_data,
00452                         std::vector<boost::shared_ptr<TextElement> >& text_elements) const;
00453 
00459     Pt   DetermineLines(const std::string& text, Flags<TextFormat>& format, X box_width,
00460                         const std::vector<boost::shared_ptr<TextElement> >& text_elements,
00461                         std::vector<LineData>& line_data) const;
00462 
00466     Pt   TextExtent(const std::string& text, Flags<TextFormat> format = FORMAT_NONE,
00467                     X box_width = X0) const;
00468 
00470     Pt   TextExtent(const std::string& text, const std::vector<LineData>& line_data) const;
00472 
00476     static void RegisterKnownTag(const std::string& tag);
00477 
00480     static void RemoveKnownTag(const std::string& tag);
00481 
00484     static void ClearKnownTags();
00485  
00487 
00488     GG_ABSTRACT_EXCEPTION(Exception);
00489 
00491     GG_CONCRETE_EXCEPTION(BadFile, GG::Font, Exception);
00492 
00494     GG_CONCRETE_EXCEPTION(InvalidPointSize, GG::Font, Exception);
00495 
00498     GG_CONCRETE_EXCEPTION(UnscalableFont, GG::Font, Exception);
00499 
00502     GG_CONCRETE_EXCEPTION(BadFace, GG::Font, Exception);
00503 
00506     GG_CONCRETE_EXCEPTION(BadPointSize, GG::Font, Exception);
00507 
00510     GG_CONCRETE_EXCEPTION(BadGlyph, GG::Font, Exception);
00512 
00517     static void ThrowBadGlyph(const std::string& format_str, boost::uint32_t c);
00518 
00519 protected: 
00521     Font(); 
00522 
00523 
00524 private:
00527     struct Glyph
00528     {
00529         Glyph(); 
00530         Glyph(const boost::shared_ptr<Texture>& texture, const Pt& ul, const Pt& lr,
00531               X lb, X adv); 
00532 
00533         SubTexture  sub_texture;   
00534         X           left_bearing;  
00535         X           advance;       
00536         X           width;         
00537     };
00538 
00539     typedef boost::unordered_map<boost::uint32_t, Glyph> GlyphMap;
00540 
00541     Pt DetermineLinesImpl(const std::string& text,
00542                           Flags<TextFormat>& format,
00543                           X box_width,
00544                           std::vector<LineData>& line_data,
00545                           std::vector<boost::shared_ptr<TextElement> >* text_elements_ptr) const;
00546 
00547     FT_Error          GetFace(FT_Face& face);
00548     FT_Error          GetFace(const std::vector<unsigned char>& file_contents, FT_Face& face);
00549     void              CheckFace(FT_Face font, FT_Error error);
00550     void              Init(FT_Face& font);
00551     bool              GenerateGlyph(FT_Face font, boost::uint32_t ch);
00552     void              ValidateFormat(Flags<TextFormat>& format) const;
00553     inline X          RenderGlyph(const Pt& pt, const Glyph& glyph,
00554                                   const RenderState* render_state) const;
00555     void              HandleTag(const boost::shared_ptr<FormattingTag>& tag, double* orig_color,
00556                                 RenderState& render_state) const;
00557     bool              IsDefaultFont();
00558     boost::shared_ptr<Font>
00559                       GetDefaultFont(unsigned int pts);
00560 
00561     std::string          m_font_filename;
00562     unsigned int         m_pt_sz;
00563     std::vector<UnicodeCharset>
00564                          m_charsets;    
00565     Y                    m_ascent;      
00566     Y                    m_descent;     
00567     Y                    m_height;      
00568     Y                    m_lineskip;    
00569     double               m_underline_offset; 
00570     double               m_underline_height; 
00571     double               m_italics_offset;   
00572     X                    m_space_width; 
00573     GlyphMap             m_glyphs;      
00574     std::vector<boost::shared_ptr<Texture> >
00575                          m_textures;    
00576 
00577     static std::set<std::string>   s_action_tags; 
00578     static std::set<std::string>   s_known_tags;  
00579 
00580     friend class boost::serialization::access;
00581     template <class Archive>
00582     void serialize(Archive& ar, const unsigned int version);
00583 };
00584 
00586 GG_API std::ostream& operator<<(std::ostream& os, const Font::Substring& substr);
00587 
00592 GG_API CPSize CodePointIndexOf(std::size_t line, CPSize index,
00593                                const std::vector<Font::LineData>& line_data);
00594 
00599 GG_API StrSize StringIndexOf(std::size_t line, CPSize index,
00600                              const std::vector<Font::LineData>& line_data);
00601 
00606 GG_API std::pair<std::size_t, CPSize>
00607 LinePositionOf(CPSize index, const std::vector<Font::LineData>& line_data);
00608 
00609 
00626 class GG_API FontManager
00627 {
00628 private:
00631     struct GG_API FontKey
00632     {
00633         FontKey(const std::string& str, unsigned int pts); 
00634         bool operator<(const FontKey& rhs) const; 
00635 
00636         std::string  filename; 
00637         unsigned int points;   
00638     };
00639 
00640 public: 
00642 
00645     boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts);
00646 
00650     boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
00651                                     const std::vector<unsigned char>& file_contents);
00652 
00656     template <class CharSetIter>
00657     boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
00658                                     CharSetIter first, CharSetIter last);
00659 
00664     template <class CharSetIter>
00665     boost::shared_ptr<Font> GetFont(const std::string& font_filename, unsigned int pts,
00666                                     const std::vector<unsigned char>& file_contents,
00667                                     CharSetIter first, CharSetIter last);
00668 
00671     void                    FreeFont(const std::string& font_filename, unsigned int pts);
00673 
00674 private:
00675     FontManager();
00676     template <class CharSetIter>
00677     boost::shared_ptr<Font> GetFontImpl(const std::string& font_filename, unsigned int pts,
00678                                         const std::vector<unsigned char>* file_contents,
00679                                         CharSetIter first, CharSetIter last);
00680 
00681     std::map<FontKey, boost::shared_ptr<Font> > m_rendered_fonts;
00682 
00683     static const boost::shared_ptr<Font> EMPTY_FONT;
00684 
00685     friend GG_API FontManager& GetFontManager();
00686 };
00687 
00689 GG_API FontManager& GetFontManager();
00690 
00692 GG_EXCEPTION(FailedFTLibraryInit);
00693 
00694 namespace detail {
00695     template <class CharT, bool CharIsSigned = boost::is_signed<CharT>::value>
00696     struct ValidUTFChar;
00697 
00698     template <class CharT>
00699     struct ValidUTFChar<CharT, true>
00700     {
00701         bool operator()(CharT c)
00702             { return 0x0 <= c; }
00703     };
00704 
00705     template <class CharT>
00706     struct ValidUTFChar<CharT, false>
00707     {
00708         bool operator()(CharT c)
00709             { return c <= 0x7f; }
00710     };
00711 
00712     struct SerializableString : public std::string
00713     {
00714         template <class Archive>
00715         void serialize(Archive& ar, const unsigned int version)
00716             { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(std::string); }
00717     };
00718 
00719     struct GG_API FTFaceWrapper
00720     {
00721         FTFaceWrapper();
00722         ~FTFaceWrapper();
00723         FT_Face m_face;
00724     };
00725 }
00726 
00727 } // namespace GG
00728 
00729 
00730 // template implementations
00731 template <class Archive>
00732 void GG::Font::Substring::serialize(Archive& ar, const unsigned int version)
00733 {
00734     detail::SerializableString* mutable_str =
00735         static_cast<detail::SerializableString*>(const_cast<std::string*>(str));
00736     ar  & BOOST_SERIALIZATION_NVP(mutable_str)
00737         & BOOST_SERIALIZATION_NVP(first)
00738         & BOOST_SERIALIZATION_NVP(second);
00739     if (Archive::is_loading::value)
00740         str = mutable_str;
00741 }
00742 
00743 template <class Archive>
00744 void GG::Font::TextElement::serialize(Archive& ar, const unsigned int version)
00745 {
00746     ar  & BOOST_SERIALIZATION_NVP(text)
00747         & BOOST_SERIALIZATION_NVP(widths)
00748         & boost::serialization::make_nvp("whitespace", const_cast<bool&>(whitespace))
00749         & boost::serialization::make_nvp("newline", const_cast<bool&>(newline));
00750 }
00751 
00752 template <class Archive>
00753 void GG::Font::FormattingTag::serialize(Archive& ar, const unsigned int version)
00754 {
00755     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(TextElement)
00756         & BOOST_SERIALIZATION_NVP(params)
00757         & BOOST_SERIALIZATION_NVP(tag_name)
00758         & boost::serialization::make_nvp("close_tag", const_cast<bool&>(close_tag));
00759 }
00760 
00761 template <class Archive>
00762 void GG::Font::LineData::CharData::serialize(Archive& ar, const unsigned int version)
00763 {
00764     ar  & BOOST_SERIALIZATION_NVP(extent)
00765         & BOOST_SERIALIZATION_NVP(string_index)
00766         & BOOST_SERIALIZATION_NVP(string_size)
00767         & BOOST_SERIALIZATION_NVP(code_point_index)
00768         & BOOST_SERIALIZATION_NVP(tags);
00769 }
00770 
00771 template <class Archive>
00772 void GG::Font::LineData::serialize(Archive& ar, const unsigned int version)
00773 {
00774     ar  & BOOST_SERIALIZATION_NVP(char_data)
00775         & BOOST_SERIALIZATION_NVP(justification);
00776 }
00777 
00778 template <class CharSetIter>
00779 GG::Font::Font(const std::string& font_filename, unsigned int pts,
00780                CharSetIter first, CharSetIter last) :
00781     m_font_filename(font_filename),
00782     m_pt_sz(pts),
00783     m_charsets(first, last),
00784     m_ascent(0),
00785     m_descent(0),
00786     m_height(0),
00787     m_lineskip(0),
00788     m_underline_offset(0.0),
00789     m_underline_height(0.0),
00790     m_italics_offset(0.0),
00791     m_space_width(0)
00792 {
00793     if (m_font_filename != "") {
00794         detail::FTFaceWrapper wrapper;
00795         FT_Error error = GetFace(wrapper.m_face);
00796         CheckFace(wrapper.m_face, error);
00797         Init(wrapper.m_face);
00798     }
00799 }
00800 
00801 template <class CharSetIter>
00802 GG::Font::Font(const std::string& font_filename, unsigned int pts,
00803                const std::vector<unsigned char>& file_contents,
00804                CharSetIter first, CharSetIter last) :
00805     m_font_filename(font_filename),
00806     m_pt_sz(pts),
00807     m_charsets(first, last),
00808     m_ascent(0),
00809     m_descent(0),
00810     m_height(0),
00811     m_lineskip(0),
00812     m_underline_offset(0.0),
00813     m_underline_height(0.0),
00814     m_italics_offset(0.0),
00815     m_space_width(0)
00816 {
00817     assert(!file_contents.empty());
00818     detail::FTFaceWrapper wrapper;
00819     FT_Error error = GetFace(file_contents, wrapper.m_face);
00820     CheckFace(wrapper.m_face, error);
00821     Init(wrapper.m_face);
00822 }
00823 
00824 template <class Archive>
00825 void GG::Font::serialize(Archive& ar, const unsigned int version)
00826 {
00827     ar  & BOOST_SERIALIZATION_NVP(m_font_filename)
00828         & BOOST_SERIALIZATION_NVP(m_pt_sz)
00829         & BOOST_SERIALIZATION_NVP(m_charsets);
00830 
00831     if (Archive::is_loading::value) {
00832         if (!m_font_filename.empty() && 0 < m_pt_sz) {
00833             try {
00834                 if (IsDefaultFont()) {
00835                     *this = *GetDefaultFont(m_pt_sz);
00836                 } else if (m_font_filename != "") {
00837                     detail::FTFaceWrapper wrapper;
00838                     FT_Error error = GetFace(wrapper.m_face);
00839                     CheckFace(wrapper.m_face, error);
00840                     Init(wrapper.m_face);
00841                 }
00842             } catch (const Exception& e) {
00843                 // take no action; the Font must have been uninitialized when saved
00844             }
00845         }
00846     }
00847 }
00848 
00849 template <class CharSetIter>
00850 boost::shared_ptr<GG::Font>
00851 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
00852                          CharSetIter first, CharSetIter last)
00853 { return GetFontImpl(font_filename, pts, 0, first, last); }
00854 
00855 template <class CharSetIter>
00856 boost::shared_ptr<GG::Font>
00857 GG::FontManager::GetFont(const std::string& font_filename, unsigned int pts,
00858                          const std::vector<unsigned char>& file_contents,
00859                          CharSetIter first, CharSetIter last)
00860 { return GetFontImpl(font_filename, pts, &file_contents, first, last); }
00861 
00862 
00863 template <class CharSetIter>
00864 boost::shared_ptr<GG::Font>
00865 GG::FontManager::GetFontImpl(const std::string& font_filename, unsigned int pts,
00866                              const std::vector<unsigned char>* file_contents,
00867                              CharSetIter first, CharSetIter last)
00868 {
00869     FontKey key(font_filename, pts);
00870     std::map<FontKey, boost::shared_ptr<Font> >::iterator it = m_rendered_fonts.find(key);
00871     if (it == m_rendered_fonts.end()) { // if no such font has been created, create it now
00872         if (font_filename == "") {
00873             // keeps this function from throwing; "" is the only invalid font
00874             // filename that shouldn't throw
00875             return EMPTY_FONT;
00876         } else {
00877             boost::shared_ptr<Font> font(
00878                 file_contents ?
00879                 new Font(font_filename, pts, *file_contents, first, last) :
00880                 new Font(font_filename, pts, first, last)
00881             );
00882             m_rendered_fonts[key] = font;
00883             return m_rendered_fonts[key];
00884         }
00885     // if a font like this has been created, but it doesn't have all the right
00886     // glyphs, release it and create a new one
00887     } else {
00888         std::set<UnicodeCharset> requested_charsets(first, last);
00889         std::set<UnicodeCharset> found_charsets(it->second->UnicodeCharsets().begin(),
00890                                                 it->second->UnicodeCharsets().end());
00891         if (requested_charsets != found_charsets) {
00892             std::vector<UnicodeCharset> united_charsets;
00893             std::set_union(requested_charsets.begin(), requested_charsets.end(),
00894                            found_charsets.begin(), found_charsets.end(),
00895                            std::back_inserter(united_charsets));
00896             m_rendered_fonts.erase(it);
00897             boost::shared_ptr<Font> font(
00898                 file_contents ?
00899                 new Font(font_filename, pts, *file_contents,
00900                          united_charsets.begin(), united_charsets.end()) :
00901                 new Font(font_filename, pts,
00902                          united_charsets.begin(), united_charsets.end())
00903             );
00904             m_rendered_fonts[key] = font;
00905             return m_rendered_fonts[key];
00906         } else { // otherwise, the font we found works, so just return it
00907             return it->second;
00908         }
00909     }
00910 }
00911 
00912 #endif // _GG_Font_h_