4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "lisp/parser.hpp"
27 #include "lisp/lisp.hpp"
30 #include "drawing_context.hpp"
33 Font::Font(const std::string& file, const std::string& shadowfile,
34 int w, int h, int shadowsize)
35 : chars(0), shadow_chars(0), w(w), h(h), shadowsize(shadowsize)
37 chars = new Surface(file);
38 shadow_chars = new Surface(shadowfile);
41 char_count = ((int) chars->get_height() / h) * 16;
51 Font::get_text_width(const std::string& text) const
53 /** Let's calculate the size of the biggest paragraph */
54 std::string::size_type l, hl, ol;
59 l = text.find("\n", l+1);
60 if(l == std::string::npos)
68 for (unsigned int i = 0; i < text.size(); i++)
69 if ((unsigned char) text[i] > 0xC2 && (unsigned char) text[i] < 0xC6)
70 hl--; // control characters are a WASTE.
76 Font::get_text_height(const std::string& text) const
78 /** Let's calculate height of the text */
79 std::string::size_type l, hh;
83 l = text.find("\n", l+1);
84 if(l == std::string::npos)
93 Font::get_height() const
99 Font::wrap_to_width(const std::string& s, int max_width, std::string* overflow) const
101 return wrap_to_chars(s, max_width / w, overflow);
105 Font::wrap_to_chars(const std::string& s, int line_length, std::string* overflow)
107 // if text is already smaller, return full text
108 if ((int)s.length() <= line_length) {
109 if (overflow) *overflow = "";
113 // if we can find a whitespace character to break at, return text up to this character
115 while ((i > 0) && (s[i] != ' ')) i--;
117 if (overflow) *overflow = s.substr(i+1);
118 return s.substr(0, i);
121 // FIXME: wrap at line_length, taking care of multibyte characters
122 if (overflow) *overflow = "";
127 Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
128 DrawingEffect drawing_effect, float alpha) const
130 /* Cut lines changes into seperate strings, needed to support center/right text
131 alignments with break lines.
132 Feel free to replace this hack with a more elegant solution
135 std::string::size_type l, i, y;
140 l = text.find("\n", i);
141 if(l == std::string::npos) {
146 if(l > sizeof(temp)-1)
149 temp[text.copy(temp, l - i, i)] = '\0';
151 // calculate X positions based on the alignment type
152 Vector pos = Vector(pos_);
153 if(alignment == CENTER_ALLIGN)
154 pos.x -= get_text_width(temp) / 2;
155 else if(alignment == RIGHT_ALLIGN)
156 pos.x -= get_text_width(temp);
158 draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
166 Font::draw_text(const std::string& text, const Vector& pos,
167 DrawingEffect drawing_effect, float alpha) const
170 draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
171 drawing_effect, alpha);
173 draw_chars(chars, text, pos, drawing_effect, alpha);
179 * returns true if this byte matches a bitmask of 10xx.xxxx, i.e. it is the 2nd, 3rd or 4th byte of a multibyte utf8 string
181 bool has_multibyte_mark(unsigned char c) {
182 return ((c & 0300) == 0200);
186 * gets unicode character at byte position @a p of UTF-8 encoded @a text, then advances @a p to the next character.
187 * @throws std::runtime_error if decoding fails.
188 * See unicode standard section 3.10 table 3-5 and 3-6 for details.
190 uint32_t decode_utf8(const std::string& text, size_t& p)
192 uint32_t c1 = (unsigned char) text[p+0];
194 if (has_multibyte_mark(c1)) std::runtime_error("Malformed utf-8 sequence");
196 if ((c1 & 0200) == 0000) {
197 // 0xxx.xxxx: 1 byte sequence
201 else if ((c1 & 0340) == 0300) {
202 // 110x.xxxx: 2 byte sequence
203 if(p+1 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
204 uint32_t c2 = (unsigned char) text[p+1];
205 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
207 return (c1 & 0037) << 6 | (c2 & 0077);
209 else if ((c1 & 0360) == 0340) {
210 // 1110.xxxx: 3 byte sequence
211 if(p+2 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
212 uint32_t c2 = (unsigned char) text[p+1];
213 uint32_t c3 = (unsigned char) text[p+2];
214 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
215 if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
217 return (c1 & 0017) << 12 | (c2 & 0077) << 6 | (c3 & 0077);
219 else if ((c1 & 0370) == 0360) {
220 // 1111.0xxx: 4 byte sequence
221 if(p+3 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
222 uint32_t c2 = (unsigned char) text[p+1];
223 uint32_t c3 = (unsigned char) text[p+2];
224 uint32_t c4 = (unsigned char) text[p+4];
225 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
226 if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
227 if (!has_multibyte_mark(c4)) throw std::runtime_error("Malformed utf-8 sequence");
229 return (c1 & 0007) << 18 | (c2 & 0077) << 12 | (c3 & 0077) << 6 | (c4 & 0077);
231 throw std::runtime_error("Malformed utf-8 sequence");
237 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
238 DrawingEffect drawing_effect, float alpha) const
242 while(i < text.size()) {
245 c = decode_utf8(text, i);
247 catch (std::runtime_error) {
248 log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + i)) << " found " << std::endl;
254 // a non-printable character?
265 font_index = c - first_char;
266 // we don't have the control chars 0x80-0xa0 in the font
270 log_debug << "Unsupported utf-8 character '" << c << "' found" << std::endl;
275 if(font_index < 0 || font_index >= (int) char_count) {
276 log_debug << "Unsupported utf-8 character found" << std::endl;
280 int source_x = (font_index % 16) * w;
281 int source_y = (font_index / 16) * h;
282 pchars->draw_part(source_x, source_y, p.x, p.y, w, h, alpha,