Saving SVGtree

Hi,

I wanted to save a copy of a tree generated with the svg-tree renderer. Not seeing an option and as it also did not seem to work locally (within swipl, I thought it might have an option to output to file) I. copied the generated SVG from the website source code, and with a corresponding header pasted it into an empty file. This mostly worked, however it seems that the text of all the endpoints/terminals is either not included or not rendered. Tested in both Affinity Designer and Inkscape. Does anybody else know more and might have a solution?
In the discussion “Truncated image rendering with svgtree in swish” it was suggested to use a graphviz renderer instead, but SVG is just more portable, so any ideas would be welcome.

Possibly.

If SWISH is a requirement then I can not be of any practical help. If using SWI-Prolog from the top level is allowed then I might be of assistance.

For generating graphics depicting graphs (node/edge) which includes trees, SVG is nice but the image is static. A better solution I have found is to use Cytoscape.js, which should not to be confused with Cystoscape, that allows one to interact with the image and data. Granted it is a much greater level of complexity but for certain needs worth the effort.

See: Wiki Discussion: Generating Cytoscape.js graphs with SWI-Prolog. It gives a working example but the example is still far from any I would use in a production setting.

Can you give more clarity to

  1. The goal, e.g. display a tree based on Prolog facts and the facts are simply edge(From,To).
  2. The constraints (these are just examples, please create a new set from scratch)
    a. Can only use SWISH
    b. Must be able to display with an internet browser
    c. The image only needs to be static.
    d. Prefer to avoid transformation applications like GraphViz and Cytoscape.js
    e. Generate output format using only SWI-Prolog
    f. Can make use of html//1
    g. Can make use of SWI-Prolog packs

GraphViz can generate SVG. I have done this often in the past but have moved away from GraphViz a few years ago as I prefer dynamic interaction to static displays.

See this post for an example of an SVG generated using GraphViz which was based on Prolog facts.

1 Like

Thanks for your reply. Using swipl/swi-prolog is also fine. In principle I do not mind explorable data, however I still also have need for fixed graphs so that they can be included in publications.

  1. The goal, e.g. display a tree based on Prolog facts and the facts are simply edge(From,To) .

The purpose right now is to display a parse tree from a DCG, consisting of atoms/facts or atoms and lists. These can get quite big, so for bigger ones, an interactive one might be very nice. The facts are like node([node(otherfact), node(somefact).... node([...])...]

The constraints (these are just examples, please create a new set from scratch)

  • Should be able to output also to a fixed format, ideally to latex or a format like png or svg that can be inserted into a word processor.
  • Ideally output using only SWI-Prolog or at least have minimal additional complexity.

Thanks. I have at least a basic representation from graphiz to SVG for now. The other post was helpful after all.

This looks like an adjacency list. If so the SWI-Prolog library ugraphs might be of use. I find it more useful for verifying the correctness of constructed graphs than any other use.


There is an SWI-Prolog pack that notes it works with LaTeX but I have not used it. sldnfdraw. While not directly useful the source code might be a source of ideas.

The data is there, but it is not visible.
Seems that the foreignObject tags do not render properly in a standalone SVG document.

I have tried to give the span tag the attribute (i.e. <span xmlns="http://www.w3.org/1999/xhtml">_1592</span>) but it doesn’t work.

I would suggest to simplify the document, using your favorite text processor to change the combination <foreignObject><span>PAYLOAD</span></foreignObject> to <text>PAYLOAD</text>:

      <polyline class="branch" points="21.4583,49 59.3833,35"></polyline>
      <foreignObject width="38.91667175292969" height="21" class="tree-label" x="2px" y="49px">
        <span xmlns="http://www.w3.org/1999/xhtml">_1592</span>
      </foreignObject>

to

      <polyline class="branch" points="21.4583,49 59.3833,35"></polyline>
      <text x="2px" y="49px">
        _1592
      </text>

A simple DCG that performs the transformation:

/*  File:    foreignObject_to_text.pl
    Author:  Carlo,,,
    Created: Aug 28 2021
    Purpose: see https://swi-prolog.discourse.group/t/saving-svgtree/4343
*/

:- module(foreignObject_to_text,
          [transform/0,
           transform/2
          ]).

:- use_module(library(dcg/basics)).
:- use_module(library(debug)).

transform :-
    transform('~/swish-rendered.svg', '~/modified.svg').

transform(S,T) :-
    maplist(expand_file_name,[S,T],[[Se],[Te]]),
    open(Te,write,Ts),
    (   phrase_from_file(foreignObject_to_text(Ts),Se)
    ->  true
    ;   writeln('something goes wrong')
    ),
    close(Ts).

foreignObject_to_text(_) --> [].
foreignObject_to_text(Ts) -->
    foreignObject(Payload, Attrs),
    {memberchk(x=X,Attrs),
     memberchk(y=Y,Attrs),
     format(Ts,'<text x=~s y=~s dx="0" dy="10">~s</text>', [X,Y,Payload]),
     debug(foreignObject_to_text,'transformed ~q', [(Payload,X,Y)])
    },
    !, foreignObject_to_text(Ts).
foreignObject_to_text(Ts) -->
    [C],
    {put(Ts,C)},
    foreignObject_to_text(Ts).

foreignObject(Payload, Attrs) -->
    blanks,
    "<foreignObject", attrs(Attrs), ">",
    span(Payload,_),
    "</foreignObject>",
    blanks.

span(Payload, Attrs) -->
    {debug(foreignObject_to_text,in_span,[])},
    blanks,
    "<span", attrs(Attrs), ">",
    blanks,
    string(Payload),
    "</span>",
    blanks,
    {debug(foreignObject_to_text,'out_span ~q',[span(Attrs,Payload)])}.

attrs([]) --> [].
attrs([A|Attrs]) -->
    attr(A),
    attrs(Attrs).

attr(Key=Value) -->
    whites,
    key(Key_),
    "=",
    value(Value_),
    {maplist(atom_codes,[Key,Value],[Key_,Value_])}.

key(Key) --> string(Key).
value(Value) -->
    string_without([0' ,0'>],Value).

The dy attribute should be computed, right now it’s hardcoded to 10