GNU Octave  4.2.1
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
ft-text-renderer.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2016-2017 John W. Eaton
4 Copyright (C) 2009-2016 Michael Goffioul
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #if defined (HAVE_CONFIG_H)
25 # include "config.h"
26 #endif
27 
28 #include "base-text-renderer.h"
29 
30 #if defined (HAVE_FREETYPE)
31 
32 #if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
33 #pragma GCC diagnostic push
34 #pragma GCC diagnostic ignored "-Wold-style-cast"
35 #endif
36 
37 #include <ft2build.h>
38 #include FT_FREETYPE_H
39 
40 #if defined (HAVE_FONTCONFIG)
41 # include <fontconfig/fontconfig.h>
42 #endif
43 
44 #if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
45 #pragma GCC diagnostic pop
46 #endif
47 
48 #include <clocale>
49 #include <cwchar>
50 #include <iostream>
51 #include <map>
52 #include <utility>
53 
54 #include "singleton-cleanup.h"
55 
56 #include "error.h"
57 #include "pr-output.h"
58 #include "text-renderer.h"
59 
60 // FIXME: maybe issue at most one warning per glyph/font/size/weight
61 // combination.
62 
63 static void
65 {
66  warning_with_id ("Octave:missing-glyph",
67  "text_renderer: skipping missing glyph for character '%x'", c);
68 }
69 
70 static void
71 warn_glyph_render (FT_ULong c)
72 {
73  warning_with_id ("Octave:glyph-render",
74  "text_renderer: unable to render glyph for character '%x'", c);
75 }
76 
77 #if defined (_MSC_VER)
78 // FIXME: is this really needed?
79 //
80 // This is just a trick to avoid multiple symbol definitions.
81 // PermMatrix.h contains a dllexport'ed Array<octave_idx_type>
82 // that will cause MSVC not to generate a new instantiation and
83 // use the imported one instead.
84 # include "PermMatrix.h"
85 #endif
86 
87 // Forward declaration
88 static void ft_face_destroyed (void *object);
89 
90 namespace octave
91 {
92  class
94  {
95  public:
96  static bool instance_ok (void)
97  {
98  bool retval = true;
99 
100  if (! instance)
101  {
102  instance = new ft_manager ();
103 
104  if (instance)
105  singleton_cleanup_list::add (cleanup_instance);
106  }
107 
108  if (! instance)
109  error ("unable to create ft_manager!");
110 
111  return retval;
112  }
113 
114  static void cleanup_instance (void) { delete instance; instance = 0; }
115 
116  static FT_Face get_font (const std::string& name, const std::string& weight,
117  const std::string& angle, double size)
118  {
119  return (instance_ok ()
120  ? instance->do_get_font (name, weight, angle, size)
121  : 0);
122  }
123 
124  static void font_destroyed (FT_Face face)
125  {
126  if (instance_ok ())
127  instance->do_font_destroyed (face);
128  }
129 
130  private:
131 
133 
134  typedef std::pair<std::string, double> ft_key;
135  typedef std::map<ft_key, FT_Face> ft_cache;
136 
137  // Cache the fonts loaded by FreeType. This cache only contains
138  // weak references to the fonts, strong references are only present
139  // in class text_renderer.
140  ft_cache cache;
141 
142  private:
143 
144  // No copying!
145 
146  ft_manager (const ft_manager&);
147 
148  ft_manager& operator = (const ft_manager&);
149 
150  ft_manager (void)
151  : library (), freetype_initialized (false), fontconfig_initialized (false)
152  {
153  if (FT_Init_FreeType (&library))
154  error ("unable to initialize FreeType library");
155  else
156  freetype_initialized = true;
157 
158 #if defined (HAVE_FONTCONFIG)
159  if (! FcInit ())
160  error ("unable to initialize fontconfig library");
161  else
162  fontconfig_initialized = true;
163 #endif
164  }
165 
166  ~ft_manager (void)
167  {
168  if (freetype_initialized)
169  FT_Done_FreeType (library);
170 
171 #if defined (HAVE_FONTCONFIG)
172  // FIXME: Skip the call to FcFini because it can trigger the assertion
173  //
174  // octave: fccache.c:507: FcCacheFini: Assertion 'fcCacheChains[i] == ((void *)0)' failed.
175  //
176  // if (fontconfig_initialized)
177  // FcFini ();
178 #endif
179  }
180 
181  FT_Face do_get_font (const std::string& name, const std::string& weight,
182  const std::string& angle, double size)
183  {
184  FT_Face retval = 0;
185 
186 #if defined (HAVE_FT_REFERENCE_FACE)
187  // Look first into the font cache, then use fontconfig. If the font
188  // is present in the cache, simply add a reference and return it.
189 
190  ft_key key (name + ":" + weight + ":" + angle, size);
191  ft_cache::const_iterator it = cache.find (key);
192 
193  if (it != cache.end ())
194  {
195  FT_Reference_Face (it->second);
196  return it->second;
197  }
198 #endif
199 
201 
202 #if defined (HAVE_FONTCONFIG)
203  if (fontconfig_initialized)
204  {
205  int fc_weight, fc_angle;
206 
207  if (weight == "bold")
208  fc_weight = FC_WEIGHT_BOLD;
209  else if (weight == "light")
210  fc_weight = FC_WEIGHT_LIGHT;
211  else if (weight == "demi")
212  fc_weight = FC_WEIGHT_DEMIBOLD;
213  else
214  fc_weight = FC_WEIGHT_NORMAL;
215 
216  if (angle == "italic")
217  fc_angle = FC_SLANT_ITALIC;
218  else if (angle == "oblique")
219  fc_angle = FC_SLANT_OBLIQUE;
220  else
221  fc_angle = FC_SLANT_ROMAN;
222 
223  FcPattern *pat = FcPatternCreate ();
224 
225  FcPatternAddString (pat, FC_FAMILY,
226  (reinterpret_cast<const FcChar8*>
227  (name == "*" ? "sans" : name.c_str ())));
228 
229  FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
230  FcPatternAddInteger (pat, FC_SLANT, fc_angle);
231  FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
232 
233  if (FcConfigSubstitute (0, pat, FcMatchPattern))
234  {
235  FcResult res;
236  FcPattern *match;
237 
238  FcDefaultSubstitute (pat);
239  match = FcFontMatch (0, pat, &res);
240 
241  // FIXME: originally, this test also required that
242  // res != FcResultNoMatch. Is that really needed?
243  if (match)
244  {
245  unsigned char *tmp;
246 
247  FcPatternGetString (match, FC_FILE, 0, &tmp);
248  file = reinterpret_cast<char*> (tmp);
249  }
250  else
251  ::warning ("could not match any font: %s-%s-%s-%g",
252  name.c_str (), weight.c_str (), angle.c_str (),
253  size);
254 
255  if (match)
256  FcPatternDestroy (match);
257  }
258 
259  FcPatternDestroy (pat);
260  }
261 #endif
262 
263  if (file.empty ())
264  {
265 #if defined (OCTAVE_USE_WINDOWS_API)
266  file = "C:/WINDOWS/Fonts/verdana.ttf";
267 #else
268  // FIXME: find a "standard" font for UNIX platforms
269 #endif
270  }
271 
272  if (! file.empty ())
273  {
274  if (FT_New_Face (library, file.c_str (), 0, &retval))
275  ::warning ("ft_manager: unable to load font: %s", file.c_str ());
276 #if defined (HAVE_FT_REFERENCE_FACE)
277  else
278  {
279  // Install a finalizer to notify ft_manager that the font is
280  // being destroyed. The class ft_manager only keeps weak
281  // references to font objects.
282 
283  retval->generic.data = new ft_key (key);
284  retval->generic.finalizer = ft_face_destroyed;
285 
286  // Insert loaded font into the cache.
287 
288  cache[key] = retval;
289  }
290 #endif
291  }
292 
293  return retval;
294  }
295 
296  void do_font_destroyed (FT_Face face)
297  {
298  if (face->generic.data)
299  {
300  ft_key *pkey = reinterpret_cast<ft_key*> (face->generic.data);
301 
302  cache.erase (*pkey);
303  delete pkey;
304  face->generic.data = 0;
305  }
306  }
307 
308  private:
309  FT_Library library;
312  };
313 
315 
316 }
317 
318 static void
319 ft_face_destroyed (void *object)
320 {
321  octave::ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object));
322 }
323 
324 namespace octave
325 {
326  class
329  {
330  public:
331 
332  enum
333  {
334  MODE_BBOX = 0,
335  MODE_RENDER = 1
336  };
337 
338  enum
339  {
340  ROTATION_0 = 0,
341  ROTATION_90 = 1,
342  ROTATION_180 = 2,
343  ROTATION_270 = 3
344  };
345 
346  public:
347 
349  : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0),
350  xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX),
351  color (dim_vector (1, 3), 0)
352  { }
353 
354  ~ft_text_renderer (void) { }
355 
356  void visit (text_element_string& e);
357 
358  void visit (text_element_list& e);
359 
360  void visit (text_element_subscript& e);
361 
362  void visit (text_element_superscript& e);
363 
364  void visit (text_element_color& e);
365 
366  void visit (text_element_fontsize& e);
367 
368  void visit (text_element_fontname& e);
369 
370  void visit (text_element_fontstyle& e);
371 
372  void visit (text_element_symbol& e);
373 
374  void visit (text_element_combined& e);
375 
376  void reset (void);
377 
378  uint8NDArray get_pixels (void) const { return pixels; }
379 
380  Matrix get_boundingbox (void) const { return bbox; }
381 
382  uint8NDArray render (text_element *elt, Matrix& box,
383  int rotation = ROTATION_0);
384 
385  Matrix get_extent (text_element *elt, double rotation = 0.0);
386  Matrix get_extent (const std::string& txt, double rotation,
387  const caseless_str& interpreter);
388 
389  void set_font (const std::string& name, const std::string& weight,
390  const std::string& angle, double size);
391 
392  void set_color (const Matrix& c);
393 
394  void set_mode (int m);
395 
396  void text_to_pixels (const std::string& txt,
397  uint8NDArray& pxls, Matrix& bbox,
398  int halign, int valign, double rotation,
399  const caseless_str& interpreter,
400  bool handle_rotation);
401 
402  private:
403 
404  int rotation_to_mode (double rotation) const;
405 
406  // No copying!
407 
409 
410  ft_text_renderer& operator = (const ft_text_renderer&);
411 
412  // Class to hold information about fonts and a strong
413  // reference to the font objects loaded by FreeType.
414 
416  {
417  public:
418 
419  ft_font (void)
420  : text_renderer::font (), face (0) { }
421 
422  ft_font (const std::string& nm, const std::string& wt,
423  const std::string& ang, double sz, FT_Face f = 0)
424  : text_renderer::font (nm, wt, ang, sz), face (f)
425  { }
426 
427  ft_font (const ft_font& ft);
428 
429  ~ft_font (void)
430  {
431  if (face)
432  FT_Done_Face (face);
433  }
434 
435  ft_font& operator = (const ft_font& ft);
436 
437  bool is_valid (void) const { return get_face (); }
438 
439  FT_Face get_face (void) const;
440 
441  private:
442 
443  mutable FT_Face face;
444  };
445 
446  void push_new_line (void);
447 
448  void update_line_bbox (void);
449 
450  void compute_bbox (void);
451 
452  int compute_line_xoffset (const Matrix& lb) const;
453 
454  FT_UInt process_character (FT_ULong code, FT_UInt previous = 0);
455 
456  public:
457 
458  void text_to_strlist (const std::string& txt,
459  std::list<text_renderer::string>& lst, Matrix& bbox,
460  int halign, int valign, double rotation,
461  const caseless_str& interp);
462 
463  private:
464 
465  // The current font used by the renderer.
467 
468  // Used to stored the bounding box corresponding to the rendered text.
469  // The bounding box has the form [x, y, w, h] where x and y represent the
470  // coordinates of the bottom left corner relative to the anchor point of
471  // the text (== start of text on the baseline). Due to font descent or
472  // multiple lines, the value y is usually negative.
474 
475  // Used to stored the rendered text. It's a 3D matrix with size MxNx4
476  // where M and N are the width and height of the bounding box.
478 
479  // Used to store the bounding box of each line. This is used to layout
480  // multiline text properly.
481  std::list<Matrix> line_bbox;
482 
483  // The current horizontal alignment. This is used to align multi-line text.
484  int halign;
485 
486  // The X offset for the next glyph.
487  int xoffset;
488 
489  // The Y offset of the baseline for the current line.
491 
492  // The Y offset of the baseline for the next glyph. The offset is relative
493  // to line_yoffset. The total Y offset is computed with:
494  // line_yoffset + yoffset.
495  int yoffset;
496 
497  // The current mode of the rendering process (box computing or rendering).
498  int mode;
499 
500  // The base color of the rendered text.
502 
503  // A list of parsed strings to be used for printing.
504  std::list<text_renderer::string> strlist;
505 
506  // The X offset of the baseline for the current line.
508 
509  };
510 
511  void
513  const std::string& angle, double size)
514  {
515  // FIXME: take "fontunits" into account
516 
517  font = ft_font (name, weight, angle, size, 0);
518  }
519 
520  void
522  {
523  switch (mode)
524  {
525  case MODE_BBOX:
526  {
527  // Create a new bbox entry based on the current font.
528 
529  FT_Face face = font.get_face ();
530 
531  if (face)
532  {
533  int asc = face->size->metrics.ascender >> 6;
534  int desc = face->size->metrics.descender >> 6;
535  int h = face->size->metrics.height >> 6;
536 
537  Matrix bb (1, 5, 0.0);
538 
539  bb(1) = desc;
540  bb(3) = asc - desc;
541  bb(4) = h;
542 
543  line_bbox.push_back (bb);
544 
545  xoffset = yoffset = 0;
546  }
547  }
548  break;
549 
550  case MODE_RENDER:
551  {
552  // Move to the next line bbox, adjust xoffset based on alignment
553  // and yoffset based on the old and new line bbox.
554 
555  Matrix old_bbox = line_bbox.front ();
556  line_bbox.pop_front ();
557  Matrix new_bbox = line_bbox.front ();
558 
560  line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3)));
561  yoffset = 0;
562  }
563  break;
564  }
565  }
566 
567  int
569  {
570  if (! bbox.is_empty ())
571  {
572  switch (halign)
573  {
574  case 0:
575  return 0;
576  case 1:
577  return (bbox(2) - lb(2)) / 2;
578  case 2:
579  return (bbox(2) - lb(2));
580  }
581  }
582 
583  return 0;
584  }
585 
586  void
588  {
589  // Stack the various line bbox together and compute the final
590  // bounding box for the entire text string.
591 
592  bbox = Matrix ();
593 
594  switch (line_bbox.size ())
595  {
596  case 0:
597  break;
598 
599  case 1:
600  bbox = line_bbox.front ().extract (0, 0, 0, 3);
601  break;
602 
603  default:
604  for (std::list<Matrix>::const_iterator it = line_bbox.begin ();
605  it != line_bbox.end (); ++it)
606  {
607  if (bbox.is_empty ())
608  bbox = it->extract (0, 0, 0, 3);
609  else
610  {
611  bbox(1) -= (*it)(3);
612  bbox(3) += (*it)(3);
613  bbox(2) = octave::math::max (bbox(2), (*it)(2));
614  }
615  }
616  break;
617  }
618  }
619 
620  void
622  {
623  // Called after a font change, when in MODE_BBOX mode, to update the
624  // current line bbox with the new font metrics. This also includes the
625  // current yoffset, that is the offset of the current glyph's baseline
626  // the line's baseline.
627 
628  if (mode == MODE_BBOX)
629  {
630  int asc = font.get_face ()->size->metrics.ascender >> 6;
631  int desc = font.get_face ()->size->metrics.descender >> 6;
632 
633  Matrix& bb = line_bbox.back ();
634 
635  if ((yoffset + desc) < bb(1))
636  {
637  // The new font goes below the bottom of the current bbox.
638 
639  int delta = bb(1) - (yoffset + desc);
640 
641  bb(1) -= delta;
642  bb(3) += delta;
643  }
644 
645  if ((yoffset + asc) > (bb(1) + bb(3)))
646  {
647  // The new font goes above the top of the current bbox.
648 
649  int delta = (yoffset + asc) - (bb(1) + bb(3));
650 
651  bb(3) += delta;
652  }
653  }
654  }
655 
656  void
658  {
659  mode = m;
660 
661  switch (mode)
662  {
663  case MODE_BBOX:
664  xoffset = line_yoffset = yoffset = 0;
665  bbox = Matrix (1, 4, 0.0);
666  line_bbox.clear ();
667  push_new_line ();
668  break;
669 
670  case MODE_RENDER:
671  if (bbox.numel () != 4)
672  {
673  ::error ("ft_text_renderer: invalid bounding box, cannot render");
674 
675  xoffset = line_yoffset = yoffset = 0;
676  pixels = uint8NDArray ();
677  }
678  else
679  {
680  dim_vector d (4, octave_idx_type (bbox(2)),
681  octave_idx_type (bbox(3)));
682  pixels = uint8NDArray (d, static_cast<uint8_t> (0));
684  line_yoffset = -bbox(1)-1;
685  yoffset = 0;
686  }
687  break;
688 
689  default:
690  error ("ft_text_renderer: invalid mode '%d'", mode);
691  break;
692  }
693  }
694 
695  FT_UInt
696  ft_text_renderer::process_character (FT_ULong code, FT_UInt previous)
697  {
698  FT_Face face = font.get_face ();
699  FT_UInt glyph_index = 0;
700 
701  if (face)
702  {
703  glyph_index = FT_Get_Char_Index (face, code);
704 
705  if (code != '\n'
706  && (! glyph_index
707  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
708  {
709  glyph_index = 0;
710  warn_missing_glyph (code);
711  }
712  else
713  {
714  switch (mode)
715  {
716  case MODE_RENDER:
717  if (code == '\n')
718  {
719  glyph_index = FT_Get_Char_Index (face, ' ');
720  if (! glyph_index
721  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
722  {
723  glyph_index = 0;
724  warn_missing_glyph (' ');
725  }
726  else
727  push_new_line ();
728  }
729  else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
730  {
731  glyph_index = 0;
732  warn_glyph_render (code);
733  }
734  else
735  {
736  FT_Bitmap& bitmap = face->glyph->bitmap;
737  int x0, y0;
738 
739  if (previous)
740  {
741  FT_Vector delta;
742 
743  FT_Get_Kerning (face, previous, glyph_index,
744  FT_KERNING_DEFAULT, &delta);
745  xoffset += (delta.x >> 6);
746  }
747 
748  x0 = xoffset + face->glyph->bitmap_left;
749  y0 = line_yoffset + yoffset + face->glyph->bitmap_top;
750 
751  // 'w' seems to have a negative -1
752  // face->glyph->bitmap_left, this is so we don't
753  // index out of bound, and assumes we've allocated
754  // the right amount of horizontal space in the bbox.
755  if (x0 < 0)
756  x0 = 0;
757 
758  for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++)
759  for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++)
760  {
761  unsigned char pix = bitmap.buffer[r*bitmap.width+c];
762  if (x0+c < 0 || x0+c >= pixels.dim2 ()
763  || y0-r < 0 || y0-r >= pixels.dim3 ())
764  {
765  //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)",
766  // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ());
767  }
768  else if (pixels(3, x0+c, y0-r).value () == 0)
769  {
770  pixels(0, x0+c, y0-r) = color(0);
771  pixels(1, x0+c, y0-r) = color(1);
772  pixels(2, x0+c, y0-r) = color(2);
773  pixels(3, x0+c, y0-r) = pix;
774  }
775  }
776 
777  xoffset += (face->glyph->advance.x >> 6);
778  }
779  break;
780 
781  case MODE_BBOX:
782  if (code == '\n')
783  {
784  glyph_index = FT_Get_Char_Index (face, ' ');
785  if (! glyph_index
786  || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
787  {
788  glyph_index = 0;
789  warn_missing_glyph (' ');
790  }
791  else
792  push_new_line ();
793  }
794  else
795  {
796  Matrix& bb = line_bbox.back ();
797 
798  // If we have a previous glyph, use kerning information.
799  // This usually means moving a bit backward before adding
800  // the next glyph. That is, "delta.x" is usually < 0.
801  if (previous)
802  {
803  FT_Vector delta;
804 
805  FT_Get_Kerning (face, previous, glyph_index,
806  FT_KERNING_DEFAULT, &delta);
807 
808  xoffset += (delta.x >> 6);
809  }
810 
811  // Extend current X offset box by the width of the current
812  // glyph. Then extend the line bounding box if necessary.
813 
814  xoffset += (face->glyph->advance.x >> 6);
815  bb(2) = octave::math::max (bb(2), xoffset);
816  }
817  break;
818  }
819  }
820  }
821 
822  return glyph_index;
823  }
824 
825  void
827  std::list<text_renderer::string>& lst,
828  Matrix& box,
829  int ha, int va, double rot,
830  const caseless_str& interp)
831  {
832  uint8NDArray pxls;
833 
834  // First run text_to_pixels which will also build the string list
835 
836  text_to_pixels (txt, pxls, box, ha, va, rot, interp, false);
837 
838  lst = strlist;
839  }
840 
841  void
843  {
844  if (font.is_valid ())
845  {
846  FT_UInt glyph_index, previous = 0;
847 
849  size_t n = str.length ();
850  size_t curr = 0;
851  size_t idx = 0;
852  mbstate_t ps;
853  memset (&ps, 0, sizeof (ps)); // Initialize state to 0.
854  wchar_t wc;
855 
857 
858  while (n > 0)
859  {
860  size_t r = std::mbrtowc (&wc, str.data () + curr, n, &ps);
861 
862  if (r > 0
863  && r != static_cast<size_t> (-1)
864  && r != static_cast<size_t> (-2))
865  {
866  n -= r;
867  curr += r;
868 
869  if (wc == L'\n')
870  {
871  // Finish previous string in srtlist before processing
872  // the newline character
873  fs.set_y (line_yoffset + yoffset);
874  fs.set_color (color);
875  std::string s = str.substr (idx, curr - idx - 1);
876  if (! s.empty ())
877  {
878  fs.set_string (s);
879  strlist.push_back (fs);
880  }
881  }
882 
883  glyph_index = process_character (wc, previous);
884 
885  if (wc == L'\n')
886  {
887  previous = 0;
888  // Start a new string in strlist
889  idx = curr;
890  fs = text_renderer::string (str.substr (idx), font,
892 
893  }
894  else
895  previous = glyph_index;
896  }
897  else
898  {
899  if (r != 0)
900  ::warning ("ft_text_renderer: failed to decode string `%s' with "
901  "locale `%s'", str.c_str (),
902  std::setlocale (LC_CTYPE, 0));
903  break;
904  }
905  }
906 
907  if (! fs.get_string ().empty ())
908  {
909  fs.set_y (line_yoffset + yoffset);
910  fs.set_color (color);
911  strlist.push_back (fs);
912  }
913  }
914  }
915 
916  void
918  {
919  // Save and restore (after processing the list) the current font and color.
920 
921  ft_font saved_font (font);
922  uint8NDArray saved_color (color);
923 
925 
926  font = saved_font;
927  color = saved_color;
928  }
929 
930  void
932  {
933  ft_font saved_font (font);
934  int saved_line_yoffset = line_yoffset;
935  int saved_yoffset = yoffset;
936 
938  font.get_size () - 2);
939 
940  if (font.is_valid ())
941  {
942  int h = font.get_face ()->size->metrics.height >> 6;
943 
944  // Shifting the baseline by 2/3 the font height seems to produce
945  // decent result.
946  yoffset -= (h * 2) / 3;
947 
948  if (mode == MODE_BBOX)
949  update_line_bbox ();
950  }
951 
953 
954  font = saved_font;
955  // If line_yoffset changed, this means we moved to a new line; hence yoffset
956  // cannot be restored, because the saved value is not relevant anymore.
957  if (line_yoffset == saved_line_yoffset)
958  yoffset = saved_yoffset;
959  }
960 
961  void
963  {
964  ft_font saved_font (font);
965  int saved_line_yoffset = line_yoffset;
966  int saved_yoffset = yoffset;
967 
969  font.get_size () - 2);
970 
971  if (saved_font.is_valid ())
972  {
973  int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6;
974 
975  // Shifting the baseline by 2/3 base font ascender seems to produce
976  // decent result.
977  yoffset += (s_asc * 2) / 3;
978 
979  if (mode == MODE_BBOX)
980  update_line_bbox ();
981  }
982 
984 
985  font = saved_font;
986  // If line_yoffset changed, this means we moved to a new line; hence yoffset
987  // cannot be restored, because the saved value is not relevant anymore.
988  if (line_yoffset == saved_line_yoffset)
989  yoffset = saved_yoffset;
990  }
991 
992  void
994  {
995  if (mode == MODE_RENDER)
996  set_color (e.get_color ());
997  }
998 
999  void
1001  {
1002  double sz = e.get_fontsize ();
1003 
1004  // FIXME: Matlab documentation says that the font size is expressed
1005  // in the text object FontUnit.
1006 
1008 
1009  if (mode == MODE_BBOX)
1010  update_line_bbox ();
1011  }
1012 
1013  void
1015  {
1017  font.get_size ());
1018 
1019  if (mode == MODE_BBOX)
1020  update_line_bbox ();
1021  }
1022 
1023  void
1025  {
1026  switch (e.get_fontstyle ())
1027  {
1029  set_font (font.get_name (), "normal", "normal", font.get_size ());
1030  break;
1031 
1033  set_font (font.get_name (), "bold", "normal", font.get_size ());
1034  break;
1035 
1037  set_font (font.get_name (), "normal", "italic", font.get_size ());
1038  break;
1039 
1041  set_font (font.get_name (), "normal", "oblique", font.get_size ());
1042  break;
1043  }
1044 
1045  if (mode == MODE_BBOX)
1046  update_line_bbox ();
1047  }
1048 
1049  void
1051  {
1052  uint32_t code = e.get_symbol_code ();
1053 
1055 
1057  {
1058  process_character (code);
1059  fs.set_code (code);
1060  }
1061  else if (font.is_valid ())
1062  ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
1063 
1064  if (fs.get_code ())
1065  {
1066  fs.set_y (line_yoffset + yoffset);
1067  fs.set_color (color);
1068  strlist.push_back (fs);
1069  }
1070  }
1071 
1072  void
1074  {
1075  int saved_xoffset = xoffset;
1076  int max_xoffset = xoffset;
1077 
1078  for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it)
1079  {
1080  xoffset = saved_xoffset;
1081  (*it)->accept (*this);
1082  max_xoffset = octave::math::max (xoffset, max_xoffset);
1083  }
1084 
1085  xoffset = max_xoffset;
1086  }
1087 
1088  void
1090  {
1091  set_mode (MODE_BBOX);
1092  set_color (Matrix (1, 3, 0.0));
1093  }
1094 
1095  void
1097  {
1098  if (c.numel () == 3)
1099  {
1100  color(0) = static_cast<uint8_t> (c(0)*255);
1101  color(1) = static_cast<uint8_t> (c(1)*255);
1102  color(2) = static_cast<uint8_t> (c(2)*255);
1103  }
1104  else
1105  ::warning ("ft_text_renderer::set_color: invalid color");
1106  }
1107 
1108  uint8NDArray
1110  {
1111  set_mode (MODE_BBOX);
1112  elt->accept (*this);
1113  compute_bbox ();
1114  box = bbox;
1115 
1117  // Clear the list of parsed strings
1118  strlist.clear ();
1119 
1120  if (pixels.numel () > 0)
1121  {
1122  elt->accept (*this);
1123 
1124  switch (rotation)
1125  {
1126  case ROTATION_0:
1127  break;
1128 
1129  case ROTATION_90:
1130  {
1131  Array<octave_idx_type> perm (dim_vector (3, 1));
1132  perm(0) = 0;
1133  perm(1) = 2;
1134  perm(2) = 1;
1135  pixels = pixels.permute (perm);
1136 
1137  Array<idx_vector> idx (dim_vector (3, 1));
1138  idx(0) = idx_vector (':');
1139  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
1140  idx(2) = idx_vector (':');
1141  pixels = uint8NDArray (pixels.index (idx));
1142  }
1143  break;
1144 
1145  case ROTATION_180:
1146  {
1147  Array<idx_vector> idx (dim_vector (3, 1));
1148  idx(0) = idx_vector (':');
1149  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
1150  idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
1151  pixels = uint8NDArray (pixels.index (idx));
1152  }
1153  break;
1154 
1155  case ROTATION_270:
1156  {
1157  Array<octave_idx_type> perm (dim_vector (3, 1));
1158  perm(0) = 0;
1159  perm(1) = 2;
1160  perm(2) = 1;
1161  pixels = pixels.permute (perm);
1162 
1163  Array<idx_vector> idx (dim_vector (3, 1));
1164  idx(0) = idx_vector (':');
1165  idx(1) = idx_vector (':');
1166  idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
1167  pixels = uint8NDArray (pixels.index (idx));
1168  }
1169  break;
1170  }
1171  }
1172 
1173  return pixels;
1174  }
1175 
1176  // Note:
1177  // x-extent accurately measures width of glyphs.
1178  // y-extent is overly large because it is measured from baseline-to-baseline.
1179  // Calling routines, such as ylabel, may need to account for this mismatch.
1180 
1181  Matrix
1183  {
1184  set_mode (MODE_BBOX);
1185  elt->accept (*this);
1186  compute_bbox ();
1187 
1188  Matrix extent (1, 2, 0.0);
1189 
1190  switch (rotation_to_mode (rotation))
1191  {
1192  case ROTATION_0:
1193  case ROTATION_180:
1194  extent(0) = bbox(2);
1195  extent(1) = bbox(3);
1196  break;
1197 
1198  case ROTATION_90:
1199  case ROTATION_270:
1200  extent(0) = bbox(3);
1201  extent(1) = bbox(2);
1202  }
1203 
1204  return extent;
1205  }
1206 
1207  Matrix
1208  ft_text_renderer::get_extent (const std::string& txt, double rotation,
1209  const caseless_str& interpreter)
1210  {
1211  text_element *elt = text_parser::parse (txt, interpreter);
1212  Matrix extent = get_extent (elt, rotation);
1213  delete elt;
1214 
1215  return extent;
1216  }
1217 
1218  int
1219  ft_text_renderer::rotation_to_mode (double rotation) const
1220  {
1221  // Clip rotation to range [0, 360]
1222  while (rotation < 0)
1223  rotation += 360.0;
1224  while (rotation > 360.0)
1225  rotation -= 360.0;
1226 
1227  if (rotation == 0.0)
1228  return ROTATION_0;
1229  else if (rotation == 90.0)
1230  return ROTATION_90;
1231  else if (rotation == 180.0)
1232  return ROTATION_180;
1233  else if (rotation == 270.0)
1234  return ROTATION_270;
1235  else
1236  return ROTATION_0;
1237  }
1238 
1239  void
1241  uint8NDArray& pxls, Matrix& box,
1242  int _halign, int valign, double rotation,
1243  const caseless_str& interpreter,
1244  bool handle_rotation)
1245  {
1246  int rot_mode = rotation_to_mode (rotation);
1247 
1248  halign = _halign;
1249 
1250  text_element *elt = text_parser::parse (txt, interpreter);
1251  pxls = render (elt, box, rot_mode);
1252  delete elt;
1253 
1254  if (pxls.is_empty ())
1255  return; // nothing to render
1256 
1257  switch (halign)
1258  {
1259  case 1:
1260  box(0) = -box(2)/2;
1261  break;
1262 
1263  case 2:
1264  box(0) = -box(2);
1265  break;
1266 
1267  default:
1268  box(0) = 0;
1269  break;
1270  }
1271 
1272  switch (valign)
1273  {
1274  case 1:
1275  box(1) = -box(3)/2;
1276  break;
1277 
1278  case 2:
1279  box(1) = -box(3);
1280  break;
1281 
1282  case 3:
1283  break;
1284 
1285  case 4:
1286  box(1) = -box(3)-box(1);
1287  break;
1288 
1289  default:
1290  box(1) = 0;
1291  break;
1292  }
1293 
1294  if (handle_rotation)
1295  {
1296  switch (rot_mode)
1297  {
1298  case ROTATION_90:
1299  std::swap (box(0), box(1));
1300  std::swap (box(2), box(3));
1301  box(0) = -box(0)-box(2);
1302  break;
1303 
1304  case ROTATION_180:
1305  box(0) = -box(0)-box(2);
1306  box(1) = -box(1)-box(3);
1307  break;
1308 
1309  case ROTATION_270:
1310  std::swap (box(0), box(1));
1311  std::swap (box(2), box(3));
1312  box(1) = -box(1)-box(3);
1313  break;
1314  }
1315  }
1316  }
1317 
1319  : text_renderer::font (ft), face (0)
1320  {
1321 #if defined (HAVE_FT_REFERENCE_FACE)
1322  FT_Face ft_face = ft.get_face ();
1323 
1324  if (ft_face && FT_Reference_Face (ft_face) == 0)
1325  face = ft_face;
1326 #endif
1327  }
1328 
1331  {
1332  if (&ft != this)
1333  {
1335 
1336  if (face)
1337  {
1338  FT_Done_Face (face);
1339  face = 0;
1340  }
1341 
1342 #if defined (HAVE_FT_REFERENCE_FACE)
1343  FT_Face ft_face = ft.get_face ();
1344 
1345  if (ft_face && FT_Reference_Face (ft_face) == 0)
1346  face = ft_face;
1347 #endif
1348  }
1349 
1350  return *this;
1351  }
1352 
1353  FT_Face
1355  {
1356  if (! face && ! name.empty ())
1357  {
1358  face = ft_manager::get_font (name, weight, angle, size);
1359 
1360  if (face)
1361  {
1362  if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
1363  ::warning ("ft_text_renderer: unable to set font size to %g", size);
1364  }
1365  else
1366  ::warning ("ft_text_renderer: unable to load appropriate font");
1367  }
1368 
1369  return face;
1370  }
1371 }
1372 
1373 #endif
1374 
1375 namespace octave
1376 {
1379  {
1380 #if defined (HAVE_FREETYPE)
1381  return new ft_text_renderer ();
1382 #else
1383  return 0;
1384 #endif
1385  }
1386 }
ft_font & operator=(const ft_font &ft)
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:803
bool is_empty(void) const
Definition: Array.h:575
static FT_Face get_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
Octave interface to the compression and uncompression libraries.
Definition: aepbalance.cc:47
For example cd octave end example noindent changes the current working directory to file
Definition: dirfns.cc:120
MArray< T > permute(const Array< octave_idx_type > &vec, bool inv=false) const
Definition: MArray.h:94
Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition: dMatrix.cc:398
static void ft_face_destroyed(void *object)
uint8NDArray render(text_element *elt, Matrix &box, int rotation=ROTATION_0)
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:363
F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T F77_REAL F77_REAL &F77_RET_T F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE const F77_DBLE * f
std::pair< std::string, double > ft_key
Return the CPU time used by your Octave session The first output is the total time spent executing your process and is equal to the sum of second and third which are the number of CPU seconds spent executing in user mode and the number of CPU seconds spent executing in system mode
Definition: data.cc:6386
std::map< ft_key, FT_Face > ft_cache
intNDArray< octave_uint8 > uint8NDArray
Definition: uint8NDArray.h:33
octave_idx_type dim2(void) const
Definition: Array.h:408
std::list< Matrix > line_bbox
fontstyle get_fontstyle(void) const
Definition: txt-eng.h:219
T max(T x, T y)
Definition: lo-mappers.h:391
static ft_manager * instance
std::string get_name(void) const
uint32_t get_code(void) const
void text_to_strlist(const std::string &txt, std::list< text_renderer::string > &lst, Matrix &bbox, int halign, int valign, double rotation, const caseless_str &interp)
void set_color(const Matrix &c)
void error(const char *fmt,...)
Definition: error.cc:570
std::list< text_element * >::iterator iterator
Definition: base-list.h:40
double get_fontsize(void) const
Definition: txt-eng.h:261
uint8NDArray get_pixels(void) const
std::string get_string(void) const
s
Definition: file-io.cc:2682
void set_string(const std::string &s)
Matrix get_color(void)
Definition: txt-eng.h:305
i e
Definition: data.cc:2724
FT_Face do_get_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T F77_REAL F77_REAL &F77_RET_T F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
static void warn_glyph_render(FT_ULong c)
int get_symbol(void) const
Definition: txt-eng.h:98
std::list< text_renderer::string > strlist
bool swap
Definition: load-save.cc:725
void set_color(const uint8NDArray &c)
void set_code(const uint32_t c)
octave_idx_type dim3(void) const
Definition: Array.h:417
base_text_renderer * make_ft_text_renderer(void)
int rotation_to_mode(double rotation) const
double h
Definition: graphics.cc:11205
OCTAVE_EXPORT octave_value_list any number nd example oindent prints the prompt xample Pick a any number!nd example oindent and waits for the user to enter a value The string entered by the user is evaluated as an so it may be a literal a variable name
Definition: input.cc:871
iterator end(void)
Definition: base-list.h:86
OCTAVE_EXPORT octave_value_list isdir nd deftypefn *std::string nm
Definition: utils.cc:941
#define OCTINTERP_API
Definition: mexproto.h:69
octave_idx_type index(const T *src, octave_idx_type n, T *dest) const
Definition: idx-vector.h:611
std::string get_weight(void) const
ft_font(const std::string &nm, const std::string &wt, const std::string &ang, double sz, FT_Face f=0)
nd deftypefn *octave_map m
Definition: ov-struct.cc:2058
static void add(fptr f)
uint32_t get_symbol_code(void) const
Definition: txt-eng.cc:31
double get_size(void) const
std::string str
Definition: hash.cc:118
virtual void accept(text_processor &p)=0
void visit(text_element_string &e)
Matrix get_extent(text_element *elt, double rotation=0.0)
static void warn_missing_glyph(FT_ULong c)
std::string string_value(void) const
Definition: txt-eng.h:74
double tmp
Definition: data.cc:6300
is false
Definition: cellfun.cc:398
octave_value retval
Definition: data.cc:6294
do not permute tem code
Definition: balance.cc:90
virtual text_element * parse(const std::string &s)=0
void text_to_pixels(const std::string &txt, uint8NDArray &pxls, Matrix &bbox, int halign, int valign, double rotation, const caseless_str &interpreter, bool handle_rotation)
Matrix get_boundingbox(void) const
Definition: dMatrix.h:37
sz
Definition: data.cc:5342
the sparsity preserving column transformation such that that defines the pivoting threshold can be given in which case it defines the c
Definition: lu.cc:138
int compute_line_xoffset(const Matrix &lb) const
void warning(const char *fmt,...)
Definition: error.cc:788
std::string get_angle(void) const
bool empty(void) const
Definition: ovl.h:98
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=0)
Convert the Java object pointed to by jobj_arg with class jcls_arg to an Octave value.
Definition: ov-java.cc:1192
void do_font_destroyed(FT_Face face)
font & operator=(const font &ft)
Definition: text-renderer.h:90
FT_UInt process_character(FT_ULong code, FT_UInt previous=0)
static bool instance_ok(void)
octave::sys::file_stat fs(filename)
void set_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
OCTAVE_EXPORT octave_value_list any number nd example oindent prints the prompt xample Pick a any number!nd example oindent and waits for the user to enter a value The string entered by the user is evaluated as an so it may be a literal a variable or any other valid Octave code The number of return their size
Definition: input.cc:871
void set_y(const double y0)
static void cleanup_instance(void)
virtual void visit(text_element_string &e)=0
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
static void font_destroyed(FT_Face face)
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:854
const std::string & get_fontname(void) const
Definition: txt-eng.h:240
Array< T > index(const idx_vector &i) const
Indexing without resizing.
Definition: Array.cc:718
iterator begin(void)
Definition: base-list.h:83