Font.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2004-2006 The Pentagram team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #include "pent_include.h"
00020 
00021 #include "Font.h"
00022 
00023 namespace Pentagram {
00024 
00025 DEFINE_RUNTIME_CLASSTYPE_CODE_BASE_CLASS(Font)
00026 
00027 Font::Font() : highRes(false)
00028 {
00029 
00030 }
00031 
00032 
00033 Font::~Font()
00034 {
00035 }
00036 
00037 
00038 void Font::getTextSize(const std::string& text,
00039                                            int& resultwidth, int& resultheight,
00040                                            unsigned int& remaining,
00041                                            int width, int height, TextAlign align,
00042                                            bool u8specials)
00043 {
00044         std::list<PositionedText> tmp;
00045         tmp = typesetText<Traits>(this, text, remaining,
00046                                                           width, height, align, u8specials,
00047                                                           resultwidth, resultheight);
00048 }
00049 
00050 
00051 //static
00052 bool Font::Traits::canBreakAfter(std::string::const_iterator& i)
00053 {
00054         // It's not really relevant what we do here, because this probably will
00055         // not be used at normal font sizes.
00056         return true;
00057 }
00058 
00059 
00060 //static
00061 bool Font::SJISTraits::canBreakAfter(std::string::const_iterator& i)
00062 {
00063         std::string::const_iterator j = i;
00064         uint32 u1 = unicode(j);
00065 
00066         // See: http://www.wesnoth.org/wiki/JapaneseTranslation#Word-Wrapping
00067         // and: http://ja.wikipedia.org/wiki/%E7%A6%81%E5%89%87
00068 
00069         switch (u1) {
00070         case 0xff08: case 0x3014: case 0xff3b: case 0xff5b: case 0x3008:
00071         case 0x300a: case 0x300c: case 0x300e: case 0x3010: case 0x2018:
00072         case 0x201c:
00073                 return false;
00074         default:
00075                 break;
00076         }
00077 
00078         uint32 u2 = unicode(j);
00079         switch (u2) {
00080         case 0x3001: case 0x3002: case 0xff0c: case 0xff0e: case 0xff09:
00081         case 0x3015: case 0xff3d: case 0xff5d: case 0x3009: case 0x300b:
00082         case 0x300d: case 0x300f: case 0x3011: case 0x2019: case 0x201d:
00083         case 0x309d: case 0x309e: case 0x30fd: case 0x30fe: case 0x3005:
00084         case 0xff1f: case 0xff01: case 0xff1a: case 0xff1b: case 0x3041:
00085         case 0x3043: case 0x3045: case 0x3047: case 0x3049: case 0x3083:
00086         case 0x3085: case 0x3087: case 0x308e: case 0x30a1: case 0x30a3:
00087         case 0x30a5: case 0x30a7: case 0x30a9: case 0x30e3: case 0x30e5:
00088         case 0x30e7: case 0x30ee: case 0x3063: case 0x30f5: case 0x30c3:
00089         case 0x30f6: case 0x30fb: case 0x2026: case 0x30fc:
00090                 return false;
00091         default:
00092                 break;
00093         }
00094 
00095         // Also don't allow breaking between roman characters
00096         if (((u1 >= 'A' && u1 <= 'Z') || (u1 >= 'a' && u1 <= 'z')) &&
00097                 ((u2 >= 'A' && u2 <= 'Z') || (u2 >= 'a' && u2 <= 'z')))
00098         {
00099                 return false;
00100         }
00101         return true;
00102 }
00103 
00104 }
00105 
00106 template<class T>
00107 static void findWordEnd(const std::string& text,
00108                                                 std::string::const_iterator& iter, bool u8specials)
00109 {
00110         while (iter != text.end()) {
00111                 if (T::isSpace(iter, u8specials)) return; 
00112                 T::advance(iter);
00113         }
00114 }
00115 
00116 template<class T>
00117 static void passSpace(const std::string& text,
00118                                           std::string::const_iterator& iter, bool u8specials)
00119 {
00120         while (iter != text.end()) {
00121                 if (!T::isSpace(iter, u8specials)) return;
00122                 T::advance(iter);
00123         }
00124         return;
00125 }
00126 
00127 
00128 
00129 
00130 /*
00131   Special characters in U8:
00132 
00133 @ = bullet for conversation options
00134 ~ = line break
00135 % = tab
00136 * = line break on graves and plaques, possibly page break in books
00137 CHECKME: any others? (page breaks for books?)
00138 
00139 */
00140 
00141 template<class T>
00142 std::list<PositionedText> typesetText(Pentagram::Font* font,
00143                                                                           const std::string& text,
00144                                                                           unsigned int& remaining,
00145                                                                           int width, int height,
00146                                                                           Pentagram::Font::TextAlign align,
00147                                                                           bool u8specials,
00148                                                                           int& resultwidth, int& resultheight,
00149                                                                           std::string::size_type cursor)
00150 {
00151 #if 0
00152         pout << "typeset (" << width << "," << height << ") : "
00153                  << text << std::endl;
00154 #endif
00155 
00156         // be optimistic and assume everything will fit
00157         remaining = text.size();
00158 
00159         std::string curline;
00160 
00161         int totalwidth = 0;
00162         int totalheight = 0;
00163 
00164         std::list<PositionedText> lines;
00165         PositionedText line;
00166 
00167         std::string::const_iterator iter = text.begin();
00168         std::string::const_iterator cursoriter = text.begin();
00169         if (cursor != std::string::npos) cursoriter += cursor;
00170         std::string::const_iterator curlinestart = text.begin();
00171 
00172         bool breakhere = false;
00173         while (true)
00174         {
00175                 if (iter == text.end() || breakhere || T::isBreak(iter, u8specials))
00176                 {
00177                         // break here
00178                         int stringwidth = 0, stringheight = 0;
00179                         font->getStringSize(curline, stringwidth, stringheight);
00180                         line.dims.x = 0; line.dims.y = totalheight;
00181                         line.dims.w = stringwidth;
00182                         line.dims.h = stringheight;
00183                         line.text = curline;
00184                         line.cursor = std::string::npos;
00185                         if (cursor != std::string::npos && cursoriter >= curlinestart &&
00186                                 (cursoriter < iter || (!breakhere && cursoriter == iter)))
00187                         {
00188                                 line.cursor = cursoriter - curlinestart;
00189                                 if (line.dims.w == 0) {
00190                                         stringwidth = line.dims.w = 2;
00191                                 }
00192                         }
00193                         lines.push_back(line);
00194 
00195                         if (stringwidth > totalwidth) totalwidth = stringwidth;
00196                         totalheight += font->getBaselineSkip();
00197 
00198                         curline = "";
00199 
00200                         if (iter == text.end())
00201                                 break; // done
00202 
00203                         if (breakhere) {
00204                                 breakhere = false;
00205                                 curlinestart = iter;
00206                         } else {
00207                                 T::advance(iter);
00208                                 curlinestart = iter;
00209                         }
00210 
00211                         if (height != 0 && totalheight + font->getHeight() > height) {
00212                                 // next line won't fit
00213                                 remaining = curlinestart - text.begin();
00214                                 break;
00215                         }
00216 
00217                 } else {
00218 
00219                         // see if next word still fits on the current line
00220                         std::string::const_iterator nextword = iter;
00221                         passSpace<T>(text, nextword, u8specials);
00222 
00223                         // process spaces
00224                         bool foundLF = false;
00225                         std::string spaces;
00226                         for (; iter < nextword; T::advance(iter)) {
00227                                 if (T::isBreak(iter, u8specials)) {
00228                                         foundLF = true;
00229                                         break;
00230                                 } else if (T::isTab(iter, u8specials)) {
00231                                         spaces.append("    ");
00232                                 } else if (!curline.empty()) {
00233                                         spaces.append(" ");
00234                                 }
00235                         }
00236                         if (foundLF) continue;
00237 
00238                         // process word
00239                         std::string::const_iterator endofnextword = iter;
00240                         findWordEnd<T>(text, endofnextword, u8specials);
00241                         int stringwidth = 0, stringheight = 0;
00242                         std::string newline = curline + spaces +
00243                                 text.substr(nextword-text.begin(),endofnextword-nextword);
00244                         font->getStringSize(newline, stringwidth, stringheight);
00245 
00246                         // if not, break line before this word
00247                         if (width != 0 && stringwidth > width) {
00248                                 if (!curline.empty()) {
00249                                         iter = nextword;
00250                                 } else {
00251                                         // word is longer than the line; have to break in mid-word
00252                                         // FIXME: this is rather inefficient; binary search?
00253                                         // FIXME: clean up...
00254                                         iter = nextword;
00255                                         std::string::const_iterator saveiter;
00256                                         std::string::const_iterator saveiter_fail;
00257                                         std::string curline_fail;
00258                                         newline = spaces;
00259                                         bool breakok = true;
00260                                         int breakcount = -1;
00261                                         do {
00262                                                 if (breakok) {
00263                                                         curline = newline;
00264                                                         saveiter = iter;
00265                                                         breakcount++;
00266                                                 }
00267                                                 curline_fail = newline;
00268                                                 saveiter_fail = iter;
00269 
00270                                                 if (iter == text.end()) break;
00271 
00272                                                 breakok = T::canBreakAfter(iter);
00273 
00274                                                 // try next character
00275                                                 T::advance(iter);
00276                                                 newline = spaces + text.substr(nextword-text.begin(),
00277                                                                                                            iter-nextword);
00278                                                 font->getStringSize(newline, stringwidth,stringheight);
00279                                         } while (stringwidth <= width);
00280                                         if (breakcount > 0) {
00281                                                 iter = saveiter;
00282                                         } else {
00283                                                 iter = saveiter_fail;
00284                                                 curline = curline_fail;
00285                                         }
00286                                 }
00287                                 breakhere = true;
00288                                 continue;
00289                         } else {
00290                                 // copy next word into curline
00291                                 curline = newline;
00292                                 iter = endofnextword;
00293                         }
00294                 }
00295         }
00296 
00297         if (lines.size() == 1 && align == Pentagram::Font::TEXT_LEFT) {
00298                 // only one line, so use the actual text width
00299             width = totalwidth;
00300         }
00301 
00302         if (width != 0) totalwidth = width;
00303 
00304         // adjust total height
00305         totalheight -= font->getBaselineSkip();
00306         totalheight += font->getHeight();
00307 
00308         // fixup x coordinates of lines
00309         std::list<PositionedText>::iterator lineiter;
00310         for (lineiter = lines.begin(); lineiter != lines.end(); ++lineiter) {
00311                 switch (align) {
00312                 case Pentagram::Font::TEXT_LEFT:
00313                         break;
00314                 case Pentagram::Font::TEXT_RIGHT:
00315                         lineiter->dims.x = totalwidth - lineiter->dims.w;
00316                         break;
00317                 case Pentagram::Font::TEXT_CENTER:
00318                         lineiter->dims.x = (totalwidth - lineiter->dims.w) / 2;
00319                         break;
00320                 }
00321 #if 0
00322                 pout << lineiter->dims.x << "," << lineiter->dims.y << " "
00323                          << lineiter->dims.w << "," << lineiter->dims.h << ": "
00324                          << lineiter->text << std::endl;
00325 #endif
00326         }
00327 
00328         resultwidth = totalwidth;
00329         resultheight = totalheight;
00330 
00331         return lines;
00332 }
00333 
00334 
00335 // explicit instantiations
00336 template
00337 std::list<PositionedText> typesetText<Pentagram::Font::Traits>
00338 (Pentagram::Font* font, const std::string& text,
00339  unsigned int& remaining, int width, int height,
00340  Pentagram::Font::TextAlign align, bool u8specials,
00341  int& resultwidth, int& resultheight, std::string::size_type cursor);
00342 
00343 template
00344 std::list<PositionedText> typesetText<Pentagram::Font::SJISTraits>
00345 (Pentagram::Font* font, const std::string& text,
00346  unsigned int& remaining, int width, int height,
00347  Pentagram::Font::TextAlign align, bool u8specials,
00348  int& resultwidth, int& resultheight, std::string::size_type cursor);

Generated on Fri Jul 27 22:27:15 2007 for pentagram by  doxygen 1.4.7