Mercurial > octave
diff libinterp/corefcn/gl2ps-print.cc @ 29470:2ae4764180c6
Initial implementation of a LaTeX interpreter (bug #59546).
* NEWS: Announce basic support for "latex" interpreter.
* plot.txi: Restructure the "Use of the interpreter Property" section to include
3 subsections, one for each interpreter type. In "Printing and Saving Plots"
describe the new behavior.
* print.m: Document new behavior.
* contributors.in: Add Andrej Lojdl, a former GSoC student that worked on this
subject. Even though there is not much left of his original work, it served
as a very useful starting point.
* base-text-renderer.h, base-text-renderer.cc: New enum to store symbolic
constants for rotation angles.
(base_text_renderer::rotate_pixels): New utility method. Code extracted
from ft_text_renderer::render.
(base_text_renderer::rotation_to_mode): Moved from ft_text_renderer class.
(base_text_renderer::fix_bbox_anchor): New utility method. Code extracted
from ft_text_renderer::text_to_pixels.
* ft_text_renderer.cc (ft_text_renderer::render,
ft_text_renderer::text_to_pixels): Make use of base class utility functions.
* gl2ps-print.cc: New counter variable m_svg_def_index for safe inclusion of
defs coming from dvisvgm.
(gl2ps_renderer::format_svg_element): New function to manipulate svg elements
obtained from dvisvgm so as to position them properly on the figure and
change their color.
(gl2ps_renderer::strlist_to_svg): If the str_list object returned from
text_to_strlist contains an svg element, use format_svg_element to handle
position and color and then return it. Otherwise, use <g> and <text> elements
rather than <text> and <tspan> elements which are not supported by Qt's svg
renderer (svg-tiny implementation).
(gl2ps_renderer::strlist_to_ps): If the str_list object returned from
text_to_strlist contains an svg element, raise a warning about the
necessity of using -svgconvert.
* latex-text-renderer.h, latex-text-renderer.cc: New files to hold the
latex_interpreter class.
* libinterp/corefcn/module.mk: Add new files to the build system.
* text-renderer.h, text-renderer.cc (text_renderer): New data member latex_rep
to hold a pointer to an instance of a latex_renderer.
(text_renderer::~text_renderer, get_extent, set_anti_aliasing, set_font,
set_color, text_to_pixels, text_to_strlist): Duplicate action of the
latex_rep.
(text_renderer::latex_ok): New method to test the usability of the
latex_renderer.
(text_renderer::string::svg_element, text_renderer::string::set_svg_element,
text_renderer::string::get_svg_element): New string data member to hold
preformated svg element. Provide accessor methods.
* acinclude.m4: Add QtSvg to the list of imported QT_MODULES.
* octave-svgconvert.cc: Overhaul program to make use of Qt's QSvgRenderer
when rendering to PDF.
author | Pantxo Diribarne <pantxo.diribarne@gmail.com> |
---|---|
date | Mon, 22 Mar 2021 21:32:54 +0100 |
parents | 7854d5752dd2 |
children | aef11bb4e6d1 |
line wrap: on
line diff
--- a/libinterp/corefcn/gl2ps-print.cc Mon Mar 29 07:54:26 2021 +0200 +++ b/libinterp/corefcn/gl2ps-print.cc Mon Mar 22 21:32:54 2021 +0100 @@ -64,8 +64,8 @@ gl2ps_renderer (opengl_functions& glfcns, FILE *_fp, const std::string& _term) - : opengl_renderer (glfcns), fp (_fp), term (_term), - fontsize (), fontname (), buffer_overflow (false) + : opengl_renderer (glfcns), fp (_fp), term (_term), fontsize (), + fontname (), buffer_overflow (false), m_svg_def_index (0) { } ~gl2ps_renderer (void) = default; @@ -266,7 +266,11 @@ Matrix box, double rotation, std::list<text_renderer::string>& lst); - // Build an svg text element from a list of parsed strings. + // Build an svg text element from a list of parsed strings + std::string format_svg_element (std::string str, Matrix bbox, + double rotation, ColumnVector coord_pix, + Matrix color); + std::string strlist_to_svg (double x, double y, double z, Matrix box, double rotation, std::list<text_renderer::string>& lst); @@ -283,6 +287,7 @@ double fontsize; std::string fontname; bool buffer_overflow; + std::size_t m_svg_def_index; }; static bool @@ -487,8 +492,7 @@ error ("gl2ps_renderer::draw: internal pipe error"); } } - else if (! header_found - && term.find ("svg") != std::string::npos) + else if (term.find ("svg") != std::string::npos) { // FIXME: gl2ps uses pixel units for SVG format. // Modify resulting svg to use points instead. @@ -496,7 +500,7 @@ // make header_found true for SVG if gl2ps is fixed. std::string srchstr (str); size_t pos = srchstr.find ("px"); - if (pos != std::string::npos) + if (! header_found && pos != std::string::npos) { header_found = true; srchstr[pos+1] = 't'; // "px" -> "pt" @@ -794,20 +798,134 @@ } std::string + gl2ps_renderer::format_svg_element (std::string str, Matrix box, + double rotation, ColumnVector coord_pix, + Matrix color) + { + // Extract <defs> elements and change their id to avoid conflict with + // defs coming from another svg string + std::string::size_type n1 = str.find ("<defs>"); + if (n1 == std::string::npos) + return std::string (); + + std::string id, new_id; + n1 = str.find ("<path", ++n1); + std::string::size_type n2; + + while (n1 != std::string::npos) + { + // Extract the identifier id='identifier' + n1 = str.find ("id='", n1) + 4; + n2 = str.find ("'", n1); + id = str.substr (n1, n2-n1); + + new_id = std::to_string (m_svg_def_index) + "-" + id ; + + str.replace (n1, n2-n1, new_id); + + std::string::size_type n_ref = str.find ("#" + id); + + while (n_ref != std::string::npos) + { + str.replace (n_ref + 1, id.length (), new_id); + n_ref = str.find ("#" + id); + } + + n1 = str.find ("<path", n1); + } + + m_svg_def_index++; + + n1 = str.find ("<defs>"); + n2 = str.find ("</defs>") + 7; + + std::string defs = str.substr (n1, n2-n1); + + // Extract the group containing the <use> elements and transform its + // coordinates using the bbox and coordinates info. + + // Extract the original viewBox anchor + n1 = str.find ("viewBox='") + 9; + if (n1 == std::string::npos) + return std::string (); + + n2 = str.find (" ", n1); + double original_x0 = std::stod (str.substr (n1, n2-n1)); + + n1 = n2+1; + n2 = str.find (" ", n1); + double original_y0 = std::stod (str.substr (n1, n2-n1)); + + // First look for local transform in the original svg + std::string orig_trans; + n1 = str.find ("<g id='page1' transform='"); + if (n1 != std::string::npos) + { + n1 += 25; + n2 = str.find ("'", n1); + orig_trans = str.substr (n1, n2-n1); + n1 = n2 + 1; + } + else + { + n1 = str.find ("<g id='page1'"); + n1 += 13; + } + + n2 = str.find ("</g>", n1) + 4; + + // The first applied transformation is the right-most + // 1* Apply original transform + std::string tform = orig_trans; + + // 2* Move the anchor to the final position + tform = std::string ("translate") + + "(" + std::to_string (box(0) - original_x0 + coord_pix(0)) + + "," + std::to_string (-(box(3) + box(1)) - original_y0 + coord_pix(1)) + + ") " + tform; + + // 3* Rotate around the final position + if (rotation != 0) + tform = std::string ("rotate") + + "(" + std::to_string (-rotation) + + "," + std::to_string (coord_pix(0)) + + "," + std::to_string (coord_pix(1)) + + ") " + tform; + + // Fill color + std::string fill = "fill='rgb(" + + std::to_string (static_cast<uint8_t> (color(0) * 255.0)) + "," + + std::to_string (static_cast<uint8_t> (color(1) * 255.0)) + "," + + std::to_string (static_cast<uint8_t> (color(2) * 255.0)) + ")' "; + + std::string use_group = "<g " + + fill + + "transform='" + tform + "'" + + str.substr (n1, n2-n1); + + return defs + "\n" + use_group; + } + + std::string gl2ps_renderer::strlist_to_svg (double x, double y, double z, Matrix box, double rotation, std::list<text_renderer::string>& lst) { + //Use pixel coordinates to conform to gl2ps + ColumnVector coord_pix = get_transform ().transform (x, y, z, false); + if (lst.empty ()) return ""; - //Use pixel coordinates to conform to gl2ps - ColumnVector coord_pix = get_transform ().transform (x, y, z, false); + // This may already be an svg image. + std::string svg = lst.front ().get_svg_element (); + if (! svg.empty ()) + return format_svg_element (svg, box, rotation, coord_pix, + lst.front ().get_color ()); + // Rotation and translation are applied to the whole group std::ostringstream os; - os << R"(<text xml:space="preserve" )"; - - // Rotation and translation are applied to the whole text element + os << R"(<g xml:space="preserve" )"; os << "transform=\"" << "translate(" << coord_pix(0) + box(0) << "," << coord_pix(1) - box(1) << ") rotate(" << -rotation << "," << -box(0) << "," << box(1) @@ -826,10 +944,10 @@ << "font-size=\"" << size << "\">"; - // build a tspan for each element in the strlist + // Build a text element for each element in the strlist for (p = lst.begin (); p != lst.end (); p++) { - os << "<tspan "; + os << "<text "; if (name.compare (p->get_family ())) os << "font-family=\"" << p->get_family () << "\" "; @@ -882,9 +1000,9 @@ os << chr.str (); } } - os << "</tspan>"; + os << "</text>"; } - os << "</text>"; + os << "</g>"; return os.str (); } @@ -894,6 +1012,26 @@ Matrix box, double rotation, std::list<text_renderer::string>& lst) { + if (lst.empty ()) + return ""; + else if (lst.size () == 1) + { + static bool warned = false; + // This may be an svg image, not handled in native eps format. + if (! lst.front ().get_svg_element ().empty ()) + { + if (! warned) + { + warned = true; + warning_with_id ("Octave:print:unhandled-svg-content", + "print: unhandled LaTeX strings. " + "Use -svgconvert option or -d*latex* output " + "device."); + } + return ""; + } + } + // Translate and rotate coordinates in order to use bottom-left alignment fix_strlist_position (x, y, z, box, rotation, lst); Matrix prev_color (1, 3, -1);