1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/ultima8/misc/pent_include.h"
24
25 #include "ultima/ultima8/graphics/fonts/font.h"
26
27 namespace Ultima {
28 namespace Ultima8 {
29
Font()30 Font::Font() : _highRes(false) {
31 }
32
33
~Font()34 Font::~Font() {
35 }
36
37
getTextSize(const Std::string & text,int32 & resultwidth,int32 & resultheight,unsigned int & remaining,int32 width,int32 height,TextAlign align,bool u8specials)38 void Font::getTextSize(const Std::string &text,
39 int32 &resultwidth, int32 &resultheight,
40 unsigned int &remaining,
41 int32 width, int32 height, TextAlign align,
42 bool u8specials) {
43 Std::list<PositionedText> tmp;
44 tmp = typesetText<Traits>(this, text, remaining,
45 width, height, align, u8specials,
46 resultwidth, resultheight);
47 }
48
49
50 //static
canBreakAfter(Std::string::const_iterator & i)51 bool Font::Traits::canBreakAfter(Std::string::const_iterator &i) {
52 // It's not really relevant what we do here, because this probably will
53 // not be used at normal font sizes.
54 return true;
55 }
56
57
58 //static
canBreakAfter(Std::string::const_iterator & i)59 bool Font::SJISTraits::canBreakAfter(Std::string::const_iterator &i) {
60 Std::string::const_iterator j = i;
61 uint32 u1 = unicode(j);
62
63 // See: http://www.wesnoth.org/wiki/JapaneseTranslation#Word-Wrapping
64 // and: http://ja.wikipedia.org/wiki/%E7%A6%81%E5%89%87
65
66 switch (u1) {
67 case 0xff08:
68 case 0x3014:
69 case 0xff3b:
70 case 0xff5b:
71 case 0x3008:
72 case 0x300a:
73 case 0x300c:
74 case 0x300e:
75 case 0x3010:
76 case 0x2018:
77 case 0x201c:
78 return false;
79 default:
80 break;
81 }
82
83 uint32 u2 = unicode(j);
84 switch (u2) {
85 case 0x3001:
86 case 0x3002:
87 case 0xff0c:
88 case 0xff0e:
89 case 0xff09:
90 case 0x3015:
91 case 0xff3d:
92 case 0xff5d:
93 case 0x3009:
94 case 0x300b:
95 case 0x300d:
96 case 0x300f:
97 case 0x3011:
98 case 0x2019:
99 case 0x201d:
100 case 0x309d:
101 case 0x309e:
102 case 0x30fd:
103 case 0x30fe:
104 case 0x3005:
105 case 0xff1f:
106 case 0xff01:
107 case 0xff1a:
108 case 0xff1b:
109 case 0x3041:
110 case 0x3043:
111 case 0x3045:
112 case 0x3047:
113 case 0x3049:
114 case 0x3083:
115 case 0x3085:
116 case 0x3087:
117 case 0x308e:
118 case 0x30a1:
119 case 0x30a3:
120 case 0x30a5:
121 case 0x30a7:
122 case 0x30a9:
123 case 0x30e3:
124 case 0x30e5:
125 case 0x30e7:
126 case 0x30ee:
127 case 0x3063:
128 case 0x30f5:
129 case 0x30c3:
130 case 0x30f6:
131 case 0x30fb:
132 case 0x2026:
133 case 0x30fc:
134 return false;
135 default:
136 break;
137 }
138
139 // Also don't allow breaking between roman characters
140 if (((u1 >= 'A' && u1 <= 'Z') || (u1 >= 'a' && u1 <= 'z')) &&
141 ((u2 >= 'A' && u2 <= 'Z') || (u2 >= 'a' && u2 <= 'z'))) {
142 return false;
143 }
144 return true;
145 }
146
147 template<class T>
findWordEnd(const Std::string & text,Std::string::const_iterator & iter,bool u8specials)148 static void findWordEnd(const Std::string &text,
149 Std::string::const_iterator &iter, bool u8specials) {
150 while (iter != text.end()) {
151 if (T::isSpace(iter, u8specials)) return;
152 T::advance(iter);
153 }
154 }
155
156 template<class T>
passSpace(const Std::string & text,Std::string::const_iterator & iter,bool u8specials)157 static void passSpace(const Std::string &text,
158 Std::string::const_iterator &iter, bool u8specials) {
159 while (iter != text.end()) {
160 if (!T::isSpace(iter, u8specials)) return;
161 T::advance(iter);
162 }
163 return;
164 }
165
166
167
168
169 /*
170 Special characters in U8:
171
172 @ = bullet for conversation options
173 ~ = line break
174 % = tab
175 * = line break on graves and plaques, possibly page break in books
176 CHECKME: any others? (page breaks for books?)
177
178 */
179
180 template<class T>
typesetText(Font * font,const Std::string & text,unsigned int & remaining,int32 width,int32 height,Font::TextAlign align,bool u8specials,int32 & resultwidth,int32 & resultheight,Std::string::size_type cursor)181 Std::list<PositionedText> typesetText(Font *font,
182 const Std::string &text, unsigned int &remaining, int32 width, int32 height,
183 Font::TextAlign align, bool u8specials, int32 &resultwidth,
184 int32 &resultheight, Std::string::size_type cursor) {
185 #if 0
186 pout << "typeset (" << width << "," << height << ") : "
187 << text << Std::endl;
188 #endif
189
190 // be optimistic and assume everything will fit
191 remaining = text.size();
192
193 Std::string curline;
194
195 int totalwidth = 0;
196 int totalheight = 0;
197
198 Std::list<PositionedText> lines;
199 PositionedText line;
200
201 Std::string::const_iterator iter = text.begin();
202 Std::string::const_iterator cursoriter = text.begin();
203 if (cursor != Std::string::npos) cursoriter += cursor;
204 Std::string::const_iterator curlinestart = text.begin();
205
206 bool breakhere = false;
207 while (true) {
208 if (iter == text.end() || breakhere || T::isBreak(iter, u8specials)) {
209 // break here
210 int32 stringwidth = 0, stringheight = 0;
211 font->getStringSize(curline, stringwidth, stringheight);
212 line._dims.left = 0;
213 line._dims.top = totalheight;
214 line._dims.setWidth(stringwidth);
215 line._dims.setHeight(stringheight);
216 line._text = curline;
217 line._cursor = Std::string::npos;
218 if (cursor != Std::string::npos && cursoriter >= curlinestart &&
219 (cursoriter < iter || (!breakhere && cursoriter == iter))) {
220 line._cursor = cursoriter - curlinestart;
221 if (line._dims.width() == 0) {
222 stringwidth = 2;
223 line._dims.setWidth(stringwidth);
224 }
225 }
226 lines.push_back(line);
227
228 if (stringwidth > totalwidth) totalwidth = stringwidth;
229 totalheight += font->getBaselineSkip();
230
231 curline = "";
232
233 if (iter == text.end())
234 break; // done
235
236 if (breakhere) {
237 breakhere = false;
238 curlinestart = iter;
239 } else {
240 T::advance(iter);
241 curlinestart = iter;
242 }
243
244 if (height != 0 && totalheight + font->getHeight() > height) {
245 // next line won't fit
246 remaining = curlinestart - text.begin();
247 break;
248 }
249
250 } else {
251
252 // see if next word still fits on the current line
253 Std::string::const_iterator nextword = iter;
254 passSpace<T>(text, nextword, u8specials);
255
256 // process spaces
257 bool foundLF = false;
258 Std::string spaces;
259 for (; iter < nextword; T::advance(iter)) {
260 if (T::isBreak(iter, u8specials)) {
261 foundLF = true;
262 break;
263 } else if (T::isTab(iter, u8specials)) {
264 spaces.append(" ");
265 } else if (!curline.empty()) {
266 spaces.append(" ");
267 }
268 }
269 if (foundLF) continue;
270
271 // process word
272 Std::string::const_iterator endofnextword = iter;
273 findWordEnd<T>(text, endofnextword, u8specials);
274 int32 stringwidth = 0, stringheight = 0;
275 Std::string newline = curline + spaces +
276 text.substr(nextword - text.begin(), endofnextword - nextword);
277 font->getStringSize(newline, stringwidth, stringheight);
278
279 // if not, break line before this word
280 if (width != 0 && stringwidth > width) {
281 if (!curline.empty()) {
282 iter = nextword;
283 } else {
284 // word is longer than the line; have to break in mid-word
285 // FIXME: this is rather inefficient; binary search?
286 // FIXME: clean up...
287 iter = nextword;
288 Std::string::const_iterator saveiter = nextword; // Dummy initialization
289 Std::string::const_iterator saveiter_fail;
290 Std::string curline_fail;
291 newline = spaces;
292 bool breakok = true;
293 int breakcount = -1;
294 do {
295 if (breakok) {
296 curline = newline;
297 saveiter = iter;
298 breakcount++;
299 }
300 curline_fail = newline;
301 saveiter_fail = iter;
302
303 if (iter == text.end()) break;
304
305 breakok = T::canBreakAfter(iter);
306
307 // try next character
308 T::advance(iter);
309 newline = spaces + text.substr(nextword - text.begin(),
310 iter - nextword);
311 font->getStringSize(newline, stringwidth, stringheight);
312 } while (stringwidth <= width);
313 if (breakcount > 0) {
314 iter = saveiter;
315 } else {
316 iter = saveiter_fail;
317 curline = curline_fail;
318 }
319 }
320 breakhere = true;
321 continue;
322 } else {
323 // copy next word into curline
324 curline = newline;
325 iter = endofnextword;
326 }
327 }
328 }
329
330 if (lines.size() == 1 && align == Font::TEXT_LEFT) {
331 // only one line, so use the actual text width
332 width = totalwidth;
333 }
334
335 if (width != 0) totalwidth = width;
336
337 // adjust total height
338 totalheight -= font->getBaselineSkip();
339 totalheight += font->getHeight();
340
341 // fixup x coordinates of lines
342 Std::list<PositionedText>::iterator lineiter;
343 for (lineiter = lines.begin(); lineiter != lines.end(); ++lineiter) {
344 switch (align) {
345 case Font::TEXT_LEFT:
346 break;
347 case Font::TEXT_RIGHT:
348 lineiter->_dims.moveTo(totalwidth - lineiter->_dims.width(), lineiter->_dims.top);
349 break;
350 case Font::TEXT_CENTER:
351 lineiter->_dims.moveTo((totalwidth - lineiter->_dims.width()) / 2, lineiter->_dims.top);
352 break;
353 }
354 #if 0
355 pout << lineiter->_dims.x << "," << lineiter->_dims.y << " "
356 << lineiter->_dims.width() << "," << lineiter->_dims.height() << ": "
357 << lineiter->text << Std::endl;
358 #endif
359 }
360
361 resultwidth = totalwidth;
362 resultheight = totalheight;
363
364 return lines;
365 }
366
367
368 // explicit instantiations
369 template
370 Std::list<PositionedText> typesetText<Font::Traits>
371 (Font *font, const Std::string &text,
372 unsigned int &remaining, int32 width, int32 height,
373 Font::TextAlign align, bool u8specials,
374 int32 &resultwidth, int32 &resultheight, Std::string::size_type cursor);
375
376 template
377 Std::list<PositionedText> typesetText<Font::SJISTraits>
378 (Font *font, const Std::string &text,
379 unsigned int &remaining, int32 width, int32 height,
380 Font::TextAlign align, bool u8specials,
381 int32 &resultwidth, int32 &resultheight, Std::string::size_type cursor);
382
383 } // End of namespace Ultima8
384 } // End of namespace Ultima
385