1(***********************************************************************)
2(*                                                                     *)
3(*                          HEVEA                                      *)
4(*                                                                     *)
5(*  Luc Maranget, projet PARA, INRIA Rocquencourt                      *)
6(*                                                                     *)
7(*  Copyright 1998 Institut National de Recherche en Informatique et   *)
8(*  Automatique.  Distributed only by permission.                      *)
9(*                                                                     *)
10(***********************************************************************)
11
12let header = "$Id: htmlMath.ml,v 1.49 2012-06-05 14:55:39 maranget Exp $"
13
14
15open Misc
16open Parse_opts
17open HtmlCommon
18open MyStack
19
20
21
22let delay_stack = MyStack.create "delay_stack"
23(* delaying output .... *)
24
25let delay f =
26  if !verbose > 2 then prerr_flags "=> delay" ;
27  push stacks.s_vsize flags.vsize ;
28  flags.vsize <- 0;
29  push delay_stack f ;
30  open_block DELAY "" ;
31  if !verbose > 2 then prerr_flags "<= delay"
32
33let flush x =
34  if !verbose > 2 then
35    prerr_flags ("=> flush arg is ``"^string_of_int x^"''");
36  try_close_block DELAY ;
37  let ps,_,pout = pop_out out_stack in
38  if ps <> DELAY then
39    raise (Misc.Fatal ("html: Flush attempt on: "^string_of_block ps)) ;
40  let mods = as_envs !cur_out.active !cur_out.pending in
41  do_close_mods () ;
42  let old_out = !cur_out in
43  cur_out := pout ;
44  let f = pop delay_stack in
45  f x ;
46  Out.copy old_out.out !cur_out.out ;
47  flags.empty <- false ; flags.blank <- false ;
48  !cur_out.pending <- mods ;
49  flags.vsize <- max (pop stacks.s_vsize) flags.vsize ;
50  if !verbose > 2 then
51    prerr_flags "<= flush"
52
53(* put functions *)
54
55let put  = HtmlCommon.put
56and put_char = HtmlCommon.put_char
57
58let put_in_math s =
59  if flags.in_pre && !pedantic then
60    put s
61  else begin
62    put "<i>";
63    put s;
64    put "</i>";
65    flags.empty <- false; flags.blank <- false;
66  end
67
68(*----------*)
69(* DISPLAYS *)
70(*----------*)
71
72let display_cell_arg tdarg =
73  let arg =
74    if !displayverb then
75      "class=\"vdcell\""
76    else
77      "class=\"dcell\"" in
78  match tdarg with
79    | "" -> arg
80    | _  -> arg ^ " " ^ tdarg
81
82let open_display_cell tdarg = open_block TD (display_cell_arg tdarg)
83
84let begin_item_display f is_freeze =
85  if !verbose > 2 then begin
86    Printf.fprintf stderr "begin_item_display: ncols=%d empty=%s" flags.ncols (sbool flags.empty) ;
87    prerr_newline ()
88  end ;
89  open_display_cell "" ;
90  open_block DFLOW "" ;
91  if is_freeze then freeze f
92
93
94and end_item_display () =
95  let f,is_freeze = pop_freeze () in
96  let _ = close_flow_loc check_empty DFLOW in
97  if close_flow_loc check_empty TD then
98    flags.ncols <- flags.ncols + 1;
99  if !verbose > 2 then begin
100    Printf.fprintf stderr "end_item_display: ncols=%d stck: " flags.ncols;
101    pretty_stack out_stack
102  end;
103  flags.vsize,f,is_freeze
104
105(********************************************************
106                                                         *                                                      *
107                                                         *    To open display with vertical alignment arguments  *
108                                                         *                                                       *
109*********************************************************)
110
111let open_display_varg centering varg =
112  if !verbose > 2 then begin
113    Printf.fprintf stderr "open_display: "
114  end ;
115  try_open_display () ;
116  open_block (DISPLAY centering) varg ;
117  open_display_cell "" ;
118  open_block DFLOW "" ;
119  if !verbose > 2 then begin
120    pretty_cur !cur_out ;
121    prerr_endline ""
122  end
123
124
125(*
126  let open_display_varg_harg centering varg harg =
127  if !verbose > 2 then begin
128  Printf.fprintf stderr "open_display: "
129  end ;
130  try_open_display () ;
131  open_block (DISPLAY centering) (varg^harg);
132  open_display_cell "" ;
133  open_block DFLOW "" ;
134  if !verbose > 2 then begin
135  pretty_cur !cur_out ;
136  prerr_endline ""
137  end
138*)
139
140let open_display centering = open_display_varg centering "style=\"vertical-align:middle\""
141
142(* argument force forces the display structure,
143   when false, the TABLE/TR/TD may be spared in two situation
144   1. No display cell at all (n=0)
145   2. One display cell, one empty cell *)
146let close_display force =
147  if !verbose > 2 then begin
148    prerr_flags "=> close_display " ; pretty_stack out_stack ;
149    Out.debug stderr !cur_out.out
150  end ;
151  if not (flush_freeze ()) then begin
152    close_flow DFLOW ;
153    if !verbose > 3 then begin
154      Printf.eprintf "Just closed DFLOW " ;  pretty_stack out_stack ;
155      Out.debug stderr !cur_out.out
156    end ;
157    let n = flags.ncols in
158    if !verbose > 2 then
159      Printf.fprintf stderr "=> close_display, ncols=%d\n" n ;
160    if (n = 0 && not flags.blank && not force) then begin
161      if !verbose > 2 then begin
162        prerr_string "No Display n=0" ;
163        (Out.debug stderr !cur_out.out);
164        prerr_endline ""
165      end;
166      let active = !cur_out.active and pending = !cur_out.pending in
167      do_close_mods () ;
168      let ps,_,_pout = pop_out out_stack in
169      if ps <> TD then
170        failclose "close_display" ps TD ;
171      do_close_mods () ;
172      try_close_block TD ;
173      let ps,_,ppout = pop_out out_stack in
174      begin match ps with
175        | DISPLAY _ -> ()
176        | _ ->
177            failclose "close_display" ps (DISPLAY false)
178      end;
179      try_close_block ps ;
180      let old_out = !cur_out in
181      cur_out := ppout ;
182      do_close_mods () ;
183      Out.copy old_out.out !cur_out.out ;
184      flags.empty <- false ; flags.blank <- false ;
185      !cur_out.pending <- as_envs active pending
186    end else if (n=1 && flags.blank && not force) then begin
187      if !verbose > 2 then begin
188        prerr_string "No display n=1";
189        (Out.debug stderr !cur_out.out);
190        prerr_endline "" ;
191      end;
192      close_flow FORGET ;
193      let active = !cur_out.active and pending = !cur_out.pending in
194      let ps,_,pout = pop_out out_stack in
195      begin match ps with
196        | DISPLAY _ -> ()
197        | _ ->
198            failclose "close_display" ps (DISPLAY false)
199      end ;
200      try_close_block ps ;
201      let old_out = !cur_out in
202      cur_out := pout ;
203      do_close_mods () ;
204      Out.copy_no_tag old_out.out !cur_out.out ;
205      flags.empty <- false ; flags.blank <- false ;
206      !cur_out.pending <- as_envs active pending
207    end else begin
208      if !verbose > 2 then begin
209        prerr_string ("One Display n="^string_of_int n) ;
210        (Out.debug stderr !cur_out.out);
211        prerr_endline ""
212      end;
213      flags.empty <- flags.blank ;
214      close_flow TD ;
215      close_flow (DISPLAY false)
216    end ;
217    try_close_display ()
218  end ;
219  if !verbose > 2 then
220    prerr_flags ("<= close_display")
221
222
223let do_item_display force =
224  if !verbose > 2 then begin
225    prerr_endline ("Item Display ncols="^string_of_int flags.ncols^" table_inside="^sbool flags.table_inside^", force="^sbool force) ;
226    pretty_stack out_stack
227  end ;
228  if (force && not flags.empty) || flags.table_inside then begin
229    let f,is_freeze = pop_freeze () in
230    push stacks.s_saved_inside
231      (pop stacks.s_saved_inside || flags.table_inside) ;
232    flags.table_inside <- false ;
233    let active  = !cur_out.active
234    and pending = !cur_out.pending in
235    flags.ncols <- flags.ncols + 1 ;
236    close_flow DFLOW ;
237    close_flow TD ;
238    if !verbose > 2 then begin
239      prerr_endline "Added Item to Display" ;
240      Out.debug stderr !cur_out.out ;
241    end;
242    open_display_cell "" ;
243    open_block DFLOW "" ;
244    !cur_out.pending <- as_envs active pending ;
245    !cur_out.active <- [] ;
246    if is_freeze then push out_stack (Freeze f)
247  end else begin
248    if !verbose > 2 then begin
249      Out.debug stderr !cur_out.out ;
250      prerr_endline "No Item" ;
251      prerr_endline ("flags: empty="^sbool flags.empty^" blank="^sbool flags.blank)
252    end
253  end
254
255let item_display () = do_item_display false
256and force_item_display () = do_item_display true
257
258
259let erase_display () =
260  erase_block DFLOW ;
261  erase_block TD ;
262  erase_block (DISPLAY false);
263  try_close_display ()
264
265
266let open_maths display =
267  push stacks.s_in_math flags.in_math;
268  flags.in_math <- true;
269  open_group "";
270  if display then open_display true
271
272let close_maths display =
273  (* force a table in that case, because we want to apply style class *)
274  if display then close_display true ;
275  close_group () ;
276  flags.in_math <- pop stacks.s_in_math
277
278(* vertical display *)
279
280let open_vdisplay center display =
281  if !verbose > 1 then
282    prerr_endline "open_vdisplay";
283  if not display then  raise (Misc.Fatal ("VDISPLAY in non-display mode"));
284  open_block TABLE (display_arg center !verbose)
285
286and close_vdisplay () =
287  if !verbose > 1 then
288    prerr_endline "close_vdisplay";
289  close_block TABLE
290
291and open_vdisplay_row trarg tdarg  =
292  if !verbose > 1 then
293    prerr_endline "open_vdisplay_row";
294  open_block TR trarg ;
295  open_display_cell tdarg ;
296  open_display false
297
298and close_vdisplay_row () =
299  if !verbose > 1 then
300    prerr_endline "close_vdisplay_row";
301  close_display false ;
302  force_block TD "&nbsp;"  ;
303  close_block TR
304
305
306
307(* Sup/Sub stuff *)
308
309let put_sup_sub display scanner (arg : string Lexstate.arg) =
310  if display then open_display false else open_block INTERN "" ;
311  scanner arg ;
312  if display then close_display false else close_block INTERN
313
314let reput_sup_sub tag = function
315  | "" -> ()
316  | s  ->
317      open_block INTERN "" ;
318      clearstyle () ;
319      if not  (flags.in_pre && !pedantic) then begin
320        put_char '<' ;
321        put tag ;
322        put_char '>'
323      end ;
324      put s ;
325      if not  (flags.in_pre && !pedantic) then begin
326        put "</" ;
327        put tag ;
328        put_char '>'
329      end ;
330      close_block INTERN
331
332
333let standard_sup_sub scanner what sup sub display =
334  let sup,fsup =
335    hidden_to_string (fun () -> put_sup_sub display scanner sup)
336  in
337  let sub,fsub =
338    hidden_to_string (fun () -> put_sup_sub display scanner sub) in
339
340  if display && (fsub.table_inside || fsup.table_inside) then begin
341    force_item_display () ;
342    open_vdisplay false display ;
343    if sup <> "" then begin
344      open_vdisplay_row "" "" ;
345      clearstyle () ;
346      put sup ;
347      close_vdisplay_row ()
348    end ;
349    open_vdisplay_row "" "" ;
350    what ();
351    close_vdisplay_row () ;
352    if sub <> "" then begin
353      open_vdisplay_row "" "" ;
354      clearstyle () ;
355      put sub ;
356      close_vdisplay_row ()
357    end ;
358    close_vdisplay () ;
359    force_item_display ()
360  end else begin
361    what ();
362    reput_sup_sub "sub" sub ;
363    reput_sup_sub "sup" sup
364  end
365
366
367let limit_sup_sub scanner what sup sub display =
368  let sup = to_string (fun () -> put_sup_sub display scanner sup)
369  and sub = to_string (fun () -> put_sup_sub display scanner sub) in
370  if sup = "" && sub = "" then
371    what ()
372  else begin
373    force_item_display () ;
374    open_vdisplay false display ;
375    open_vdisplay_row "" "style=\"text-align:center\"" ;
376    put sup ;
377    close_vdisplay_row () ;
378    open_vdisplay_row "" "style=\"text-align:center\"" ;
379    what () ;
380    close_vdisplay_row () ;
381    open_vdisplay_row "" "style=\"text-align:center\"" ;
382    put sub ;
383    close_vdisplay_row () ;
384    close_vdisplay () ;
385    force_item_display ()
386  end
387
388let int_sup_sub something vsize scanner what sup sub display =
389  let sup = to_string (fun () -> put_sup_sub display scanner sup)
390  and sub = to_string (fun () -> put_sup_sub display scanner sub) in
391  if something then begin
392    force_item_display () ;
393    what () ;
394    force_item_display ()
395  end ;
396  if sup <> "" || sub <> "" then begin
397    open_vdisplay false display ;
398    open_vdisplay_row "" "style=\"text-align:left\"" ;
399    put sup ;
400    close_vdisplay_row () ;
401    open_vdisplay_row "" "style=\"text-align:left\"" ;
402    for _i = 2 to vsize do
403      skip_line ()
404    done ;
405    close_vdisplay_row () ;
406    open_vdisplay_row "" "style=\"text-align:left\"" ;
407    put sub ;
408    close_vdisplay_row () ;
409    close_vdisplay () ;
410    force_item_display ()
411  end
412
413
414let insert_vdisplay open_fun =
415  if !verbose > 2 then begin
416    prerr_flags "=> insert_vdisplay" ;
417  end ;
418  try
419    let mods = to_pending !cur_out.pending !cur_out.active in
420    let bs,bargs,bout = pop_out out_stack in
421    if bs <> DFLOW then
422      failclose "insert_vdisplay" bs DFLOW ;
423    let ps,pargs,pout = pop_out out_stack in
424    if ps <> TD then
425      failclose "insert_vdisplay" ps TD ;
426    let pps,ppargs,ppout = pop_out out_stack  in
427    let center =
428      match pps with
429        | DISPLAY b -> b
430        | _ -> failclose "insert_vdisplay" pps (DISPLAY false) in
431    let new_out = create_status_from_scratch false [] in
432    push_out out_stack (DISPLAY false,ppargs,new_out) ;
433    push_out out_stack (ps,pargs,pout) ;
434    push_out out_stack (bs,bargs,bout) ;
435    close_display false ;
436    cur_out := ppout ;
437    let () = open_fun center in (* force bool -> unit' type  *)
438    do_put (Out.to_string new_out.out) ;
439    flags.empty <- false ; flags.blank <- false ;
440    if !verbose > 2 then begin
441      prerr_string "insert_vdisplay -> " ;
442      pretty_mods stderr mods ;
443      prerr_newline ()
444    end ;
445    if !verbose > 2 then
446      prerr_flags "<= insert_vdisplay" ;
447    mods
448  with PopFreeze ->
449    raise (UserError "\\over should be properly parenthesized")
450
451
452let line_in_vdisplay_row () =
453  open_block TR "" ;
454  open_block TD "class=\"hbar\"" ;
455  (*
456    close_mods () ;
457    line_in_table () ;
458  *)
459  force_block TD "" ;
460  force_block TR ""
461
462let over _lexbuf =
463  let mods = insert_vdisplay
464    (fun center ->
465      open_vdisplay center true ;
466      open_vdisplay_row "" "style=\"text-align:center\"") in
467  close_vdisplay_row () ;
468  line_in_vdisplay_row () ;
469  open_vdisplay_row "" "style=\"text-align:center\"" ;
470  close_mods () ;
471  open_mods mods ;
472  freeze
473    (fun () ->
474      close_vdisplay_row () ;
475      close_vdisplay ())
476
477(* Gestion of left and right delimiters *)
478
479let left _ k_delim k =
480  let _,f,is_freeze = end_item_display () in
481  delay
482    (fun vsize ->
483      begin_item_display (fun () -> ()) false ;
484      k_delim vsize ;
485      ignore (end_item_display ()) ;
486      begin_item_display (fun () -> ()) false ;
487      k vsize ;
488      let _ = end_item_display () in
489      ()) ;
490  begin_item_display f is_freeze
491
492let right _ k_delim =
493  let vsize,f,is_freeze = end_item_display () in
494  begin_item_display (fun () -> ()) false ;
495  k_delim vsize;
496  ignore (end_item_display ()) ;
497  flush vsize ;
498  begin_item_display f is_freeze ;
499  vsize
500