GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
txt-eng-ft.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2009-2013 Michael Goffioul
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #if defined (HAVE_FREETYPE)
28 
29 #if defined (HAVE_FONTCONFIG)
30 #include <fontconfig/fontconfig.h>
31 #endif
32 
33 #include <clocale>
34 #include <cwchar>
35 #include <iostream>
36 #include <map>
37 #include <utility>
38 
39 #include "singleton-cleanup.h"
40 
41 #include "error.h"
42 #include "pr-output.h"
43 #include "txt-eng-ft.h"
44 
45 // FIXME: maybe issue at most one warning per glyph/font/size/weight
46 // combination.
47 
48 static void
49 gripe_missing_glyph (FT_ULong c)
50 {
51  warning_with_id ("Octave:missing-glyph",
52  "ft_render: skipping missing glyph for character '%x'",
53  c);
54 }
55 
56 static void
57 gripe_glyph_render (FT_ULong c)
58 {
59  warning_with_id ("Octave:glyph-render",
60  "ft_render: unable to render glyph for character '%x'",
61  c);
62 }
63 
64 #ifdef _MSC_VER
65 // This is just a trick to avoid multiple symbol definitions.
66 // PermMatrix.h contains a dllexport'ed Array<octave_idx_type>
67 // that will cause MSVC not to generate a new instantiation and
68 // use the imported one instead.
69 #include "PermMatrix.h"
70 #endif
71 
72 // Forward declaration
73 static void ft_face_destroyed (void* object);
74 
75 class
77 {
78 public:
79  static bool instance_ok (void)
80  {
81  bool retval = true;
82 
83  if (! instance)
84  {
85  instance = new ft_manager ();
86 
87  if (instance)
88  singleton_cleanup_list::add (cleanup_instance);
89  }
90 
91  if (! instance)
92  {
93  ::error ("unable to create ft_manager!");
94 
95  retval = false;
96  }
97 
98  return retval;
99  }
100 
101  static void cleanup_instance (void) { delete instance; instance = 0; }
102 
103  static FT_Face get_font (const std::string& name, const std::string& weight,
104  const std::string& angle, double size)
105  {
106  return (instance_ok ()
107  ? instance->do_get_font (name, weight, angle, size)
108  : 0);
109  }
110 
111  static void font_destroyed (FT_Face face)
112  {
113  if (instance_ok ())
114  instance->do_font_destroyed (face);
115  }
116 
117 private:
118 
120 
121  typedef std::pair<std::string, double> ft_key;
122  typedef std::map<ft_key, FT_Face> ft_cache;
123 
124  // Cache the fonts loaded by freetype. This cache only contains
125  // weak references to the fonts, strong references are only present
126  // in class ft_render.
127  ft_cache cache;
128 
129 private:
130 
131  // No copying!
132 
133  ft_manager (const ft_manager&);
134 
135  ft_manager& operator = (const ft_manager&);
136 
137  ft_manager (void)
138  : library (), freetype_initialized (false), fontconfig_initialized (false)
139  {
140  if (FT_Init_FreeType (&library))
141  ::error ("unable to initialize freetype library");
142  else
143  freetype_initialized = true;
144 
145 #if defined (HAVE_FONTCONFIG)
146  if (! FcInit ())
147  ::error ("unable to initialize fontconfig library");
148  else
149  fontconfig_initialized = true;
150 #endif
151  }
152 
153  ~ft_manager (void)
154  {
155  if (freetype_initialized)
156  FT_Done_FreeType (library);
157 
158 #if defined (HAVE_FONTCONFIG)
159  // FIXME: Skip the call to FcFini because it can trigger the assertion
160  //
161  // octave: fccache.c:507: FcCacheFini: Assertion 'fcCacheChains[i] == ((void *)0)' failed.
162  //
163  // if (fontconfig_initialized)
164  // FcFini ();
165 #endif
166  }
167 
168 
169  FT_Face do_get_font (const std::string& name, const std::string& weight,
170  const std::string& angle, double size)
171  {
172  FT_Face retval = 0;
173 
174 #if HAVE_FT_REFERENCE_FACE
175  // Look first into the font cache, then use fontconfig. If the font
176  // is present in the cache, simply add a reference and return it.
177 
178  ft_key key (name + ":" + weight + ":" + angle, size);
179  ft_cache::const_iterator it = cache.find (key);
180 
181  if (it != cache.end ())
182  {
183  FT_Reference_Face (it->second);
184  return it->second;
185  }
186 #endif
187 
188  std::string file;
189 
190 #if defined (HAVE_FONTCONFIG)
191  if (fontconfig_initialized)
192  {
193  int fc_weight, fc_angle;
194 
195  if (weight == "bold")
196  fc_weight = FC_WEIGHT_BOLD;
197  else if (weight == "light")
198  fc_weight = FC_WEIGHT_LIGHT;
199  else if (weight == "demi")
200  fc_weight = FC_WEIGHT_DEMIBOLD;
201  else
202  fc_weight = FC_WEIGHT_NORMAL;
203 
204  if (angle == "italic")
205  fc_angle = FC_SLANT_ITALIC;
206  else if (angle == "oblique")
207  fc_angle = FC_SLANT_OBLIQUE;
208  else
209  fc_angle = FC_SLANT_ROMAN;
210 
211  FcPattern *pat = FcPatternCreate ();
212 
213  FcPatternAddString (pat, FC_FAMILY,
214  (reinterpret_cast<const FcChar8*>
215  (name == "*" ? "sans" : name.c_str ())));
216 
217  FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
218  FcPatternAddInteger (pat, FC_SLANT, fc_angle);
219  FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
220 
221  if (FcConfigSubstitute (0, pat, FcMatchPattern))
222  {
223  FcResult res;
224  FcPattern *match;
225 
226  FcDefaultSubstitute (pat);
227  match = FcFontMatch (0, pat, &res);
228 
229  // FIXME: originally, this test also required that
230  // res != FcResultNoMatch. Is that really needed?
231  if (match)
232  {
233  unsigned char *tmp;
234 
235  FcPatternGetString (match, FC_FILE, 0, &tmp);
236  file = reinterpret_cast<char*> (tmp);
237  }
238  else
239  ::warning ("could not match any font: %s-%s-%s-%g",
240  name.c_str (), weight.c_str (), angle.c_str (),
241  size);
242 
243  if (match)
244  FcPatternDestroy (match);
245  }
246 
247  FcPatternDestroy (pat);
248  }
249 #endif
250 
251  if (file.empty ())
252  {
253 #ifdef __WIN32__
254  file = "C:/WINDOWS/Fonts/verdana.ttf";
255 #else
256  // FIXME: find a "standard" font for UNIX platforms
257 #endif
258  }
259 
260  if (! file.empty ())
261  {
262  if (FT_New_Face (library, file.c_str (), 0, &retval))
263  ::warning ("ft_manager: unable to load font: %s", file.c_str ());
264 #if HAVE_FT_REFERENCE_FACE
265  else
266  {
267  // Install a finalizer to notify ft_manager that the font is
268  // being destroyed. The class ft_manager only keeps weak
269  // references to font objects.
270 
271  retval->generic.data = new ft_key (key);
272  retval->generic.finalizer = ft_face_destroyed;
273 
274  // Insert loaded font into the cache.
275 
276  cache[key] = retval;
277  }
278 #endif
279  }
280 
281  return retval;
282  }
283 
284  void do_font_destroyed (FT_Face face)
285  {
286  if (face->generic.data)
287  {
288  ft_key* pkey = reinterpret_cast<ft_key*> (face->generic.data);
289 
290  cache.erase (*pkey);
291  delete pkey;
292  face->generic.data = 0;
293  }
294  }
295 
296 private:
297  FT_Library library;
300 };
301 
303 
304 static void
305 ft_face_destroyed (void* object)
306 { ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object)); }
307 
308 // ---------------------------------------------------------------------------
309 
311  : text_processor (), font (), bbox (1, 4, 0.0), halign (0), xoffset (0),
312  line_yoffset (0), yoffset (0), mode (MODE_BBOX),
313  color (dim_vector (1, 3), 0)
314 {
315 }
316 
318 {
319 }
320 
321 void
322 ft_render::set_font (const std::string& name, const std::string& weight,
323  const std::string& angle, double size)
324 {
325  // FIXME: take "fontunits" into account
326 
327  font = ft_font (name, weight, angle, size, 0);
328 }
329 
330 void
332 {
333  switch (mode)
334  {
335  case MODE_BBOX:
336  {
337  // Create a new bbox entry based on the current font.
338 
339  FT_Face face = font.get_face ();
340 
341  if (face)
342  {
343  int asc = face->size->metrics.ascender >> 6;
344  int desc = face->size->metrics.descender >> 6;
345  int h = face->size->metrics.height >> 6;
346 
347  Matrix bb (1, 5, 0.0);
348 
349  bb(1) = desc;
350  bb(3) = asc - desc;
351  bb(4) = h;
352 
353  line_bbox.push_back (bb);
354 
355  xoffset = yoffset = 0;
356  }
357  }
358  break;
359 
360  case MODE_RENDER:
361  {
362  // Move to the next line bbox, adjust xoffset based on alignment
363  // and yoffset based on the old and new line bbox.
364 
365  Matrix old_bbox = line_bbox.front ();
366  line_bbox.pop_front ();
367  Matrix new_bbox = line_bbox.front ();
368 
369  xoffset = compute_line_xoffset (new_bbox);
370  line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3)));
371  yoffset = 0;
372  }
373  break;
374  }
375 }
376 
377 int
379 {
380  if (! bbox.is_empty ())
381  {
382  switch (halign)
383  {
384  case 0:
385  return 0;
386  case 1:
387  return (bbox(2) - lb(2)) / 2;
388  case 2:
389  return (bbox(2) - lb(2));
390  }
391  }
392 
393  return 0;
394 }
395 
396 void
398 {
399  // Stack the various line bbox together and compute the final
400  // bounding box for the entire text string.
401 
402  bbox = Matrix ();
403 
404  switch (line_bbox.size ())
405  {
406  case 0:
407  break;
408  case 1:
409  bbox = line_bbox.front ().extract (0, 0, 0, 3);
410  break;
411  default:
412  for (std::list<Matrix>::const_iterator it = line_bbox.begin ();
413  it != line_bbox.end (); ++it)
414  {
415  if (bbox.is_empty ())
416  bbox = it->extract (0, 0, 0, 3);
417  else
418  {
419  bbox(1) -= (*it)(3);
420  bbox(3) += (*it)(3);
421  bbox(2) = xmax (bbox(2), (*it)(2));
422  }
423  }
424  break;
425  }
426 }
427 
428 void
430 {
431  // Called after a font change, when in MODE_BBOX mode, to update the
432  // current line bbox with the new font metrics. This also includes the
433  // current yoffset, that is the offset of the current glyph's baseline
434  // the line's baseline.
435 
436  if (mode == MODE_BBOX)
437  {
438  int asc = font.get_face ()->size->metrics.ascender >> 6;
439  int desc = font.get_face ()->size->metrics.descender >> 6;
440 
441  Matrix& bb = line_bbox.front ();
442 
443  if ((yoffset + desc) < bb(1))
444  {
445  // The new font goes below the bottom of the current bbox.
446 
447  int delta = bb(1) - (yoffset + desc);
448 
449  bb(1) -= delta;
450  bb(3) += delta;
451  }
452 
453  if ((yoffset + asc) > (bb(1) + bb(3)))
454  {
455  // The new font goes above the top of the current bbox.
456 
457  int delta = (yoffset + asc) - (bb(1) + bb(3));
458 
459  bb(3) += delta;
460  }
461  }
462 }
463 
464 void
466 {
467  mode = m;
468 
469  switch (mode)
470  {
471  case MODE_BBOX:
472  xoffset = line_yoffset = yoffset = 0;
473  bbox = Matrix (1, 4, 0.0);
474  line_bbox.clear ();
475  push_new_line ();
476  break;
477  case MODE_RENDER:
478  if (bbox.numel () != 4)
479  {
480  ::warning ("ft_render: invalid bounding box, cannot render");
481 
482  xoffset = line_yoffset = yoffset = 0;
483  pixels = uint8NDArray ();
484  }
485  else
486  {
487  pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)),
488  static_cast<uint8_t> (0));
490  line_yoffset = -bbox(1)-1;
491  yoffset = 0;
492  }
493  break;
494  default:
495  ::error ("ft_render: invalid mode '%d'", mode);
496  break;
497  }
498 }
499 
500 FT_UInt
501 ft_render::process_character (FT_ULong code, FT_UInt previous)
502 {
503  FT_Face face = font.get_face ();
504  FT_UInt glyph_index = 0;
505 
506  if (face)
507  {
508  glyph_index = FT_Get_Char_Index (face, code);
509 
510  if (code != '\n'
511  && (! glyph_index
512  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
513  {
514  glyph_index = 0;
515  gripe_missing_glyph (code);
516  }
517  else
518  {
519  switch (mode)
520  {
521  case MODE_RENDER:
522  if (code == '\n')
523  {
524  glyph_index = FT_Get_Char_Index (face, ' ');
525  if (! glyph_index
526  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
527  {
528  glyph_index = 0;
529  gripe_missing_glyph (' ');
530  }
531  else
532  push_new_line ();
533  }
534  else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
535  {
536  glyph_index = 0;
537  gripe_glyph_render (code);
538  }
539  else
540  {
541  FT_Bitmap& bitmap = face->glyph->bitmap;
542  int x0, y0;
543 
544  if (previous)
545  {
546  FT_Vector delta;
547 
548  FT_Get_Kerning (face, previous, glyph_index,
549  FT_KERNING_DEFAULT, &delta);
550  xoffset += (delta.x >> 6);
551  }
552 
553  x0 = xoffset + face->glyph->bitmap_left;
554  y0 = line_yoffset + yoffset + face->glyph->bitmap_top;
555 
556  // 'w' seems to have a negative -1
557  // face->glyph->bitmap_left, this is so we don't
558  // index out of bound, and assumes we we allocated
559  // the right amount of horizontal space in the bbox.
560  if (x0 < 0)
561  x0 = 0;
562 
563  for (int r = 0; r < bitmap.rows; r++)
564  for (int c = 0; c < bitmap.width; c++)
565  {
566  unsigned char pix = bitmap.buffer[r*bitmap.width+c];
567  if (x0+c < 0 || x0+c >= pixels.dim2 ()
568  || y0-r < 0 || y0-r >= pixels.dim3 ())
569  {
570  //::warning ("ft_render: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)",
571  // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ());
572  }
573  else if (pixels(3, x0+c, y0-r).value () == 0)
574  {
575  pixels(0, x0+c, y0-r) = color(0);
576  pixels(1, x0+c, y0-r) = color(1);
577  pixels(2, x0+c, y0-r) = color(2);
578  pixels(3, x0+c, y0-r) = pix;
579  }
580  }
581 
582  xoffset += (face->glyph->advance.x >> 6);
583  }
584  break;
585 
586  case MODE_BBOX:
587  if (code == '\n')
588  {
589  glyph_index = FT_Get_Char_Index (face, ' ');
590  if (! glyph_index
591  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
592  {
593  glyph_index = 0;
594  gripe_missing_glyph (' ');
595  }
596  else
597  push_new_line ();
598  }
599  else
600  {
601  Matrix& bb = line_bbox.back ();
602 
603  // If we have a previous glyph, use kerning information.
604  // This usually means moving a bit backward before adding
605  // the next glyph. That is, "delta.x" is usually < 0.
606  if (previous)
607  {
608  FT_Vector delta;
609 
610  FT_Get_Kerning (face, previous, glyph_index,
611  FT_KERNING_DEFAULT, &delta);
612 
613  xoffset += (delta.x >> 6);
614  }
615 
616  // Extend current X offset box by the width of the current
617  // glyph. Then extend the line bounding box if necessary.
618 
619  xoffset += (face->glyph->advance.x >> 6);
620  bb(2) = xmax (bb(2), xoffset);
621  }
622  break;
623  }
624  }
625  }
626 
627  return glyph_index;
628 }
629 
630 void
632 {
633  if (font.is_valid ())
634  {
635  FT_UInt glyph_index, previous = 0;
636 
637  std::string str = e.string_value ();
638  size_t n = str.length (), curr = 0;
639  mbstate_t ps;
640  memset (&ps, 0, sizeof (ps)); // Initialize state to 0.
641  wchar_t wc;
642 
643  while (n > 0)
644  {
645  size_t r = gnulib::mbrtowc (&wc, str.data () + curr, n, &ps);
646 
647  if (r > 0
648  && r != static_cast<size_t> (-1)
649  && r != static_cast<size_t> (-2))
650  {
651  n -= r;
652  curr += r;
653 
654  glyph_index = process_character (wc, previous);
655 
656  if (wc == L'\n')
657  previous = 0;
658  else
659  previous = glyph_index;
660  }
661  else
662  {
663  if (r != 0)
664  ::warning ("ft_render: failed to decode string `%s' with "
665  "locale `%s'", str.c_str (),
666  std::setlocale (LC_CTYPE, 0));
667  break;
668  }
669  }
670  }
671 }
672 
673 void
675 {
676  // Save and restore (after processing the list) the current font and color.
677 
678  ft_font saved_font (font);
679  uint8NDArray saved_color (color);
680 
682 
683  font = saved_font;
684  color = saved_color;
685 }
686 
687 void
689 {
690  ft_font saved_font (font);
691  int saved_line_yoffset = line_yoffset;
692  int saved_yoffset = yoffset;
693 
695  font.get_size () - 2);
696 
697  if (font.is_valid ())
698  {
699  int h = font.get_face ()->size->metrics.height >> 6;
700 
701  // Shifting the baseline by 2/3 the font height seems to produce
702  // decent result.
703  yoffset -= (h * 2) / 3;
704 
705  if (mode == MODE_BBOX)
706  update_line_bbox ();
707  }
708 
710 
711  font = saved_font;
712  // If line_yoffset changed, this means we moved to a new line; hence yoffset
713  // cannot be restored, because the saved value is not relevant anymore.
714  if (line_yoffset == saved_line_yoffset)
715  yoffset = saved_yoffset;
716 }
717 
718 void
720 {
721  ft_font saved_font (font);
722  int saved_line_yoffset = line_yoffset;
723  int saved_yoffset = yoffset;
724 
726  font.get_size () - 2);
727 
728  if (saved_font.is_valid ())
729  {
730  int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6;
731 
732  // Shifting the baseline by 2/3 base font ascender seems to produce
733  // decent result.
734  yoffset += (s_asc * 2) / 3;
735 
736  if (mode == MODE_BBOX)
737  update_line_bbox ();
738  }
739 
741 
742  font = saved_font;
743  // If line_yoffset changed, this means we moved to a new line; hence yoffset
744  // cannot be restored, because the saved value is not relevant anymore.
745  if (line_yoffset == saved_line_yoffset)
746  yoffset = saved_yoffset;
747 }
748 
749 void
751 {
752  if (mode == MODE_RENDER)
753  set_color (e.get_color ());
754 }
755 
756 void
758 {
759  double sz = e.get_fontsize ();
760 
761  // FIXME: Matlab documentation says that the font size is expressed
762  // in the text object FontUnit.
763 
765 
766  if (mode == MODE_BBOX)
767  update_line_bbox ();
768 }
769 
770 void
772 {
774  font.get_size ());
775 
776  if (mode == MODE_BBOX)
777  update_line_bbox ();
778 }
779 
780 void
782 {
783  switch (e.get_fontstyle ())
784  {
786  set_font (font.get_name (), "normal", "normal", font.get_size ());
787  break;
789  set_font (font.get_name (), "bold", "normal", font.get_size ());
790  break;
792  set_font (font.get_name (), "normal", "italic", font.get_size ());
793  break;
795  set_font (font.get_name (), "normal", "oblique", font.get_size ());
796  break;
797  }
798 
799  if (mode == MODE_BBOX)
800  update_line_bbox ();
801 }
802 
803 void
805 {
806  uint32_t code = e.get_symbol_code ();
807 
809  process_character (code);
810  else if (font.is_valid ())
811  ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
812 }
813 
814 void
816 {
817  int saved_xoffset = xoffset;
818  int max_xoffset = xoffset;
819 
820  for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it)
821  {
822  xoffset = saved_xoffset;
823  (*it)->accept (*this);
824  max_xoffset = xmax (xoffset, max_xoffset);
825  }
826 
827  xoffset = max_xoffset;
828 }
829 
830 void
832 {
834  set_color (Matrix (1, 3, 0.0));
835 }
836 
837 void
839 {
840  if (c.numel () == 3)
841  {
842  color(0) = static_cast<uint8_t> (c(0)*255);
843  color(1) = static_cast<uint8_t> (c(1)*255);
844  color(2) = static_cast<uint8_t> (c(2)*255);
845  }
846  else
847  ::warning ("ft_render::set_color: invalid color");
848 }
849 
851 ft_render::render (text_element* elt, Matrix& box, int rotation)
852 {
854  elt->accept (*this);
855  compute_bbox ();
856  box = bbox;
857 
859  if (pixels.numel () > 0)
860  {
861  elt->accept (*this);
862 
863  switch (rotation)
864  {
865  case ROTATION_0:
866  break;
867  case ROTATION_90:
868  {
869  Array<octave_idx_type> perm (dim_vector (3, 1));
870  perm(0) = 0;
871  perm(1) = 2;
872  perm(2) = 1;
873  pixels = pixels.permute (perm);
874 
875  Array<idx_vector> idx (dim_vector (3, 1));
876  idx(0) = idx_vector (':');
877  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
878  idx(2) = idx_vector (':');
879  pixels = uint8NDArray (pixels.index (idx));
880  }
881  break;
882  case ROTATION_180:
883  {
884  Array<idx_vector> idx (dim_vector (3, 1));
885  idx(0) = idx_vector (':');
886  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
887  idx(2)= idx_vector (pixels.dim3 ()-1, -1, -1);
888  pixels = uint8NDArray (pixels.index (idx));
889  }
890  break;
891  case ROTATION_270:
892  {
893  Array<octave_idx_type> perm (dim_vector (3, 1));
894  perm(0) = 0;
895  perm(1) = 2;
896  perm(2) = 1;
897  pixels = pixels.permute (perm);
898 
899  Array<idx_vector> idx (dim_vector (3, 1));
900  idx(0) = idx_vector (':');
901  idx(1) = idx_vector (':');
902  idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
903  pixels = uint8NDArray (pixels.index (idx));
904  }
905  break;
906  }
907  }
908 
909  return pixels;
910 }
911 
912 // Note:
913 // x-extent accurately measures width of glyphs.
914 // y-extent is overly large because it is measured from baseline-to-baseline.
915 // Calling routines, such as ylabel, may need to account for this mismatch.
916 
917 Matrix
918 ft_render::get_extent (text_element *elt, double rotation)
919 {
921  elt->accept (*this);
922  compute_bbox ();
923 
924  Matrix extent (1, 2, 0.0);
925 
926  switch (rotation_to_mode (rotation))
927  {
928  case ROTATION_0:
929  case ROTATION_180:
930  extent(0) = bbox(2);
931  extent(1) = bbox(3);
932  break;
933  case ROTATION_90:
934  case ROTATION_270:
935  extent(0) = bbox(3);
936  extent(1) = bbox(2);
937  }
938 
939  return extent;
940 }
941 
942 Matrix
943 ft_render::get_extent (const std::string& txt, double rotation,
944  const caseless_str& interpreter)
945 {
946  text_element *elt = text_parser::parse (txt, interpreter);
947  Matrix extent = get_extent (elt, rotation);
948  delete elt;
949 
950  return extent;
951 }
952 
953 int
954 ft_render::rotation_to_mode (double rotation) const
955 {
956  // Clip rotation to range [0, 360]
957  while (rotation < 0)
958  rotation += 360.0;
959  while (rotation > 360.0)
960  rotation -= 360.0;
961 
962  if (rotation == 0.0)
963  return ROTATION_0;
964  else if (rotation == 90.0)
965  return ROTATION_90;
966  else if (rotation == 180.0)
967  return ROTATION_180;
968  else if (rotation == 270.0)
969  return ROTATION_270;
970  else
971  return ROTATION_0;
972 }
973 
974 void
975 ft_render::text_to_pixels (const std::string& txt,
976  uint8NDArray& pixels_, Matrix& box,
977  int _halign, int valign, double rotation,
978  const caseless_str& interpreter)
979 {
980  int rot_mode = rotation_to_mode (rotation);
981 
982  halign = _halign;
983 
984  text_element *elt = text_parser::parse (txt, interpreter);
985  pixels_ = render (elt, box, rot_mode);
986  delete elt;
987 
988  if (pixels_.numel () == 0)
989  {
990  // nothing to render
991  return;
992  }
993 
994  switch (halign)
995  {
996  default: box(0) = 0; break;
997  case 1: box(0) = -box(2)/2; break;
998  case 2: box(0) = -box(2); break;
999  }
1000  switch (valign)
1001  {
1002  default: box(1) = 0; break;
1003  case 1: box(1) = -box(3)/2; break;
1004  case 2: box(1) = -box(3); break;
1005  case 3: break;
1006  case 4: box(1) = -box(3)-box(1); break;
1007  }
1008 
1009  switch (rot_mode)
1010  {
1011  case ROTATION_90:
1012  std::swap (box(0), box(1));
1013  std::swap (box(2), box(3));
1014  box(0) = -box(0)-box(2);
1015  break;
1016  case ROTATION_180:
1017  box(0) = -box(0)-box(2);
1018  box(1) = -box(1)-box(3);
1019  break;
1020  case ROTATION_270:
1021  std::swap (box(0), box(1));
1022  std::swap (box(2), box(3));
1023  box(1) = -box(1)-box(3);
1024  break;
1025  }
1026 }
1027 
1029  : name (ft.name), weight (ft.weight), angle (ft.angle), size (ft.size),
1030  face (0)
1031 {
1032 #if HAVE_FT_REFERENCE_FACE
1033  FT_Face ft_face = ft.get_face ();
1034 
1035  if (ft_face && FT_Reference_Face (ft_face) == 0)
1036  face = ft_face;
1037 #endif
1038 }
1039 
1042 {
1043  if (&ft != this)
1044  {
1045  name = ft.name;
1046  weight = ft.weight;
1047  angle = ft.angle;
1048  size = ft.size;
1049  if (face)
1050  {
1051  FT_Done_Face (face);
1052  face = 0;
1053  }
1054 
1055 #if HAVE_FT_REFERENCE_FACE
1056  FT_Face ft_face = ft.get_face ();
1057 
1058  if (ft_face && FT_Reference_Face (ft_face) == 0)
1059  face = ft_face;
1060 #endif
1061  }
1062 
1063  return *this;
1064 }
1065 
1066 FT_Face
1068 {
1069  if (! face && ! name.empty ())
1070  {
1071  face = ft_manager::get_font (name, weight, angle, size);
1072 
1073  if (face)
1074  {
1075  if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
1076  ::warning ("ft_render: unable to set font size to %g", size);
1077  }
1078  else
1079  ::warning ("ft_render: unable to load appropriate font");
1080  }
1081 
1082  return face;
1083 }
1084 
1085 #endif // HAVE_FREETYPE