GNU Octave  4.0.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-2015 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)
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 ();
639  size_t curr = 0;
640  mbstate_t ps;
641  memset (&ps, 0, sizeof (ps)); // Initialize state to 0.
642  wchar_t wc;
643 
644  while (n > 0)
645  {
646  size_t r = gnulib::mbrtowc (&wc, str.data () + curr, n, &ps);
647 
648  if (r > 0
649  && r != static_cast<size_t> (-1)
650  && r != static_cast<size_t> (-2))
651  {
652  n -= r;
653  curr += r;
654 
655  glyph_index = process_character (wc, previous);
656 
657  if (wc == L'\n')
658  previous = 0;
659  else
660  previous = glyph_index;
661  }
662  else
663  {
664  if (r != 0)
665  ::warning ("ft_render: failed to decode string `%s' with "
666  "locale `%s'", str.c_str (),
667  std::setlocale (LC_CTYPE, 0));
668  break;
669  }
670  }
671  }
672 }
673 
674 void
676 {
677  // Save and restore (after processing the list) the current font and color.
678 
679  ft_font saved_font (font);
680  uint8NDArray saved_color (color);
681 
683 
684  font = saved_font;
685  color = saved_color;
686 }
687 
688 void
690 {
691  ft_font saved_font (font);
692  int saved_line_yoffset = line_yoffset;
693  int saved_yoffset = yoffset;
694 
696  font.get_size () - 2);
697 
698  if (font.is_valid ())
699  {
700  int h = font.get_face ()->size->metrics.height >> 6;
701 
702  // Shifting the baseline by 2/3 the font height seems to produce
703  // decent result.
704  yoffset -= (h * 2) / 3;
705 
706  if (mode == MODE_BBOX)
707  update_line_bbox ();
708  }
709 
711 
712  font = saved_font;
713  // If line_yoffset changed, this means we moved to a new line; hence yoffset
714  // cannot be restored, because the saved value is not relevant anymore.
715  if (line_yoffset == saved_line_yoffset)
716  yoffset = saved_yoffset;
717 }
718 
719 void
721 {
722  ft_font saved_font (font);
723  int saved_line_yoffset = line_yoffset;
724  int saved_yoffset = yoffset;
725 
727  font.get_size () - 2);
728 
729  if (saved_font.is_valid ())
730  {
731  int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6;
732 
733  // Shifting the baseline by 2/3 base font ascender seems to produce
734  // decent result.
735  yoffset += (s_asc * 2) / 3;
736 
737  if (mode == MODE_BBOX)
738  update_line_bbox ();
739  }
740 
742 
743  font = saved_font;
744  // If line_yoffset changed, this means we moved to a new line; hence yoffset
745  // cannot be restored, because the saved value is not relevant anymore.
746  if (line_yoffset == saved_line_yoffset)
747  yoffset = saved_yoffset;
748 }
749 
750 void
752 {
753  if (mode == MODE_RENDER)
754  set_color (e.get_color ());
755 }
756 
757 void
759 {
760  double sz = e.get_fontsize ();
761 
762  // FIXME: Matlab documentation says that the font size is expressed
763  // in the text object FontUnit.
764 
766 
767  if (mode == MODE_BBOX)
768  update_line_bbox ();
769 }
770 
771 void
773 {
775  font.get_size ());
776 
777  if (mode == MODE_BBOX)
778  update_line_bbox ();
779 }
780 
781 void
783 {
784  switch (e.get_fontstyle ())
785  {
787  set_font (font.get_name (), "normal", "normal", font.get_size ());
788  break;
790  set_font (font.get_name (), "bold", "normal", font.get_size ());
791  break;
793  set_font (font.get_name (), "normal", "italic", font.get_size ());
794  break;
796  set_font (font.get_name (), "normal", "oblique", font.get_size ());
797  break;
798  }
799 
800  if (mode == MODE_BBOX)
801  update_line_bbox ();
802 }
803 
804 void
806 {
807  uint32_t code = e.get_symbol_code ();
808 
810  process_character (code);
811  else if (font.is_valid ())
812  ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
813 }
814 
815 void
817 {
818  int saved_xoffset = xoffset;
819  int max_xoffset = xoffset;
820 
821  for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it)
822  {
823  xoffset = saved_xoffset;
824  (*it)->accept (*this);
825  max_xoffset = xmax (xoffset, max_xoffset);
826  }
827 
828  xoffset = max_xoffset;
829 }
830 
831 void
833 {
835  set_color (Matrix (1, 3, 0.0));
836 }
837 
838 void
840 {
841  if (c.numel () == 3)
842  {
843  color(0) = static_cast<uint8_t> (c(0)*255);
844  color(1) = static_cast<uint8_t> (c(1)*255);
845  color(2) = static_cast<uint8_t> (c(2)*255);
846  }
847  else
848  ::warning ("ft_render::set_color: invalid color");
849 }
850 
852 ft_render::render (text_element* elt, Matrix& box, int rotation)
853 {
855  elt->accept (*this);
856  compute_bbox ();
857  box = bbox;
858 
860  if (pixels.numel () > 0)
861  {
862  elt->accept (*this);
863 
864  switch (rotation)
865  {
866  case ROTATION_0:
867  break;
868  case ROTATION_90:
869  {
870  Array<octave_idx_type> perm (dim_vector (3, 1));
871  perm(0) = 0;
872  perm(1) = 2;
873  perm(2) = 1;
874  pixels = pixels.permute (perm);
875 
876  Array<idx_vector> idx (dim_vector (3, 1));
877  idx(0) = idx_vector (':');
878  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
879  idx(2) = idx_vector (':');
880  pixels = uint8NDArray (pixels.index (idx));
881  }
882  break;
883  case ROTATION_180:
884  {
885  Array<idx_vector> idx (dim_vector (3, 1));
886  idx(0) = idx_vector (':');
887  idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
888  idx(2)= idx_vector (pixels.dim3 ()-1, -1, -1);
889  pixels = uint8NDArray (pixels.index (idx));
890  }
891  break;
892  case ROTATION_270:
893  {
894  Array<octave_idx_type> perm (dim_vector (3, 1));
895  perm(0) = 0;
896  perm(1) = 2;
897  perm(2) = 1;
898  pixels = pixels.permute (perm);
899 
900  Array<idx_vector> idx (dim_vector (3, 1));
901  idx(0) = idx_vector (':');
902  idx(1) = idx_vector (':');
903  idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
904  pixels = uint8NDArray (pixels.index (idx));
905  }
906  break;
907  }
908  }
909 
910  return pixels;
911 }
912 
913 // Note:
914 // x-extent accurately measures width of glyphs.
915 // y-extent is overly large because it is measured from baseline-to-baseline.
916 // Calling routines, such as ylabel, may need to account for this mismatch.
917 
918 Matrix
919 ft_render::get_extent (text_element *elt, double rotation)
920 {
922  elt->accept (*this);
923  compute_bbox ();
924 
925  Matrix extent (1, 2, 0.0);
926 
927  switch (rotation_to_mode (rotation))
928  {
929  case ROTATION_0:
930  case ROTATION_180:
931  extent(0) = bbox(2);
932  extent(1) = bbox(3);
933  break;
934  case ROTATION_90:
935  case ROTATION_270:
936  extent(0) = bbox(3);
937  extent(1) = bbox(2);
938  }
939 
940  return extent;
941 }
942 
943 Matrix
944 ft_render::get_extent (const std::string& txt, double rotation,
945  const caseless_str& interpreter)
946 {
947  text_element *elt = text_parser::parse (txt, interpreter);
948  Matrix extent = get_extent (elt, rotation);
949  delete elt;
950 
951  return extent;
952 }
953 
954 int
955 ft_render::rotation_to_mode (double rotation) const
956 {
957  // Clip rotation to range [0, 360]
958  while (rotation < 0)
959  rotation += 360.0;
960  while (rotation > 360.0)
961  rotation -= 360.0;
962 
963  if (rotation == 0.0)
964  return ROTATION_0;
965  else if (rotation == 90.0)
966  return ROTATION_90;
967  else if (rotation == 180.0)
968  return ROTATION_180;
969  else if (rotation == 270.0)
970  return ROTATION_270;
971  else
972  return ROTATION_0;
973 }
974 
975 void
976 ft_render::text_to_pixels (const std::string& txt,
977  uint8NDArray& pixels_, Matrix& box,
978  int _halign, int valign, double rotation,
979  const caseless_str& interpreter)
980 {
981  int rot_mode = rotation_to_mode (rotation);
982 
983  halign = _halign;
984 
985  text_element *elt = text_parser::parse (txt, interpreter);
986  pixels_ = render (elt, box, rot_mode);
987  delete elt;
988 
989  if (pixels_.numel () == 0)
990  {
991  // nothing to render
992  return;
993  }
994 
995  switch (halign)
996  {
997  default: box(0) = 0; break;
998  case 1: box(0) = -box(2)/2; break;
999  case 2: box(0) = -box(2); break;
1000  }
1001  switch (valign)
1002  {
1003  default: box(1) = 0; break;
1004  case 1: box(1) = -box(3)/2; break;
1005  case 2: box(1) = -box(3); break;
1006  case 3: break;
1007  case 4: box(1) = -box(3)-box(1); break;
1008  }
1009 
1010  switch (rot_mode)
1011  {
1012  case ROTATION_90:
1013  std::swap (box(0), box(1));
1014  std::swap (box(2), box(3));
1015  box(0) = -box(0)-box(2);
1016  break;
1017  case ROTATION_180:
1018  box(0) = -box(0)-box(2);
1019  box(1) = -box(1)-box(3);
1020  break;
1021  case ROTATION_270:
1022  std::swap (box(0), box(1));
1023  std::swap (box(2), box(3));
1024  box(1) = -box(1)-box(3);
1025  break;
1026  }
1027 }
1028 
1030  : name (ft.name), weight (ft.weight), angle (ft.angle), size (ft.size),
1031  face (0)
1032 {
1033 #if HAVE_FT_REFERENCE_FACE
1034  FT_Face ft_face = ft.get_face ();
1035 
1036  if (ft_face && FT_Reference_Face (ft_face) == 0)
1037  face = ft_face;
1038 #endif
1039 }
1040 
1043 {
1044  if (&ft != this)
1045  {
1046  name = ft.name;
1047  weight = ft.weight;
1048  angle = ft.angle;
1049  size = ft.size;
1050  if (face)
1051  {
1052  FT_Done_Face (face);
1053  face = 0;
1054  }
1055 
1056 #if HAVE_FT_REFERENCE_FACE
1057  FT_Face ft_face = ft.get_face ();
1058 
1059  if (ft_face && FT_Reference_Face (ft_face) == 0)
1060  face = ft_face;
1061 #endif
1062  }
1063 
1064  return *this;
1065 }
1066 
1067 FT_Face
1069 {
1070  if (! face && ! name.empty ())
1071  {
1072  face = ft_manager::get_font (name, weight, angle, size);
1073 
1074  if (face)
1075  {
1076  if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
1077  ::warning ("ft_render: unable to set font size to %g", size);
1078  }
1079  else
1080  ::warning ("ft_render: unable to load appropriate font");
1081  }
1082 
1083  return face;
1084 }
1085 
1086 #endif // HAVE_FREETYPE
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:696
static bool instance_ok(void)
Definition: toplev.h:128
void push_new_line(void)
Definition: txt-eng-ft.cc:331
static void font_destroyed(FT_Face face)
Definition: txt-eng-ft.cc:111
void visit(text_element_string &e)
Definition: txt-eng-ft.cc:631
bool is_empty(void) const
Definition: Array.h:472
ft_manager(void)
Definition: txt-eng-ft.cc:137
int halign
Definition: txt-eng-ft.h:188
static void gripe_glyph_render(FT_ULong c)
Definition: txt-eng-ft.cc:57
int compute_line_xoffset(const Matrix &lb) const
Definition: txt-eng-ft.cc:378
MArray< T > permute(const Array< octave_idx_type > &vec, bool inv=false) const
Definition: MArray.h:74
void update_line_bbox(void)
Definition: txt-eng-ft.cc:429
Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition: dMatrix.cc:620
static void gripe_missing_glyph(FT_ULong c)
Definition: txt-eng-ft.cc:49
void set_mode(int m)
Definition: txt-eng-ft.cc:465
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:275
OCTINTERP_API octave_value box(JNIEnv *jni_env, jobject jobj, jclass jcls=0)
intNDArray< octave_uint8 > uint8NDArray
Definition: uint8NDArray.h:31
octave_idx_type dim2(void) const
Definition: Array.h:320
fontstyle get_fontstyle(void) const
Definition: txt-eng.h:217
Complex xmax(const Complex &x, const Complex &y)
Definition: lo-mappers.cc:269
ft_font & operator=(const ft_font &ft)
Definition: txt-eng-ft.cc:1042
FT_UInt process_character(FT_ULong code, FT_UInt previous=0)
Definition: txt-eng-ft.cc:501
void error(const char *fmt,...)
Definition: error.cc:476
bool is_valid(void) const
Definition: txt-eng-ft.h:138
ft_font font
Definition: txt-eng-ft.h:170
~ft_manager(void)
Definition: txt-eng-ft.cc:153
void reset(void)
Definition: txt-eng-ft.cc:832
double get_fontsize(void) const
Definition: txt-eng.h:259
bool freetype_initialized
Definition: txt-eng-ft.cc:298
void compute_bbox(void)
Definition: txt-eng-ft.cc:397
bool fontconfig_initialized
Definition: txt-eng-ft.cc:299
static void ft_face_destroyed(void *object)
Definition: txt-eng-ft.cc:305
int yoffset
Definition: txt-eng-ft.h:199
std::list< Matrix > line_bbox
Definition: txt-eng-ft.h:185
Matrix get_color(void)
Definition: txt-eng.h:303
int rotation_to_mode(double rotation) const
Definition: txt-eng-ft.cc:955
uint8NDArray pixels
Definition: txt-eng-ft.h:181
std::string angle
Definition: txt-eng-ft.h:153
int get_symbol(void) const
Definition: txt-eng.h:96
std::string name
Definition: txt-eng-ft.h:151
std::string get_name(void) const
Definition: txt-eng-ft.h:140
static FT_Face get_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
Definition: txt-eng-ft.cc:103
std::string get_weight(void) const
Definition: txt-eng-ft.h:142
void do_font_destroyed(FT_Face face)
Definition: txt-eng-ft.cc:284
octave_idx_type dim3(void) const
Definition: Array.h:329
std::list< text_element * >::iterator iterator
Definition: base-list.h:36
std::string get_angle(void) const
Definition: txt-eng-ft.h:144
Matrix bbox
Definition: txt-eng-ft.h:177
iterator end(void)
Definition: base-list.h:81
ft_render(void)
Definition: txt-eng-ft.cc:310
~ft_render(void)
Definition: txt-eng-ft.cc:317
uint8NDArray render(text_element *elt, Matrix &box, int rotation=ROTATION_0)
Definition: txt-eng-ft.cc:852
octave_idx_type index(const T *src, octave_idx_type n, T *dest) const
Definition: idx-vector.h:621
static void add(fptr f)
uint32_t get_symbol_code(void) const
Definition: txt-eng.cc:31
virtual void accept(text_processor &p)=0
uint8NDArray color
Definition: txt-eng-ft.h:205
std::string string_value(void) const
Definition: txt-eng.h:72
void set_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
Definition: txt-eng-ft.cc:322
static void cleanup_instance(void)
Definition: txt-eng-ft.cc:101
virtual text_element * parse(const std::string &s)=0
void set_color(Matrix c)
Definition: txt-eng-ft.cc:839
static bool instance_ok(void)
Definition: txt-eng-ft.cc:79
Definition: dMatrix.h:35
size_t size(T const (&)[z])
Definition: help.cc:103
FT_Library library
Definition: txt-eng-ft.cc:297
std::pair< std::string, double > ft_key
Definition: txt-eng-ft.cc:121
iterator begin(void)
Definition: base-list.h:78
int line_yoffset
Definition: txt-eng-ft.h:194
FT_Face get_face(void) const
Definition: txt-eng-ft.cc:1068
void warning(const char *fmt,...)
Definition: error.cc:681
std::map< ft_key, FT_Face > ft_cache
Definition: txt-eng-ft.cc:122
void text_to_pixels(const std::string &txt, uint8NDArray &pixels_, Matrix &bbox, int halign, int valign, double rotation, const caseless_str &interpreter="tex")
Definition: txt-eng-ft.cc:976
ft_cache cache
Definition: txt-eng-ft.cc:127
int xoffset
Definition: txt-eng-ft.h:191
static ft_manager * instance
Definition: txt-eng-ft.cc:119
Matrix get_extent(text_element *elt, double rotation=0.0)
Definition: txt-eng-ft.cc:919
static bool match(const std::string &filename_arg, const std::string &path_elt_arg)
Definition: kpse.cc:1738
FT_Face do_get_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
Definition: txt-eng-ft.cc:169
static octave_call_stack * instance
Definition: toplev.h:351
double get_size(void) const
Definition: txt-eng-ft.h:146
virtual void visit(text_element_string &e)=0
static void cleanup_instance(void)
Definition: toplev.h:353
const std::string & get_fontname(void) const
Definition: txt-eng.h:238
Array< T > index(const idx_vector &i) const
Indexing without resizing.
Definition: Array.cc:716
std::string weight
Definition: txt-eng-ft.h:152