-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathcpdfocg.ml
134 lines (129 loc) · 5.91 KB
/
cpdfocg.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
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
| _ -> ()