From b1349c0b7e153b4bd5224e5af7640f47102c99dc Mon Sep 17 00:00:00 2001 From: John Whitington Date: Tue, 19 Oct 2021 16:18:15 +0100 Subject: [PATCH] Imposition first go --- cpdf.ml | 125 ++++++++++++++++++++----------------------------- cpdf.mli | 2 + cpdfcommand.ml | 11 ++--- 3 files changed, 58 insertions(+), 80 deletions(-) diff --git a/cpdf.ml b/cpdf.ml index 79eb7d7..b6edc49 100644 --- a/cpdf.ml +++ b/cpdf.ml @@ -2733,46 +2733,7 @@ let copy_annotations range frompdf topdf = Pdfpage.change_pages true !pdf (rev !pages) | _ -> assert false -(* \section{N-up} *) - -(* 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] +(* Imposition *) (* Union two rest dictionaries from the same PDF. *) let combine_pdf_rests pdf a b = @@ -2785,7 +2746,7 @@ let combine_pdf_rests pdf a b = | Pdf.Dictionary entries -> entries | _ -> [] 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 a_entries = 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 combined_known_entries = option_map combine_entries keys_to_combine in 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 -had their objects renumbered so as not to clash.*) -let twoup_pages_inner isstack fast pdf = function +had their objects renumbered so as not to clash. *) +let impose_pages n x y columns rtl btt center margin spacing linewidth mediabox' fast pdf = function | [] -> assert false | (h::_) as pages -> 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 (* Change the pattern matrices before combining resources *) let pages, h = - let r = - map2 - (fun p t -> change_pattern_matrices_page pdf t p) - pages transforms - in - r, List.hd r + let r = map2 (fun p t -> change_pattern_matrices_page pdf t p) pages transforms in + (r, List.hd r) 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 @@ -2829,9 +2804,7 @@ let twoup_pages_inner isstack fast pdf = function let transform_stream clipbox contents transform = let clipops = let minx, miny, maxx, maxy = Pdf.parse_rectangle clipbox in - [Pdfops.Op_re (minx, miny, maxx -. minx, maxy -. miny); - Pdfops.Op_W; - Pdfops.Op_n] + [Pdfops.Op_re (minx, miny, maxx -. minx, maxy -. miny); Pdfops.Op_W; Pdfops.Op_n] in (* If fast, no q/Q protection and no parsing of operators. *) if fast then @@ -2856,34 +2829,38 @@ let twoup_pages_inner isstack fast pdf = function pages transforms) in - {Pdfpage.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.mediabox = mediabox'; Pdfpage.rotate = h.Pdfpage.rotate; Pdfpage.content = content'; 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 pdf = upright pagenums pdf in - let pages = Pdfpage.pages_of_pagetree pdf in - let pagesets = splitinto 2 pages in - let renumbered = map (Pdfpage.renumber_pages pdf) pagesets in - let pages' = map (f_pages pdf) renumbered in - let changes = map (fun x -> (x, (x + 1) / 2)) pagenums in - (*print_changes changes;*) - Pdfpage.change_pages ~changes true pdf pages' + let pages = Pdfpage.pages_of_pagetree pdf in + let pagesets = splitinto n pages in + let renumbered = map (Pdfpage.renumber_pages pdf) pagesets 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 + (n - 1)) / n)) pagenums in + 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} *) let get_info raw pdf = diff --git a/cpdf.mli b/cpdf.mli index 0d449ba..26d53d5 100644 --- a/cpdf.mli +++ b/cpdf.mli @@ -305,6 +305,8 @@ val remove_annotations : int list -> Pdf.t -> Pdf.t (** {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, rotating them 90 degrees to do so. The new mediabox is thus larger. Bool true (fast) if assume well-formed ISO content streams. *) diff --git a/cpdfcommand.ml b/cpdfcommand.ml index bd4cc4e..0b7f62e 100644 --- a/cpdfcommand.ml +++ b/cpdfcommand.ml @@ -3327,8 +3327,6 @@ let collate (names, pdfs, ranges) = done; split3 (rev !nis) -let impose fit columns rtl btt center margin spacing linewidth pdf = pdf - (* Main function *) let go () = match args.op with @@ -4033,10 +4031,11 @@ let go () = | Some TwoUpStack -> write_pdf false (Cpdf.twoup_stack args.fast (get_single_pdf args.op false)) | Some Impose fit -> - write_pdf false - (impose fit args.impose_columns args.impose_rtl args.impose_btt args.impose_center - args.impose_margin args.impose_spacing args.impose_linewidth - (get_single_pdf args.op false)) + let pdf = get_single_pdf args.op false in + let x, y = Cpdfcoord.parse_coordinate pdf args.coord in + write_pdf 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) -> let overpdf = match over with