Split off a little code
This commit is contained in:
parent
1377c5af83
commit
5ae9ffe25f
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
||||||
# Build the cpdf command line tools and top level
|
# Build the cpdf command line tools and top level
|
||||||
MODS = cpdfstream tjutil tjutf16 tjllist tjparsermonad tjjson \
|
MODS = cpdfstream tjutil tjutf16 tjllist tjparsermonad tjjson \
|
||||||
xmlm cpdfjson cpdfstrftime cpdfcoord \
|
xmlm cpdferror cpdfjson cpdfstrftime cpdfcoord cpdfattach \
|
||||||
cpdfpagespec cpdfposition cpdf cpdfcommand
|
cpdfpagespec cpdfposition cpdf cpdfcommand
|
||||||
|
|
||||||
SOURCES = $(foreach x,$(MODS),$(x).ml $(x).mli) cpdfcommandrun.ml
|
SOURCES = $(foreach x,$(MODS),$(x).ml $(x).mli) cpdfcommandrun.ml
|
||||||
|
|
229
cpdf.ml
229
cpdf.ml
|
@ -1,6 +1,7 @@
|
||||||
(* CPDF Core routines *)
|
(* CPDF Core routines *)
|
||||||
open Pdfutil
|
open Pdfutil
|
||||||
open Pdfio
|
open Pdfio
|
||||||
|
open Cpdferror
|
||||||
|
|
||||||
let debug = ref false
|
let debug = ref false
|
||||||
|
|
||||||
|
@ -434,12 +435,6 @@ let protect fast pdf resources content =
|
||||||
let qs = addstream (many Pdfops.Op_Q deficit @ [Pdfops.Op_Q]) in
|
let qs = addstream (many Pdfops.Op_Q deficit @ [Pdfops.Op_Q]) in
|
||||||
[Pdf.Indirect q] @ content @ [Pdf.Indirect qs]
|
[Pdf.Indirect q] @ content @ [Pdf.Indirect qs]
|
||||||
|
|
||||||
exception SoftError of string
|
|
||||||
|
|
||||||
let error s = raise (SoftError s)
|
|
||||||
|
|
||||||
exception HardError of string
|
|
||||||
|
|
||||||
(* Union two resource dictionaries from the same PDF. *)
|
(* Union two resource dictionaries from the same PDF. *)
|
||||||
let combine_pdf_resources pdf a b =
|
let combine_pdf_resources pdf a b =
|
||||||
let a_entries =
|
let a_entries =
|
||||||
|
@ -532,228 +527,6 @@ let presentation range t d h i dir effect_dur pdf =
|
||||||
in
|
in
|
||||||
Pdfpage.change_pages true pdf pages'
|
Pdfpage.change_pages true pdf pages'
|
||||||
|
|
||||||
(* Attaching files *)
|
|
||||||
let attach_file ?memory keepversion topage pdf file =
|
|
||||||
let data =
|
|
||||||
match memory with
|
|
||||||
Some data -> data
|
|
||||||
| None ->
|
|
||||||
let ch = open_in_bin file in
|
|
||||||
let len = in_channel_length ch in
|
|
||||||
let stream = mkbytes len in
|
|
||||||
let i = input_of_channel ch in
|
|
||||||
setinit i stream 0 len;
|
|
||||||
close_in ch;
|
|
||||||
stream
|
|
||||||
in
|
|
||||||
let filestream =
|
|
||||||
Pdf.Stream
|
|
||||||
(ref (Pdf.Dictionary
|
|
||||||
[("/Length", Pdf.Integer (bytes_size data));
|
|
||||||
("/Type", Pdf.Name "/EmbeddedFile");
|
|
||||||
("/Params",
|
|
||||||
Pdf.Dictionary
|
|
||||||
[("/Size", Pdf.Integer (bytes_size data));
|
|
||||||
("/CheckSum", Pdf.String (Digest.string (string_of_bytes data)))
|
|
||||||
])],
|
|
||||||
Pdf.Got data))
|
|
||||||
in
|
|
||||||
let filestream_num = Pdf.addobj pdf filestream in
|
|
||||||
let basename = Pdftext.pdfdocstring_of_utf8 (Filename.basename file) in
|
|
||||||
let filespec =
|
|
||||||
Pdf.Dictionary
|
|
||||||
[("/EF", Pdf.Dictionary ["/F", Pdf.Indirect filestream_num]);
|
|
||||||
("/F", Pdf.String basename);
|
|
||||||
("/Type", Pdf.Name "/Filespec");
|
|
||||||
("/Desc", Pdf.String "");
|
|
||||||
("/UF", Pdf.String basename)]
|
|
||||||
in
|
|
||||||
match topage with
|
|
||||||
| None ->
|
|
||||||
(* Look up /Names and /EmbeddedFiles and /Names. *)
|
|
||||||
let rootdict = Pdf.lookup_obj pdf pdf.Pdf.root in
|
|
||||||
let namedict =
|
|
||||||
match Pdf.lookup_direct pdf "/Names" rootdict with
|
|
||||||
| None -> Pdf.Dictionary []
|
|
||||||
| Some namedict -> namedict
|
|
||||||
in
|
|
||||||
let embeddednamedict =
|
|
||||||
match Pdf.lookup_direct pdf "/EmbeddedFiles" namedict with
|
|
||||||
| None -> Pdf.Dictionary []
|
|
||||||
| Some embeddednamedict -> embeddednamedict
|
|
||||||
in
|
|
||||||
let elts =
|
|
||||||
match Pdf.lookup_direct pdf "/Names" embeddednamedict with
|
|
||||||
| Some (Pdf.Array elts) -> elts
|
|
||||||
| _ -> []
|
|
||||||
in
|
|
||||||
let filespecobj = Pdf.addobj pdf filespec in
|
|
||||||
let names' = Pdf.Array (elts @ [Pdf.String basename; Pdf.Indirect filespecobj]) in
|
|
||||||
let embeddednamedict' = Pdf.add_dict_entry embeddednamedict "/Names" names' in
|
|
||||||
let namedict' = Pdf.add_dict_entry namedict "/EmbeddedFiles" embeddednamedict' in
|
|
||||||
let rootdict' = Pdf.add_dict_entry rootdict "/Names" namedict' in
|
|
||||||
let rootnum = Pdf.addobj pdf rootdict' in
|
|
||||||
{pdf with
|
|
||||||
Pdf.minor = if keepversion then pdf.Pdf.minor else max pdf.Pdf.minor 4;
|
|
||||||
Pdf.root = rootnum;
|
|
||||||
Pdf.trailerdict =
|
|
||||||
Pdf.add_dict_entry
|
|
||||||
pdf.Pdf.trailerdict "/Root" (Pdf.Indirect rootnum)}
|
|
||||||
| Some pagenumber ->
|
|
||||||
let pages = Pdfpage.pages_of_pagetree pdf in
|
|
||||||
if pagenumber < 0 || pagenumber > length pages then error "attach_file: Page not found" else
|
|
||||||
let page = select pagenumber pages in
|
|
||||||
let annots =
|
|
||||||
match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
|
||||||
| Some (Pdf.Array annots) -> annots
|
|
||||||
| _ -> []
|
|
||||||
in
|
|
||||||
let rect =
|
|
||||||
let minx, miny, maxx, maxy = Pdf.parse_rectangle page.Pdfpage.mediabox in
|
|
||||||
Pdf.Array [Pdf.Real 18.; Pdf.Real (maxy -. 45.); Pdf.Real 45.; Pdf.Real (maxy -. 18.)]
|
|
||||||
in
|
|
||||||
let filespecobj = Pdf.addobj pdf filespec in
|
|
||||||
let annot =
|
|
||||||
Pdf.Dictionary
|
|
||||||
[("/FS", Pdf.Indirect filespecobj);
|
|
||||||
("/Subtype", Pdf.Name "/FileAttachment");
|
|
||||||
("/Contents", Pdf.String basename);
|
|
||||||
("/Rect", rect)]
|
|
||||||
in
|
|
||||||
let annots' = Pdf.Array (annot::annots) in
|
|
||||||
let page' =
|
|
||||||
{page with Pdfpage.rest = Pdf.add_dict_entry page.Pdfpage.rest "/Annots" annots'}
|
|
||||||
in
|
|
||||||
let pages' = replace_number pagenumber page' pages in
|
|
||||||
let pdf = Pdfpage.change_pages true pdf pages' in
|
|
||||||
{pdf with
|
|
||||||
Pdf.minor = if keepversion then pdf.Pdf.minor else max pdf.Pdf.minor 4}
|
|
||||||
|
|
||||||
type attachment =
|
|
||||||
{name : string;
|
|
||||||
pagenumber : int;
|
|
||||||
data : unit -> Pdfio.bytes}
|
|
||||||
|
|
||||||
let list_attached_files pdf =
|
|
||||||
let toplevel =
|
|
||||||
match Pdf.lookup_direct pdf "/Root" pdf.Pdf.trailerdict with
|
|
||||||
| None -> []
|
|
||||||
| Some rootdict ->
|
|
||||||
match Pdf.lookup_direct pdf "/Names" rootdict with
|
|
||||||
| None -> []
|
|
||||||
| Some namedict ->
|
|
||||||
match Pdf.lookup_direct pdf "/EmbeddedFiles" namedict with
|
|
||||||
| Some nametree ->
|
|
||||||
map
|
|
||||||
(function (x, ef) ->
|
|
||||||
match Pdf.lookup_direct pdf "/EF" ef with
|
|
||||||
| Some ((Pdf.Dictionary _) as d) ->
|
|
||||||
begin match Pdf.lookup_direct pdf "/F" d with
|
|
||||||
| Some stream ->
|
|
||||||
{name = x;
|
|
||||||
pagenumber = 0;
|
|
||||||
data =
|
|
||||||
(fun () ->
|
|
||||||
try
|
|
||||||
Pdf.getstream stream;
|
|
||||||
Pdfcodec.decode_pdfstream pdf stream;
|
|
||||||
match stream with
|
|
||||||
Pdf.Stream {contents = (_, Pdf.Got data)} -> data
|
|
||||||
| _ -> raise Not_found
|
|
||||||
with
|
|
||||||
_ -> raise (Pdf.PDFError "could not retreive attachment data"))}
|
|
||||||
| None -> raise (Pdf.PDFError "/F not found")
|
|
||||||
end
|
|
||||||
| _ -> raise (Pdf.PDFError "/EF not found"))
|
|
||||||
(option_map
|
|
||||||
(function (Pdf.String s, ef) -> Some (s, ef) | _ -> None)
|
|
||||||
(Pdf.contents_of_nametree pdf nametree))
|
|
||||||
| _ -> []
|
|
||||||
in let pagelevel =
|
|
||||||
let pages = Pdfpage.pages_of_pagetree pdf in
|
|
||||||
flatten
|
|
||||||
(map2
|
|
||||||
(fun page pagenumber ->
|
|
||||||
option_map
|
|
||||||
(function annot ->
|
|
||||||
match Pdf.lookup_direct pdf "/Subtype" annot with
|
|
||||||
| Some (Pdf.Name "/FileAttachment") ->
|
|
||||||
(match Pdf.lookup_direct pdf "/Contents" annot with
|
|
||||||
| Some (Pdf.String s) ->
|
|
||||||
begin match Pdf.lookup_direct pdf "/FS" annot with
|
|
||||||
| Some ((Pdf.Dictionary _) as d) ->
|
|
||||||
(*Printf.eprintf "%s\n%!" (Pdfwrite.string_of_pdf d);*)
|
|
||||||
begin match Pdf.lookup_direct pdf "/EF" d with
|
|
||||||
| Some ((Pdf.Dictionary _) as d) ->
|
|
||||||
begin match Pdf.lookup_direct pdf "/F" d with
|
|
||||||
| Some stream ->
|
|
||||||
Some
|
|
||||||
{name = s;
|
|
||||||
pagenumber = pagenumber;
|
|
||||||
data =
|
|
||||||
(fun () ->
|
|
||||||
try
|
|
||||||
Pdf.getstream stream;
|
|
||||||
Pdfcodec.decode_pdfstream pdf stream;
|
|
||||||
match stream with
|
|
||||||
Pdf.Stream {contents = (_, Pdf.Got data)} -> data
|
|
||||||
| _ -> raise Not_found
|
|
||||||
with
|
|
||||||
_ -> raise (Pdf.PDFError "could not retreive attachment data"))}
|
|
||||||
| _ -> raise (Pdf.PDFError "no /F found in attachment")
|
|
||||||
end
|
|
||||||
| _ ->
|
|
||||||
Some
|
|
||||||
{name = s;
|
|
||||||
pagenumber = pagenumber;
|
|
||||||
data = (fun () -> raise (Pdf.PDFError "no attachment data"))}
|
|
||||||
end
|
|
||||||
| _ -> None
|
|
||||||
end
|
|
||||||
| _ -> None)
|
|
||||||
| _ -> None)
|
|
||||||
(match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
|
||||||
| Some (Pdf.Array annots) -> annots
|
|
||||||
| _ -> []))
|
|
||||||
pages
|
|
||||||
(indx pages))
|
|
||||||
in
|
|
||||||
toplevel @ pagelevel
|
|
||||||
|
|
||||||
(* \section{Remove Attached files} *)
|
|
||||||
let remove_attached_files_on_pages pdf =
|
|
||||||
let remove_from_page page =
|
|
||||||
{page with Pdfpage.rest =
|
|
||||||
Pdf.add_dict_entry page.Pdfpage.rest "/Annots"
|
|
||||||
(Pdf.Array
|
|
||||||
(option_map
|
|
||||||
(function annot ->
|
|
||||||
match Pdf.lookup_direct pdf "/Subtype" annot with
|
|
||||||
| Some (Pdf.Name "/FileAttachment") -> None
|
|
||||||
| _ -> Some annot)
|
|
||||||
(match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
|
||||||
| Some (Pdf.Array annots) -> annots
|
|
||||||
| _ -> [])))}
|
|
||||||
in
|
|
||||||
Pdfpage.change_pages true pdf (map remove_from_page (Pdfpage.pages_of_pagetree pdf))
|
|
||||||
|
|
||||||
let remove_attached_files pdf =
|
|
||||||
let pdf = remove_attached_files_on_pages pdf in
|
|
||||||
match Pdf.lookup_direct pdf "/Root" pdf.Pdf.trailerdict with
|
|
||||||
| None -> pdf
|
|
||||||
| Some rootdict ->
|
|
||||||
match Pdf.lookup_direct pdf "/Names" rootdict with
|
|
||||||
| None -> pdf
|
|
||||||
| Some namedict ->
|
|
||||||
let namedict' = Pdf.remove_dict_entry namedict "/EmbeddedFiles" in
|
|
||||||
let rootdict' = Pdf.add_dict_entry rootdict "/Names" namedict' in
|
|
||||||
let rootdict'num = Pdf.addobj pdf rootdict' in
|
|
||||||
{pdf with
|
|
||||||
Pdf.root =
|
|
||||||
rootdict'num;
|
|
||||||
Pdf.trailerdict =
|
|
||||||
Pdf.add_dict_entry pdf.Pdf.trailerdict "/Root" (Pdf.Indirect rootdict'num)}
|
|
||||||
|
|
||||||
(* \section{Copy an /ID from one file to another} *)
|
(* \section{Copy an /ID from one file to another} *)
|
||||||
let copy_id keepversion copyfrom copyto =
|
let copy_id keepversion copyfrom copyto =
|
||||||
match Pdf.lookup_direct copyfrom "/ID" copyfrom.Pdf.trailerdict with
|
match Pdf.lookup_direct copyfrom "/ID" copyfrom.Pdf.trailerdict with
|
||||||
|
|
20
cpdf.mli
20
cpdf.mli
|
@ -9,11 +9,6 @@ all - the PDF string is output as-is. [UTF8] converts loslessly to UTF8.
|
||||||
correspond to 7 bit ASCII. *)
|
correspond to 7 bit ASCII. *)
|
||||||
type encoding = Raw | UTF8 | Stripped
|
type encoding = Raw | UTF8 | Stripped
|
||||||
|
|
||||||
exception SoftError of string
|
|
||||||
exception HardError of string
|
|
||||||
(** Two exceptions recommended for use with the library, though currently not
|
|
||||||
raised by any function in this module. Cpdfcommand uses them extensively. *)
|
|
||||||
|
|
||||||
(** {2 Debug} *)
|
(** {2 Debug} *)
|
||||||
|
|
||||||
(** Debug: Print out a PDF in readable form to the terminal *)
|
(** Debug: Print out a PDF in readable form to the terminal *)
|
||||||
|
@ -91,21 +86,6 @@ adds a presentation on the pages in [range]. See cpdfmanual.pdf for details.
|
||||||
val presentation : int list -> string option ->
|
val presentation : int list -> string option ->
|
||||||
float option -> bool -> bool -> int -> float -> Pdf.t -> Pdf.t
|
float option -> bool -> bool -> int -> float -> Pdf.t -> Pdf.t
|
||||||
|
|
||||||
(** {2 File Attachments} *)
|
|
||||||
(** [attach_file keepversion topage pdf filename] attaches the file in [filename] to the pdf, optionally to a page (rather than document-level). If keepversion is true, the PDF version number won't be altered. *)
|
|
||||||
val attach_file : ?memory:Pdfio.bytes -> bool -> int option -> Pdf.t -> string -> Pdf.t
|
|
||||||
|
|
||||||
(** Remove attached files. *)
|
|
||||||
val remove_attached_files : Pdf.t -> Pdf.t
|
|
||||||
|
|
||||||
type attachment =
|
|
||||||
{name : string;
|
|
||||||
pagenumber : int;
|
|
||||||
data : unit -> Pdfio.bytes}
|
|
||||||
|
|
||||||
(** List attached files. Attachment name and page number. Page 0 is document level. *)
|
|
||||||
val list_attached_files : Pdf.t -> attachment list
|
|
||||||
|
|
||||||
(** {2 Bookmarks} *)
|
(** {2 Bookmarks} *)
|
||||||
|
|
||||||
(** [parse_bookmark_file verify pdf input] parses the bookmark file in [input].
|
(** [parse_bookmark_file verify pdf input] parses the bookmark file in [input].
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
open Pdfutil
|
||||||
|
open Pdfio
|
||||||
|
open Cpdferror
|
||||||
|
|
||||||
|
(* Attaching files *)
|
||||||
|
let attach_file ?memory keepversion topage pdf file =
|
||||||
|
let data =
|
||||||
|
match memory with
|
||||||
|
Some data -> data
|
||||||
|
| None ->
|
||||||
|
let ch = open_in_bin file in
|
||||||
|
let len = in_channel_length ch in
|
||||||
|
let stream = mkbytes len in
|
||||||
|
let i = input_of_channel ch in
|
||||||
|
setinit i stream 0 len;
|
||||||
|
close_in ch;
|
||||||
|
stream
|
||||||
|
in
|
||||||
|
let filestream =
|
||||||
|
Pdf.Stream
|
||||||
|
(ref (Pdf.Dictionary
|
||||||
|
[("/Length", Pdf.Integer (bytes_size data));
|
||||||
|
("/Type", Pdf.Name "/EmbeddedFile");
|
||||||
|
("/Params",
|
||||||
|
Pdf.Dictionary
|
||||||
|
[("/Size", Pdf.Integer (bytes_size data));
|
||||||
|
("/CheckSum", Pdf.String (Digest.string (string_of_bytes data)))
|
||||||
|
])],
|
||||||
|
Pdf.Got data))
|
||||||
|
in
|
||||||
|
let filestream_num = Pdf.addobj pdf filestream in
|
||||||
|
let basename = Pdftext.pdfdocstring_of_utf8 (Filename.basename file) in
|
||||||
|
let filespec =
|
||||||
|
Pdf.Dictionary
|
||||||
|
[("/EF", Pdf.Dictionary ["/F", Pdf.Indirect filestream_num]);
|
||||||
|
("/F", Pdf.String basename);
|
||||||
|
("/Type", Pdf.Name "/Filespec");
|
||||||
|
("/Desc", Pdf.String "");
|
||||||
|
("/UF", Pdf.String basename)]
|
||||||
|
in
|
||||||
|
match topage with
|
||||||
|
| None ->
|
||||||
|
(* Look up /Names and /EmbeddedFiles and /Names. *)
|
||||||
|
let rootdict = Pdf.lookup_obj pdf pdf.Pdf.root in
|
||||||
|
let namedict =
|
||||||
|
match Pdf.lookup_direct pdf "/Names" rootdict with
|
||||||
|
| None -> Pdf.Dictionary []
|
||||||
|
| Some namedict -> namedict
|
||||||
|
in
|
||||||
|
let embeddednamedict =
|
||||||
|
match Pdf.lookup_direct pdf "/EmbeddedFiles" namedict with
|
||||||
|
| None -> Pdf.Dictionary []
|
||||||
|
| Some embeddednamedict -> embeddednamedict
|
||||||
|
in
|
||||||
|
let elts =
|
||||||
|
match Pdf.lookup_direct pdf "/Names" embeddednamedict with
|
||||||
|
| Some (Pdf.Array elts) -> elts
|
||||||
|
| _ -> []
|
||||||
|
in
|
||||||
|
let filespecobj = Pdf.addobj pdf filespec in
|
||||||
|
let names' = Pdf.Array (elts @ [Pdf.String basename; Pdf.Indirect filespecobj]) in
|
||||||
|
let embeddednamedict' = Pdf.add_dict_entry embeddednamedict "/Names" names' in
|
||||||
|
let namedict' = Pdf.add_dict_entry namedict "/EmbeddedFiles" embeddednamedict' in
|
||||||
|
let rootdict' = Pdf.add_dict_entry rootdict "/Names" namedict' in
|
||||||
|
let rootnum = Pdf.addobj pdf rootdict' in
|
||||||
|
{pdf with
|
||||||
|
Pdf.minor = if keepversion then pdf.Pdf.minor else max pdf.Pdf.minor 4;
|
||||||
|
Pdf.root = rootnum;
|
||||||
|
Pdf.trailerdict =
|
||||||
|
Pdf.add_dict_entry
|
||||||
|
pdf.Pdf.trailerdict "/Root" (Pdf.Indirect rootnum)}
|
||||||
|
| Some pagenumber ->
|
||||||
|
let pages = Pdfpage.pages_of_pagetree pdf in
|
||||||
|
if pagenumber < 0 || pagenumber > length pages then error "attach_file: Page not found" else
|
||||||
|
let page = select pagenumber pages in
|
||||||
|
let annots =
|
||||||
|
match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
||||||
|
| Some (Pdf.Array annots) -> annots
|
||||||
|
| _ -> []
|
||||||
|
in
|
||||||
|
let rect =
|
||||||
|
let minx, miny, maxx, maxy = Pdf.parse_rectangle page.Pdfpage.mediabox in
|
||||||
|
Pdf.Array [Pdf.Real 18.; Pdf.Real (maxy -. 45.); Pdf.Real 45.; Pdf.Real (maxy -. 18.)]
|
||||||
|
in
|
||||||
|
let filespecobj = Pdf.addobj pdf filespec in
|
||||||
|
let annot =
|
||||||
|
Pdf.Dictionary
|
||||||
|
[("/FS", Pdf.Indirect filespecobj);
|
||||||
|
("/Subtype", Pdf.Name "/FileAttachment");
|
||||||
|
("/Contents", Pdf.String basename);
|
||||||
|
("/Rect", rect)]
|
||||||
|
in
|
||||||
|
let annots' = Pdf.Array (annot::annots) in
|
||||||
|
let page' =
|
||||||
|
{page with Pdfpage.rest = Pdf.add_dict_entry page.Pdfpage.rest "/Annots" annots'}
|
||||||
|
in
|
||||||
|
let pages' = replace_number pagenumber page' pages in
|
||||||
|
let pdf = Pdfpage.change_pages true pdf pages' in
|
||||||
|
{pdf with
|
||||||
|
Pdf.minor = if keepversion then pdf.Pdf.minor else max pdf.Pdf.minor 4}
|
||||||
|
|
||||||
|
type attachment =
|
||||||
|
{name : string;
|
||||||
|
pagenumber : int;
|
||||||
|
data : unit -> Pdfio.bytes}
|
||||||
|
|
||||||
|
let list_attached_files pdf =
|
||||||
|
let toplevel =
|
||||||
|
match Pdf.lookup_direct pdf "/Root" pdf.Pdf.trailerdict with
|
||||||
|
| None -> []
|
||||||
|
| Some rootdict ->
|
||||||
|
match Pdf.lookup_direct pdf "/Names" rootdict with
|
||||||
|
| None -> []
|
||||||
|
| Some namedict ->
|
||||||
|
match Pdf.lookup_direct pdf "/EmbeddedFiles" namedict with
|
||||||
|
| Some nametree ->
|
||||||
|
map
|
||||||
|
(function (x, ef) ->
|
||||||
|
match Pdf.lookup_direct pdf "/EF" ef with
|
||||||
|
| Some ((Pdf.Dictionary _) as d) ->
|
||||||
|
begin match Pdf.lookup_direct pdf "/F" d with
|
||||||
|
| Some stream ->
|
||||||
|
{name = x;
|
||||||
|
pagenumber = 0;
|
||||||
|
data =
|
||||||
|
(fun () ->
|
||||||
|
try
|
||||||
|
Pdf.getstream stream;
|
||||||
|
Pdfcodec.decode_pdfstream pdf stream;
|
||||||
|
match stream with
|
||||||
|
Pdf.Stream {contents = (_, Pdf.Got data)} -> data
|
||||||
|
| _ -> raise Not_found
|
||||||
|
with
|
||||||
|
_ -> raise (Pdf.PDFError "could not retreive attachment data"))}
|
||||||
|
| None -> raise (Pdf.PDFError "/F not found")
|
||||||
|
end
|
||||||
|
| _ -> raise (Pdf.PDFError "/EF not found"))
|
||||||
|
(option_map
|
||||||
|
(function (Pdf.String s, ef) -> Some (s, ef) | _ -> None)
|
||||||
|
(Pdf.contents_of_nametree pdf nametree))
|
||||||
|
| _ -> []
|
||||||
|
in let pagelevel =
|
||||||
|
let pages = Pdfpage.pages_of_pagetree pdf in
|
||||||
|
flatten
|
||||||
|
(map2
|
||||||
|
(fun page pagenumber ->
|
||||||
|
option_map
|
||||||
|
(function annot ->
|
||||||
|
match Pdf.lookup_direct pdf "/Subtype" annot with
|
||||||
|
| Some (Pdf.Name "/FileAttachment") ->
|
||||||
|
(match Pdf.lookup_direct pdf "/Contents" annot with
|
||||||
|
| Some (Pdf.String s) ->
|
||||||
|
begin match Pdf.lookup_direct pdf "/FS" annot with
|
||||||
|
| Some ((Pdf.Dictionary _) as d) ->
|
||||||
|
(*Printf.eprintf "%s\n%!" (Pdfwrite.string_of_pdf d);*)
|
||||||
|
begin match Pdf.lookup_direct pdf "/EF" d with
|
||||||
|
| Some ((Pdf.Dictionary _) as d) ->
|
||||||
|
begin match Pdf.lookup_direct pdf "/F" d with
|
||||||
|
| Some stream ->
|
||||||
|
Some
|
||||||
|
{name = s;
|
||||||
|
pagenumber = pagenumber;
|
||||||
|
data =
|
||||||
|
(fun () ->
|
||||||
|
try
|
||||||
|
Pdf.getstream stream;
|
||||||
|
Pdfcodec.decode_pdfstream pdf stream;
|
||||||
|
match stream with
|
||||||
|
Pdf.Stream {contents = (_, Pdf.Got data)} -> data
|
||||||
|
| _ -> raise Not_found
|
||||||
|
with
|
||||||
|
_ -> raise (Pdf.PDFError "could not retreive attachment data"))}
|
||||||
|
| _ -> raise (Pdf.PDFError "no /F found in attachment")
|
||||||
|
end
|
||||||
|
| _ ->
|
||||||
|
Some
|
||||||
|
{name = s;
|
||||||
|
pagenumber = pagenumber;
|
||||||
|
data = (fun () -> raise (Pdf.PDFError "no attachment data"))}
|
||||||
|
end
|
||||||
|
| _ -> None
|
||||||
|
end
|
||||||
|
| _ -> None)
|
||||||
|
| _ -> None)
|
||||||
|
(match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
||||||
|
| Some (Pdf.Array annots) -> annots
|
||||||
|
| _ -> []))
|
||||||
|
pages
|
||||||
|
(indx pages))
|
||||||
|
in
|
||||||
|
toplevel @ pagelevel
|
||||||
|
|
||||||
|
(* \section{Remove Attached files} *)
|
||||||
|
let remove_attached_files_on_pages pdf =
|
||||||
|
let remove_from_page page =
|
||||||
|
{page with Pdfpage.rest =
|
||||||
|
Pdf.add_dict_entry page.Pdfpage.rest "/Annots"
|
||||||
|
(Pdf.Array
|
||||||
|
(option_map
|
||||||
|
(function annot ->
|
||||||
|
match Pdf.lookup_direct pdf "/Subtype" annot with
|
||||||
|
| Some (Pdf.Name "/FileAttachment") -> None
|
||||||
|
| _ -> Some annot)
|
||||||
|
(match Pdf.lookup_direct pdf "/Annots" page.Pdfpage.rest with
|
||||||
|
| Some (Pdf.Array annots) -> annots
|
||||||
|
| _ -> [])))}
|
||||||
|
in
|
||||||
|
Pdfpage.change_pages true pdf (map remove_from_page (Pdfpage.pages_of_pagetree pdf))
|
||||||
|
|
||||||
|
let remove_attached_files pdf =
|
||||||
|
let pdf = remove_attached_files_on_pages pdf in
|
||||||
|
match Pdf.lookup_direct pdf "/Root" pdf.Pdf.trailerdict with
|
||||||
|
| None -> pdf
|
||||||
|
| Some rootdict ->
|
||||||
|
match Pdf.lookup_direct pdf "/Names" rootdict with
|
||||||
|
| None -> pdf
|
||||||
|
| Some namedict ->
|
||||||
|
let namedict' = Pdf.remove_dict_entry namedict "/EmbeddedFiles" in
|
||||||
|
let rootdict' = Pdf.add_dict_entry rootdict "/Names" namedict' in
|
||||||
|
let rootdict'num = Pdf.addobj pdf rootdict' in
|
||||||
|
{pdf with
|
||||||
|
Pdf.root =
|
||||||
|
rootdict'num;
|
||||||
|
Pdf.trailerdict =
|
||||||
|
Pdf.add_dict_entry pdf.Pdf.trailerdict "/Root" (Pdf.Indirect rootdict'num)}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
(** {2 File Attachments} *)
|
||||||
|
(** [attach_file keepversion topage pdf filename] attaches the file in [filename] to the pdf, optionally to a page (rather than document-level). If keepversion is true, the PDF version number won't be altered. *)
|
||||||
|
val attach_file : ?memory:Pdfio.bytes -> bool -> int option -> Pdf.t -> string -> Pdf.t
|
||||||
|
|
||||||
|
(** Remove attached files. *)
|
||||||
|
val remove_attached_files : Pdf.t -> Pdf.t
|
||||||
|
|
||||||
|
type attachment =
|
||||||
|
{name : string;
|
||||||
|
pagenumber : int;
|
||||||
|
data : unit -> Pdfio.bytes}
|
||||||
|
|
||||||
|
(** List attached files. Attachment name and page number. Page 0 is document level. *)
|
||||||
|
val list_attached_files : Pdf.t -> attachment list
|
|
@ -27,17 +27,17 @@ it had been loaded. *)
|
||||||
let pdfread_pdf_of_input ?revision a b c =
|
let pdfread_pdf_of_input ?revision a b c =
|
||||||
try Pdfread.pdf_of_input ?revision a b c with
|
try Pdfread.pdf_of_input ?revision a b c with
|
||||||
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
||||||
raise (Cpdf.SoftError "Bad owner or user password when reading document")
|
raise (Cpdferror.SoftError "Bad owner or user password when reading document")
|
||||||
|
|
||||||
let pdfread_pdf_of_channel_lazy ?revision ?source b c d =
|
let pdfread_pdf_of_channel_lazy ?revision ?source b c d =
|
||||||
try Pdfread.pdf_of_channel_lazy ?revision ?source b c d with
|
try Pdfread.pdf_of_channel_lazy ?revision ?source b c d with
|
||||||
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
||||||
raise (Cpdf.SoftError "Bad owner or user password when reading document")
|
raise (Cpdferror.SoftError "Bad owner or user password when reading document")
|
||||||
|
|
||||||
let pdfread_pdf_of_file ?revision a b c =
|
let pdfread_pdf_of_file ?revision a b c =
|
||||||
try Pdfread.pdf_of_file ?revision a b c with
|
try Pdfread.pdf_of_file ?revision a b c with
|
||||||
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
Pdf.PDFError s when String.length s >=10 && String.sub s 0 10 = "Encryption" ->
|
||||||
raise (Cpdf.SoftError "Bad owner or user password when reading document")
|
raise (Cpdferror.SoftError "Bad owner or user password when reading document")
|
||||||
|
|
||||||
let optstring = function
|
let optstring = function
|
||||||
| "" -> None
|
| "" -> None
|
||||||
|
@ -2352,7 +2352,7 @@ let rec get_single_pdf ?(decrypt=true) ?(fail=false) op read_lazy =
|
||||||
else
|
else
|
||||||
pdfread_pdf_of_file ?revision (optstring u) (optstring o) inname
|
pdfread_pdf_of_file ?revision (optstring u) (optstring o) inname
|
||||||
with
|
with
|
||||||
| Cpdf.SoftError _ as e -> raise e (* Bad owner or user password *)
|
| Cpdferror.SoftError _ as e -> raise e (* Bad owner or user password *)
|
||||||
| _ ->
|
| _ ->
|
||||||
if args.gs_malformed then
|
if args.gs_malformed then
|
||||||
begin
|
begin
|
||||||
|
@ -2446,7 +2446,7 @@ let rec get_pdf_from_input_kind ?(read_lazy=false) ?(decrypt=true) ?(fail=false)
|
||||||
else
|
else
|
||||||
pdfread_pdf_of_file ?revision (optstring u) (optstring o) s
|
pdfread_pdf_of_file ?revision (optstring u) (optstring o) s
|
||||||
with
|
with
|
||||||
| Cpdf.SoftError _ as e -> raise e (* Bad owner or user password *)
|
| Cpdferror.SoftError _ as e -> raise e (* Bad owner or user password *)
|
||||||
| e ->
|
| e ->
|
||||||
Printf.printf "%s\n" (Printexc.to_string e);
|
Printf.printf "%s\n" (Printexc.to_string e);
|
||||||
if args.gs_malformed then
|
if args.gs_malformed then
|
||||||
|
@ -4046,9 +4046,9 @@ let go () =
|
||||||
write_pdf false (Cpdf.scale_contents ~fast:args.fast args.position scale pdf range)
|
write_pdf false (Cpdf.scale_contents ~fast:args.fast args.position scale pdf range)
|
||||||
| Some ListAttachedFiles ->
|
| Some ListAttachedFiles ->
|
||||||
let pdf = get_single_pdf args.op false in
|
let pdf = get_single_pdf args.op false in
|
||||||
let attachments = Cpdf.list_attached_files pdf in
|
let attachments = Cpdfattach.list_attached_files pdf in
|
||||||
iter
|
iter
|
||||||
(fun a -> Printf.printf "%i %s\n" a.Cpdf.pagenumber a.Cpdf.name)
|
(fun a -> Printf.printf "%i %s\n" a.Cpdfattach.pagenumber a.Cpdfattach.name)
|
||||||
attachments;
|
attachments;
|
||||||
flprint ""
|
flprint ""
|
||||||
| Some DumpAttachedFiles ->
|
| Some DumpAttachedFiles ->
|
||||||
|
@ -4059,7 +4059,7 @@ let go () =
|
||||||
| Stdout -> error "Can't dump attachments to stdout"
|
| Stdout -> error "Can't dump attachments to stdout"
|
||||||
end
|
end
|
||||||
| Some RemoveAttachedFiles ->
|
| Some RemoveAttachedFiles ->
|
||||||
write_pdf false (Cpdf.remove_attached_files (get_single_pdf args.op false))
|
write_pdf false (Cpdfattach.remove_attached_files (get_single_pdf args.op false))
|
||||||
| Some (AttachFile files) ->
|
| Some (AttachFile files) ->
|
||||||
begin match args.inputs with
|
begin match args.inputs with
|
||||||
| [(k, _, _, _, _, _) as input] ->
|
| [(k, _, _, _, _, _) as input] ->
|
||||||
|
@ -4072,7 +4072,7 @@ let go () =
|
||||||
| Some s -> Some (int_of_string s)
|
| Some s -> Some (int_of_string s)
|
||||||
with _ -> error "Bad -to-page"
|
with _ -> error "Bad -to-page"
|
||||||
in
|
in
|
||||||
let pdf = fold_left (Cpdf.attach_file args.keepversion topage) pdf (rev files) in
|
let pdf = fold_left (Cpdfattach.attach_file args.keepversion topage) pdf (rev files) in
|
||||||
write_pdf false pdf
|
write_pdf false pdf
|
||||||
| _ -> error "attach file: No input file specified"
|
| _ -> error "attach file: No input file specified"
|
||||||
end
|
end
|
||||||
|
@ -4499,8 +4499,8 @@ let go_withargv argv =
|
||||||
if args.debug then raise e else exit 2
|
if args.debug then raise e else exit 2
|
||||||
else
|
else
|
||||||
raise StayOnError
|
raise StayOnError
|
||||||
| Cpdf.SoftError s -> soft_error s
|
| Cpdferror.SoftError s -> soft_error s
|
||||||
| Cpdf.HardError s -> error s
|
| Cpdferror.HardError s -> error s
|
||||||
| e ->
|
| e ->
|
||||||
prerr_string
|
prerr_string
|
||||||
("cpdf encountered an unexpected error. Technical Details follow:\n" ^
|
("cpdf encountered an unexpected error. Technical Details follow:\n" ^
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
(** Two exceptions recommended for use with the library, though currently not
|
||||||
|
raised by any function in this module. Cpdfcommand uses them extensively. *)
|
||||||
|
exception SoftError of string
|
||||||
|
|
||||||
|
exception HardError of string
|
||||||
|
|
||||||
|
let error s = raise (SoftError s)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
exception SoftError of string
|
||||||
|
val error : string -> 'a
|
||||||
|
exception HardError of string
|
Loading…
Reference in New Issue