1 // Font.cc
2 // Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 
22 #include "Font.hh"
23 #include "FontImp.hh"
24 #include "StringUtil.hh"
25 #include "stringstream.hh"
26 #include "App.hh"
27 #include "GContext.hh"
28 #include "XFontImp.hh"
29 
30 // for antialias
31 #ifdef USE_XFT
32 #include "XftFontImp.hh"
33 #endif // USE_XFT
34 
35 // for multibyte support
36 #ifdef USE_XMB
37 #include "XmbFontImp.hh"
38 #endif //USE_XMB
39 
40 #include <cstring>
41 #include <cstdlib>
42 #include <list>
43 #include <map>
44 #include <typeinfo>
45 #include <langinfo.h>
46 
47 #ifdef HAVE_SETLOCALE
48 #include <locale.h>
49 #endif //HAVE_SETLOCALE
50 
51 using std::string;
52 using std::map;
53 using std::list;
54 
55 namespace {
56 
57 // use to map <font1>|<font2>|<font3> => <fontthatworks>
58 typedef map<string, string> StringMap;
59 typedef StringMap::iterator StringMapIt;
60 
61 // stores <fontthatworks and the fontimp
62 typedef map<string, FbTk::FontImp* > FontCache;
63 typedef FontCache::iterator FontCacheIt;
64 
65 
resetEffects(FbTk::Font & font)66 void resetEffects(FbTk::Font& font) {
67     int nr_scr = DefaultScreen(FbTk::App::instance()->display());
68     font.setHalo(false);
69     font.setHaloColor(FbTk::Color("white", nr_scr));
70     font.setShadow(false);
71     font.setShadowColor(FbTk::Color("black", nr_scr));
72     font.setShadowOffY(2);
73     font.setShadowOffX(2);
74 }
75 
76 
77 StringMap s_lookup_map;
78 FontCache s_font_cache;
79 bool s_multibyte = false; // if the fontimp should be a multibyte font
80 bool s_utf8mode = false; // should the font use utf8 font imp
81 
82 
83 } // end nameless namespace
84 
85 
86 
87 namespace FbTk {
88 
89 const char Font::DEFAULT_FONT[] = "__DEFAULT__";
90 
91 
shutdown()92 void Font::shutdown() {
93 
94     FontCacheIt fit;
95     for (fit = s_font_cache.begin(); fit != s_font_cache.end(); ++fit) {
96         FontImp* font = fit->second;
97         if (font) {
98             FontCacheIt it;
99             for (it = fit; it != s_font_cache.end(); ++it)
100                 if (it->second == font)
101                     it->second = 0;
102             delete font;
103         }
104     }
105 }
106 
multibyte()107 bool Font::multibyte() {
108     return s_multibyte;
109 }
110 
utf8()111 bool Font::utf8() {
112     return s_utf8mode;
113 }
114 
115 
Font(const char * name)116 Font::Font(const char *name):
117     m_fontimp(0),
118     m_shadow(false), m_shadow_color("black", DefaultScreen(App::instance()->display())),
119     m_shadow_offx(2), m_shadow_offy(2),
120     m_halo(false), m_halo_color("white", DefaultScreen(App::instance()->display()))
121 {
122     // MB_CUR_MAX returns the size of a char in the current locale
123     if (MB_CUR_MAX > 1) // more than one byte, then we're multibyte
124         s_multibyte = true;
125 
126 
127     char *locale_codeset = 0;
128 
129     // openbsd doesnt have this (yet?)
130 #if defined(CODESET) && !defined(_WIN32)
131     locale_codeset = nl_langinfo(CODESET);
132 #endif
133 
134     // check for utf-8 mode
135     if (locale_codeset && strcmp("UTF-8", locale_codeset) == 0) {
136         s_utf8mode = true;
137     } else if (locale_codeset != 0) {
138         s_utf8mode = FbStringUtil::haveUTF8();
139     }
140 
141     if (name != 0) {
142         load(name);
143     }
144 
145 }
146 
~Font()147 Font::~Font() {
148 }
149 
load(const string & name)150 bool Font::load(const string &name) {
151 
152     if (name.empty())
153         return false;
154 
155     StringMapIt lookup_entry;
156     FontCacheIt cache_entry;
157 
158     // check if one of <font1>|<font2>|<font3> is already there
159     if ((lookup_entry = s_lookup_map.find(name)) != s_lookup_map.end() &&
160             (cache_entry = s_font_cache.find(lookup_entry->second)) != s_font_cache.end()) {
161         m_fontstr = cache_entry->first;
162         m_fontimp = cache_entry->second;
163         resetEffects(*this);
164         return true;
165      }
166 
167     // split up the namelist
168     typedef list<string> StringList;
169     typedef StringList::iterator StringListIt;
170     StringList names;
171     FbTk::StringUtil::stringtok<StringList>(names, name, "|");
172 
173     StringListIt name_it;
174     for (name_it = names.begin(); name_it != names.end(); ++name_it) {
175         FbTk::StringUtil::removeTrailingWhitespace(*name_it);
176         FbTk::StringUtil::removeFirstWhitespace(*name_it);
177 
178         if ((cache_entry = s_font_cache.find(*name_it)) != s_font_cache.end()) {
179             m_fontstr = cache_entry->first;
180             m_fontimp = cache_entry->second;
181             s_lookup_map[name] = m_fontstr;
182             resetEffects(*this);
183             return true;
184         }
185 
186         FontImp* tmp_font(0);
187 
188         // Xft and X/Xmb fonts have different defaults
189         // (fixed doesn't really work right with Xft, especially rotated)
190 
191         // HOWEVER, note that if a Xft-style font is requested (does not start
192         // with "-"), and it turns out to be a bitmapped XFont, then Xft will
193         // load it, BUT it does not currently (2007-01-05) rotate bitmapped
194         // fonts (ok-ish), nor adjust the baseline for its lack of rotation
195         // (not ok: messes up placement). I can't see a neat way around this,
196         // other than the user re-specifying their font explicitly in XFont
197         // form so we don't use the Xft backend.
198 
199         std::string realname = *name_it;
200 
201 #ifdef USE_XFT
202         if ((*name_it)[0] != '-') {
203 
204             if (*name_it == Font::DEFAULT_FONT)
205                 realname = "monospace";
206 
207             tmp_font = new XftFontImp(0, utf8());
208         }
209 #endif // USE_XFT
210 
211         if (!tmp_font) {
212             if (*name_it == Font::DEFAULT_FONT)
213                 realname = "fixed";
214 
215 #ifdef USE_XMB
216 
217             if (multibyte() || utf8()) {
218                 tmp_font = new XmbFontImp(0, utf8());
219             }
220 #endif // USE_XMB
221         }
222 
223         if (!tmp_font) {
224            tmp_font = new XFontImp();
225         }
226 
227         if (tmp_font && tmp_font->load(realname.c_str())) {
228             s_lookup_map[name] = (*name_it);
229             m_fontimp = tmp_font;
230             s_font_cache[(*name_it)] = tmp_font;
231             m_fontstr = name;
232             resetEffects(*this);
233             return true;
234         }
235 
236         delete tmp_font;
237     }
238 
239     return false;
240 }
241 
textWidth(const char * text,unsigned int size) const242 unsigned int Font::textWidth(const char* text, unsigned int size) const {
243     return m_fontimp->textWidth(text, size);
244 }
245 
textWidth(const BiDiString & text) const246 unsigned int Font::textWidth(const BiDiString &text) const {
247     return textWidth(text.visual().c_str(), text.visual().size());
248 }
249 
height() const250 unsigned int Font::height() const {
251     return m_fontimp->height();
252 }
253 
ascent() const254 int Font::ascent() const {
255     return m_fontimp->ascent();
256 }
257 
descent() const258 int Font::descent() const {
259     return m_fontimp->descent();
260 }
261 
validOrientation(FbTk::Orientation orient)262 bool Font::validOrientation(FbTk::Orientation orient) {
263     return m_fontimp->validOrientation(orient);
264 }
265 
drawText(const FbDrawable & w,int screen,GC gc,const char * text,size_t len,int x,int y,Orientation orient) const266 void Font::drawText(const FbDrawable &w, int screen, GC gc,
267                     const char* text, size_t len, int x, int y,
268                     Orientation orient) const {
269 
270     if (!text || !*text || len == 0)
271         return;
272 
273     // draw "effects" first
274     if (m_shadow) {
275         FbTk::GContext shadow_gc(w);
276         shadow_gc.setForeground(m_shadow_color);
277         m_fontimp->drawText(w, screen, shadow_gc.gc(), text, len,
278                  x + m_shadow_offx, y + m_shadow_offy, orient);
279     } else if (m_halo) {
280         FbTk::GContext halo_gc(w);
281         halo_gc.setForeground(m_halo_color);
282         m_fontimp->drawText(w, screen, halo_gc.gc(), text, len, x + 1, y + 1, orient);
283         m_fontimp->drawText(w, screen, halo_gc.gc(), text, len, x - 1, y + 1, orient);
284         m_fontimp->drawText(w, screen, halo_gc.gc(), text, len, x - 1, y - 1, orient);
285         m_fontimp->drawText(w, screen, halo_gc.gc(), text, len, x + 1, y - 1, orient);
286     }
287 
288     m_fontimp->drawText(w, screen, gc, text, len, x, y, orient);
289 }
290 
291 }
292 
293