GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
gl2ps-print.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2009-2018 Shai Ayal
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
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License 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 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 // Both header files are required outside of HAVE_GLP2S_H
28 #include "errwarn.h"
29 #include "gl2ps-print.h"
30 
31 #if defined (HAVE_GL2PS_H) && defined (HAVE_OPENGL)
32 
33 #include <cstdio>
34 
35 #include <limits>
36 
37 #include <gl2ps.h>
38 
39 #include "lo-mappers.h"
40 #include "oct-locbuf.h"
41 #include "tmpfile-wrapper.h"
42 #include "unistd-wrappers.h"
43 #include "unwind-prot.h"
44 
45 #include "gl-render.h"
46 #include "oct-opengl.h"
47 #include "sighandlers.h"
48 #include "sysdep.h"
49 #include "text-renderer.h"
50 
51 namespace octave
52 {
53  static void
54  safe_pclose (FILE *f)
55  {
56  if (f)
57  octave_pclose (f);
58  }
59 
60  static void
61  safe_fclose (FILE *f)
62  {
63  if (f)
64  std::fclose (f);
65  }
66 
67  class
68  OCTINTERP_API
69  gl2ps_renderer : public opengl_renderer
70  {
71  public:
72 
73  gl2ps_renderer (FILE *_fp, const std::string& _term)
74  : opengl_renderer () , fp (_fp), term (_term), fontsize (),
75  fontname (), buffer_overflow (false)
76  { }
77 
78  ~gl2ps_renderer (void) = default;
79 
80  // FIXME: should we import the functions from the base class and
81  // overload them here, or should we use a different name so we don't
82  // have to do this? Without the using declaration or a name change,
83  // the base class functions will be hidden. That may be OK, but it
84  // can also cause some confusion.
86 
87  void draw (const graphics_object& go, const std::string& print_cmd);
88 
89  protected:
90 
91  Matrix render_text (const std::string& txt,
92  double x, double y, double z,
93  int halign, int valign, double rotation = 0.0);
94 
95  void set_font (const base_properties& props);
96 
97  static bool has_alpha (const graphics_handle& h)
98  {
99  bool retval = false;
101 
102  if (! go.valid_object ())
103  return retval;
104 
105  if (go.isa ("axes") || go.isa ("hggroup"))
106  {
107  Matrix children = go.get ("children").matrix_value ();
108  for (octave_idx_type ii = 0; ii < children.numel (); ii++)
109  {
110  retval = has_alpha (graphics_handle (children(ii)));
111  if (retval)
112  break;
113  }
114  }
115  else if (go.isa ("patch") || go.isa ("surface"))
116  {
117  octave_value fa = go.get ("facealpha");
118  if (fa.is_scalar_type () && fa.is_double_type ()
119  && fa.double_value () < 1)
120  retval = true;
121  }
122 
123  return retval;
124  }
125 
126  void draw_axes (const axes::properties& props)
127  {
128  // Initialize a sorting tree (viewport) in gl2ps for each axes
129  GLint vp[4];
130  glGetIntegerv (GL_VIEWPORT, vp);
131  gl2psBeginViewport (vp);
132 
133 
134  // Don't remove hidden primitives when some of them are transparent
135  GLint opts;
136  gl2psGetOptions (&opts);
137  if (has_alpha (props.get___myhandle__ ()))
138  {
139  opts &= ~GL2PS_OCCLUSION_CULL;
140  // FIXME: currently the GL2PS_BLEND (which is more an equivalent of
141  // GL_ALPHA_TEST than GL_BLEND) is not working on a per primitive
142  // basis. We thus set it once per viewport.
143  gl2psEnable (GL2PS_BLEND);
144  }
145  else
146  {
147  opts |= GL2PS_OCCLUSION_CULL;
148  gl2psDisable (GL2PS_BLEND);
149  }
150 
151  gl2psSetOptions (opts);
152 
153  // Draw and finish () or there may be primitives missing in the gl2ps
154  // output.
156  finish ();
157 
158  // Finalize viewport
159  GLint state = gl2psEndViewport ();
160  if (state == GL2PS_NO_FEEDBACK && props.is_visible ())
161  warning ("gl2ps_renderer::draw_axes: empty feedback buffer and/or nothing else to print");
162  else if (state == GL2PS_ERROR)
163  error ("gl2ps_renderer::draw_axes: gl2psEndPage returned GL2PS_ERROR");
164 
165  buffer_overflow |= (state == GL2PS_OVERFLOW);
166 
167  // Don't draw background for subsequent viewports (legends, subplots,
168  // etc.)
169  gl2psGetOptions (&opts);
170  opts &= ~GL2PS_DRAW_BACKGROUND;
171  gl2psSetOptions (opts);
172  }
173 
174  void draw_text (const text::properties& props);
175 
176  void draw_pixels (int w, int h, const float *data);
177  void draw_pixels (int w, int h, const uint8_t *data);
178  void draw_pixels (int w, int h, const uint16_t *data);
179 
180  void init_marker (const std::string& m, double size, float width)
181  {
182  opengl_renderer::init_marker (m, size, width);
183 
184  // FIXME: gl2ps can't handle closed contours and we set linecap/linejoin
185  // round to obtain a better looking result for some markers.
186  if (m == "o" || m == "v" || m == "^" || m == ">" || m == "<" || m == "h"
187  || m == "hexagram" || m == "p" || m == "pentagram")
188  {
189  set_linejoin ("round");
190  set_linecap ("round");
191  }
192  else
193  {
194  set_linejoin ("miter");
195  set_linecap ("square");
196  }
197  }
198 
199  void set_linestyle (const std::string& s, bool use_stipple = false,
200  double linewidth = 0.5)
201  {
202  opengl_renderer::set_linestyle (s, use_stipple, linewidth);
203 
204  if (s == "-" && ! use_stipple)
205  gl2psDisable (GL2PS_LINE_STIPPLE);
206  else
207  gl2psEnable (GL2PS_LINE_STIPPLE);
208  }
209 
210  void set_linecap (const std::string& s)
211  {
213 
214 #if defined (HAVE_GL2PSLINEJOIN)
215  if (s == "butt")
216  gl2psLineCap (GL2PS_LINE_CAP_BUTT);
217  else if (s == "square")
218  gl2psLineCap (GL2PS_LINE_CAP_SQUARE);
219  else if (s == "round")
220  gl2psLineCap (GL2PS_LINE_CAP_ROUND);
221 #endif
222  }
223 
224  void set_linejoin (const std::string& s)
225  {
227 
228 #if defined (HAVE_GL2PSLINEJOIN)
229  if (s == "round")
230  gl2psLineJoin (GL2PS_LINE_JOIN_ROUND);
231  else if (s == "miter")
232  gl2psLineJoin (GL2PS_LINE_JOIN_MITER);
233  else if (s == "chamfer")
234  gl2psLineJoin (GL2PS_LINE_JOIN_BEVEL);
235 #endif
236  }
237 
238  void set_polygon_offset (bool on, float offset = 0.0f)
239  {
240  if (on)
241  {
243  gl2psEnable (GL2PS_POLYGON_OFFSET_FILL);
244  }
245  else
246  {
247  gl2psDisable (GL2PS_POLYGON_OFFSET_FILL);
249  }
250  }
251 
252  void set_linewidth (float w)
253  {
254  gl2psLineWidth (w);
255  }
256 
257  private:
258 
259  // Use xform to compute the coordinates of the string list
260  // that have been parsed by freetype.
261  void fix_strlist_position (double x, double y, double z,
262  Matrix box, double rotation,
263  std::list<text_renderer::string>& lst);
264 
265  // Build an svg text element from a list of parsed strings.
266  std::string strlist_to_svg (double x, double y, double z, Matrix box,
267  double rotation,
268  std::list<octave::text_renderer::string>& lst);
269 
270  // Build a list of postscript commands from a list of parsed strings.
271  std::string strlist_to_ps (double x, double y, double z, Matrix box,
272  double rotation,
273  std::list<octave::text_renderer::string>& lst);
274 
275  int alignment_to_mode (int ha, int va) const;
276  FILE *fp;
277  caseless_str term;
278  double fontsize;
279  std::string fontname;
280  bool buffer_overflow;
281  };
282 
283  static bool
284  has_2D_axes (const graphics_handle& h)
285  {
286  bool retval = true;
288 
289  if (! go.valid_object ())
290  return retval;
291 
292  if (go.isa ("figure") || go.isa ("uipanel"))
293  {
294  Matrix children = go.get ("children").matrix_value ();
295  for (octave_idx_type ii = 0; ii < children.numel (); ii++)
296  {
297  retval = has_2D_axes (graphics_handle (children(ii)));
298  if (! retval)
299  break;
300  }
301  }
302  else if (go.isa ("axes"))
303  {
304  axes::properties& ap
305  = reinterpret_cast<axes::properties&> (go.get_properties ());
306  retval = ap.get_is2D (true);
307  }
308 
309  return retval;
310  }
311 
312  void
313  gl2ps_renderer::draw (const graphics_object& go, const std::string& print_cmd)
314  {
315  static bool in_draw = false;
316  static std::string old_print_cmd;
317  static GLint buffsize;
318 
319  if (! in_draw)
320  {
321  unwind_protect frame;
322 
323  frame.protect_var (in_draw);
324 
325  in_draw = true;
326 
327  GLint gl2ps_term = GL2PS_PS;
328  if (term.find ("eps") != std::string::npos)
329  gl2ps_term = GL2PS_EPS;
330  else if (term.find ("pdf") != std::string::npos)
331  gl2ps_term = GL2PS_PDF;
332  else if (term.find ("ps") != std::string::npos)
333  gl2ps_term = GL2PS_PS;
334  else if (term.find ("svg") != std::string::npos)
335  gl2ps_term = GL2PS_SVG;
336  else if (term.find ("pgf") != std::string::npos)
337  gl2ps_term = GL2PS_PGF;
338  else if (term.find ("tex") != std::string::npos)
339  gl2ps_term = GL2PS_TEX;
340  else
341  warning ("gl2ps_renderer::draw: Unknown terminal %s, using 'ps'",
342  term.c_str ());
343 
344  GLint gl2ps_text = 0;
345  if (term.find ("notxt") != std::string::npos)
346  gl2ps_text = GL2PS_NO_TEXT;
347 
348  // Default sort order optimizes for 3D plots
349  GLint gl2ps_sort = GL2PS_BSP_SORT;
350 
351  // FIXME: gl2ps does not provide a way to change the sorting algorythm
352  // on a viewport basis, we thus disable sorting only if all axes are 2D
353  if (has_2D_axes (go.get ("__myhandle__")))
354  gl2ps_sort = GL2PS_NO_SORT;
355 
356  // Use a temporary file in case an overflow happens
357  FILE *tmpf = octave_tmpfile_wrapper ();
358 
359  if (! tmpf)
360  error ("gl2ps_renderer::draw: couldn't open temporary file for printing");
361 
362  frame.add_fcn (safe_fclose, tmpf);
363 
364  // Reset buffsize, unless this is 2nd pass of a texstandalone print.
365  if (term.find ("tex") == std::string::npos)
366  buffsize = 2*1024*1024;
367  else
368  buffsize /= 2;
369 
370  buffer_overflow = true;
371 
372  while (buffer_overflow)
373  {
374  buffer_overflow = false;
375  buffsize *= 2;
376 
377  std::fseek (tmpf, 0, SEEK_SET);
378  octave_ftruncate_wrapper (fileno (tmpf), 0);
379 
380  // For LaTeX output the print process uses 2 drawnow() commands.
381  // The first one is for the pdf/ps/eps graph to be included. The
382  // print_cmd is saved as old_print_cmd. Then the second drawnow()
383  // outputs the tex-file and the graphic filename to be included is
384  // extracted from old_print_cmd.
385 
386  std::string include_graph;
387 
388  size_t found_redirect = old_print_cmd.find ('>');
389 
390  if (found_redirect != std::string::npos)
391  include_graph = old_print_cmd.substr (found_redirect + 1);
392  else
393  include_graph = old_print_cmd;
394 
395  size_t n_begin = include_graph.find_first_not_of (' ');
396 
397  if (n_begin != std::string::npos)
398  {
399  size_t n_end = include_graph.find_last_not_of (' ');
400  include_graph = include_graph.substr (n_begin,
401  n_end - n_begin + 1);
402  }
403  else
404  include_graph = "foobar-inc";
405 
406  // FIXME: workaround gl2ps drawing 2 background planes, the first
407  // eventually being black and producing visual artifacts
408  const figure::properties& fprop
409  = dynamic_cast<const figure::properties&> (go.get_properties ());
410  Matrix c = fprop.get_color_rgb ();
411  glClearColor (c(0), c(1), c(2), 1);
412 
413  // GL2PS_SILENT was removed to allow gl2ps to print errors on stderr
414  GLint ret = gl2psBeginPage ("gl2ps_renderer figure", "Octave",
415  nullptr, gl2ps_term, gl2ps_sort,
416  (GL2PS_BEST_ROOT
417  | gl2ps_text
418  | GL2PS_DRAW_BACKGROUND
419  | GL2PS_NO_PS3_SHADING
420  | GL2PS_USE_CURRENT_VIEWPORT),
421  GL_RGBA, 0, nullptr, 0, 0, 0,
422  buffsize, tmpf, include_graph.c_str ());
423  if (ret == GL2PS_ERROR)
424  {
425  old_print_cmd.clear ();
426  error ("gl2ps_renderer::draw: gl2psBeginPage returned GL2PS_ERROR");
427  }
428 
430 
431  if (buffer_overflow)
432  warning ("gl2ps_renderer::draw: retrying with buffer size: %.1E B\n", double (2*buffsize));
433 
434  if (! buffer_overflow)
435  old_print_cmd = print_cmd;
436 
437  // Don't check return value of gl2psEndPage, it is not meaningful.
438  // Errors and warnings are checked after gl2psEndViewport in
439  // gl2ps_renderer::draw_axes instead.
440  gl2psEndPage ();
441  }
442 
443  // Copy temporary file to pipe
444  std::fseek (tmpf, 0, SEEK_SET);
445  char str[8192]; // 8 kB is a common kernel buffersize
446  size_t nread, nwrite;
447  nread = 1;
448 
449  // In EPS terminal read the header line by line and insert a
450  // new procedure
451  const char* fcn = "/SRX { gsave FCT moveto rotate xshow grestore } BD\n";
452  bool header_found = ! (term.find ("eps") != std::string::npos
453  || term.find ("svg") != std::string::npos);
454 
455  while (! feof (tmpf) && nread)
456  {
457  if (! header_found && std::fgets (str, 8192, tmpf))
458  nread = strlen (str);
459  else
460  nread = std::fread (str, 1, 8192, tmpf);
461 
462  if (nread)
463  {
464  if (! header_found && std::strncmp (str, "/SBCR", 5) == 0)
465  {
466  header_found = true;
467  nwrite = std::fwrite (fcn, 1, strlen (fcn), fp);
468  if (nwrite != strlen (fcn))
469  {
470  // FIXME: is this the best thing to do here?
472  error ("gl2ps_renderer::draw: internal pipe error");
473  }
474  }
475  else if (! header_found
476  && term.find ("svg") != std::string::npos)
477  {
478  // FIXME: gl2ps uses pixel units for SVG format.
479  // Modify resulting svg to use points instead.
480  // Remove this "else if" block, and
481  // make header_found true for SVG if gl2ps is fixed.
482  std::string srchstr (str);
483  size_t pos = srchstr.find ("px");
484  if (pos != std::string::npos)
485  {
486  header_found = true;
487  srchstr[pos+1] = 't'; // "px" -> "pt"
488  // Assume the second occurrence is at the same line
489  pos = srchstr.find ("px", pos);
490  srchstr[pos+1] = 't'; // "px" -> "pt"
491  std::strcpy (str, srchstr.c_str ());
492  }
493  }
494 
495  nwrite = std::fwrite (str, 1, nread, fp);
496  if (nwrite != nread)
497  {
498  // FIXME: is this the best thing to do here?
499  respond_to_pending_signals (); // Clear SIGPIPE signal
500  error ("gl2ps_renderer::draw: internal pipe error");
501  }
502  }
503  }
504  }
505  else
507  }
508 
509  int
510  gl2ps_renderer::alignment_to_mode (int ha, int va) const
511  {
512  int gl2psa = GL2PS_TEXT_BL;
513 
514  if (ha == 0)
515  {
516  if (va == 0 || va == 3)
517  gl2psa=GL2PS_TEXT_BL;
518  else if (va == 2)
519  gl2psa=GL2PS_TEXT_TL;
520  else if (va == 1)
521  gl2psa=GL2PS_TEXT_CL;
522  }
523  else if (ha == 2)
524  {
525  if (va == 0 || va == 3)
526  gl2psa=GL2PS_TEXT_BR;
527  else if (va == 2)
528  gl2psa=GL2PS_TEXT_TR;
529  else if (va == 1)
530  gl2psa=GL2PS_TEXT_CR;
531  }
532  else if (ha == 1)
533  {
534  if (va == 0 || va == 3)
535  gl2psa=GL2PS_TEXT_B;
536  else if (va == 2)
537  gl2psa=GL2PS_TEXT_T;
538  else if (va == 1)
539  gl2psa=GL2PS_TEXT_C;
540  }
541 
542  return gl2psa;
543  }
544 
545  void
546  gl2ps_renderer::fix_strlist_position (double x, double y, double z,
547  Matrix box, double rotation,
548  std::list<text_renderer::string>& lst)
549  {
550  for (auto& txtobj : lst)
551  {
552  // Get pixel coordinates
553  ColumnVector coord_pix = get_transform ().transform (x, y, z, false);
554 
555  // Translate and rotate
556  double rot = rotation * 4.0 * atan (1.0) / 180;
557  coord_pix(0) += (txtobj.get_x () + box(0))*cos (rot)
558  - (txtobj.get_y () + box(1))*sin (rot);
559  coord_pix(1) -= (txtobj.get_y () + box(1))*cos (rot)
560  + (txtobj.get_x () + box(0))*sin (rot);
561 
562  GLint vp[4];
563  glGetIntegerv (GL_VIEWPORT, vp);
564 
565  txtobj.set_x (coord_pix(0));
566  txtobj.set_y (vp[3] - coord_pix(1));
567  txtobj.set_z (coord_pix(2));
568  }
569  }
570 
571  static std::string
572  code_to_symbol (uint32_t code)
573  {
575 
576  uint32_t idx = code - 945;
577  if (idx < 25)
578  {
579  std::string characters ("abgdezhqiklmnxoprVstufcyw");
580  retval = characters[idx];
581  return retval;
582  }
583 
584  idx = code - 913;
585  if (idx < 25)
586  {
587  std::string characters ("ABGDEZHQIKLMNXOPRVSTUFCYW");
588  retval = characters[idx];
589  }
590  else if (code == 978)
591  retval = "U";
592  else if (code == 215)
593  retval = "\xb4";
594  else if (code == 177)
595  retval = "\xb1";
596  else if (code == 8501)
597  retval = "\xc0";
598  else if (code == 8465)
599  retval = "\xc1";
600  else if (code == 8242)
601  retval = "\xa2";
602  else if (code == 8736)
603  retval = "\xd0";
604  else if (code == 172)
605  retval = "\xd8";
606  else if (code == 9829)
607  retval = "\xa9";
608  else if (code == 8472)
609  retval = "\xc3";
610  else if (code == 8706)
611  retval = "\xb6";
612  else if (code == 8704)
613  retval = "\x22";
614  else if (code == 9827)
615  retval = "\xa7";
616  else if (code == 9824)
617  retval = "\xaa";
618  else if (code == 8476)
619  retval = "\xc2";
620  else if (code == 8734)
621  retval = "\xa5";
622  else if (code == 8730)
623  retval = "\xd6";
624  else if (code == 8707)
625  retval = "\x24";
626  else if (code == 9830)
627  retval = "\xa8";
628  else if (code == 8747)
629  retval = "\xf2";
630  else if (code == 8727)
631  retval = "\x2a";
632  else if (code == 8744)
633  retval = "\xda";
634  else if (code == 8855)
635  retval = "\xc4";
636  else if (code == 8901)
637  retval = "\xd7";
638  else if (code == 8728)
639  retval = "\xb0";
640  else if (code == 8745)
641  retval = "\xc7";
642  else if (code == 8743)
643  retval = "\xd9";
644  else if (code == 8856)
645  retval = "\xc6";
646  else if (code == 8729)
647  retval = "\xb7";
648  else if (code == 8746)
649  retval = "\xc8";
650  else if (code == 8853)
651  retval = "\xc5";
652  else if (code == 8804)
653  retval = "\xa3";
654  else if (code == 8712)
655  retval = "\xce";
656  else if (code == 8839)
657  retval = "\xca";
658  else if (code == 8801)
659  retval = "\xba";
660  else if (code == 8773)
661  retval = "\x40";
662  else if (code == 8834)
663  retval = "\xcc";
664  else if (code == 8805)
665  retval = "\xb3";
666  else if (code == 8715)
667  retval = "\x27";
668  else if (code == 8764)
669  retval = "\x7e";
670  else if (code == 8733)
671  retval = "\xb5";
672  else if (code == 8838)
673  retval = "\xcd";
674  else if (code == 8835)
675  retval = "\xc9";
676  else if (code == 8739)
677  retval = "\xbd";
678  else if (code == 8776)
679  retval = "\xbb";
680  else if (code == 8869)
681  retval = "\x5e";
682  else if (code == 8656)
683  retval = "\xdc";
684  else if (code == 8592)
685  retval = "\xac";
686  else if (code == 8658)
687  retval = "\xde";
688  else if (code == 8594)
689  retval = "\xae";
690  else if (code == 8596)
691  retval = "\xab";
692  else if (code == 8593)
693  retval = "\xad";
694  else if (code == 8595)
695  retval = "\xaf";
696  else if (code == 8970)
697  retval = "\xeb";
698  else if (code == 8971)
699  retval = "\xfb";
700  else if (code == 10216)
701  retval = "\xe1";
702  else if (code == 10217)
703  retval = "\xf1";
704  else if (code == 8968)
705  retval = "\xe9";
706  else if (code == 8969)
707  retval = "\xf9";
708  else if (code == 8800)
709  retval = "\xb9";
710  else if (code == 8230)
711  retval = "\xbc";
712  else if (code == 176)
713  retval = "\xb0";
714  else if (code == 8709)
715  retval = "\xc6";
716  else if (code == 169)
717  retval = "\xd3";
718 
719  if (retval.empty ())
720  warning ("print: unhandled symbol %d", code);
721 
722  return retval;
723  }
724 
725  static std::string
726  select_font (caseless_str fn, bool isbold, bool isitalic)
727  {
728  std::transform (fn.begin (), fn.end (), fn.begin (), ::tolower);
729  std::string fontname;
730  if (fn == "times" || fn == "times-roman")
731  {
732  if (isitalic && isbold)
733  fontname = "Times-BoldItalic";
734  else if (isitalic)
735  fontname = "Times-Italic";
736  else if (isbold)
737  fontname = "Times-Bold";
738  else
739  fontname = "Times-Roman";
740  }
741  else if (fn == "courier")
742  {
743  if (isitalic && isbold)
744  fontname = "Courier-BoldOblique";
745  else if (isitalic)
746  fontname = "Courier-Oblique";
747  else if (isbold)
748  fontname = "Courier-Bold";
749  else
750  fontname = "Courier";
751  }
752  else if (fn == "symbol")
753  fontname = "Symbol";
754  else if (fn == "zapfdingbats")
755  fontname = "ZapfDingbats";
756  else
757  {
758  if (isitalic && isbold)
759  fontname = "Helvetica-BoldOblique";
760  else if (isitalic)
761  fontname = "Helvetica-Oblique";
762  else if (isbold)
763  fontname = "Helvetica-Bold";
764  else
765  fontname = "Helvetica";
766  }
767  return fontname;
768  }
769 
770  static void
771  escape_character (const std::string chr, std::string& str)
772  {
773  std::size_t idx = str.find (chr);
774  while (idx != std::string::npos)
775  {
776  str.insert (idx, 1, '\\');
777  idx = str.find (chr, idx + 2);
778  }
779  }
780 
782  gl2ps_renderer::strlist_to_svg (double x, double y, double z,
783  Matrix box, double rotation,
784  std::list<octave::text_renderer::string>& lst)
785  {
786  if (lst.empty ())
787  return "";
788 
789  //Use pixel coordinates to conform to gl2ps
790  ColumnVector coord_pix = get_transform ().transform (x, y, z, false);
791 
792  std::ostringstream os;
793  os << "<text xml:space=\"preserve\" ";
794 
795  // Rotation and translation are applied to the whole text element
796  os << "transform=\""
797  << "translate(" << coord_pix(0) + box(0) << "," << coord_pix(1) - box(1)
798  << ") rotate(" << -rotation << "," << -box(0) << "," << box(1)
799  << ")\" ";
800 
801  // Use the first entry for the base text font
802  auto p = lst.begin ();
803  std::string name = p->get_family ();
804  std::string weight = p->get_weight ();
805  std::string angle = p->get_angle ();
806  double size = p->get_size ();
807 
808  os << "font-family=\"" << name << "\" "
809  << "font-weight=\"" << weight << "\" "
810  << "font-style=\"" << angle << "\" "
811  << "font-size=\"" << size << "\">";
812 
813 
814  // build a tspan for each element in the strlist
815  for (p = lst.begin (); p != lst.end (); p++)
816  {
817  os << "<tspan ";
818 
819  if (name.compare (p->get_family ()))
820  os << "font-family=\"" << p->get_family () << "\" ";
821 
822  if (weight.compare (p->get_weight ()))
823  os << "font-weight=\"" << p->get_weight () << "\" ";
824 
825  if (angle.compare (p->get_angle ()))
826  os << "font-style=\"" << p->get_angle () << "\" ";
827 
828  if (size != p->get_size ())
829  os << "font-size=\"" << p->get_size () << "\" ";
830 
831  os << "y=\"" << - p->get_y () << "\" ";
832 
833  Matrix col = p->get_color ();
834  os << "fill=\"rgb(" << col(0)*255 << ","
835  << col(1)*255 << "," << col(2)*255 << ")\" ";
836 
837  // provide an x coordinate for each character in the string
838  os << "x=\"";
839  std::vector<double> xdata = p->get_xdata ();
840  for (auto q = xdata.begin (); q != xdata.end (); q++)
841  os << (*q) << " ";
842  os << "\"";
843 
844  os << ">";
845 
846  // translate unicode and special xml characters
847  if (p->get_code ())
848  os << "&#" << p->get_code () << ";";
849  else
850  {
851  const std::string str = p->get_string ();
852  for (auto q = str.begin (); q != str.end (); q++)
853  {
854  std::stringstream chr;
855  chr << *q;
856  if (chr.str () == "\"")
857  os << "&quot;";
858  else if (chr.str () == "'")
859  os << "&apos;";
860  else if (chr.str () == "&")
861  os << "&amp;";
862  else if (chr.str () == "<")
863  os << "&lt;";
864  else if (chr.str () == ">")
865  os << "&gt;";
866  else
867  os << chr.str ();
868  }
869  }
870  os << "</tspan>";
871  }
872  os << "</text>";
873 
874  return os.str ();
875  }
876 
878  gl2ps_renderer::strlist_to_ps (double x, double y, double z,
879  Matrix box, double rotation,
880  std::list<octave::text_renderer::string>& lst)
881  {
882  // Translate and rotate coordinates in order to use bottom-left alignment
883  fix_strlist_position (x, y, z, box, rotation, lst);
884  Matrix prev_color (1, 3, -1);
885 
886  std::ostringstream ss;
887  ss << "gsave\n";
888 
889  for (const auto& txtobj : lst)
890  {
891  // Color
892  if (txtobj.get_color () != prev_color)
893  {
894  prev_color = txtobj.get_color ();
895  for (int i = 0; i < 3; i++)
896  ss << prev_color(i) << " ";
897 
898  ss << "C\n";
899  }
900 
901  // String
903  if (txtobj.get_code ())
904  {
905  fontname = "Symbol";
906  str = code_to_symbol (txtobj.get_code ());
907  }
908  else
909  {
910  fontname = select_font (txtobj.get_name (),
911  txtobj.get_weight () == "bold",
912  txtobj.get_angle () == "italic");
913  str = txtobj.get_string ();
914  }
915 
916  escape_character ("(", str);
917  escape_character (")", str);
918 
919  ss << "(" << str << ") [";
920 
921  std::vector<double> xdata = txtobj.get_xdata ();
922  for (size_t i = 1; i < xdata.size (); i++)
923  ss << xdata[i] - xdata[i-1] << " ";
924 
925  ss << "10] " << rotation << " " << txtobj.get_x ()
926  << " " << txtobj.get_y () << " " << txtobj.get_size ()
927  << " /" << fontname << " SRX\n";
928  }
929 
930  ss << "grestore\n";
931 
932  return ss.str ();
933  }
934 
935  Matrix
936  gl2ps_renderer::render_text (const std::string& txt,
937  double x, double y, double z,
938  int ha, int va, double rotation)
939  {
940  std::string saved_font = fontname;
941 
942  if (txt.empty ())
943  return Matrix (1, 4, 0.0);
944 
945  Matrix bbox;
946  std::string str = txt;
947  std::list<text_renderer::string> lst;
948 
949  text_to_strlist (str, lst, bbox, ha, va, rotation);
950  glRasterPos3d (x, y, z);
951 
952  // For svg/eps directly dump a preformated text element into gl2ps output
953  if (term.find ("svg") != std::string::npos)
954  {
955  std::string elt = strlist_to_svg (x, y, z, bbox, rotation, lst);
956  if (! elt.empty ())
957  gl2psSpecial (GL2PS_SVG, elt.c_str ());
958  }
959  else if (term.find ("eps") != std::string::npos)
960  {
961  std::string elt = strlist_to_ps (x, y, z, bbox, rotation, lst);
962  if (! elt.empty ())
963  gl2psSpecial (GL2PS_EPS, elt.c_str ());
964 
965  }
966  else
967  gl2psTextOpt (str.c_str (), fontname.c_str (), fontsize,
968  alignment_to_mode (ha, va), rotation);
969 
970  fontname = saved_font;
971 
972  return bbox;
973  }
974 
975  void
976  gl2ps_renderer::set_font (const base_properties& props)
977  {
979 
980  // Set the interpreter so that text_to_pixels can parse strings properly
981  if (props.has_property ("interpreter"))
982  set_interpreter (props.get ("interpreter").string_value ());
983 
984  fontsize = props.get ("__fontsize_points__").double_value ();
985 
986  caseless_str fn = props.get ("fontname").xtolower ().string_value ();
987  bool isbold =
988  (props.get ("fontweight").xtolower ().string_value () == "bold");
989  bool isitalic =
990  (props.get ("fontangle").xtolower ().string_value () == "italic");
991 
992  fontname = select_font (fn, isbold, isitalic);
993  }
994 
995  void
996  gl2ps_renderer::draw_pixels (int w, int h, const float *data)
997  {
998  // Clip data between 0 and 1 for float values
999  OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h);
1000 
1001  for (int i = 0; i < 3*h*w; i++)
1002  tmp_data[i] = (data[i] < 0.0f ? 0.0f : (data[i] > 1.0f ? 1.0f : data[i]));
1003 
1004  gl2psDrawPixels (w, h, 0, 0, GL_RGB, GL_FLOAT, tmp_data);
1005  }
1006 
1007  void
1008  gl2ps_renderer::draw_pixels (int w, int h, const uint8_t *data)
1009  {
1010  // gl2psDrawPixels only supports the GL_FLOAT type.
1011 
1012  OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h);
1013 
1014  static const float maxval = std::numeric_limits<uint8_t>::max ();
1015 
1016  for (int i = 0; i < 3*w*h; i++)
1017  tmp_data[i] = data[i] / maxval;
1018 
1019  draw_pixels (w, h, tmp_data);
1020  }
1021 
1022  void
1023  gl2ps_renderer::draw_pixels (int w, int h, const uint16_t *data)
1024  {
1025  // gl2psDrawPixels only supports the GL_FLOAT type.
1026 
1027  OCTAVE_LOCAL_BUFFER (float, tmp_data, 3*w*h);
1028 
1029  static const float maxval = std::numeric_limits<uint16_t>::max ();
1030 
1031  for (int i = 0; i < 3*w*h; i++)
1032  tmp_data[i] = data[i] / maxval;
1033 
1034  draw_pixels (w, h, tmp_data);
1035  }
1036 
1037  void
1038  gl2ps_renderer::draw_text (const text::properties& props)
1039  {
1040  if (props.get_string ().isempty ())
1041  return;
1042 
1043  draw_text_background (props, true);
1044 
1045  // First set font properties: freetype will use them to compute
1046  // coordinates and gl2ps will retrieve the color directly from the
1047  // feedback buffer
1048  set_font (props);
1049  set_color (props.get_color_rgb ());
1050 
1051  std::string saved_font = fontname;
1052 
1053  // Alignment
1054  int halign = 0;
1055  int valign = 0;
1056 
1057  if (props.horizontalalignment_is ("center"))
1058  halign = 1;
1059  else if (props.horizontalalignment_is ("right"))
1060  halign = 2;
1061 
1062  if (props.verticalalignment_is ("top"))
1063  valign = 2;
1064  else if (props.verticalalignment_is ("baseline"))
1065  valign = 3;
1066  else if (props.verticalalignment_is ("middle"))
1067  valign = 1;
1068 
1069  // FIXME: handle margin and surrounding box
1070  // Matrix bbox;
1071 
1072  const Matrix pos = get_transform ().scale (props.get_data_position ());
1073  std::string str = props.get_string ().string_vector_value ().join ("\n");
1074 
1075  render_text (str, pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0,
1076  halign, valign, props.get_rotation ());
1077  }
1078 }
1079 
1080 #endif
1081 
1082 namespace octave
1083 {
1084  // If the name of the stream begins with '|', open a pipe to the command
1085  // named by the rest of the string. Otherwise, write to the named file.
1086 
1087  void
1089  const std::string& term)
1090  {
1091 #if defined (HAVE_GL2PS_H) && defined (HAVE_OPENGL)
1092 
1093  // FIXME: should we have a way to create a file that begins with the
1094  // character '|'?
1095 
1096  bool have_cmd = stream.length () > 1 && stream[0] == '|';
1097 
1098  FILE *fp = nullptr;
1099 
1101 
1102  if (have_cmd)
1103  {
1104  // Create process and pipe gl2ps output to it.
1105 
1106  std::string cmd = stream.substr (1);
1107 
1108  fp = octave_popen (cmd.c_str (), "w");
1109 
1110  if (! fp)
1111  error (R"(print: failed to open pipe "%s")", stream.c_str ());
1112 
1113  frame.add_fcn (safe_pclose, fp);
1114  }
1115  else
1116  {
1117  // Write gl2ps output directly to file.
1118 
1119  fp = std::fopen (stream.c_str (), "w");
1120 
1121  if (! fp)
1122  error (R"(gl2ps_print: failed to create file "%s")", stream.c_str ());
1123 
1124  frame.add_fcn (safe_fclose, fp);
1125  }
1126 
1127  gl2ps_renderer rend (fp, term);
1128 
1129  Matrix pos = fig.get ("position").matrix_value ();
1130  rend.set_viewport (pos(2), pos(3));
1131  rend.draw (fig, stream);
1132 
1133  // Make sure buffered commands are finished!!!
1134  rend.finish ();
1135 
1136 #else
1137  octave_unused_parameter (fig);
1138  octave_unused_parameter (stream);
1139  octave_unused_parameter (term);
1140 
1141  err_disabled_feature ("gl2ps_print", "gl2ps");
1142 #endif
1143  }
1144 }
FILE * octave_popen(const char *command, const char *mode)
Definition: sysdep.cc:506
virtual void init_marker(const std::string &m, double size, float width)
Definition: gl-render.cc:4091
std::string string_value(bool force=false) const
Definition: ov.h:955
int octave_pclose(FILE *f)
Definition: sysdep.cc:524
virtual void set_polygon_offset(bool on, float offset=0.0f)
Definition: gl-render.cc:3922
int octave_ftruncate_wrapper(int fd, off_t sz)
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 const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE * f
virtual void draw(const graphics_object &go, bool toplevel=true)
Definition: gl-render.cc:620
void add_fcn(void(*fcn)(void))
void error(const char *fmt,...)
Definition: error.cc:578
Complex atan(const Complex &x)
Definition: lo-mappers.h:80
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
s
Definition: file-io.cc:2729
void respond_to_pending_signals(void)
Definition: sighandlers.cc:106
octave_function * fcn
Definition: ov-class.cc:1754
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2786
virtual void draw_axes(const axes::properties &props)
Definition: gl-render.cc:2088
bool valid_object(void) const
Definition: graphics.in.h:2806
double h
Definition: graphics.cc:11808
nd deftypefn *std::string name
Definition: sysdep.cc:647
OCTAVE_EXPORT octave_value_list or both For fclose
Definition: file-io.cc:676
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=nullptr)
virtual bool has_property(const caseless_str &) const
Definition: graphics.in.h:2230
virtual octave_value get(const caseless_str &pname) const
octave_value get(bool all=false) const
Definition: graphics.in.h:2711
std::complex< double > w(std::complex< double > z, double relerr=0)
void gl2ps_print(const graphics_object &fig, const std::string &stream, const std::string &term)
write the output to stdout if nargout otherwise return the output in a character string able ode tem append Append to the destination instead of overwriting tem ascii Save a matrix in a text file without a header or any other information The matrix must be D and only the real part of any complex value is written to the file Numbers are stored in single precision format and separated by spaces Additional options for the and gzip can also be used to convert the files for backward compatibility This option is only available if Octave was built with a link to the zlib libraries end table The list of variables to save may use wildcard patterns containing the following special characters
Definition: load-save.cc:1612
std::string str
Definition: hash.cc:118
is false
Definition: cellfun.cc:400
octave_value retval
Definition: data.cc:6246
FILE * octave_tmpfile_wrapper(void)
base_properties & get_properties(void)
Definition: graphics.in.h:2788
do not permute tem code
Definition: balance.cc:90
Definition: dMatrix.h:36
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:75
virtual void set_linestyle(const std::string &s, bool stipple=false, double linewidth=0.5)
Definition: gl-render.cc:3971
static uint32_t state[624]
Definition: randmtzig.cc:183
void warning(const char *fmt,...)
Definition: error.cc:801
octave::unwind_protect frame
Definition: graphics.cc:12190
octave_value xtolower(void) const
Definition: ov.h:1468
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
virtual void set_linejoin(const std::string &)
Definition: gl-render.h:102
p
Definition: lu.cc:138
double double_value(bool frc_str_conv=false) const
Definition: ov.h:822
the element is set to zero In other the statement xample y
Definition: data.cc:5264
#define SEEK_SET
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:41
static graphics_object get_object(double val)
Definition: graphics.in.h:6098
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5410
for i
Definition: data.cc:5264
bool is_double_type(void) const
Definition: ov.h:648
bool strncmp(const T &str_a, const T &str_b, const typename T::size_type n)
True if the first N characters are the same.
Definition: oct-string.cc:146
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
virtual void set_font(const base_properties &props)
Definition: gl-render.cc:3913
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:50
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:888
bool get_is2D(bool include_kids=false) const
Definition: graphics.in.h:3484
bool is_scalar_type(void) const
Definition: ov.h:717
octave::stream os
Definition: file-io.cc:627
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:834
more on
Definition: toplev.cc:236
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 const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x