txt-eng-ft.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2009-2012 Michael Goffioul
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #if defined (HAVE_FREETYPE)
00028 
00029 #if defined (HAVE_FONTCONFIG)
00030 #include <fontconfig/fontconfig.h>
00031 #endif
00032 
00033 #include <iostream>
00034 
00035 #include "singleton-cleanup.h"
00036 
00037 #include "error.h"
00038 #include "pr-output.h"
00039 #include "txt-eng-ft.h"
00040 
00041 // FIXME -- maybe issue at most one warning per glyph/font/size/weight
00042 // combination.
00043 
00044 static void
00045 gripe_missing_glyph (char c)
00046 {
00047   warning_with_id ("Octave:missing-glyph",
00048                    "ft_render: skipping missing glyph for character '%c'",
00049                    c);
00050 }
00051 
00052 static void
00053 gripe_glyph_render (char c)
00054 {
00055   warning_with_id ("Octave:glyph-render",
00056                    "ft_render: unable to render glyph for character '%c'",
00057                    c);
00058 }
00059 
00060 #ifdef _MSC_VER
00061 // This is just a trick to avoid multiply symbols definition.
00062 // PermMatrix.h contains a dllexport'ed Array<octave_idx_type>
00063 // that will make MSVC not to generate new instantiation and
00064 // use the imported one.
00065 #include "PermMatrix.h"
00066 #endif
00067 
00068 class
00069 ft_manager
00070 {
00071 public:
00072   static bool instance_ok (void)
00073     {
00074       bool retval = true;
00075 
00076       if (! instance)
00077         {
00078           instance = new ft_manager ();
00079 
00080           if (instance)
00081             singleton_cleanup_list::add (cleanup_instance);
00082         }
00083 
00084       if (! instance)
00085         {
00086           ::error ("unable to create ft_manager!");
00087 
00088           retval = false;
00089         }
00090 
00091       return retval;
00092     }
00093 
00094   static void cleanup_instance (void) { delete instance; instance = 0; }
00095 
00096   static FT_Face get_font (const std::string& name, const std::string& weight,
00097                            const std::string& angle, double size)
00098     { return (instance_ok ()
00099               ? instance->do_get_font (name, weight, angle, size)
00100               : 0); }
00101 
00102 private:
00103 
00104   static ft_manager *instance;
00105 
00106 private:
00107 
00108   // No copying!
00109 
00110   ft_manager (const ft_manager&);
00111 
00112   ft_manager& operator = (const ft_manager&);
00113 
00114   ft_manager (void)
00115     : library (), freetype_initialized (false), fontconfig_initialized (false)
00116     {
00117       if (FT_Init_FreeType (&library))
00118         ::error ("unable to initialize freetype library");
00119       else
00120         freetype_initialized = true;
00121 
00122 #if defined (HAVE_FONTCONFIG)
00123       if (! FcInit ())
00124         ::error ("unable to initialize fontconfig library");
00125       else
00126         fontconfig_initialized = true;
00127 #endif
00128     }
00129 
00130   ~ft_manager (void)
00131     {
00132       if (freetype_initialized)
00133         FT_Done_FreeType (library);
00134 
00135 #if defined (HAVE_FONTCONFIG)
00136       // FIXME -- Skip the call to FcFini because it can trigger the
00137       // assertion
00138       //
00139       //   octave: fccache.c:507: FcCacheFini: Assertion 'fcCacheChains[i] == ((void *)0)' failed.
00140       //
00141       // if (fontconfig_initialized)
00142       //   FcFini ();
00143 #endif
00144     }
00145 
00146 
00147   FT_Face do_get_font (const std::string& name, const std::string& weight,
00148                        const std::string& angle, double size)
00149     {
00150       FT_Face retval = 0;
00151 
00152       std::string file;
00153 
00154 #if defined (HAVE_FONTCONFIG)
00155       if (fontconfig_initialized)
00156         {
00157           int fc_weight, fc_angle;
00158 
00159           if (weight == "bold")
00160             fc_weight = FC_WEIGHT_BOLD;
00161           else if (weight == "light")
00162             fc_weight = FC_WEIGHT_LIGHT;
00163           else if (weight == "demi")
00164             fc_weight = FC_WEIGHT_DEMIBOLD;
00165           else
00166             fc_weight = FC_WEIGHT_NORMAL;
00167 
00168           if (angle == "italic")
00169             fc_angle = FC_SLANT_ITALIC;
00170           else if (angle == "oblique")
00171             fc_angle = FC_SLANT_OBLIQUE;
00172           else
00173             fc_angle = FC_SLANT_ROMAN;
00174 
00175           FcPattern *pat = FcPatternCreate ();
00176 
00177           FcPatternAddString (pat, FC_FAMILY,
00178                               (reinterpret_cast<const FcChar8*>
00179                                (name == "*" ? "sans" : name.c_str ())));
00180 
00181           FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
00182           FcPatternAddInteger (pat, FC_SLANT, fc_angle);
00183           FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
00184 
00185           if (FcConfigSubstitute (0, pat, FcMatchPattern))
00186             {
00187               FcResult res;
00188               FcPattern *match;
00189 
00190               FcDefaultSubstitute (pat);
00191               match = FcFontMatch (0, pat, &res);
00192 
00193               // FIXME -- originally, this test also required that
00194               // res != FcResultNoMatch.  Is that really needed?
00195               if (match)
00196                 {
00197                   unsigned char *tmp;
00198 
00199                   FcPatternGetString (match, FC_FILE, 0, &tmp);
00200                   file = reinterpret_cast<char*> (tmp);
00201                 }
00202               else
00203                 ::warning ("could not match any font: %s-%s-%s-%g",
00204                          name.c_str (), weight.c_str (), angle.c_str (),
00205                          size);
00206 
00207               if (match)
00208                 FcPatternDestroy (match);
00209             }
00210 
00211           FcPatternDestroy (pat);
00212         }
00213 #endif
00214 
00215       if (file.empty ())
00216         {
00217 #ifdef __WIN32__
00218           file = "C:/WINDOWS/Fonts/verdana.ttf";
00219 #else
00220           // FIXME: find a "standard" font for UNIX platforms
00221 #endif
00222         }
00223 
00224       if (! file.empty () && FT_New_Face (library, file.c_str (), 0, &retval))
00225         ::warning ("ft_manager: unable to load font: %s", file.c_str ());
00226 
00227       return retval;
00228     }
00229 
00230 private:
00231   FT_Library library;
00232   bool freetype_initialized;
00233   bool fontconfig_initialized;
00234 };
00235 
00236 ft_manager* ft_manager::instance = 0;
00237 
00238 // ---------------------------------------------------------------------------
00239 
00240 ft_render::ft_render (void)
00241     : text_processor (), face (0), bbox (1, 4, 0.0),
00242       xoffset (0), yoffset (0), multiline_halign (0),
00243       multiline_align_xoffsets(), mode (MODE_BBOX),
00244       red (0), green (0), blue (0)
00245 {
00246 }
00247 
00248 ft_render::~ft_render (void)
00249 {
00250   if (face)
00251     FT_Done_Face (face);
00252 }
00253 
00254 void
00255 ft_render::set_font (const std::string& name, const std::string& weight,
00256                      const std::string& angle, double size)
00257 {
00258   if (face)
00259     FT_Done_Face (face);
00260 
00261   // FIXME: take "fontunits" into account
00262   face = ft_manager::get_font (name, weight, angle, size);
00263 
00264   if (face)
00265     {
00266       if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
00267         ::warning ("ft_render: unable to set font size to %d", size);
00268     }
00269   else
00270     ::warning ("ft_render: unable to load appropriate font");
00271 }
00272 
00273 void
00274 ft_render::set_mode (int m)
00275 {
00276   mode = m;
00277 
00278   switch (mode)
00279     {
00280     case MODE_BBOX:
00281       xoffset = yoffset = 0;
00282       bbox = Matrix (1, 4, 0.0);
00283       break;
00284     case MODE_RENDER:
00285       if (bbox.numel () != 4)
00286         {
00287           ::warning ("ft_render: invalid bounding box, cannot render");
00288 
00289           xoffset = yoffset = 0;
00290           pixels = uint8NDArray ();
00291         }
00292       else
00293         {
00294           pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)),
00295                                  static_cast<uint8_t> (0));
00296           xoffset = 0;
00297           yoffset = -bbox(1)-1;
00298         }
00299       break;
00300     default:
00301       ::error ("ft_render: invalid mode '%d'", mode);
00302       break;
00303     }
00304 }
00305 
00306 void
00307 ft_render::visit (text_element_string& e)
00308 {
00309   if (face)
00310     {
00311       int line_index = 0;
00312       FT_UInt box_line_width = 0;
00313       std::string str = e.string_value ();
00314       FT_UInt glyph_index, previous = 0;
00315 
00316       if (mode == MODE_BBOX)
00317         multiline_align_xoffsets.clear();
00318       else if (mode == MODE_RENDER)
00319         xoffset += multiline_align_xoffsets[line_index];
00320 
00321       for (size_t i = 0; i < str.length (); i++)
00322         {
00323           glyph_index = FT_Get_Char_Index (face, str[i]);
00324 
00325           if (str[i] != '\n'
00326               && (! glyph_index
00327               || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
00328             gripe_missing_glyph (str[i]);
00329           else
00330             {
00331               switch (mode)
00332                 {
00333                 case MODE_RENDER:
00334                   if (str[i] == '\n')
00335                     {
00336                     glyph_index = FT_Get_Char_Index(face, ' ');
00337                     if (!glyph_index || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
00338                       {
00339                         gripe_missing_glyph (' ');
00340                       }
00341                     else
00342                       {
00343                         line_index++;
00344                         xoffset = multiline_align_xoffsets[line_index];
00345                         yoffset -= (face->size->metrics.height >> 6);
00346                       }
00347                     }
00348                   else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
00349                     {
00350                       gripe_glyph_render (str[i]);
00351                     }
00352                   else
00353                     {
00354                       FT_Bitmap& bitmap = face->glyph->bitmap;
00355                       int x0, y0;
00356 
00357                       if (previous)
00358                         {
00359                           FT_Vector delta;
00360 
00361                           FT_Get_Kerning (face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
00362                           xoffset += (delta.x >> 6);
00363                         }
00364 
00365                       x0 = xoffset+face->glyph->bitmap_left;
00366                       y0 = yoffset+face->glyph->bitmap_top;
00367 
00368                       // 'w' seems to have a negative -1
00369                       // face->glyph->bitmap_left, this is so we don't
00370                       // index out of bound, and assumes we we allocated
00371                       // the right amount of horizontal space in the bbox.
00372                       if (x0 < 0)
00373                         x0 = 0;
00374 
00375                       for (int r = 0; r < bitmap.rows; r++)
00376                         for (int c = 0; c < bitmap.width; c++)
00377                           {
00378                             unsigned char pix = bitmap.buffer[r*bitmap.width+c];
00379                             if (x0+c < 0 || x0+c >= pixels.dim2()
00380                                 || y0-r < 0 || y0-r >= pixels.dim3())
00381                               {
00382                                 //::error ("out-of-bound indexing!!");
00383                               }
00384                             else if (pixels(3, x0+c, y0-r).value () == 0)
00385                               {
00386                                 pixels(0, x0+c, y0-r) = red;
00387                                 pixels(1, x0+c, y0-r) = green;
00388                                 pixels(2, x0+c, y0-r) = blue;
00389                                 pixels(3, x0+c, y0-r) = pix;
00390                               }
00391                           }
00392 
00393                       xoffset += (face->glyph->advance.x >> 6);
00394                     }
00395                   break;
00396 
00397                 case MODE_BBOX:
00398                   if (str[i] == '\n')
00399                     {
00400                       glyph_index = FT_Get_Char_Index(face, ' ');
00401                       if (! glyph_index
00402                           || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
00403                       {
00404                         gripe_missing_glyph (' ');
00405                       }
00406                     else
00407                       {
00408                         multiline_align_xoffsets.push_back(box_line_width);
00409                         // Reset the pixel width for this newline, so we don't
00410                         // allocate a bounding box larger than the horizontal
00411                         // width of the multi-line
00412                         box_line_width = 0;
00413                         bbox(1) -= (face->size->metrics.height >> 6);
00414                       }
00415                     }
00416                   else
00417                     {
00418                     // width
00419                     if (previous)
00420                       {
00421                         FT_Vector delta;
00422 
00423                         FT_Get_Kerning (face, previous, glyph_index,
00424                                         FT_KERNING_DEFAULT, &delta);
00425 
00426                         box_line_width += (delta.x >> 6);
00427                       }
00428 
00429                     box_line_width += (face->glyph->advance.x >> 6);
00430 
00431                     int asc, desc;
00432 
00433                     if (false /*tight*/)
00434                       {
00435                         desc = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
00436                         asc = face->glyph->metrics.horiBearingY;
00437                       }
00438                     else
00439                       {
00440                         asc = face->size->metrics.ascender;
00441                         desc = face->size->metrics.descender;
00442                       }
00443 
00444                     asc = yoffset + (asc >> 6);
00445                     desc = yoffset + (desc >> 6);
00446 
00447                     if (desc < bbox(1))
00448                       {
00449                         bbox(3) += (bbox(1) - desc);
00450                         bbox(1) = desc;
00451                       }
00452                     if (asc > (bbox(3)+bbox(1)))
00453                       bbox(3) = asc-bbox(1);
00454                     if (bbox(2) < box_line_width)
00455                       bbox(2) = box_line_width;
00456                   }
00457                   break;
00458                 }
00459                 if (str[i] == '\n')
00460                   previous = 0;
00461                 else
00462                   previous = glyph_index;
00463             }
00464         }
00465       if (mode == MODE_BBOX)
00466         {
00467           /* Push last the width associated with the last line */
00468           multiline_align_xoffsets.push_back(box_line_width);
00469 
00470           for (unsigned int i = 0; i < multiline_align_xoffsets.size(); i++)
00471             {
00472             /* Center align */
00473             if (multiline_halign == 1)
00474               multiline_align_xoffsets[i] = (bbox(2) - multiline_align_xoffsets[i])/2;
00475             /* Right align */
00476             else if (multiline_halign == 2)
00477               multiline_align_xoffsets[i] = (bbox(2) - multiline_align_xoffsets[i]);
00478             /* Left align */
00479             else
00480               multiline_align_xoffsets[i] = 0;
00481             }
00482         }
00483     }
00484 }
00485 
00486 void
00487 ft_render::reset (void)
00488 {
00489   set_mode (MODE_BBOX);
00490   set_color (Matrix (1, 3, 0.0));
00491 }
00492 
00493 void
00494 ft_render::set_color (Matrix c)
00495 {
00496   if (c.numel () == 3)
00497     {
00498       red = static_cast<uint8_t> (c(0)*255);
00499       green = static_cast<uint8_t> (c(1)*255);
00500       blue = static_cast<uint8_t> (c(2)*255);
00501     }
00502   else
00503     ::warning ("ft_render::set_color: invalid color");
00504 }
00505 
00506 uint8NDArray
00507 ft_render::render (text_element* elt, Matrix& box, int rotation)
00508 {
00509   set_mode (MODE_BBOX);
00510   elt->accept (*this);
00511   box = bbox;
00512 
00513   set_mode (MODE_RENDER);
00514   if (pixels.numel () > 0)
00515     {
00516       elt->accept (*this);
00517 
00518       switch (rotation)
00519         {
00520         case ROTATION_0:
00521           break;
00522         case ROTATION_90:
00523             {
00524               Array<octave_idx_type> perm (dim_vector (3, 1));
00525               perm(0) = 0;
00526               perm(1) = 2;
00527               perm(2) = 1;
00528               pixels = pixels.permute (perm);
00529 
00530               Array<idx_vector> idx (dim_vector (3, 1));
00531               idx(0) = idx_vector (':');
00532               idx(1) = idx_vector (pixels.dim2()-1, -1, -1);
00533               idx(2) = idx_vector (':');
00534               pixels = uint8NDArray (pixels.index (idx));
00535             }
00536           break;
00537         case ROTATION_180:
00538             {
00539               Array<idx_vector> idx (dim_vector (3, 1));
00540               idx(0) = idx_vector (':');
00541               idx(1) = idx_vector (pixels.dim2()-1, -1, -1);
00542               idx(2)=  idx_vector (pixels.dim3()-1, -1, -1);
00543               pixels = uint8NDArray (pixels.index (idx));
00544             }
00545           break;
00546         case ROTATION_270:
00547             {
00548               Array<octave_idx_type> perm (dim_vector (3, 1));
00549               perm(0) = 0;
00550               perm(1) = 2;
00551               perm(2) = 1;
00552               pixels = pixels.permute (perm);
00553 
00554               Array<idx_vector> idx (dim_vector (3, 1));
00555               idx(0) = idx_vector (':');
00556               idx(1) = idx_vector (':');
00557               idx(2) = idx_vector (pixels.dim3()-1, -1, -1);
00558               pixels = uint8NDArray (pixels.index (idx));
00559             }
00560           break;
00561         }
00562     }
00563 
00564   return pixels;
00565 }
00566 
00567 Matrix
00568 ft_render::get_extent (text_element *elt, double rotation)
00569 {
00570   set_mode (MODE_BBOX);
00571   elt->accept (*this);
00572 
00573   Matrix extent (1, 2, 0.0);
00574 
00575   switch (rotation_to_mode (rotation))
00576     {
00577     case ROTATION_0:
00578     case ROTATION_180:
00579       extent(0) = bbox(2);
00580       extent(1) = bbox(3);
00581       break;
00582     case ROTATION_90:
00583     case ROTATION_270:
00584       extent(0) = bbox(3);
00585       extent(1) = bbox(2);
00586     }
00587 
00588   return extent;
00589 }
00590 
00591 Matrix
00592 ft_render::get_extent (const std::string& txt, double rotation)
00593 {
00594   text_element *elt = text_parser_none ().parse (txt);
00595   Matrix extent = get_extent (elt, rotation);
00596   delete elt;
00597 
00598   return extent;
00599 }
00600 
00601 int
00602 ft_render::rotation_to_mode (double rotation) const
00603 {
00604   if (rotation == 0.0)
00605     return ROTATION_0;
00606   else if (rotation == 90.0)
00607     return ROTATION_90;
00608   else if (rotation == 180.0)
00609     return ROTATION_180;
00610   else if (rotation == 270.0)
00611     return ROTATION_270;
00612   else
00613     return ROTATION_0;
00614 }
00615 
00616 void
00617 ft_render::text_to_pixels (const std::string& txt,
00618                            uint8NDArray& pixels_, Matrix& box,
00619                            int halign, int valign, double rotation)
00620 {
00621   // FIXME: clip "rotation" between 0 and 360
00622   int rot_mode = rotation_to_mode (rotation);
00623 
00624   multiline_halign = halign;
00625 
00626   text_element *elt = text_parser_none ().parse (txt);
00627   pixels_ = render (elt, box, rot_mode);
00628   delete elt;
00629 
00630   if (pixels_.numel () == 0)
00631     {
00632       // nothing to render
00633       return;
00634     }
00635 
00636   switch (halign)
00637     {
00638     default: box(0) = 0; break;
00639     case 1: box(0) = -box(2)/2; break;
00640     case 2: box(0) = -box(2); break;
00641     }
00642   switch (valign)
00643     {
00644     default: box(1) = 0; break;
00645     case 1: box(1) = -box(3)/2; break;
00646     case 2: box(1) = -box(3); break;
00647     case 3: break;
00648     }
00649 
00650   switch (rot_mode)
00651     {
00652     case ROTATION_90:
00653       std::swap (box(0), box(1));
00654       std::swap (box(2), box(3));
00655       box(0) = -box(0)-box(2);
00656       break;
00657     case ROTATION_180:
00658       box(0) = -box(0)-box(2);
00659       box(1) = -box(1)-box(3);
00660       break;
00661     case ROTATION_270:
00662       std::swap (box(0), box(1));
00663       std::swap (box(2), box(3));
00664       box(1) = -box(1)-box(3);
00665       break;
00666     }
00667 }
00668 
00669 #endif // HAVE_FREETYPE
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines