Source file latex.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
(** *)
module XR = Xtmpl.Rewrite
module Xml = Xtmpl.Xml
let gensym = let cpt = ref 0 in fun () -> incr cpt; !cpt;;
let cache = Hashtbl.create 111;;
let latex_code_use_packages l =
String.concat ""
(List.map (fun s -> Printf.sprintf "\\usepackage%s\n" s) l)
;;
let build_preambule ~packages ~defs ~def_files =
Printf.sprintf "\\pagestyle{empty}\n\\usepackage[utf8]{inputenc}\n%s\n%s\n%s"
(latex_code_use_packages packages)
(String.concat "\n"
(List.map (fun file -> "\\input{"^file^"}") def_files)
)
defs
;;
let get_latex_defs stog env =
let (stog, def_files) =
let (stog, xmls) = Engine.get_in_env stog env ("", "latex-defs-files") in
match xmls with
[] ->
(stog, [])
| _ ->
let s =
match xmls with
[XR.D s] -> s.Xtmpl.Types.text
| _ ->
let msg = "Invalid filenames: "^(XR.to_string xmls) in
failwith msg
in
let files = List.map Stog_base.Misc.strip_string
(Stog_base.Misc.split_string s [',' ; ';'])
in
let files = List.filter ((<>) "") files in
match files with
[] -> (stog, files)
| _ ->
let (stog, path) = Engine.get_path stog env in
let (_, doc) = Types.doc_by_path stog path in
let dir = Filename.dirname doc.Types.doc_src in
let f filename =
if Filename.is_relative filename
then Filename.concat dir filename
else filename
in
(stog, List.map f files)
in
let (stog, defs) =
let (stog, xmls) = Engine.get_in_env stog env ("", "latex-defs") in
let defs = match xmls with [] -> "" | _ -> XR.to_string xmls in
(stog, defs)
in
(stog, defs, def_files)
;;
let make_svg outdir ?(packages=[]) ?(scale=1.1) ?(def_files=[]) ?defs latex_code =
let defs = match defs with None -> "" | Some s -> s^"\n" in
try Hashtbl.find cache latex_code
with Not_found ->
let tex = Filename.temp_file "stog" ".tex" in
let tex_aux = Filename.chop_extension tex ^ ".aux" in
let tex_log = Filename.chop_extension tex ^ ".log" in
let code = Printf.sprintf
"\\documentclass[12pt]{article}
%s
\\begin{document}
%s
\\end{document}
"
(build_preambule ~packages ~defs ~def_files)
latex_code
in
let base = Filename.chop_extension tex in
let dvi = base^".dvi" in
let svg = Filename.concat outdir
(Filename.basename (Printf.sprintf "_latex%d.svg" (gensym())))
in
Stog_base.Misc.file_of_string ~file: tex code;
let log = Filename.temp_file "stog" ".log" in
let command = Printf.sprintf
"(latex -output-directory=%s -interaction=batchmode %s > %s 2>&1) && \
dvisvgm -e --scale=%f -M 1.5 --no-fonts %s -s 2>> %s > %s"
(Filename.get_temp_dir_name () |> Filename.quote)
(Filename.quote tex) (Filename.quote log)
scale
(Filename.quote dvi)
(Filename.quote log) (Filename.quote svg)
in
match Sys.command command with
0 ->
List.iter (fun f -> try Sys.remove f with _ -> ())
[ tex ; tex_aux ; tex_log ; dvi ; log ];
Hashtbl.add cache latex_code svg;
svg
| n ->
let log = Stog_base.Misc.string_of_file log in
List.iter (fun f -> try Sys.remove f with _ -> ())
[ tex_aux ; tex_log ; log ];
failwith
(Printf.sprintf "Command failed [%d]: %s\n=== log ===\n%s\n=== tex code ===\n%s"
n command log latex_code)
;;
let code_of_subs =
let f b = function
XR.D code -> Buffer.add_string b code.Xtmpl.Types.text
| xml -> failwith (Printf.sprintf "Invalid latex code: %s" (XR.to_string [xml]))
in
function
| [ XR.D code] -> code.Xtmpl.Types.text
| subs ->
let b = Buffer.create 256 in
List.iter (f b) subs;
Buffer.contents b
;;
let get_packages stog env args =
let (stog, s) =
match XR.get_att_cdata args ("","packages") with
Some s -> (stog, s)
| None ->
let (stog, xmls) = Engine.get_in_args_or_env stog env args ("","latex-packages") in
match xmls with
[XR.D s] -> (stog, s.Xtmpl.Types.text)
| _ -> (stog, "")
in
let l = List.map Stog_base.Misc.strip_string (Stog_base.Misc.split_string s [';']) in
(stog, l)
;;
let fun_latex stog env ?loc args subs =
let code = code_of_subs subs in
let (stog, packages) = get_packages stog env args in
let showcode = XR.opt_att_cdata args ("", "showcode") = "true" in
let (stog, defs, def_files) = get_latex_defs stog env in
let (stog, scale) =
let (stog, xmls) = Engine.get_in_env stog env ("", "latex-svg-scale") in
let scale =
match xmls with
[] -> None
| _ ->
let s = XR.to_string xmls in
try Some (float_of_string s)
with _ -> failwith (Printf.sprintf "Invalid latex-svg-scale %S" s)
in
(stog, scale)
in
let svg = Filename.basename
(make_svg stog.Types.stog_outdir ~packages ?scale ~def_files ~defs code)
in
let url = Url.concat stog.Types.stog_base_url svg in
let xmls =
(XR.node ("","img")
~atts:
(XR.atts_of_list
[ ("", "class"), [XR.cdata "latex"] ;
("", "src"), [XR.cdata (Url.to_string url) ] ;
("", "alt"), [XR.cdata code] ;
("", "title"), [XR.cdata code]
])
[]
) ::
(match showcode with
false -> []
| true ->
[ XR.node ("","hcode")
~atts: (XR.atts_one ("","lang") [XR.cdata "tex"])
[XR.cdata code]
]
)
in
(stog, xmls)
;;
let fun_latex_body stog env ?loc args subs =
let (stog, packages) = get_packages stog env args in
let (stog, defs, def_files) = get_latex_defs stog env in
let code = code_of_subs subs in
let code =
(build_preambule ~packages ~defs ~def_files)^
"\n\\begin{document}"^code^"\\end{document}"
in
let (stog, path) = Engine.get_path stog env in
let (_, doc) = Types.doc_by_path stog path in
let doc_dir = Filename.dirname doc.Types.doc_src in
let (stog, xmls) = Engine.get_in_env stog env ("","sectionning") in
let sectionning =
match xmls with
[XR.D { Xtmpl.Types.text } ] ->
List.map Stog_base.Misc.strip_string
(Stog_base.Misc.split_string text [','])
| _ -> Tags.default_sectionning
in
let params = {
prefix = None ;
ext_file_prefix = "" ;
envs = [] ;
Of_latex.sectionning = sectionning ;
image_sizes = Of_latex.SMap.empty ;
}
in
let (tex,_) = Of_latex.parse params code doc_dir in
(stog, Of_latex.to_xml tex.Of_latex.body)
;;