Imposition first go

This commit is contained in:
John Whitington 2021-10-19 16:18:15 +01:00
parent 73dba40576
commit b1349c0b7e
3 changed files with 58 additions and 80 deletions

125
cpdf.ml
View File

@ -2733,46 +2733,7 @@ let copy_annotations range frompdf topdf =
Pdfpage.change_pages true !pdf (rev !pages) Pdfpage.change_pages true !pdf (rev !pages)
| _ -> assert false | _ -> assert false
(* \section{N-up} *) (* Imposition *)
(* Given a number to fit and a mediabox, return a list of transforms for the
2 pages. *)
let twoup_transforms mediabox =
let width, height =
match Pdf.parse_rectangle mediabox with
xmin, ymin, xmax, ymax -> xmax -. xmin, ymax -. ymin
in
let width_exceeds_height = width > height in
let rotate = Pdftransform.Rotate ((0., 0.), rad_of_deg 90.)
in let sc =
if width_exceeds_height
then fmin (height /. width) ((width /. 2.) /. height)
else fmin (width /. height) ((height /. 2.) /. width)
in
let scale = Pdftransform.Scale ((0., 0.), sc, sc) in
let tr0, tr1 =
if width_exceeds_height then
Pdftransform.Translate (height *. sc, 0.),
Pdftransform.Translate (height *. sc *. 2., 0.)
else
Pdftransform.Translate (height *. sc, 0.),
Pdftransform.Translate (height *. sc, width *. sc)
in
let t0 = Pdftransform.matrix_of_transform [tr0; rotate; scale]
in let t1 = Pdftransform.matrix_of_transform [tr1; rotate; scale] in
[t0; t1]
let twoup_stack_transforms mediabox =
let width, height =
match Pdf.parse_rectangle mediabox with
xmin, ymin, xmax, ymax -> xmax -. xmin, ymax -. ymin
in
let rotate = Pdftransform.Rotate ((0., 0.), rad_of_deg 90.)
in let tr0 = Pdftransform.Translate (height, 0.)
in let tr1 = Pdftransform.Translate (height, width) in
let t0 = Pdftransform.matrix_of_transform [tr0; rotate]
in let t1 = Pdftransform.matrix_of_transform [tr1; rotate] in
[t0; t1]
(* Union two rest dictionaries from the same PDF. *) (* Union two rest dictionaries from the same PDF. *)
let combine_pdf_rests pdf a b = let combine_pdf_rests pdf a b =
@ -2785,7 +2746,7 @@ let combine_pdf_rests pdf a b =
| Pdf.Dictionary entries -> entries | Pdf.Dictionary entries -> entries
| _ -> [] | _ -> []
in in
let keys_to_combine = ["/Annots"] in let keys_to_combine = ["/Annots"] in (* FIXME Check to see if anything else needed here *)
let combine_entries key = let combine_entries key =
let a_entries = let a_entries =
match Pdf.lookup_direct pdf key a with match Pdf.lookup_direct pdf key a with
@ -2805,23 +2766,37 @@ let combine_pdf_rests pdf a b =
let unknown_keys_b = lose (fun (k, _) -> mem k keys_to_combine) b_entries in let unknown_keys_b = lose (fun (k, _) -> mem k keys_to_combine) b_entries in
let combined_known_entries = option_map combine_entries keys_to_combine in let combined_known_entries = option_map combine_entries keys_to_combine in
Pdf.Dictionary (unknown_keys_a @ unknown_keys_b @ combined_known_entries) Pdf.Dictionary (unknown_keys_a @ unknown_keys_b @ combined_known_entries)
(* Calculate the transformation matrices for a single imposed output page. *)
let impose_transforms n x y column rtl btt center margin spacing linewidth mediabox =
let width, height =
match Pdf.parse_rectangle mediabox with
xmin, ymin, xmax, ymax -> xmax -. xmin, ymax -. ymin
in
let trs = ref [] in
let x = int_of_float x in
let y = int_of_float y in
for row = y - 1 downto 0 do
for col = 0 to x - 1 do
trs :=
Pdftransform.matrix_of_transform
[Pdftransform.Translate (width *. float_of_int col, height *. float_of_int row)]::!trs;
done
done;
rev !trs
(* Combine two pages into one throughout the document. The pages have already (* Combine two pages into one throughout the document. The pages have already
had their objects renumbered so as not to clash.*) had their objects renumbered so as not to clash. *)
let twoup_pages_inner isstack fast pdf = function let impose_pages n x y columns rtl btt center margin spacing linewidth mediabox' fast pdf = function
| [] -> assert false | [] -> assert false
| (h::_) as pages -> | (h::_) as pages ->
let transforms = let transforms =
take (((if isstack then twoup_stack_transforms else twoup_transforms) h.Pdfpage.mediabox)) (length pages) take (impose_transforms n x y columns rtl btt center margin spacing linewidth h.Pdfpage.mediabox) (length pages)
in in
(* Change the pattern matrices before combining resources *) (* Change the pattern matrices before combining resources *)
let pages, h = let pages, h =
let r = let r = map2 (fun p t -> change_pattern_matrices_page pdf t p) pages transforms in
map2 (r, List.hd r)
(fun p t -> change_pattern_matrices_page pdf t p)
pages transforms
in
r, List.hd r
in in
let resources' = pair_reduce (combine_pdf_resources pdf) (map (fun p -> p.Pdfpage.resources) pages) in let resources' = pair_reduce (combine_pdf_resources pdf) (map (fun p -> p.Pdfpage.resources) pages) in
let rest' = pair_reduce (combine_pdf_rests pdf) (map (fun p -> p.Pdfpage.rest) pages) in let rest' = pair_reduce (combine_pdf_rests pdf) (map (fun p -> p.Pdfpage.rest) pages) in
@ -2829,9 +2804,7 @@ let twoup_pages_inner isstack fast pdf = function
let transform_stream clipbox contents transform = let transform_stream clipbox contents transform =
let clipops = let clipops =
let minx, miny, maxx, maxy = Pdf.parse_rectangle clipbox in let minx, miny, maxx, maxy = Pdf.parse_rectangle clipbox in
[Pdfops.Op_re (minx, miny, maxx -. minx, maxy -. miny); [Pdfops.Op_re (minx, miny, maxx -. minx, maxy -. miny); Pdfops.Op_W; Pdfops.Op_n]
Pdfops.Op_W;
Pdfops.Op_n]
in in
(* If fast, no q/Q protection and no parsing of operators. *) (* If fast, no q/Q protection and no parsing of operators. *)
if fast then if fast then
@ -2856,34 +2829,38 @@ let twoup_pages_inner isstack fast pdf = function
pages pages
transforms) transforms)
in in
{Pdfpage.mediabox = {Pdfpage.mediabox = mediabox';
if isstack then
let width, height =
match Pdf.parse_rectangle h.Pdfpage.mediabox with
xmin, ymin, xmax, ymax -> xmax -. xmin, ymax -. ymin
in
Pdf.Array [Pdf.Real 0.; Pdf.Real 0.; Pdf.Real height; Pdf.Real (width *. 2.)]
else
h.Pdfpage.mediabox;
Pdfpage.rotate = h.Pdfpage.rotate; Pdfpage.rotate = h.Pdfpage.rotate;
Pdfpage.content = content'; Pdfpage.content = content';
Pdfpage.resources = resources'; Pdfpage.resources = resources';
Pdfpage.rest = if isstack then Pdf.remove_dict_entry rest' "/CropBox" else rest'} Pdfpage.rest = rest'}
let f_twoup f_pages pdf = (* FIXME impose to use cropbox by default? See what happens with cropbox above. Clarify? Also point to hardbox. *)
let impose ~x ~y ~fit ~columns ~rtl ~btt ~center ~margin ~spacing ~linewidth ~fast pdf =
Printf.printf
"impose: x = %f, y = %f, fit = %b, columns = %b, rtl = %b, btt = %b, center = %b, margin = %f, spacing = %f, linewidth = %f, fast = %b\n"
x y fit columns rtl btt center margin spacing linewidth fast;
let n = int_of_float x * int_of_float y in
let firstpage = hd (Pdfpage.pages_of_pagetree pdf) in
let mediabox' =
if fit then Pdf.Array [Pdf.Real 0.; Pdf.Real 0.; Pdf.Real x; Pdf.Real y] else
let _, _, w, h = Pdf.parse_rectangle firstpage.Pdfpage.mediabox in
Pdf.Array [Pdf.Real 0.; Pdf.Real 0.; Pdf.Real (w *. x); Pdf.Real (h *. y)]
in
let pagenums = ilist 1 (Pdfpage.endpage pdf) in let pagenums = ilist 1 (Pdfpage.endpage pdf) in
let pdf = upright pagenums pdf in let pdf = upright pagenums pdf in
let pages = Pdfpage.pages_of_pagetree pdf in let pages = Pdfpage.pages_of_pagetree pdf in
let pagesets = splitinto 2 pages in let pagesets = splitinto n pages in
let renumbered = map (Pdfpage.renumber_pages pdf) pagesets in let renumbered = map (Pdfpage.renumber_pages pdf) pagesets in
let pages' = map (f_pages pdf) renumbered in let pages' = map (impose_pages n x y columns rtl btt center margin spacing linewidth mediabox' fast pdf) renumbered in
let changes = map (fun x -> (x, (x + 1) / 2)) pagenums in let changes = map (fun x -> (x, (x + (n - 1)) / n)) pagenums in
(*print_changes changes;*) Pdfpage.change_pages ~changes true pdf pages'
Pdfpage.change_pages ~changes true pdf pages'
let twoup fast pdf = f_twoup (twoup_pages_inner false fast) pdf (* FIXME *)
let twoup fast pdf = pdf (*f_twoup (twoup_pages_inner false fast) pdf*)
let twoup_stack fast pdf = f_twoup (twoup_pages_inner true fast) pdf (* FIXME *)
let twoup_stack fast pdf = pdf (*f_twoup (twoup_pages_inner true fast) pdf*)
(* \section{Output info} *) (* \section{Output info} *)
let get_info raw pdf = let get_info raw pdf =

View File

@ -305,6 +305,8 @@ val remove_annotations : int list -> Pdf.t -> Pdf.t
(** {2 Imposition} *) (** {2 Imposition} *)
val impose : x:float -> y:float -> fit:bool -> columns:bool -> rtl:bool -> btt:bool -> center:bool -> margin:float -> spacing:float -> linewidth:float -> fast:bool -> Pdf.t -> Pdf.t
(** The twoup_stack operation puts two logical pages on each physical page, (** The twoup_stack operation puts two logical pages on each physical page,
rotating them 90 degrees to do so. The new mediabox is thus larger. Bool true rotating them 90 degrees to do so. The new mediabox is thus larger. Bool true
(fast) if assume well-formed ISO content streams. *) (fast) if assume well-formed ISO content streams. *)

View File

@ -3327,8 +3327,6 @@ let collate (names, pdfs, ranges) =
done; done;
split3 (rev !nis) split3 (rev !nis)
let impose fit columns rtl btt center margin spacing linewidth pdf = pdf
(* Main function *) (* Main function *)
let go () = let go () =
match args.op with match args.op with
@ -4033,10 +4031,11 @@ let go () =
| Some TwoUpStack -> | Some TwoUpStack ->
write_pdf false (Cpdf.twoup_stack args.fast (get_single_pdf args.op false)) write_pdf false (Cpdf.twoup_stack args.fast (get_single_pdf args.op false))
| Some Impose fit -> | Some Impose fit ->
write_pdf false let pdf = get_single_pdf args.op false in
(impose fit args.impose_columns args.impose_rtl args.impose_btt args.impose_center let x, y = Cpdfcoord.parse_coordinate pdf args.coord in
args.impose_margin args.impose_spacing args.impose_linewidth write_pdf false
(get_single_pdf args.op false)) (Cpdf.impose x y fit args.impose_columns args.impose_rtl args.impose_btt args.impose_center
args.impose_margin args.impose_spacing args.impose_linewidth args.fast pdf)
| Some (StampOn over) -> | Some (StampOn over) ->
let overpdf = let overpdf =
match over with match over with