cpdf-source/cpdfocg.ml
2022-01-04 17:17:37 +00:00

135 lines
5.9 KiB
OCaml

open Pdfutil
(* 1. Get list of indirects of all OCGs from the /OCProperties, and their textual names
* 2. Calculate a change list to coalesce them
* 3. Remove any changed ones from the /OCGs and /Order and /ON and /OFF in /OCProperties
* 4. Do the changes to all indirect references in the whole pdf *)
(*FIXME Pre-existing nulls - what to do? *)
let ocg_coalesce pdf =
match Pdf.lookup_direct pdf "/OCProperties" (Pdf.catalog_of_pdf pdf) with
None -> ()
| Some ocpdict ->
let number_name_pairs =
match Pdf.lookup_direct pdf "/OCGs" ocpdict with
Some (Pdf.Array ocgs) ->
begin let numbers =
map (function Pdf.Indirect i -> i | _ -> failwith "Malformed /OCG entry") ocgs
in
let names =
map
(fun i ->
try
begin match Pdf.lookup_obj pdf i with
Pdf.Dictionary d ->
begin match Pdf.lookup_direct pdf "/Name" (Pdf.Dictionary d) with
Some (Pdf.String s) -> s
| _ -> failwith "ocg: missing name"
end
| _ ->
failwith "ocg: not a dictionary"
end
with _ -> failwith "OCG object missing")
numbers
in
combine numbers names
end
| _ -> failwith "Malformed or missing /OCGs"
in
(*iter (fun (num, name) -> Printf.printf "%i = %s\n" num name) number_name_pairs;*)
let changes =
let cf (_, name) (_, name') = compare name name' in
let sets = collate cf (List.stable_sort cf number_name_pairs) in
flatten (option_map (function [] -> None | (hnum, _)::t -> Some (map (function (tnum, _) -> (tnum, hnum)) t)) sets)
in
(*Printf.printf "\nChanges are:\n";
List.iter (fun (f, t) -> Printf.printf "%i -> %i\n" f t) changes;*)
let new_ocproperties =
let remove_from_array key nums dict =
match Pdf.lookup_direct pdf key dict with
| Some (Pdf.Array elts) ->
let elts' = option_map (function Pdf.Indirect i -> if mem i nums then None else Some (Pdf.Indirect i) | _ -> None) elts in
Pdf.add_dict_entry dict key (Pdf.Array elts')
| _ -> dict
in
let remove_from_array_inside_d key nums dict =
match Pdf.lookup_direct pdf "/D" dict with
| Some (Pdf.Dictionary ddict) ->
begin match Pdf.lookup_direct pdf key (Pdf.Dictionary ddict) with
| Some (Pdf.Array elts) ->
let elts' = option_map (function Pdf.Indirect i -> if mem i nums then None else Some (Pdf.Indirect i) | _ -> None) elts in
Pdf.add_dict_entry dict "/D" (Pdf.add_dict_entry (Pdf.Dictionary ddict) key (Pdf.Array elts'))
| _ -> dict
end
| _ -> failwith "No /D dict in OCGProperties"
in
let nums = map fst changes in
(*Printf.printf "\nto remove:\n";
List.iter (Printf.printf "%i ") nums;*)
remove_from_array "/OCGs" nums
(remove_from_array_inside_d "/ON" nums
(remove_from_array_inside_d "/OFF" nums
(remove_from_array_inside_d "/Order" nums ocpdict)))
in
(*flprint (Pdfwrite.string_of_pdf new_ocproperties);*)
let ocp_objnum = Pdf.addobj pdf new_ocproperties in
let new_catalog = Pdf.addobj pdf (Pdf.add_dict_entry (Pdf.catalog_of_pdf pdf) "/OCProperties" (Pdf.Indirect ocp_objnum)) in
pdf.Pdf.trailerdict <- Pdf.add_dict_entry pdf.Pdf.trailerdict "/Root" (Pdf.Indirect new_catalog);
pdf.Pdf.root <- new_catalog;
Pdf.objselfmap (Pdf.renumber_object_parsed pdf (hashtable_of_dictionary changes)) pdf
let ocg_get_list pdf =
let l = ref [] in
begin match Pdf.lookup_direct pdf "/OCProperties" (Pdf.catalog_of_pdf pdf) with
None -> ()
| Some ocpdict ->
match Pdf.lookup_direct pdf "/OCGs" ocpdict with
Some (Pdf.Array elts) ->
iter
(function
Pdf.Indirect i ->
(match Pdf.lookup_direct pdf "/Name" (Pdf.lookup_obj pdf i) with
Some (Pdf.String s) -> l := s::!l | _ -> ())
| _ -> ())
elts
| _ -> ()
end;
rev !l
let ocg_list pdf =
List.iter (Printf.printf "%s\n") (map Pdftext.utf8_of_pdfdocstring (ocg_get_list pdf))
let ocg_rename f t pdf =
Pdf.objselfmap
(function
Pdf.Dictionary d ->
begin match Pdf.lookup_direct pdf "/Type" (Pdf.Dictionary d) with
Some (Pdf.Name "/OCG") ->
begin match Pdf.lookup_direct pdf "/Name" (Pdf.Dictionary d) with
Some (Pdf.String s) when s = f ->
Pdf.add_dict_entry (Pdf.Dictionary d) "/Name" (Pdf.String t)
| _ -> Pdf.Dictionary d
end
| _ -> Pdf.Dictionary d
end
| x -> x
)
pdf
let ocg_order_all pdf =
match Pdf.lookup_direct pdf "/OCProperties" (Pdf.catalog_of_pdf pdf) with
None -> ()
| Some ocpdict ->
match Pdf.lookup_direct pdf "/OCGs" ocpdict with
Some (Pdf.Array elts) ->
begin match Pdf.lookup_direct pdf "/D" ocpdict with
Some (Pdf.Dictionary d) ->
let newd = Pdf.add_dict_entry (Pdf.Dictionary d) "/Order" (Pdf.Array elts) in
let new_ocproperties = Pdf.add_dict_entry ocpdict "/D" newd in
let ocp_objnum = Pdf.addobj pdf new_ocproperties in
let new_catalog = Pdf.addobj pdf (Pdf.add_dict_entry (Pdf.catalog_of_pdf pdf) "/OCProperties" (Pdf.Indirect ocp_objnum)) in
pdf.Pdf.trailerdict <- Pdf.add_dict_entry pdf.Pdf.trailerdict "/Root" (Pdf.Indirect new_catalog);
pdf.Pdf.root <- new_catalog
| _ -> ()
end
| _ -> ()