GG
|
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_