(* This file is part of licht. licht is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. licht 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 General Public License for more details. You should have received a copy of the GNU General Public License along with licht. If not, see . *) module T = Tools module NS = Odf_ns let u = UTF8.from_utf8string type t let load_xml catalog input = begin let source = Xmlm.make_input ~enc:(Some `UTF_8) (`Channel input) in let sheet = OdfLoader.load catalog source in sheet end let load catalog file = let tmp_file = Filename.temp_file "content" ".xml" in Unix.unlink tmp_file; let zip = Zip.open_in file in let content = Zip.find_entry zip "content.xml" in Zip.copy_entry_to_file zip content tmp_file; let input = open_in_bin tmp_file in Tools.try_finally (fun () -> load_xml catalog input) (fun () -> close_in input; Unix.unlink tmp_file; Zip.close_in zip ) let write_type ovalue_type cvalue_type attrs output value = begin let attrs = (NS.ovalue_type_attr, ovalue_type):: (NS.cvalue_type_attr, cvalue_type):: attrs in Xmlm.output output (`El_start (NS.table_cell_node, attrs)); Xmlm.output output (`El_start (NS.text_node, [])); Xmlm.output output (`Data value); Xmlm.output output `El_end; Xmlm.output output `El_end; end (* Writers for differents types *) let write_num = write_type "float" "float" let write_str = write_type "string" "string" let write_bool = write_type "bool" "bool" let write_error = write_type "string" "error" let write_date = write_type "date" "date" module BasicWriter = ScTypes.Type.Eval(struct type 'a t = (Xmlm.attribute list -> Xmlm.output -> unit) type 'a obs = 'a t let str s attrs output = write_str attrs output (UTF8.to_utf8string s) let bool b attrs output = write_bool attrs output (string_of_bool b) let num n attrs output = let f = DataType.Num.to_float n in let value = string_of_float f in write_num ((NS.value_attr, value)::attrs) output value let date d attrs output = let value = DataType.Date.to_string d in write_date ((NS.date_value_attr, value)::attrs) output value let observe value attrs output = value attrs output end) let write_basic: type a. 'b list -> Xmlm.output -> a ScTypes.Type.t -> unit = fun attrs output types -> BasicWriter.eval types attrs output let write_formula output attrs f = begin function | ScTypes.Result.Ok x -> write_basic attrs output x | ScTypes.Result.Error exn -> write_str attrs output "#NAME?" end (** Print a reference *) module Show_ref = struct type 'a t = UTF8.Buffer.buffer -> unit type 'a obs = UTF8.Buffer.buffer -> unit let cell t buffer = UTF8.Buffer.add_string buffer @@ u"[."; UTF8.Buffer.add_string buffer @@ Cell.to_string t; UTF8.Buffer.add_string buffer @@ u"]" let range c1 c2 buffer = UTF8.Buffer.add_string buffer @@ u"[."; UTF8.Buffer.add_string buffer @@ Cell.to_string c1; UTF8.Buffer.add_string buffer @@ u":."; UTF8.Buffer.add_string buffer @@ Cell.to_string c2; UTF8.Buffer.add_string buffer @@ u"]" let observe elem buffer = elem buffer end module Show_type = struct type 'a t = UTF8.Buffer.buffer -> unit type 'a obs = UTF8.Buffer.buffer -> unit let str s buffer = UTF8.Printf.bprintf buffer "\"%s\"" (UTF8.to_utf8string s) let num n buffer = let f = DataType.Num.to_float n in UTF8.Buffer.add_string buffer @@ u(string_of_float f) let date n buffer = DataType.Date.to_string n |> u |> UTF8.Buffer.add_string buffer let bool b buffer = UTF8.Buffer.add_string buffer @@ u(string_of_bool b) let observe elem buffer = elem buffer end module ExpressionPrinter = ScTypes.Expr.Eval(Show_expr.Show_Expr(Show_ref)(Show_type)) let write_cell output value = begin function | Expression.Undefined -> () | Expression.Basic b -> write_basic [] output b | Expression.Formula (Expression.Expression f) -> let buffer = UTF8.Buffer.create 10 in ExpressionPrinter.eval f buffer; let formula = UTF8.Buffer.contents buffer |> UTF8.to_utf8string in write_formula output [(NS.formula_attr, ("of:=" ^formula))] f value | Expression.Formula (Expression.Error (i, s)) -> write_error [(NS.formula_attr, ("of:" ^ (UTF8.to_utf8string s)))] output (UTF8.to_utf8string s) end (** Jump to the wanted position *) let goto output (from_x, from_y) (to_x, to_y) :unit = begin (** Insert as many rows than required *) let insert_rows count = begin (* Close the previous openend rows *) Xmlm.output output `El_end; if (count > 1) then ( Xmlm.output output ( `El_start ( NS.table_row_node, [(NS.table_row_repeat_attr, string_of_int (count-1))])); Xmlm.output output (`El_start (NS.table_cell_node, [])); Xmlm.output output `El_end; Xmlm.output output `El_end; ); Xmlm.output output (`El_start (NS.table_row_node, [])); 1 end (** Insert as many cells as required *) and insert_cells count = begin Xmlm.output output ( `El_start ( NS.table_cell_node, [(NS.number_columns_repeat_attr, string_of_int count)])); Xmlm.output output `El_end; end in (* Insert empty rows or columns until the desired position *) let jump_row = to_y - from_y in let from_x' = if jump_row > 0 then insert_rows jump_row else from_x in let jump_cell = to_x - from_x' in if jump_cell > 0 then insert_cells jump_cell end (** Write the cell content and return the updated position *) let f output cursor position (expr, value) = begin goto output cursor position; (* Write the value *) write_cell output value expr; (* Return the update position *) Tools.Tuple2.map1 ((+) 1) position end let save sheet file = begin let tmp_file = Filename.temp_file "content" ".xml" in Unix.unlink tmp_file; let out_channel = open_out_bin tmp_file in let zip = Zip.open_out file in let manifest = "\ \ \ " in Tools.try_finally (fun () -> let output = Xmlm.make_output (`Channel out_channel) in Xmlm.output output (`Dtd None); Xmlm.output output (`El_start (NS.document_content_node, NS.name_spaces())); Xmlm.output output (`El_start (NS.body_node, [])); Xmlm.output output (`El_start (NS.spreadsheet_node, [])); Xmlm.output output (`El_start (NS.table_node, [])); Xmlm.output output (`El_start (NS.table_row_node, [])); ignore (Sheet.fold (f output) (1,1) sheet); Xmlm.output output `El_end; Xmlm.output output `El_end; Xmlm.output output `El_end; Xmlm.output output `El_end; Xmlm.output output `El_end; close_out out_channel; Zip.copy_file_to_entry tmp_file zip "content.xml"; Zip.add_entry manifest zip "META-INF/manifest.xml" ) (fun () -> Zip.close_out zip; Unix.unlink tmp_file ) end