(*
* Formatting to HTML documents.
*
* ----------------------------------------------------------------
*
* @begin[license]
* Copyright (C) 2004 Mojave Group, Caltech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation,
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Additional permission is given to link this library with the
* OpenSSL project's "OpenSSL" library, and with the OCaml runtime,
* and you may distribute the linked executables. See the file
* LICENSE.libmojave for more details.
*
* Author: Jason Hickey
* @email{jyh@cs.caltech.edu}
* @end[license]
*)
open Lm_rformat_raw
open Lm_rformat
(*
* Kinds of input.
*)
type data =
VisibleString of string
| InvisibleString of string
| AtomicString of string
(*
* We hack the indentation in the HTML printer.
* Format the data into lines, and print the tabstops in
* invisible mode.
*
* The prefix is the white space for the left margin.
*)
type html_buffer =
{ html_line : data Queue.t;
mutable html_column : int;
mutable html_prefix : data list;
mutable html_spacer : string;
html_print_string : string -> unit;
html_print_newline : unit -> unit
}
(*
* Tagging functions.
*)
type html_tagger_fun =
NoTagger
| StringTagger of string
| FunTagger of (string -> string)
type html_tagger_pair =
{ html_tag_begin : html_tagger_fun;
html_tag_end : html_tagger_fun
}
type html_tagger = html_tagger_pair option
(*
* Have to escape special characters.
*)
let html_escape_string buffer s =
let len = String.length s in
let rec collect i j =
if j != len then
match s.[j] with
'<' ->
collect_escape i j "<"
| '>' ->
collect_escape i j ">"
| '&' ->
collect_escape i j "&"
| '"' ->
collect_escape i j """
| _ ->
collect i (succ j)
else if i = 0 then
Buffer.add_string buffer s
else if i < j then
Buffer.add_substring buffer s i (j - i)
and collect_escape i j s' =
if i < j then
Buffer.add_substring buffer s i (j - i);
Buffer.add_string buffer s';
collect (succ j) (succ j)
in
collect 0 0
(*
* For external use.
*)
let escape s =
let buf = Buffer.create 16 in
html_escape_string buf s;
Buffer.contents buf
(*
* Extract the entire line.
*)
let html_line buf =
let buffer = Buffer.create 100 in
Queue.iter (function
VisibleString s ->
html_escape_string buffer s
| InvisibleString s
| AtomicString s ->
Buffer.add_string buffer s) buf.html_line;
Buffer.contents buffer
let html_push_line buf =
let line = html_line buf in
buf.html_print_string line;
Queue.clear buf.html_line
let html_flush buf =
html_push_line buf
(*
* Get the spacer from the prefix.
*)
let html_spacer buf =
let buffer = Buffer.create 16 in
Buffer.add_string buffer "";
List.iter (fun item ->
match item with
VisibleString s ->
html_escape_string buffer s
| InvisibleString _ ->
()
| AtomicString s ->
Buffer.add_string buffer s) buf.html_prefix;
Buffer.add_string buffer "";
Buffer.contents buffer
(*
* Get a new prefix buffer.
* The entire line is (buf.html_prefix @ buf.html_current_line).
* Build the line, and truncate it.
*)
let html_prefix buf col =
let { html_column = cur;
html_prefix = prefix;
html_line = line
} = buf
in
(* Add the visible elements in the current line to the prefix *)
let prefix =
if col < cur then
prefix
else
List.rev (Queue.fold (fun prefix item ->
match item with
VisibleString _
| AtomicString _ ->
item :: prefix
| InvisibleString _ ->
prefix) (List.rev prefix) line)
in
(* Truncate the prefix to the current column *)
let rec collect prefix cur items =
match items with
item :: items ->
(match item with
VisibleString s ->
let len = String.length s in
let next = cur + len in
if next <= col then
collect (item :: prefix) next items
else
let s = String.sub s 0 (col - cur) in
VisibleString s :: prefix
| InvisibleString _ ->
collect prefix cur items
| AtomicString _ ->
let next = succ cur in
if next <= col then
collect (item :: prefix) next items
else
prefix)
| [] ->
if cur < col then
VisibleString (String.make (col - cur) 'm') :: prefix
else
prefix
in
buf.html_column <- col;
buf.html_prefix <- List.rev (collect [] 0 prefix);
buf.html_spacer <- html_spacer buf
(*
* Newline.
*
* The col is the new *absolute* tabstop.
*
* Compute the new tabstop prefix, then push the line,
* and save the new tabstop.
*)
let html_tab buf (col, _) _ =
if col = 0 then
begin
html_push_line buf;
buf.html_print_newline ();
buf.html_print_string "
\n";
buf.html_column <- 0;
buf.html_prefix <- [];
buf.html_spacer <- ""
end
else
let spacer =
if col <> buf.html_column then
html_prefix buf col;
buf.html_spacer
in
html_push_line buf;
buf.html_print_newline ();
buf.html_print_string "
\n";
buf.html_print_string spacer
(*
* Print strings.
*)
let html_print_string buf s =
Queue.add (VisibleString s) buf.html_line
let html_print_invis buf s =
Queue.add (InvisibleString s) buf.html_line
let html_print_atomic buf s =
Queue.add (AtomicString s) buf.html_line
let html_tag tagger buf =
match tagger with
Some { html_tag_begin = FunTagger tagger } ->
(fun s -> Queue.add (InvisibleString (tagger s)) buf.html_line)
| Some { html_tag_begin = StringTagger tagger } ->
(fun _s -> Queue.add (InvisibleString tagger) buf.html_line)
| Some { html_tag_begin = NoTagger }
| None ->
(fun _s -> ())
let html_etag tagger buf =
match tagger with
Some { html_tag_end = FunTagger tagger } ->
(fun s -> Queue.add (InvisibleString (tagger s)) buf.html_line)
| Some { html_tag_end = StringTagger tagger } ->
(fun _s -> Queue.add (InvisibleString tagger) buf.html_line)
| Some { html_tag_end = NoTagger }
| None ->
(fun _s -> ())
(*
* An HTML printer.
*)
let make_html_printer_aux tagger raw =
let { raw_print_string = output_string;
raw_print_newline = output_newline
} = raw
in
let print_string s =
output_string s 0 (String.length s)
in
let buf =
{ html_line = Queue.create ();
html_column = 0;
html_prefix = [];
html_spacer = "";
html_print_string = print_string;
html_print_newline = output_newline
}
in
let info =
{ print_string = html_print_string buf;
print_invis = html_print_invis buf;
print_atomic = html_print_atomic buf;
print_tab = html_tab buf;
print_begin_tag = html_tag tagger buf;
print_end_tag = html_etag tagger buf
}
in
buf, info
let make_html_printer tagger raw =
snd (make_html_printer_aux tagger raw)
let print_html_raw rmargin tagger buf raw =
let hbuf, info = make_html_printer_aux tagger raw in
print_to_printer buf rmargin info;
html_flush hbuf;
raw.raw_print_flush ()
let print_html_channel rmargin tagger buf out =
print_html_raw rmargin tagger buf (raw_channel_printer out)
let print_html_buffer rmargin tagger buf out =
print_html_raw rmargin tagger buf (raw_buffer_printer out)
let print_html_string rmargin tagger buf =
let out = Buffer.create 100 in
print_html_buffer rmargin tagger buf out;
Buffer.contents out
(*!
* @docoff
*
* -*-
* Local Variables:
* Caml-master: "compile"
* End:
* -*-
*)