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