30
31:- module(cp_graphviz,
32 [ graphviz_graph//2, 33 reply_graphviz_graph/3 34 ]). 35:- use_module(library(http/http_dispatch)). 36:- use_module(library(http/http_parameters)). 37:- use_module(library(http/http_session)). 38:- use_module(library(http/html_write)). 39:- use_module(library(http/html_head)). 40:- use_module(library(http/http_path)). 41:- use_module(library(process)). 42:- use_module(library(debug)). 43:- use_module(library(option)). 44:- use_module(library(settings)). 45:- use_module(library(semweb/rdf_db)). 46:- use_module(library(semweb/rdf_graphviz)). 47:- use_module(library(http/http_wrapper)). 48
49:- setting(graphviz:format, oneof([svg,canviz]), svg,
50 'Technique to include RDF graphs in a page'). 51
60
61:- html_resource(js('canviz.js'),
62 [ requires([ js('path/path.js'),
63 js('prototype/prototype.js')
64 ])
65 ]). 66:- html_resource(js('path/path.js'),
67 [ requires([ js('prototype/prototype.js')
68 ])
69 ]). 70
73
74:- http_handler(root('graphviz/send_graph'), send_graph, []). 75
105
106:- meta_predicate
107 graphviz_graph(1, :, ?, ?). 108:- dynamic
109 closure/4. 110
111graphviz_graph(_Closure, _:Options) -->
112 { option(render(Renderer), Options, dot),
113 \+ has_graphviz_renderer(Renderer)
114 }, !,
115 no_graph_viz(Renderer).
116graphviz_graph(Closure, Options) -->
117 { setting(graphviz:format, DefFormat),
118 Options = _:PlainOptions,
119 option(format(Format), PlainOptions, DefFormat),
120 meta_options(is_meta, Options, QOptions),
121 variant_sha1(Closure+QOptions, Hash),
122 get_time(Now),
123 assert(closure(Hash, Closure, QOptions, Now)),
124 remove_old_closures(Now)
125 },
126 graphviz_graph_fmt(Format, Hash, QOptions).
127
128
129graphviz_graph_fmt(canviz, Hash, _Options) --> !,
130 { http_link_to_id(send_graph, [hash(Hash)], HREF)
131 },
132 html_requires(js('canviz.js')),
133 html([ div(class(graph),
134 div(id(canviz), [])),
135 div(id(debug_output), []),
136 script(type('text/javascript'),
137 \[ 'document.observe(\'dom:loaded\', function() {\n',
138 ' new Canviz(\'canviz\', \'~w\');\n'-[HREF],
139 '});'
140 ])
141 ]).
142graphviz_graph_fmt(svg, Hash, Options) -->
143 { option(object_attributes(Attrs), Options, []),
144 http_link_to_id(send_graph,
145 [ hash(Hash),
146 lang(svg),
147 target('_top')
148 ], HREF)
149 },
150 html([ object([ data(HREF),
151 type('image/svg+xml')
152 | Attrs
153 ],
154 [])
155 ]).
156
157is_meta(wrap_url).
158is_meta(shape_hook).
159is_meta(bag_shape_hook).
160
161has_graphviz_renderer(Renderer) :-
162 process:exe_options(ExeOptions),
163 absolute_file_name(path(Renderer), _,
164 [ file_errors(fail)
165 | ExeOptions
166 ]).
167
168no_graph_viz(Renderer) -->
169 html(div(id('no-graph-viz'),
170 [ 'The server does not have the graphviz program ',
171 code(Renderer), ' installed in PATH. ',
172 'See ', a(href('http://www.graphviz.org/'),
173 'http://www.graphviz.org/'), ' for details.'
174 ])).
175
181
182send_graph(Request) :-
183 http_parameters(Request,
184 [ hash(Hash,
185 [ description('Hash-key to the graph-data')
186 ]),
187 lang(Lang,
188 [ default(xdot),
189 description('-TXXX option of graphviz')
190 ]),
191 target(Target,
192 [ optional(true),
193 description('Add TARGET= to all links')
194 ])
195 ]),
196 closure(Hash, Closure, Options, _),
197 call(Closure, Graph),
198 reply_graphviz_graph(Graph, Lang, [target(Target)|Options]).
199
200reply_graphviz_graph(_Graph, _Lang, Options) :-
201 option(render(Renderer), Options, dot),
202 \+ has_graphviz_renderer(Renderer), !,
203 http_current_request(Request),
204 http_reply_file(help('error.svg'), [], Request).
205reply_graphviz_graph(Graph, Lang, Options) :-
206 option(target(Target), Options, _),
207 length(Graph, Len),
208 debug(graphviz, 'Graph contains ~D triples', [Len]),
209 select_option(render(Renderer), Options, GraphOptions0, dot),
210 target_option(Target, GraphOptions0, GraphOptions),
211 atom_concat('-T', Lang, GraphLang),
212 process_create(path(Renderer), [GraphLang],
213 [ stdin(pipe(ToDOT)),
214 stdout(pipe(XDotOut)),
215 process(PID)
216 ]),
217 set_stream(ToDOT, encoding(utf8)),
218 set_stream(XDotOut, encoding(utf8)),
219 thread_create(send_to_dot(Graph, GraphOptions, ToDOT), _,
220 [ detached(true) ]),
221 call_cleanup(load_structure(stream(XDotOut),
222 SVGDom0,
223 [ dialect(xml) ]),
224 ( process_wait(PID, _Status),
225 close(XDotOut)
226 )),
227 rewrite_sgv_dom(SVGDom0, SVGDom),
228 graph_mime_type(Lang, ContentType),
229 format('Content-type: ~w~n~n', [ContentType]),
230 xml_write(current_output, SVGDom,
231 [ layout(false)
232 ]).
233
234rewrite_sgv_dom([element(svg, Attrs, Content)],
235 [element(svg, Attrs,
236 [ element(script, ['xlink:href'=SVGPan], []),
237 element(g, [ id=viewport
238 ],
239 Content)
240 ])]) :-
241 http_absolute_location(js('SVGPan.js'), SVGPan, []).
242rewrite_sgv_dom(DOM, DOM).
243
244
245target_option(Target, GraphOptions0, GraphOptions) :-
246 ( nonvar(Target)
247 -> GraphOptions = [target(Target)|GraphOptions0]
248 ; GraphOptions = GraphOptions0
249 ).
250
251
252graph_mime_type(xdot, 'text/plain; charset=UTF-8') :- !.
253graph_mime_type(svg, 'image/svg+xml; charset=UTF-8') :- !.
254graph_mime_type(Lang, 'text/plain; charset=UTF-8') :-
255 print_message(warning,
256 format('Do not know content-type for grapviz \c
257 language ~w. Please extend graph_mime_type/2',
258 Lang)).
259
260send_to_dot(Graph, Options, Out) :-
261 ( debugging(dot)
262 -> retractall(user:graphviz(_,_)),
263 assert(user:graphviz(Graph, Options))
264 ; true
265 ),
266 call_cleanup(gviz_write_rdf(Out, Graph, Options),
267 close(Out)), !.
268
269copy_graph_data(Out) :-
270 debugging(graphviz), !,
271 get_code(Out, C0),
272 copy_graph_data(C0, Out).
273copy_graph_data(Out) :-
274 copy_stream_data(Out, current_output).
275
276copy_graph_data(-1, _) :- !.
277copy_graph_data(C, Stream) :-
278 put_code(C),
279 put_code(user_error, C),
280 get_code(Stream, C2),
281 copy_graph_data(C2, Stream).
282
283
287
288remove_old_closures(Time) :-
289 ( closure(Hash, _, _, Stamp),
290 Time > Stamp+900,
291 retract(closure(Hash, _, _, Stamp)),
292 fail
293 ; true
294 )