font.cc

Go to the documentation of this file.
00001 /*
00002  *  This program is free software; you can redistribute it and/or modify
00003  *  it under the terms of the GNU General Public License as published by
00004  *  the Free Software Foundation; either version 2 of the License, or
00005  *  (at your option) any later version.
00006  *
00007  *  This program is distributed in the hope that it will be useful,
00008  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  *  GNU General Public License for more details.
00011  *
00012  *  You should have received a copy of the GNU General Public License
00013  *  along with this program; if not, write to the Free Software
00014  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00015  */
00016 
00017 #ifdef HAVE_CONFIG_H
00018 #  include <config.h>
00019 #endif
00020 
00021 #include "U7file.h"
00022 #include "databuf.h"
00023 #include "font.h"
00024 #include "ibuf8.h"
00025 #include "vgafile.h"
00026 #include "exceptions.h"
00027 
00028 #ifndef UNDER_CE
00029 using std::cout;
00030 using std::endl;
00031 using std::size_t;
00032 using std::string;
00033 using std::strncmp;
00034 #endif
00035 
00036 FontManager fontManager;
00037 
00038 //  Want a more restrictive test for space.
00039 inline bool Is_space(char c)
00040   { return c == ' ' || c == '\n' || c == '\t'; }
00041 
00042 /*
00043  *  Pass space.
00044  */
00045 
00046 static const char *Pass_space
00047   (
00048   const char *text
00049   )
00050   {
00051   while (Is_space(*text))
00052     text++;
00053   return (text);
00054   }
00055 
00056 /*
00057  *  Pass a word.
00058  */
00059 
00060 static const char *Pass_word
00061   (
00062   const char *text
00063   )
00064   {
00065   while (*text && (!Is_space(*text) || (*text == '\f') || (*text == '\v')))
00066     text++;
00067   return (text);
00068   }
00069 
00070 /*
00071  *  Draw text within a rectangular area.
00072  *  Special characters handled are:
00073  *    \n  New line.
00074  *    space Word break.
00075  *    tab Treated like a space for now.
00076  *
00077  *  Output: If out of room, -offset of end of text painted.
00078  *    Else height of text painted.
00079  */
00080 
00081 int Font::paint_text_box
00082   (
00083   Image_buffer8 *win,   // Buffer to paint in.
00084   const char *text,
00085   int x, int y,     // Top-left corner of box.
00086   int w, int h,     // Dimensions.
00087   int vert_lead,      // Extra spacing between lines.
00088   int pbreak      // End at punctuation.
00089   )
00090   {
00091   const char *start = text; // Remember the start.
00092   win->set_clip(x, y, w, h);
00093   int endx = x + w;   // Figure where to stop.
00094   int curx = x, cury = y;
00095   int height = get_text_height() + vert_lead + ver_lead;
00096   int space_width = get_text_width(" ", 1);
00097   int max_lines = h/height; // # lines that can be shown.
00098   string *lines = new string[max_lines + 1];
00099   int cur_line = 0;
00100   const char *last_punct_end = 0;// ->last period, qmark, etc.
00101           // Last punct in 'lines':
00102   int last_punct_line = -1, last_punct_offset = -1;
00103 
00104   while (*text)
00105     {
00106     switch (*text)    // Special cases.
00107       {
00108     case '\n':    // Next line.
00109       curx = x;
00110       text++;
00111       cur_line++;
00112       if (cur_line >= max_lines)
00113         break;  // No more room.
00114       continue;
00115     case '\r':    //??
00116       text++;
00117       continue;
00118     case ' ':   // Space.
00119     case '\t':
00120       {   // Pass space.
00121       const char *wrd = Pass_space(text);
00122       if (wrd != text)
00123         {
00124         int w = get_text_width(text, wrd - text);
00125         if (w <= 0)
00126           w = space_width;
00127         int nsp = w/space_width;
00128         lines[cur_line].append(nsp, ' ');
00129         curx += nsp*space_width;
00130         }
00131       text = wrd;
00132       break;
00133       }
00134       }
00135 
00136     if (cur_line >= max_lines)
00137       break;
00138           // Pass word & get its width.
00139     const char *ewrd = Pass_word(text);
00140     int width = get_text_width(text, ewrd - text);
00141     if (curx + width - hor_lead > endx)
00142       {   // Word-wrap.
00143       curx = x;
00144       cur_line++;
00145       if (cur_line >= max_lines)
00146         break;  // No more room.
00147       }
00148 
00149           // Store word.
00150     lines[cur_line].append(text, ewrd - text);
00151     curx += width;
00152     text = ewrd;    // Continue past the word.
00153           // Keep loc. of punct. endings.
00154     if (text[-1] == '.' || text[-1] == '?' || text[-1] == '!' ||
00155         text[-1] == ',')
00156       {
00157       last_punct_end = text;
00158       last_punct_line = cur_line;
00159       last_punct_offset = lines[cur_line].length();
00160       }
00161     }
00162   if (*text &&      // Out of room?
00163           // Break off at end of punct.
00164        pbreak && last_punct_end)
00165     text = Pass_space(last_punct_end);
00166   else
00167     last_punct_line = -1;
00168           // Render text.
00169   for (int i = 0; i <= cur_line; i++)
00170     {
00171     const char *str = lines[i].data();
00172     int len = lines[i].length();
00173     if (i == last_punct_line)
00174       len = last_punct_offset;
00175     paint_text(win, str, len, x, cury);
00176     cury += height;
00177     if (i == last_punct_line)
00178       break;
00179     }
00180   win->clear_clip();
00181   delete [] lines;
00182   if (*text)      // Out of room?
00183     return -(text - start); // Return -offset of end.
00184   else        // Else return height.
00185     return (cury - y);
00186   }
00187 
00188 /*
00189  *  Draw text at a given location (which is the upper-left corner of the
00190  *  place to draw.
00191  *
00192  *  Output: Width in pixels of what was drawn.
00193  */
00194 
00195 int Font::paint_text
00196   (
00197   Image_buffer8 *win,   // Buffer to paint in.
00198   const char *text,   // What to draw, 0-delimited.
00199   int xoff, int yoff    // Upper-left corner of where to start.
00200   )
00201   {
00202   int x = xoff;
00203   int chr;
00204   yoff += get_text_baseline();
00205   if (font_shapes)
00206     while ((chr = *text++) != 0)
00207       {
00208       Shape_frame *shape = font_shapes->get_frame((unsigned char)chr);
00209       if (!shape)
00210         continue;
00211       shape->paint_rle(x, yoff);
00212       x += shape->get_width() + hor_lead;
00213       }
00214   return (x - xoff);
00215   }
00216 
00217 /*
00218  *  Paint text using font from "fonts.vga".
00219  *
00220  *  Output: Width in pixels of what was painted.
00221  */
00222 
00223 int Font::paint_text
00224   (
00225   Image_buffer8 *win,   // Buffer to paint in.
00226   const char *text,   // What to draw.
00227   int textlen,      // Length of text.
00228   int xoff, int yoff    // Upper-left corner of where to start.
00229   )
00230   {
00231   int x = xoff;
00232   yoff += get_text_baseline();
00233   if (font_shapes)
00234     while (textlen--)
00235       {
00236       Shape_frame *shape= font_shapes->get_frame((unsigned char)*text++);
00237       if (!shape)
00238         continue;
00239       shape->paint_rle(x, yoff);
00240       x += shape->get_width() + hor_lead;
00241       }
00242   return (x - xoff);
00243   }
00244 
00245 /*
00246  *
00247  *  FIXED WIDTH RENDERING
00248  *
00249  */
00250 
00251 /*
00252  *  Draw text within a rectangular area.
00253  *  Special characters handled are:
00254  *    \n  New line.
00255  *    space Word break.
00256  *    tab Treated like a space for now.
00257  *
00258  *  Output: If out of room, -offset of end of text painted.
00259  *    Else height of text painted.
00260  */
00261 
00262 int Font::paint_text_box_fixedwidth
00263   (
00264   Image_buffer8 *win,   // Buffer to paint in.
00265   const char *text,
00266   int x, int y,     // Top-left corner of box.
00267   int w, int h,     // Dimensions.
00268   int char_width,     // Width of each character
00269   int vert_lead,      // Extra spacing between lines.
00270   int pbreak      // End at punctuation.
00271   )
00272   {
00273   const char *start = text; // Remember the start.
00274   win->set_clip(x, y, w, h);
00275   int endx = x + w;   // Figure where to stop.
00276   int curx = x, cury = y;
00277   int height = get_text_height() + vert_lead + ver_lead;
00278   int max_lines = h/height; // # lines that can be shown.
00279   string *lines = new string[max_lines + 1];
00280   int cur_line = 0;
00281   const char *last_punct_end = 0;// ->last period, qmark, etc.
00282           // Last punct in 'lines':
00283   int last_punct_line = -1, last_punct_offset = -1;
00284 
00285   while (*text)
00286     {
00287     switch (*text)    // Special cases.
00288       {
00289     case '\n':    // Next line.
00290       curx = x;
00291       text++;
00292       cur_line++;
00293       if (cur_line >= max_lines)
00294         break;  // No more room.
00295       continue;
00296     case ' ':   // Space.
00297     case '\t':
00298       {   // Pass space.
00299       const char *wrd = Pass_space(text);
00300       if (wrd != text)
00301         {
00302         int w = (wrd - text) * char_width;
00303         if (!w)
00304           w = char_width;
00305         int nsp = w/char_width;
00306         lines[cur_line].append(nsp, ' ');
00307         curx += nsp*char_width;
00308         }
00309       text = wrd;
00310       break;
00311       }
00312       }
00313 
00314     if (cur_line >= max_lines)
00315       break;
00316           // Pass word & get its width.
00317     const char *ewrd = Pass_word(text);
00318     int width = (ewrd - text) * char_width;
00319     if (curx + width - hor_lead > endx)
00320       {   // Word-wrap.
00321       curx = x;
00322       cur_line++;
00323       if (cur_line >= max_lines)
00324         break;  // No more room.
00325       }
00326 
00327           // Store word.
00328     lines[cur_line].append(text, ewrd - text);
00329     curx += width;
00330     text = ewrd;    // Continue past the word.
00331           // Keep loc. of punct. endings.
00332     if (text[-1] == '.' || text[-1] == '?' || text[-1] == '!' ||
00333         text[-1] == ',')
00334       {
00335       last_punct_end = text;
00336       last_punct_line = cur_line;
00337       last_punct_offset = lines[cur_line].length();
00338       }
00339     }
00340   if (*text &&      // Out of room?
00341           // Break off at end of punct.
00342        pbreak && last_punct_end)
00343     text = Pass_space(last_punct_end);
00344   else
00345     last_punct_line = -1;
00346           // Render text.
00347   for (int i = 0; i <= cur_line; i++)
00348     {
00349     const char *str = lines[i].data();
00350     int len = lines[i].length();
00351     if (i == last_punct_line)
00352       len = last_punct_offset;
00353     paint_text_fixedwidth(win, str, len, x, cury, char_width);
00354     cury += height;
00355     if (i == last_punct_line)
00356       break;
00357     }
00358   win->clear_clip();
00359   delete [] lines;
00360   if (*text)      // Out of room?
00361     return -(text - start); // Return -offset of end.
00362   else        // Else return height.
00363     return (cury - y);
00364   }
00365 
00366 /*
00367  *  Draw text at a given location (which is the upper-left corner of the
00368  *  place to draw. Text will be drawn with the fixed width specified.
00369  *
00370  *  Output: Width in pixels of what was drawn.
00371  */
00372 
00373 int Font::paint_text_fixedwidth
00374   (
00375   Image_buffer8 *win,   // Buffer to paint in.
00376   const char *text,   // What to draw, 0-delimited.
00377   int xoff, int yoff,   // Upper-left corner of where to start.
00378   int width     // Width of each character
00379   )
00380   {
00381   int x = xoff;
00382   int w;
00383   int chr;
00384   yoff += get_text_baseline();
00385   while ((chr = *text++) != 0)
00386     {
00387     Shape_frame *shape = font_shapes->get_frame((unsigned char)chr);
00388     if (!shape)
00389       continue;
00390     x += w = (width - shape->get_width()) / 2;
00391     shape->paint_rle(x, yoff);
00392     x += width - w;
00393     }
00394   return (x - xoff);
00395   }
00396 
00397 /*
00398  *  Draw text at a given location (which is the upper-left corner of the
00399  *  place to draw. Text will be drawn with the fixed width specified.
00400  *
00401  *  Output: Width in pixels of what was drawn.
00402  */
00403 
00404 int Font::paint_text_fixedwidth
00405   (
00406   Image_buffer8 *win,   // Buffer to paint in.
00407   const char *text,   // What to draw.
00408   int textlen,      // Length of text.
00409   int xoff, int yoff,   // Upper-left corner of where to start.
00410   int width     // Width of each character
00411   )
00412   {
00413   int w;
00414   int x = xoff;
00415   yoff += get_text_baseline();
00416   while (textlen--)
00417     {
00418     Shape_frame *shape = font_shapes->get_frame((unsigned char) *text++);
00419     if (!shape)
00420       continue;
00421     x += w = (width - shape->get_width()) / 2;
00422     shape->paint_rle(x, yoff);
00423     x += width - w;
00424     }
00425   return (x - xoff);
00426   }
00427 
00428 /*
00429  *  Get the width in pixels of a 0-delimited string.
00430  */
00431 
00432 int Font::get_text_width
00433   (
00434   const char *text
00435   )
00436   {
00437   int width = 0;
00438   short chr;
00439   if (font_shapes)
00440     while ((chr = *text++) != 0) {
00441       Shape_frame* shape = font_shapes->get_frame((unsigned char)chr);
00442       if (shape)
00443         width += shape->get_width() + hor_lead;
00444     }
00445   return (width);
00446   }
00447 
00448 /*
00449  *  Get the width in pixels of a string given by length.
00450  */
00451 
00452 int Font::get_text_width
00453   (
00454   const char *text,
00455   int textlen     // Length of text.
00456   )
00457   {
00458   int width = 0;
00459   if (font_shapes)
00460     while (textlen--) {
00461       Shape_frame* shape =font_shapes->get_frame((unsigned char)*text++);
00462       if (shape)
00463         width += shape->get_width() + hor_lead;
00464     }
00465   return (width);
00466   }
00467 
00468 /*
00469  *  Get font line-height.
00470  */
00471 
00472 int Font::get_text_height
00473   (
00474   )
00475   {
00476   // Note, I wont assume the fonts exist
00477   //Shape_frame *A = font_shapes->get_frame('A');
00478   //Shape_frame *y = font_shapes->get_frame('y');
00479   return highest + lowest + 1;  
00480   }
00481 
00482 /*
00483  *  Get font baseline as the distance from the top.
00484  */
00485 
00486 int Font::get_text_baseline
00487   (
00488   )
00489   {
00490   //Shape_frame *A = font_shapes->get_frame('A');
00491   return highest;
00492   }
00493 
00494 Font::Font(): font_shapes(0), font_data(0), font_buf(0), orig_font_buf(0)
00495 {
00496 }
00497 
00498 Font::Font(const char *fname, int index, int hlead, int vlead): font_shapes(0), font_data(0), font_buf(0), orig_font_buf(0)
00499 {
00500   load(fname, index, hlead, vlead);
00501 }
00502 
00503 Font::~Font()
00504 {
00505   if(font_shapes)
00506     delete font_shapes;
00507   if(font_data)
00508     delete font_data;
00509   if(orig_font_buf)
00510     delete [] orig_font_buf;
00511 }
00512 
00513 int Font::load(const char *fname, int index, int hlead, int vlead)
00514 {
00515   if(font_shapes)
00516     delete font_shapes;
00517   if (font_data)
00518     delete font_data;
00519   if(orig_font_buf)
00520     delete [] orig_font_buf;
00521   font_shapes = 0;
00522   font_data = 0;
00523   orig_font_buf = 0;
00524   try 
00525   {
00526 
00527     size_t len;
00528 
00529     U7object font_obj(fname, index);
00530     font_buf = font_obj.retrieve(len);
00531 
00532     if (!font_buf || !len) throw (exult_exception ("Unable to retrieve data"));
00533 
00534       orig_font_buf = font_buf;
00535     if(!strncmp(font_buf,"font",4)) // If it's an IFF archive...
00536       font_buf += 8;    // Skip first 8 bytes
00537     font_data = new BufferDataSource(font_buf, len);
00538     font_shapes = new Shape_file(font_data);
00539     hor_lead = hlead;
00540     ver_lead = vlead;
00541     calc_highlow();
00542   }
00543   catch (exult_exception &e)
00544   {
00545     font_data = 0;
00546     font_shapes = 0;
00547     hor_lead = 0;
00548     ver_lead = 0;
00549     orig_font_buf = 0;
00550   }
00551   return 0;
00552 }
00553 
00554 int Font::center_text(Image_buffer8 *win, int x, int y, const char *s)
00555 {
00556   return draw_text(win, x - get_text_width(s)/2, y, s);
00557 }
00558 
00559 void Font::calc_highlow()
00560 {
00561   bool unset = true;
00562 
00563   for (int i = 0; i < font_shapes->get_num_frames(); i++)
00564   {
00565     Shape_frame *f = font_shapes->get_frame(i);
00566 
00567     if (!f) continue;
00568 
00569     if (unset)
00570     {
00571       unset = false;
00572       highest = f->get_yabove();
00573       lowest = f->get_ybelow();
00574       continue;
00575     }
00576     
00577     if (f->get_yabove() > highest) highest = f->get_yabove();
00578     if (f->get_ybelow() > lowest) lowest = f->get_ybelow();
00579   }
00580 }
00581 
00582 FontManager::FontManager()
00583 {
00584 }
00585 
00586 FontManager::~FontManager()
00587 {
00588   fonts.clear();
00589 }
00590 
00591 void FontManager::add_font(const char *name, const char *archive, int index, int hlead, int vlead)
00592 {
00593   remove_font(name);
00594 
00595   Font *font = new Font(archive, index, hlead, vlead);
00596   
00597   fonts[name] = font;
00598 }
00599 
00600 void FontManager::remove_font(const char *name)
00601 {
00602   if(fonts[name]!=0) {
00603     delete fonts[name];
00604     fonts.erase(name);
00605   }
00606 }
00607 
00608 Font *FontManager::get_font(const char *name)
00609 {
00610   return fonts[name];
00611 }
00612 
00613 void FontManager::reset()
00614 {
00615 #ifndef DONT_HAVE_HASH_MAP
00616   hash_map<const char*, Font*, hashstr, eqstr>::iterator i;
00617 #else
00618   std::map<const char*, Font*, ltstr>::iterator i;
00619 #endif
00620 
00621   for (i=fonts.begin(); i != fonts.end(); ++i) {
00622     delete (*i).second;
00623   }
00624 
00625   fonts.clear();
00626 }

Generated on Mon Jul 9 14:42:50 2007 for ExultEngine by  doxygen 1.5.1