This reverts commit 8b6cc53cf024f03370d2cd5c417cb7294339cad0
[ikiwiki.git] / doc / plugins / asymptote.mdwn
1 [[!template id=plugin name=asymptote author="[[PeterSimons]]"]]
2 [[!tag type/widget]]
3
4 This plugin provides the [[ikiwiki/directive/asymptote]]
5 [[ikiwiki/directive]] which allows embedding
6 [asymptote](http://asymptote.sourceforge.net/) diagrams in a page.
7
8 Security implications: asymptote has functions for reading files and
9 other dangerous stuff, so enabling this plugin means that everyone who
10 can edit your Wiki can also read any file from your hard drive thats
11 accessible to the user running Ikiwiki. 
12
13 [[!if test="enabled(asymptote)" then="""
14 An example diagram:
15
16 [[!asymptote src="""
17 import geometry;
18 unitsize(1cm);
19 triangle t = triangle((0,0), (4,0), (0.5,2));
20 show(La="$D$", Lb="$E$", Lc="", t);
21 dot(t.A^^t.B^^t.C);
22 point pD = midpoint(t.BC); dot(pD);
23 point pE = midpoint(t.AC); dot(pE);
24 draw(pD--pE);
25
26 point A_ = (pD-t.A)*2+t.A; dot("$A'$", A_, NE);
27 draw(t.B--A_--t.C, dashed);
28 draw(t.A--A_, dashed);
29
30 point E_ = midpoint(line(t.B,A_)); dot(Label("$E'$", E_, E));
31 draw(E_--pD, dashed);
32 """]]
33 """]]
34
35 This plugin uses the [[!cpan Digest::SHA]] perl module.
36
37 The full source code is:
38
39         #! /usr/bin/perl
40
41         package IkiWiki::Plugin::asymptote;
42         use warnings;
43         use strict;
44         use Digest::MD5 qw(md5_hex);
45         use File::Temp qw(tempdir);
46         use HTML::Entities;
47         use Encode;
48         use IkiWiki 3.00;
49
50         sub import {
51                 hook(type => "getsetup", id => "asymptote", call => \&getsetup);
52                 hook(type => "preprocess", id => "asymptote", call => \&preprocess);
53         }
54
55         sub getsetup () {
56                 return
57                         plugin => {
58                                 safe => 1,
59                                 rebuild => undef,
60                                 section => "widget",
61                         },
62         }
63
64         sub preprocess (@) {
65                 my %params = @_;
66
67                 my $code = $params{src};
68                 if (! defined $code && ! length $code) {
69                         error gettext("missing src attribute");
70                 }
71                 return create($code, \%params);
72         }
73
74         sub create ($$$) {
75                 # This function calls the image generating function and returns
76                 # the <img .. /> for the generated image.
77                 my $code = shift;
78                 my $params = shift;
79
80                 my $digest = md5_hex(Encode::encode_utf8($code));
81
82                 my $imglink= $params->{page} . "/$digest.png";
83                 my $imglog =  $params->{page} .  "/$digest.log";
84                 will_render($params->{page}, $imglink);
85                 will_render($params->{page}, $imglog);
86
87                 my $imgurl=urlto($imglink, $params->{destpage});
88                 my $logurl=urlto($imglog, $params->{destpage});
89
90                 if (-e "$config{destdir}/$imglink" ||
91                     gen_image($code, $digest, $params->{page})) {
92                         return qq{<img src="$imgurl}
93                                 .(exists $params->{alt} ? qq{" alt="} . $params->{alt} : qq{})
94                                 .qq{" class="asymptote" />};
95                 }
96                 else {
97                         error qq{<a href="$logurl">}.gettext("failed to generate image from code")."</a>";
98                 }
99         }
100
101         sub gen_image ($$$$) {
102                 # Actually creates the image.
103                 my $code = shift;
104                 my $digest = shift;
105                 my $imagedir = shift;
106
107                 my $tmp = eval { create_tmp_dir($digest) };
108                 if (! $@ &&
109                     writefile("$digest.asy", $tmp, $code) &&
110                     writefile("$imagedir/$digest.png", $config{destdir}, "") &&
111                     system("asy -render=2 -offscreen -f png -o $config{destdir}/$imagedir/$digest.png $tmp/$digest.asy &>$tmp/$digest.log") == 0
112                    ) {
113                         return 1;
114                 }
115                 else {
116                         # store failure log
117                         my $log="";
118                         {
119                                 if (open(my $f, '<', "$tmp/$digest.log")) {
120                                         local $/=undef;
121                                         $log = <$f>;
122                                         close($f);
123                                 }
124                         }
125                         writefile("$digest.log", "$config{destdir}/$imagedir", $log);
126
127                         return 0;
128                 }
129         }
130
131         sub create_tmp_dir ($) {
132                 # Create a temp directory, it will be removed when ikiwiki exits.
133                 my $base = shift;
134
135                 my $template = $base.".XXXXXXXXXX";
136                 my $tmpdir = tempdir($template, TMPDIR => 1, CLEANUP => 1);
137                 return $tmpdir;
138         }
139
140         1
141