GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
gl2ps-print.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2009-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30// Both header files are required outside of HAVE_GLP2S_H
31#include "errwarn.h"
32#include "gl2ps-print.h"
33
34#if defined (HAVE_GL2PS_H) && defined (HAVE_OPENGL)
35
36#include <cstdio>
37
38#include <limits>
39
40#include <gl2ps.h>
41
42#include "file-ops.h"
43#include "filepos-wrappers.h"
44#include "lo-mappers.h"
45#include "oct-locbuf.h"
46#include "oct-env.h"
47#include "unistd-wrappers.h"
48#include "unistr-wrappers.h"
49#include "unwind-prot.h"
50
51#include "gh-manager.h"
52#include "gl-render.h"
53#include "interpreter-private.h"
54#include "oct-opengl.h"
55#include "sighandlers.h"
56#include "sysdep.h"
57#include "text-renderer.h"
58
60
61class OCTINTERP_API gl2ps_renderer : public opengl_renderer
62{
63public:
64
65 gl2ps_renderer (opengl_functions& glfcns, FILE *_fp,
66 const std::string& _term)
67 : opengl_renderer (glfcns), m_fp (_fp), m_term (_term), m_fontsize (),
68 m_fontname (), m_buffer_overflow (false), m_svg_def_index (0)
69 { }
70
71 OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (gl2ps_renderer)
72
73 ~gl2ps_renderer () = default;
74
75 // FIXME: should we import the functions from the base class and
76 // overload them here, or should we use a different name so we don't
77 // have to do this? Without the using declaration or a name change,
78 // the base class functions will be hidden. That may be OK, but it
79 // can also cause some confusion.
81
82 void draw (const graphics_object& go, const std::string& print_cmd);
83
84protected:
85
86 Matrix render_text (const std::string& txt,
87 double x, double y, double z,
88 int halign, int valign, double rotation = 0.0);
89
90 void set_font (const base_properties& props);
91
92 static bool has_alpha (const graphics_handle& h)
93 {
94 bool retval = false;
95
96 gh_manager& gh_mgr = __get_gh_manager__ ();
97
98 graphics_object go = gh_mgr.get_object (h);
99
100 if (! go.valid_object ())
101 return retval;
102
103 if (go.isa ("axes") || go.isa ("hggroup"))
104 {
105 Matrix children = go.get ("children").matrix_value ();
106 for (octave_idx_type ii = 0; ii < children.numel (); ii++)
107 {
108 retval = has_alpha (graphics_handle (children(ii)));
109 if (retval)
110 break;
111 }
112 }
113 else if (go.isa ("patch") || go.isa ("surface"))
114 {
115 octave_value fa = go.get ("facealpha");
116 if (fa.is_scalar_type () && fa.is_double_type ()
117 && fa.double_value () < 1)
118 retval = true;
119 }
120 else if (go.isa ("scatter"))
121 {
122 octave_value fa = go.get ("markerfacealpha");
123 if (fa.is_scalar_type () && fa.is_double_type ()
124 && fa.double_value () < 1)
125 retval = true;
126 }
127
128 return retval;
129 }
130
131 void draw_axes (const axes::properties& props)
132 {
133 // Initialize a sorting tree (viewport) in gl2ps for each axes
134 GLint vp[4];
135 m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
136 gl2psBeginViewport (vp);
137
138
139 // Don't remove hidden primitives when some of them are transparent
140 GLint opts;
141 gl2psGetOptions (&opts);
142 if (has_alpha (props.get___myhandle__ ()))
143 {
144 opts &= ~GL2PS_OCCLUSION_CULL;
145 // FIXME: currently the GL2PS_BLEND (which is more an equivalent of
146 // GL_ALPHA_TEST than GL_BLEND) is not working on a per primitive
147 // basis. We thus set it once per viewport.
148 gl2psEnable (GL2PS_BLEND);
149 }
150 else
151 {
152 opts |= GL2PS_OCCLUSION_CULL;
153 gl2psDisable (GL2PS_BLEND);
154 }
155
156 gl2psSetOptions (opts);
157
158 // Draw and finish () or there may be primitives missing in the gl2ps
159 // output.
161 finish ();
162
163 // Finalize viewport
164 GLint state = gl2psEndViewport ();
165 if (state == GL2PS_NO_FEEDBACK && props.is_visible ())
166 warning ("gl2ps_renderer::draw_axes: empty feedback buffer and/or nothing else to print");
167 else if (state == GL2PS_ERROR)
168 error ("gl2ps_renderer::draw_axes: gl2psEndPage returned GL2PS_ERROR");
169
170 m_buffer_overflow |= (state == GL2PS_OVERFLOW);
171
172 // Don't draw background for subsequent viewports (legends, subplots,
173 // etc.)
174 gl2psGetOptions (&opts);
175 opts &= ~GL2PS_DRAW_BACKGROUND;
176 gl2psSetOptions (opts);
177 }
178
179 void draw_text (const text::properties& props);
180
181 void draw_image (const image::properties& props);
182 void draw_pixels (int w, int h, const float *data);
183 void draw_pixels (int w, int h, const uint8_t *data);
184 void draw_pixels (int w, int h, const uint16_t *data);
185
186 void init_marker (const std::string& m, double size, float width)
187 {
188 opengl_renderer::init_marker (m, size, width);
189
190 // FIXME: gl2ps can't handle closed contours so we set linecap/linejoin
191 // round to obtain a better looking result for some markers.
192 if (m == "o" || m == "v" || m == "^" || m == ">" || m == "<" || m == "h"
193 || m == "hexagram" || m == "p" || m == "pentagram")
194 {
195 set_linejoin ("round");
196 set_linecap ("round");
197 }
198 else
199 {
200 set_linejoin ("miter");
201 set_linecap ("square");
202 }
203 }
204
205 void set_linestyle (const std::string& s, bool use_stipple = false,
206 double linewidth = 0.5)
207 {
208 opengl_renderer::set_linestyle (s, use_stipple, linewidth);
209
210 if (s == "-" && ! use_stipple)
211 gl2psDisable (GL2PS_LINE_STIPPLE);
212 else
213 gl2psEnable (GL2PS_LINE_STIPPLE);
214 }
215
216 void set_linecap (const std::string& s)
217 {
219
220#if defined (HAVE_GL2PSLINEJOIN)
221 if (s == "butt")
222 gl2psLineCap (GL2PS_LINE_CAP_BUTT);
223 else if (s == "square")
224 gl2psLineCap (GL2PS_LINE_CAP_SQUARE);
225 else if (s == "round")
226 gl2psLineCap (GL2PS_LINE_CAP_ROUND);
227#endif
228 }
229
230 void set_linejoin (const std::string& s)
231 {
233
234#if defined (HAVE_GL2PSLINEJOIN)
235 if (s == "round")
236 gl2psLineJoin (GL2PS_LINE_JOIN_ROUND);
237 else if (s == "miter")
238 gl2psLineJoin (GL2PS_LINE_JOIN_MITER);
239 else if (s == "chamfer")
240 gl2psLineJoin (GL2PS_LINE_JOIN_BEVEL);
241#endif
242 }
243
244 void set_polygon_offset (bool on, float offset = 0.0f)
245 {
246 if (on)
247 {
249 gl2psEnable (GL2PS_POLYGON_OFFSET_FILL);
250 }
251 else
252 {
253 gl2psDisable (GL2PS_POLYGON_OFFSET_FILL);
255 }
256 }
257
258 void set_linewidth (float w)
259 {
260 gl2psLineWidth (w);
261 }
262
263private:
264
265 // Use xform to compute the coordinates of the string list
266 // that have been parsed by freetype.
267 void fix_strlist_position (double x, double y, double z,
268 Matrix box, double rotation,
269 std::list<text_renderer::string>& lst);
270
271 // Build an svg text element from a list of parsed strings
272 std::string format_svg_element (std::string str, Matrix bbox,
273 double rotation, ColumnVector coord_pix,
274 Matrix color);
275
276 std::string strlist_to_svg (double x, double y, double z, Matrix box,
277 double rotation,
278 std::list<text_renderer::string>& lst);
279
280 // Build a list of postscript commands from a list of parsed strings.
281 std::string strlist_to_ps (double x, double y, double z, Matrix box,
282 double rotation,
283 std::list<text_renderer::string>& lst);
284
285 int alignment_to_mode (int ha, int va) const;
286
287 FILE *m_fp;
288 caseless_str m_term;
289 double m_fontsize;
290 std::string m_fontname;
291 bool m_buffer_overflow;
292 std::size_t m_svg_def_index;
293};
294
295static bool
296has_2D_axes (const graphics_handle& h)
297{
298 bool retval = true;
299
300 gh_manager& gh_mgr = __get_gh_manager__ ();
301
302 graphics_object go = gh_mgr.get_object (h);
303
304 if (! go.valid_object ())
305 return retval;
306
307 if (go.isa ("figure") || go.isa ("uipanel"))
308 {
309 Matrix children = go.get ("children").matrix_value ();
310 for (octave_idx_type ii = 0; ii < children.numel (); ii++)
311 {
312 retval = has_2D_axes (graphics_handle (children(ii)));
313 if (! retval)
314 break;
315 }
316 }
317 else if (go.isa ("axes"))
318 {
319 axes::properties& ap
320 = reinterpret_cast<axes::properties&> (go.get_properties ());
321 retval = ap.get_is2D (true);
322 }
323
324 return retval;
325}
326
327static std::string
328get_title (const graphics_handle& h)
329{
330 std::string retval;
331
332 gh_manager& gh_mgr = __get_gh_manager__ ();
333
334 graphics_object go = gh_mgr.get_object (h);
335
336 if (! go.valid_object ())
337 return retval;
338
339 if (go.isa ("figure"))
340 {
341 figure::properties& fp
342 = reinterpret_cast<figure::properties&> (go.get_properties ());
343
344 retval = fp.get_title ();
345 }
346
347 return retval;
348}
349
350void
351gl2ps_renderer::draw (const graphics_object& go, const std::string& print_cmd)
352{
353 static bool in_draw = false;
354 static std::string old_print_cmd;
355 static GLint buffsize;
356
357 if (! in_draw)
358 {
359 unwind_protect frame;
360
361 frame.protect_var (in_draw);
362
363 in_draw = true;
364
365 GLint gl2ps_term = GL2PS_PS;
366 if (m_term.find ("eps") != std::string::npos)
367 gl2ps_term = GL2PS_EPS;
368 else if (m_term.find ("pdf") != std::string::npos)
369 gl2ps_term = GL2PS_PDF;
370 else if (m_term.find ("ps") != std::string::npos)
371 gl2ps_term = GL2PS_PS;
372 else if (m_term.find ("svg") != std::string::npos)
373 gl2ps_term = GL2PS_SVG;
374 else if (m_term.find ("pgf") != std::string::npos)
375 gl2ps_term = GL2PS_PGF;
376 else if (m_term.find ("tex") != std::string::npos)
377 gl2ps_term = GL2PS_TEX;
378 else
379 warning ("gl2ps_renderer::draw: Unknown terminal %s, using 'ps'",
380 m_term.c_str ());
381
382 GLint gl2ps_text = 0;
383 if (m_term.find ("notxt") != std::string::npos)
384 gl2ps_text = GL2PS_NO_TEXT;
385
386 // Find Title for plot
387 const graphics_handle& myhandle = go.get ("__myhandle__");
388 std::string plot_title = get_title (myhandle);
389 if (plot_title.empty ())
390 plot_title = "Octave plot";
391
392 // Default sort order optimizes for 3D plots
393 GLint gl2ps_sort = GL2PS_BSP_SORT;
394
395 // FIXME: gl2ps does not provide a way to change the sorting algorithm
396 // on a viewport basis, we thus disable sorting only if all axes are 2D
397 if (has_2D_axes (myhandle))
398 gl2ps_sort = GL2PS_NO_SORT;
399
400 // Use a temporary file in case an overflow happens
401 std::string tmpfile (sys::tempnam (sys::env::get_temp_directory (),
402 "oct-"));
403 FILE *tmpf = sys::fopen_tmp (tmpfile, "w+b");
404
405 if (! tmpf)
406 error ("gl2ps_renderer::draw: couldn't open temporary file for printing");
407
408 frame.add ([tmpf] () { std::fclose (tmpf); });
409
410 // Reset buffsize, unless this is 2nd pass of a texstandalone print.
411 if (m_term.find ("tex") == std::string::npos)
412 buffsize = 2*1024*1024;
413 else
414 buffsize /= 2;
415
416 m_buffer_overflow = true;
417
418 while (m_buffer_overflow)
419 {
420 m_buffer_overflow = false;
421 buffsize *= 2;
422
424 octave_ftruncate_wrapper (fileno (tmpf), 0);
425
426 // For LaTeX output the print process uses 2 drawnow() commands.
427 // The first one is for the pdf/ps/eps graph to be included. The
428 // print_cmd is saved as old_print_cmd. Then the second drawnow()
429 // outputs the tex-file and the graphic filename to be included is
430 // extracted from old_print_cmd.
431
432 std::string include_graph;
433
434 std::size_t found_redirect = old_print_cmd.find ('>');
435
436 if (found_redirect != std::string::npos)
437 include_graph = old_print_cmd.substr (found_redirect + 1);
438 else
439 include_graph = old_print_cmd;
440
441 std::size_t n_begin = include_graph.find_first_not_of (R"( "')");
442
443 if (n_begin != std::string::npos)
444 {
445 // Strip any quote characters characters around filename
446 std::size_t n_end = include_graph.find_last_not_of (R"( "')");
447 include_graph = include_graph.substr (n_begin,
448 n_end - n_begin + 1);
449 // Strip path from filename
450 n_begin = include_graph.find_last_of (sys::file_ops::dir_sep_chars ());
451 include_graph = include_graph.substr (n_begin + 1);
452 }
453 else
454 include_graph = "foobar-inc";
455
456 // FIXME: workaround gl2ps drawing 2 background planes, the first
457 // eventually being black and producing visual artifacts
458 const figure::properties& fprop
459 = dynamic_cast<const figure::properties&> (go.get_properties ());
460 Matrix c = fprop.get_color_rgb ();
461 m_glfcns.glClearColor (c(0), c(1), c(2), 1);
462
463 // Allow figures to be printed at arbitrary resolution
464 set_device_pixel_ratio (fprop.get___device_pixel_ratio__ ());
465
466 // GL2PS_SILENT was removed to allow gl2ps to print errors on stderr
467 GLint ret = gl2psBeginPage (plot_title.c_str (), "Octave",
468 nullptr, gl2ps_term, gl2ps_sort,
469 (GL2PS_BEST_ROOT
470 | gl2ps_text
471 | GL2PS_DRAW_BACKGROUND
472 | GL2PS_NO_PS3_SHADING
473 | GL2PS_USE_CURRENT_VIEWPORT),
474 GL_RGBA, 0, nullptr, 0, 0, 0,
475 buffsize, tmpf, include_graph.c_str ());
476 if (ret == GL2PS_ERROR)
477 {
478 old_print_cmd.clear ();
479 error ("gl2ps_renderer::draw: gl2psBeginPage returned GL2PS_ERROR");
480 }
481
483
484 if (m_buffer_overflow)
485 warning ("gl2ps_renderer::draw: retrying with buffer size: %.1E B\n", double (2*buffsize));
486
487 if (! m_buffer_overflow)
488 old_print_cmd = print_cmd;
489
490 // Don't check return value of gl2psEndPage, it is not meaningful.
491 // Errors and warnings are checked after gl2psEndViewport in
492 // gl2ps_renderer::draw_axes instead.
493 gl2psEndPage ();
494 }
495
496 // Copy temporary file to pipe
498 char str[8192]; // 8 kB is a common kernel buffersize
499 std::size_t nread, nwrite;
500 nread = 1;
501
502 // In EPS terminal read the header line by line and insert a
503 // new procedure
504 const char *fcn = "/SRX { gsave FCT moveto rotate xshow grestore } BD\n";
505 bool header_found = ! (m_term.find ("eps") != std::string::npos
506 || m_term.find ("svg") != std::string::npos);
507
508 while (! feof (tmpf) && nread)
509 {
510 if (! header_found && std::fgets (str, 8192, tmpf))
511 nread = strlen (str);
512 else
513 nread = std::fread (str, 1, 8192, tmpf);
514
515 if (nread)
516 {
517 if (! header_found && std::strncmp (str, "/SBCR", 5) == 0)
518 {
519 header_found = true;
520 nwrite = std::fwrite (fcn, 1, strlen (fcn), m_fp);
521 if (nwrite != strlen (fcn))
522 {
523 // FIXME: is this the best thing to do here?
525 error ("gl2ps_renderer::draw: internal pipe error");
526 }
527 }
528 else if (m_term.find ("svg") != std::string::npos)
529 {
530 // FIXME: gl2ps uses pixel units for SVG format.
531 // Modify resulting svg to use points instead.
532 // Remove this "else if" block, and
533 // make header_found true for SVG if gl2ps is fixed.
534
535 // Specify number of characters because STR may have
536 // come from std::fread and not end with a NUL
537 // character.
538 std::string srchstr (str, nread);
539 std::size_t pos = srchstr.find ("<svg ");
540 if (! header_found && pos != std::string::npos)
541 {
542 header_found = true;
543 pos = srchstr.find ("px");
544 if (pos != std::string::npos)
545 {
546 srchstr[pos+1] = 't'; // "px" -> "pt"
547 // Assume the second occurrence is at the same line
548 pos = srchstr.find ("px", pos);
549 srchstr[pos+1] = 't'; // "px" -> "pt"
550 std::strcpy (str, srchstr.c_str ());
551 }
552 }
553 }
554
555 nwrite = std::fwrite (str, 1, nread, m_fp);
556 if (nwrite != nread)
557 {
558 // FIXME: is this the best thing to do here?
559 respond_to_pending_signals (); // Clear SIGPIPE signal
560 error ("gl2ps_renderer::draw: internal pipe error");
561 }
562 }
563 }
564 }
565 else
567}
568
569int
570gl2ps_renderer::alignment_to_mode (int ha, int va) const
571{
572 int gl2psa = GL2PS_TEXT_BL;
573
574 if (ha == 0)
575 {
576 if (va == 0 || va == 3)
577 gl2psa=GL2PS_TEXT_BL;
578 else if (va == 2)
579 gl2psa=GL2PS_TEXT_TL;
580 else if (va == 1)
581 gl2psa=GL2PS_TEXT_CL;
582 }
583 else if (ha == 2)
584 {
585 if (va == 0 || va == 3)
586 gl2psa=GL2PS_TEXT_BR;
587 else if (va == 2)
588 gl2psa=GL2PS_TEXT_TR;
589 else if (va == 1)
590 gl2psa=GL2PS_TEXT_CR;
591 }
592 else if (ha == 1)
593 {
594 if (va == 0 || va == 3)
595 gl2psa=GL2PS_TEXT_B;
596 else if (va == 2)
597 gl2psa=GL2PS_TEXT_T;
598 else if (va == 1)
599 gl2psa=GL2PS_TEXT_C;
600 }
601
602 return gl2psa;
603}
604
605void
606gl2ps_renderer::fix_strlist_position (double x, double y, double z,
607 Matrix box, double rotation,
608 std::list<text_renderer::string>& lst)
609{
610 for (auto& txtobj : lst)
611 {
612 // Get pixel coordinates
613 ColumnVector coord_pix = get_transform ().transform (x, y, z, false);
614
615 // Translate and rotate
616 double rot = rotation * 4.0 * atan (1.0) / 180;
617 coord_pix(0) += (txtobj.get_x () + box(0))* cos (rot)
618 - (txtobj.get_y () + box(1))* sin (rot);
619 coord_pix(1) -= (txtobj.get_y () + box(1))* cos (rot)
620 + (txtobj.get_x () + box(0))* sin (rot);
621
622 GLint vp[4];
623 m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
624
625 txtobj.set_x (coord_pix(0));
626 txtobj.set_y (vp[3] - coord_pix(1));
627 txtobj.set_z (coord_pix(2));
628 }
629}
630
631static std::string
632code_to_symbol (uint32_t code)
633{
634 std::string retval;
635
636 uint32_t idx = code - 945;
637 if (idx < 25)
638 {
639 std::string characters ("abgdezhqiklmnxoprVstufcyw");
640 retval = characters[idx];
641 return retval;
642 }
643
644 idx = code - 913;
645 if (idx < 25)
646 {
647 std::string characters ("ABGDEZHQIKLMNXOPRVSTUFCYW");
648 retval = characters[idx];
649 }
650 else if (code == 978)
651 retval = "U";
652 else if (code == 215)
653 retval = "\xb4";
654 else if (code == 177)
655 retval = "\xb1";
656 else if (code == 8501)
657 retval = "\xc0";
658 else if (code == 8465)
659 retval = "\xc1";
660 else if (code == 8242)
661 retval = "\xa2";
662 else if (code == 8736)
663 retval = "\xd0";
664 else if (code == 172)
665 retval = "\xd8";
666 else if (code == 9829)
667 retval = "\xa9";
668 else if (code == 8472)
669 retval = "\xc3";
670 else if (code == 8706)
671 retval = "\xb6";
672 else if (code == 8704)
673 retval = "\x22";
674 else if (code == 9827)
675 retval = "\xa7";
676 else if (code == 9824)
677 retval = "\xaa";
678 else if (code == 8476)
679 retval = "\xc2";
680 else if (code == 8734)
681 retval = "\xa5";
682 else if (code == 8730)
683 retval = "\xd6";
684 else if (code == 8707)
685 retval = "\x24";
686 else if (code == 9830)
687 retval = "\xa8";
688 else if (code == 8747)
689 retval = "\xf2";
690 else if (code == 8727)
691 retval = "\x2a";
692 else if (code == 8744)
693 retval = "\xda";
694 else if (code == 8855)
695 retval = "\xc4";
696 else if (code == 8901)
697 retval = "\xd7";
698 else if (code == 8728)
699 retval = "\xb0";
700 else if (code == 8745)
701 retval = "\xc7";
702 else if (code == 8743)
703 retval = "\xd9";
704 else if (code == 8856)
705 retval = "\xc6";
706 else if (code == 8729)
707 retval = "\xb7";
708 else if (code == 8746)
709 retval = "\xc8";
710 else if (code == 8853)
711 retval = "\xc5";
712 else if (code == 8804)
713 retval = "\xa3";
714 else if (code == 8712)
715 retval = "\xce";
716 else if (code == 8839)
717 retval = "\xca";
718 else if (code == 8801)
719 retval = "\xba";
720 else if (code == 8773)
721 retval = "\x40";
722 else if (code == 8834)
723 retval = "\xcc";
724 else if (code == 8805)
725 retval = "\xb3";
726 else if (code == 8715)
727 retval = "\x27";
728 else if (code == 8764)
729 retval = "\x7e";
730 else if (code == 8733)
731 retval = "\xb5";
732 else if (code == 8838)
733 retval = "\xcd";
734 else if (code == 8835)
735 retval = "\xc9";
736 else if (code == 8739)
737 retval = "\xbd";
738 else if (code == 8776)
739 retval = "\xbb";
740 else if (code == 8869)
741 retval = "\x5e";
742 else if (code == 8656)
743 retval = "\xdc";
744 else if (code == 8592)
745 retval = "\xac";
746 else if (code == 8658)
747 retval = "\xde";
748 else if (code == 8594)
749 retval = "\xae";
750 else if (code == 8596)
751 retval = "\xab";
752 else if (code == 8593)
753 retval = "\xad";
754 else if (code == 8595)
755 retval = "\xaf";
756 else if (code == 8970)
757 retval = "\xeb";
758 else if (code == 8971)
759 retval = "\xfb";
760 else if (code == 10216)
761 retval = "\xe1";
762 else if (code == 10217)
763 retval = "\xf1";
764 else if (code == 8968)
765 retval = "\xe9";
766 else if (code == 8969)
767 retval = "\xf9";
768 else if (code == 8800)
769 retval = "\xb9";
770 else if (code == 8230)
771 retval = "\xbc";
772 else if (code == 176)
773 retval = "\xb0";
774 else if (code == 8709)
775 retval = "\xc6";
776 else if (code == 169)
777 retval = "\xd3";
778
779 if (retval.empty ())
780 warning ("print: unhandled symbol %d", code);
781
782 return retval;
783}
784
785static std::string
786select_font (caseless_str fn, bool isbold, bool isitalic)
787{
788 std::transform (fn.begin (), fn.end (), fn.begin (), ::tolower);
789 std::string fontname;
790 if (fn == "times" || fn == "times-roman")
791 {
792 if (isitalic && isbold)
793 fontname = "Times-BoldItalic";
794 else if (isitalic)
795 fontname = "Times-Italic";
796 else if (isbold)
797 fontname = "Times-Bold";
798 else
799 fontname = "Times-Roman";
800 }
801 else if (fn == "courier")
802 {
803 if (isitalic && isbold)
804 fontname = "Courier-BoldOblique";
805 else if (isitalic)
806 fontname = "Courier-Oblique";
807 else if (isbold)
808 fontname = "Courier-Bold";
809 else
810 fontname = "Courier";
811 }
812 else if (fn == "symbol")
813 fontname = "Symbol";
814 else if (fn == "zapfdingbats")
815 fontname = "ZapfDingbats";
816 else
817 {
818 if (isitalic && isbold)
819 fontname = "Helvetica-BoldOblique";
820 else if (isitalic)
821 fontname = "Helvetica-Oblique";
822 else if (isbold)
823 fontname = "Helvetica-Bold";
824 else
825 fontname = "Helvetica";
826 }
827 return fontname;
828}
829
830static void
831escape_character (const std::string chr, std::string& str)
832{
833 std::size_t idx = str.find (chr);
834 while (idx != std::string::npos)
835 {
836 str.insert (idx, 1, '\\');
837 idx = str.find (chr, idx + 2);
838 }
839}
840
841std::string
842gl2ps_renderer::format_svg_element (std::string str, Matrix box,
843 double rotation, ColumnVector coord_pix,
844 Matrix color)
845{
846 // Extract <defs> elements and change their id to avoid conflict with
847 // defs coming from another svg string
848 std::string::size_type n1 = str.find ("<defs>");
849 if (n1 == std::string::npos)
850 return std::string ();
851
852 std::string id, new_id;
853 n1 = str.find ("<path", ++n1);
854 std::string::size_type n2;
855
856 while (n1 != std::string::npos)
857 {
858 // Extract the identifier id='identifier'
859 n1 = str.find ("id='", n1) + 4;
860 n2 = str.find ("'", n1);
861 id = str.substr (n1, n2-n1);
862
863 new_id = std::to_string (m_svg_def_index) + "-" + id ;
864
865 str.replace (n1, n2-n1, new_id);
866
867 std::string::size_type n_ref = str.find ("#" + id);
868
869 while (n_ref != std::string::npos)
870 {
871 str.replace (n_ref + 1, id.length (), new_id);
872 n_ref = str.find ("#" + id);
873 }
874
875 n1 = str.find ("<path", n1);
876 }
877
878 m_svg_def_index++;
879
880 n1 = str.find ("<defs>");
881 n2 = str.find ("</defs>") + 7;
882
883 std::string defs = str.substr (n1, n2-n1);
884
885 // Extract the group containing the <use> elements and transform its
886 // coordinates using the bbox and coordinates info.
887
888 // Extract the original viewBox anchor
889 n1 = str.find ("viewBox='") + 9;
890 if (n1 == std::string::npos)
891 return std::string ();
892
893 n2 = str.find (" ", n1);
894 double original_x0 = std::stod (str.substr (n1, n2-n1));
895
896 n1 = n2+1;
897 n2 = str.find (" ", n1);
898 double original_y0 = std::stod (str.substr (n1, n2-n1));
899
900 // First look for local transform in the original svg
901 std::string orig_trans;
902 n1 = str.find ("<g id='page1' transform='");
903 if (n1 != std::string::npos)
904 {
905 n1 += 25;
906 n2 = str.find ("'", n1);
907 orig_trans = str.substr (n1, n2-n1);
908 n1 = n2 + 1;
909 }
910 else
911 {
912 n1 = str.find ("<g id='page1'");
913 n1 += 13;
914 }
915
916 n2 = str.find ("</g>", n1) + 4;
917
918 // The first applied transformation is the right-most
919 // 1* Apply original transform
920 std::string tform = orig_trans;
921
922 // 2* Move the anchor to the final position
923 tform = std::string ("translate")
924 + "(" + std::to_string (box(0) - original_x0 + coord_pix(0))
925 + "," + std::to_string (-(box(3) + box(1)) - original_y0 + coord_pix(1))
926 + ") " + tform;
927
928 // 3* Rotate around the final position
929 if (rotation != 0)
930 tform = std::string ("rotate")
931 + "(" + std::to_string (-rotation)
932 + "," + std::to_string (coord_pix(0))
933 + "," + std::to_string (coord_pix(1))
934 + ") " + tform;
935
936 // Fill color
937 std::string fill = "fill='rgb("
938 + std::to_string (static_cast<uint8_t> (color(0) * 255.0)) + ","
939 + std::to_string (static_cast<uint8_t> (color(1) * 255.0)) + ","
940 + std::to_string (static_cast<uint8_t> (color(2) * 255.0)) + ")' ";
941
942 std::string use_group = "<g "
943 + fill
944 + "transform='" + tform + "'"
945 + str.substr (n1, n2-n1);
946
947 return defs + "\n" + use_group;
948}
949
950std::string
951gl2ps_renderer::strlist_to_svg (double x, double y, double z,
952 Matrix box, double rotation,
953 std::list<text_renderer::string>& lst)
954{
955 //Use pixel coordinates to conform to gl2ps
956 ColumnVector coord_pix = get_transform ().transform (x, y, z, false);
957
958 if (lst.empty ())
959 return "";
960
961 // This may already be an svg image.
962 std::string svg = lst.front ().get_svg_element ();
963 if (! svg.empty ())
964 return format_svg_element (svg, box, rotation, coord_pix,
965 lst.front ().get_color ());
966
967 // Rotation and translation are applied to the whole group
968 std::ostringstream os;
969 os << R"(<g xml:space="preserve" )";
970 os << "transform=\""
971 << "translate(" << coord_pix(0) + box(0) << "," << coord_pix(1) - box(1)
972 << ") rotate(" << -rotation << "," << -box(0) << "," << box(1)
973 << ")\" ";
974
975 // Use the first entry for the base text font
976 auto p = lst.begin ();
977 std::string name = p->get_family ();
978 std::string weight = p->get_weight ();
979 std::string angle = p->get_angle ();
980 double size = p->get_size ();
981
982 os << "font-family=\"" << name << "\" "
983 << "font-weight=\"" << weight << "\" "
984 << "font-style=\"" << angle << "\" "
985 << "font-size=\"" << size << "\">";
986
987
988 // Build a text element for each element in the strlist
989 for (p = lst.begin (); p != lst.end (); p++)
990 {
991 os << "<text ";
992
993 if (name.compare (p->get_family ()))
994 os << "font-family=\"" << p->get_family () << "\" ";
995
996 if (weight.compare (p->get_weight ()))
997 os << "font-weight=\"" << p->get_weight () << "\" ";
998
999 if (angle.compare (p->get_angle ()))
1000 os << "font-style=\"" << p->get_angle () << "\" ";
1001
1002 if (size != p->get_size ())
1003 os << "font-size=\"" << p->get_size () << "\" ";
1004
1005 os << "y=\"" << - p->get_y () << "\" ";
1006
1007 Matrix col = p->get_color ();
1008 os << "fill=\"rgb(" << col(0)*255 << ","
1009 << col(1)*255 << "," << col(2)*255 << ")\" ";
1010
1011 // provide an x coordinate for each character in the string
1012 os << "x=\"";
1013 std::vector<double> xdata = p->get_xdata ();
1014 for (const auto& q : xdata)
1015 os << q << " ";
1016 os << '"';
1017
1018 os << '>';
1019
1020 // translate unicode and special xml characters
1021 if (p->get_code ())
1022 os << "&#" << p->get_code () << ";";
1023 else
1024 {
1025 const std::string str = p->get_string ();
1026 for (const auto& q : str)
1027 {
1028 std::stringstream chr;
1029 chr << q;
1030 if (chr.str () == "\"")
1031 os << "&quot;";
1032 else if (chr.str () == "'")
1033 os << "&apos;";
1034 else if (chr.str () == "&")
1035 os << "&amp;";
1036 else if (chr.str () == "<")
1037 os << "&lt;";
1038 else if (chr.str () == ">")
1039 os << "&gt;";
1040 else
1041 os << chr.str ();
1042 }
1043 }
1044 os << "</text>";
1045 }
1046 os << "</g>";
1047
1048 return os.str ();
1049}
1050
1051std::string
1052gl2ps_renderer::strlist_to_ps (double x, double y, double z,
1053 Matrix box, double rotation,
1054 std::list<text_renderer::string>& lst)
1055{
1056 if (lst.empty ())
1057 return "";
1058 else if (lst.size () == 1)
1059 {
1060 static bool warned = false;
1061 // This may be an svg image, not handled in native eps format.
1062 if (! lst.front ().get_svg_element ().empty ())
1063 {
1064 if (! warned)
1065 {
1066 warned = true;
1067 warning_with_id ("Octave:print:unhandled-svg-content",
1068 "print: unhandled LaTeX strings. "
1069 "Use -svgconvert option or -d*latex* output "
1070 "device.");
1071 }
1072 return "";
1073 }
1074 }
1075
1076 // Translate and rotate coordinates in order to use bottom-left alignment
1077 fix_strlist_position (x, y, z, box, rotation, lst);
1078 Matrix prev_color (1, 3, -1);
1079
1080 std::ostringstream ss;
1081 ss << "gsave\n";
1082
1083 static bool warned = false;
1084
1085 for (const auto& txtobj : lst)
1086 {
1087 // Color
1088 if (txtobj.get_color () != prev_color)
1089 {
1090 prev_color = txtobj.get_color ();
1091 for (int i = 0; i < 3; i++)
1092 ss << prev_color(i) << " ";
1093
1094 ss << "C\n";
1095 }
1096
1097 // String
1098 std::string str;
1099 if (txtobj.get_code ())
1100 {
1101 m_fontname = "Symbol";
1102 str = code_to_symbol (txtobj.get_code ());
1103 }
1104 else
1105 {
1106 m_fontname = select_font (txtobj.get_name (),
1107 txtobj.get_weight () == "bold",
1108 txtobj.get_angle () == "italic");
1109
1110 // Check that the string is composed of single byte characters
1111 const std::string tmpstr = txtobj.get_string ();
1112 const uint8_t *c
1113 = reinterpret_cast<const uint8_t *> (tmpstr.c_str ());
1114
1115 for (std::size_t i = 0; i < tmpstr.size ();)
1116 {
1117 int mblen = octave_u8_strmblen_wrapper (c + i);
1118
1119 // Replace multibyte or non ascii characters by a question mark
1120 if (mblen > 1)
1121 {
1122 str += "?";
1123 if (! warned)
1124 {
1125 warning_with_id ("Octave:print:unsupported-multibyte",
1126 "print: only ASCII characters are "
1127 "supported for EPS and derived "
1128 "formats. Use the '-svgconvert' "
1129 "option for better font support.");
1130 warned = true;
1131 }
1132 }
1133 else if (mblen < 1)
1134 {
1135 mblen = 1;
1136 str += "?";
1137 if (! warned)
1138 {
1139 warning_with_id ("Octave:print:unhandled-character",
1140 "print: only ASCII characters are "
1141 "supported for EPS and derived "
1142 "formats. Use the '-svgconvert' "
1143 "option for better font support.");
1144 warned = true;
1145 }
1146 }
1147 else
1148 str += tmpstr.at (i);
1149
1150 i += mblen;
1151 }
1152 }
1153
1154 escape_character ("\\", str);
1155 escape_character ("(", str);
1156 escape_character (")", str);
1157
1158 ss << "(" << str << ") [";
1159
1160 std::vector<double> xdata = txtobj.get_xdata ();
1161 for (std::size_t i = 1; i < xdata.size (); i++)
1162 ss << xdata[i] - xdata[i-1] << " ";
1163
1164 ss << "10] " << rotation << " " << txtobj.get_x ()
1165 << " " << txtobj.get_y () << " " << txtobj.get_size ()
1166 << " /" << m_fontname << " SRX\n";
1167 }
1168
1169 ss << "grestore\n";
1170
1171 return ss.str ();
1172}
1173
1174Matrix
1175gl2ps_renderer::render_text (const std::string& txt,
1176 double x, double y, double z,
1177 int ha, int va, double rotation)
1178{
1179 std::string saved_font = m_fontname;
1180
1181 if (txt.empty ())
1182 return Matrix (1, 4, 0.0);
1183
1184 Matrix bbox;
1185 std::string str = txt;
1186 std::list<text_renderer::string> lst;
1187
1188 text_to_strlist (str, lst, bbox, ha, va, rotation);
1189 m_glfcns.glRasterPos3d (x, y, z);
1190
1191 // For svg/eps directly dump a preformated text element into gl2ps output
1192 if (m_term.find ("svg") != std::string::npos)
1193 {
1194 std::string elt = strlist_to_svg (x, y, z, bbox, rotation, lst);
1195 if (! elt.empty ())
1196 gl2psSpecial (GL2PS_SVG, elt.c_str ());
1197 }
1198 else if (m_term.find ("eps") != std::string::npos)
1199 {
1200 std::string elt = strlist_to_ps (x, y, z, bbox, rotation, lst);
1201 if (! elt.empty ())
1202 gl2psSpecial (GL2PS_EPS, elt.c_str ());
1203
1204 }
1205 else
1206 gl2psTextOpt (str.c_str (), m_fontname.c_str (), m_fontsize,
1207 alignment_to_mode (ha, va), rotation);
1208
1209 m_fontname = saved_font;
1210
1211 return bbox;
1212}
1213
1214void
1215gl2ps_renderer::set_font (const base_properties& props)
1216{
1218
1219 // Set the interpreter so that text_to_pixels can parse strings properly
1220 if (props.has_property ("interpreter"))
1221 set_interpreter (props.get ("interpreter").string_value ());
1222
1223 m_fontsize = props.get ("__fontsize_points__").double_value ();
1224
1225 caseless_str fn = props.get ("fontname").xtolower ().string_value ();
1226 bool isbold
1227 =(props.get ("fontweight").xtolower ().string_value () == "bold");
1228 bool isitalic
1229 = (props.get ("fontangle").xtolower ().string_value () == "italic");
1230
1231 m_fontname = select_font (fn, isbold, isitalic);
1232}
1233
1234void
1235gl2ps_renderer::draw_image (const image::properties& props)
1236{
1237 octave_value cdata = props.get_color_data ();
1238 const dim_vector& dv = cdata.dims ();
1239 int h = dv(0);
1240 int w = dv(1);
1241
1242 Matrix x = props.get_xdata ().matrix_value ();
1243 Matrix y = props.get_ydata ().matrix_value ();
1244
1245 // Someone wants us to draw an empty image? No way.
1246 if (x.isempty () || y.isempty ())
1247 return;
1248
1249 // Sort x/ydata and mark flipped dimensions
1250 bool xflip = false;
1251 if (x(0) > x(1))
1252 {
1253 std::swap (x(0), x(1));
1254 xflip = true;
1255 }
1256 else if (w > 1 && x(1) == x(0))
1257 x(1) = x(1) + (w-1);
1258
1259 bool yflip = false;
1260 if (y(0) > y(1))
1261 {
1262 std::swap (y(0), y(1));
1263 yflip = true;
1264 }
1265 else if (h > 1 && y(1) == y(0))
1266 y(1) = y(1) + (h-1);
1267
1268
1269 const ColumnVector p0 = m_xform.transform (x(0), y(0), 0);
1270 const ColumnVector p1 = m_xform.transform (x(1), y(1), 0);
1271
1272 if (math::isnan (p0(0)) || math::isnan (p0(1))
1273 || math::isnan (p1(0)) || math::isnan (p1(1)))
1274 {
1275 warning ("opengl_renderer: image X,Y data too large to draw");
1276 return;
1277 }
1278
1279 // image pixel size in screen pixel units
1280 float pix_dx, pix_dy;
1281 // image pixel size in normalized units
1282 float nor_dx, nor_dy;
1283
1284 if (w > 1)
1285 {
1286 pix_dx = (p1(0) - p0(0)) / (w-1);
1287 nor_dx = (x(1) - x(0)) / (w-1);
1288 }
1289 else
1290 {
1291 const ColumnVector p1w = m_xform.transform (x(1) + 1, y(1), 0);
1292 pix_dx = p1w(0) - p0(0);
1293 nor_dx = 1;
1294 }
1295
1296 if (h > 1)
1297 {
1298 pix_dy = (p1(1) - p0(1)) / (h-1);
1299 nor_dy = (y(1) - y(0)) / (h-1);
1300 }
1301 else
1302 {
1303 const ColumnVector p1h = m_xform.transform (x(1), y(1) + 1, 0);
1304 pix_dy = p1h(1) - p0(1);
1305 nor_dy = 1;
1306 }
1307
1308 // OpenGL won't draw any of the image if its origin is outside the
1309 // viewport/clipping plane so we must do the clipping ourselves.
1310
1311 int j0, j1, jj, i0, i1, ii;
1312 j0 = 0, j1 = w;
1313 i0 = 0, i1 = h;
1314
1315 float im_xmin = x(0) - nor_dx/2;
1316 float im_xmax = x(1) + nor_dx/2;
1317 float im_ymin = y(0) - nor_dy/2;
1318 float im_ymax = y(1) + nor_dy/2;
1319
1320 // Clip to axes or viewport
1321 bool do_clip = props.is_clipping ();
1322 Matrix vp = get_viewport_scaled ();
1323
1324 ColumnVector vp_lim_min
1325 = m_xform.untransform (std::numeric_limits <float>::epsilon (),
1326 std::numeric_limits <float>::epsilon ());
1327 ColumnVector vp_lim_max = m_xform.untransform (vp(2), vp(3));
1328
1329 if (vp_lim_min(0) > vp_lim_max(0))
1330 std::swap (vp_lim_min(0), vp_lim_max(0));
1331
1332 if (vp_lim_min(1) > vp_lim_max(1))
1333 std::swap (vp_lim_min(1), vp_lim_max(1));
1334
1335 float clip_xmin
1336 = do_clip ? (vp_lim_min(0) > m_xmin ? vp_lim_min(0) : m_xmin)
1337 : vp_lim_min(0);
1338
1339 float clip_ymin
1340 = do_clip ? (vp_lim_min(1) > m_ymin ? vp_lim_min(1) : m_ymin)
1341 : vp_lim_min(1);
1342
1343 float clip_xmax
1344 = do_clip ? (vp_lim_max(0) < m_xmax ? vp_lim_max(0) : m_xmax)
1345 : vp_lim_max(0);
1346
1347 float clip_ymax
1348 = do_clip ? (vp_lim_max(1) < m_ymax ? vp_lim_max(1) : m_ymax)
1349 : vp_lim_max(1);
1350
1351 if (im_xmin < clip_xmin)
1352 j0 += (clip_xmin - im_xmin)/nor_dx + 1;
1353
1354 if (im_xmax > clip_xmax)
1355 j1 -= (im_xmax - clip_xmax)/nor_dx;
1356
1357 if (im_ymin < clip_ymin)
1358 i0 += (clip_ymin - im_ymin)/nor_dy + 1;
1359
1360 if (im_ymax > clip_ymax)
1361 i1 -= (im_ymax - clip_ymax)/nor_dy;
1362
1363 if (i0 >= i1 || j0 >= j1)
1364 return;
1365
1366 float zoom_x;
1367 m_glfcns.glGetFloatv (GL_ZOOM_X, &zoom_x);
1368 float zoom_y;
1369 m_glfcns.glGetFloatv (GL_ZOOM_Y, &zoom_y);
1370
1371 m_glfcns.glPixelZoom (m_devpixratio * pix_dx, - m_devpixratio * pix_dy);
1372 m_glfcns.glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0);
1373
1374 // Expect RGB data
1375 if (dv.ndims () == 3 && dv(2) == 3)
1376 {
1377 if (cdata.is_double_type ())
1378 {
1379 const NDArray xcdata = cdata.array_value ();
1380
1381 OCTAVE_LOCAL_BUFFER (GLfloat, a,
1382 static_cast<size_t> (3)*(j1-j0)*(i1-i0));
1383
1384 for (int i = i0; i < i1; i++)
1385 {
1386 for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
1387 {
1388 if (! yflip)
1389 ii = i;
1390 else
1391 ii = h - i - 1;
1392
1393 if (! xflip)
1394 jj = j;
1395 else
1396 jj = w - j - 1;
1397
1398 a[idx] = xcdata(ii, jj, 0);
1399 a[idx+1] = xcdata(ii, jj, 1);
1400 a[idx+2] = xcdata(ii, jj, 2);
1401 }
1402 }
1403
1404 draw_pixels (j1-j0, i1-i0, a);
1405
1406 }
1407 else if (cdata.is_single_type ())
1408 {
1409 const FloatNDArray xcdata = cdata.float_array_value ();
1410
1411 OCTAVE_LOCAL_BUFFER (GLfloat, a,
1412 static_cast<size_t> (3)*(j1-j0)*(i1-i0));
1413
1414 for (int i = i0; i < i1; i++)
1415 {
1416 for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
1417 {
1418 if (! yflip)
1419 ii = i;
1420 else
1421 ii = h - i - 1;
1422
1423 if (! xflip)
1424 jj = j;
1425 else
1426 jj = w - j - 1;
1427
1428 a[idx] = xcdata(ii, jj, 0);
1429 a[idx+1] = xcdata(ii, jj, 1);
1430 a[idx+2] = xcdata(ii, jj, 2);
1431 }
1432 }
1433
1434 draw_pixels (j1-j0, i1-i0, a);
1435
1436 }
1437 else if (cdata.is_uint8_type ())
1438 {
1439 const uint8NDArray xcdata = cdata.uint8_array_value ();
1440
1441 OCTAVE_LOCAL_BUFFER (GLubyte, a,
1442 static_cast<size_t> (3)*(j1-j0)*(i1-i0));
1443
1444 for (int i = i0; i < i1; i++)
1445 {
1446 for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
1447 {
1448 if (! yflip)
1449 ii = i;
1450 else
1451 ii = h - i - 1;
1452
1453 if (! xflip)
1454 jj = j;
1455 else
1456 jj = w - j - 1;
1457
1458 a[idx] = xcdata(ii, jj, 0);
1459 a[idx+1] = xcdata(ii, jj, 1);
1460 a[idx+2] = xcdata(ii, jj, 2);
1461 }
1462 }
1463
1464 draw_pixels (j1-j0, i1-i0, a);
1465
1466 }
1467 else if (cdata.is_uint16_type ())
1468 {
1469 const uint16NDArray xcdata = cdata.uint16_array_value ();
1470
1471 OCTAVE_LOCAL_BUFFER (GLushort, a,
1472 static_cast<size_t> (3)*(j1-j0)*(i1-i0));
1473
1474 for (int i = i0; i < i1; i++)
1475 {
1476 for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
1477 {
1478 if (! yflip)
1479 ii = i;
1480 else
1481 ii = h - i - 1;
1482
1483 if (! xflip)
1484 jj = j;
1485 else
1486 jj = w - j - 1;
1487
1488 a[idx] = xcdata(ii, jj, 0);
1489 a[idx+1] = xcdata(ii, jj, 1);
1490 a[idx+2] = xcdata(ii, jj, 2);
1491 }
1492 }
1493
1494 draw_pixels (j1-j0, i1-i0, a);
1495
1496 }
1497 else
1498 warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)");
1499
1500 m_glfcns.glPixelZoom (zoom_x, zoom_y);
1501
1502 }
1503}
1504
1505void
1506gl2ps_renderer::draw_pixels (int w, int h, const float *data)
1507{
1508 // Clip data between 0 and 1 for float values
1509 OCTAVE_LOCAL_BUFFER (float, tmp_data, static_cast<size_t> (3)*w*h);
1510
1511 for (int i = 0; i < 3*h*w; i++)
1512 tmp_data[i] = (data[i] < 0.0f ? 0.0f : (data[i] > 1.0f ? 1.0f : data[i]));
1513
1514 gl2psDrawPixels (w, h, 0, 0, GL_RGB, GL_FLOAT, tmp_data);
1515}
1516
1517void
1518gl2ps_renderer::draw_pixels (int w, int h, const uint8_t *data)
1519{
1520 // gl2psDrawPixels only supports the GL_FLOAT type.
1521
1522 OCTAVE_LOCAL_BUFFER (float, tmp_data, static_cast<size_t> (3)*w*h);
1523
1524 static constexpr float MAXVAL = std::numeric_limits<uint8_t>::max ();
1525
1526 for (int i = 0; i < 3*w*h; i++)
1527 tmp_data[i] = data[i] / MAXVAL;
1528
1529 draw_pixels (w, h, tmp_data);
1530}
1531
1532void
1533gl2ps_renderer::draw_pixels (int w, int h, const uint16_t *data)
1534{
1535 // gl2psDrawPixels only supports the GL_FLOAT type.
1536
1537 OCTAVE_LOCAL_BUFFER (float, tmp_data, static_cast<size_t> (3)*w*h);
1538
1539 static constexpr float MAXVAL = std::numeric_limits<uint16_t>::max ();
1540
1541 for (int i = 0; i < 3*w*h; i++)
1542 tmp_data[i] = data[i] / MAXVAL;
1543
1544 draw_pixels (w, h, tmp_data);
1545}
1546
1547void
1548gl2ps_renderer::draw_text (const text::properties& props)
1549{
1550 if (props.get_string ().isempty ())
1551 return;
1552
1553 draw_text_background (props, true);
1554
1555 // First set font properties: freetype will use them to compute
1556 // coordinates and gl2ps will retrieve the color directly from the
1557 // feedback buffer
1558 set_font (props);
1559 set_color (props.get_color_rgb ());
1560
1561 std::string saved_font = m_fontname;
1562
1563 // Alignment
1564 int halign = 0;
1565 int valign = 0;
1566
1567 if (props.horizontalalignment_is ("center"))
1568 halign = 1;
1569 else if (props.horizontalalignment_is ("right"))
1570 halign = 2;
1571
1572 if (props.verticalalignment_is ("top"))
1573 valign = 2;
1574 else if (props.verticalalignment_is ("baseline"))
1575 valign = 3;
1576 else if (props.verticalalignment_is ("middle"))
1577 valign = 1;
1578
1579 // FIXME: handle margin and surrounding box
1580 // Matrix bbox;
1581
1582 const Matrix pos = get_transform ().scale (props.get_data_position ());
1583 std::string str = props.get_string ().string_vector_value ().join ("\n");
1584
1585 render_text (str, pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0,
1586 halign, valign, props.get_rotation ());
1587}
1588
1589OCTAVE_END_NAMESPACE(octave)
1590
1591#endif
1592
1594
1595// If the name of the stream begins with '|', open a pipe to the command
1596// named by the rest of the string. Otherwise, write to the named file.
1597
1598void
1599gl2ps_print (opengl_functions& glfcns, const graphics_object& fig,
1600 const std::string& stream, const std::string& term)
1601{
1602#if defined (HAVE_GL2PS_H) && defined (HAVE_OPENGL)
1603
1604 // FIXME: should we have a way to create a file that begins with the
1605 // character '|'?
1606
1607 bool have_cmd = stream.length () > 1 && stream[0] == '|';
1608
1609 FILE *m_fp = nullptr;
1610
1611 unwind_protect frame;
1612
1613 if (have_cmd)
1614 {
1615 // Create process and pipe gl2ps output to it.
1616
1617 std::string cmd = stream.substr (1);
1618
1619 m_fp = popen (cmd.c_str (), "w");
1620
1621 if (! m_fp)
1622 error (R"(print: failed to open pipe "%s")", stream.c_str ());
1623
1624 // Need octave:: qualifier here to avoid ambiguity.
1625 frame.add ([m_fp] () { octave::pclose (m_fp); });
1626 }
1627 else
1628 {
1629 // Write gl2ps output directly to file.
1630
1631 m_fp = sys::fopen (stream.c_str (), "w");
1632
1633 if (! m_fp)
1634 error (R"(gl2ps_print: failed to create file "%s")", stream.c_str ());
1635
1636 frame.add ([m_fp] () { std::fclose (m_fp); });
1637 }
1638
1639 gl2ps_renderer rend (glfcns, m_fp, term);
1640
1641 Matrix pos = fig.get ("position").matrix_value ();
1642 rend.set_viewport (pos(2), pos(3));
1643 rend.draw (fig, stream);
1644
1645 // Make sure buffered commands are finished!!!
1646 rend.finish ();
1647
1648#else
1649
1650 octave_unused_parameter (glfcns);
1651 octave_unused_parameter (fig);
1652 octave_unused_parameter (stream);
1653 octave_unused_parameter (term);
1654
1655 err_disabled_feature ("gl2ps_print", "gl2ps");
1656
1657#endif
1658}
1659
1660OCTAVE_END_NAMESPACE(octave)
#define SEEK_SET
bool isempty() const
Size of the specified dimension.
Definition Array.h:652
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
void add(F &&fcn, Args &&... args)
void protect_var(T &var)
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:253
graphics_object get_object(double val) const
Definition gh-manager.h:68
uint16NDArray uint16_array_value() const
Definition ov.h:974
bool is_scalar_type() const
Definition ov.h:744
bool is_single_type() const
Definition ov.h:698
bool is_uint8_type() const
Definition ov.h:718
bool is_uint16_type() const
Definition ov.h:721
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:865
bool is_double_type() const
Definition ov.h:695
uint8NDArray uint8_array_value() const
Definition ov.h:971
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:868
double double_value(bool frc_str_conv=false) const
Definition ov.h:847
dim_vector dims() const
Definition ov.h:541
virtual void init_marker(const std::string &m, double size, float width)
virtual void set_linestyle(const std::string &s, bool stipple=false, double linewidth=0.5)
virtual void set_linewidth(float w)
virtual void finish()
virtual Matrix render_text(const std::string &txt, double x, double y, double z, int halign, int valign, double rotation=0.0)
virtual void draw_image(const image::properties &props)
virtual void draw(const graphics_object &go, bool toplevel=true)
Definition gl-render.cc:719
virtual void draw_axes(const axes::properties &props)
virtual void set_polygon_offset(bool on, float offset=0.0f)
virtual void draw_text(const text::properties &props)
virtual void set_font(const base_properties &props)
virtual void set_linecap(const std::string &)
Definition gl-render.h:105
virtual void set_linejoin(const std::string &)
Definition gl-render.h:106
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void warning(const char *fmt,...)
Definition error.cc:1078
void warning_with_id(const char *id, const char *fmt,...)
Definition error.cc:1093
void error(const char *fmt,...)
Definition error.cc:1003
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition errwarn.cc:53
int octave_fseeko_wrapper(FILE *fp, off_t offset, int whence)
void gl2ps_print(opengl_functions &glfcns, const graphics_object &fig, const std::string &stream, const std::string &term)
gh_manager & __get_gh_manager__()
Complex atan(const Complex &x)
Definition lo-mappers.h:71
F77_RET_T const F77_DBLE * x
std::complex< double > w(std::complex< double > z, double relerr=0)
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
T::size_type strlen(const typename T::value_type *str)
Definition oct-string.cc:88
void respond_to_pending_signals()
FILE * popen(const char *command, const char *mode)
Definition sysdep.cc:626
int octave_ftruncate_wrapper(int fd, off_t sz)
int octave_u8_strmblen_wrapper(const uint8_t *src)