mirror of
				https://github.com/johnwhitington/cpdf-source.git
				synced 2025-06-05 22:09:39 +02:00 
			
		
		
		
	Fixed -stamp-on/-stamp-under with positions
This commit is contained in:
		
							
								
								
									
										423
									
								
								cpdf.ml
									
									
									
									
									
								
							
							
						
						
									
										423
									
								
								cpdf.ml
									
									
									
									
									
								
							| @@ -1233,156 +1233,7 @@ let list_bookmarks encoding range pdf output = | |||||||
|                    (if mark.Pdfmarks.isopen then "open" else ""))) |                    (if mark.Pdfmarks.isopen then "open" else ""))) | ||||||
|             inrange |             inrange | ||||||
|  |  | ||||||
| (* \section{Stamping files} *) |  | ||||||
| (* o is the stamp, u is the main pdf page *) | (* o is the stamp, u is the main pdf page *) | ||||||
| let do_stamp fast scale_to_fit isover pdf o u opdf = |  | ||||||
|   (* Scale page stamp o to fit page u *) |  | ||||||
|   let o = |  | ||||||
|     if scale_to_fit then |  | ||||||
|       let sxmin, symin, sxmax, symax = |  | ||||||
|         Pdf.parse_rectangle |  | ||||||
|           (match Pdf.lookup_direct pdf "/CropBox" o.Pdfpage.rest with | Some r -> r | None -> o.Pdfpage.mediabox) |  | ||||||
|       in let txmin, tymin, txmax, tymax = |  | ||||||
|         Pdf.parse_rectangle |  | ||||||
|           (match Pdf.lookup_direct pdf "/CropBox" u.Pdfpage.rest with | Some r -> r | None -> u.Pdfpage.mediabox) |  | ||||||
|       in |  | ||||||
|         let xmag = (txmax -. txmin) /. (sxmax -. sxmin) |  | ||||||
|         in let ymag = (tymax -. tymin) /. (symax -. symin) in |  | ||||||
|           let scale = |  | ||||||
|             if xmag < 0.999 && ymag < 0.999 then |  | ||||||
|               if xmag > ymag then xmag else ymag |  | ||||||
|             else if xmag >= 1.001 && ymag >= 1.001 then |  | ||||||
|               if xmag > ymag then ymag else xmag |  | ||||||
|             else if xmag >= 1.001 then ymag |  | ||||||
|             else xmag |  | ||||||
|           in |  | ||||||
|             let dx = txmin +. ((txmax -. txmin) -. (sxmax -. sxmin) *. scale) /. 2. |  | ||||||
|             in let dy = tymin +. ((tymax -. tymin) -. (symax -. symin) *. scale) /. 2. in |  | ||||||
|               let scale_op = |  | ||||||
|                 Pdfops.Op_cm |  | ||||||
|                   (Pdftransform.matrix_of_transform |  | ||||||
|                      [Pdftransform.Translate (dx, dy); |  | ||||||
|                       Pdftransform.Scale ((sxmin, symin), scale, scale)]) |  | ||||||
|               in |  | ||||||
|                 Pdfpage.prepend_operators opdf [scale_op] ~fast o |  | ||||||
|     else |  | ||||||
|       o |  | ||||||
|   in |  | ||||||
|     {u with |  | ||||||
|        Pdfpage.content = |  | ||||||
|          (if isover then ( @ ) else ( @@ )) |  | ||||||
|          [protect_removeme pdf u.Pdfpage.resources u.Pdfpage.content] |  | ||||||
|            [protect_removeme pdf o.Pdfpage.resources o.Pdfpage.content]; |  | ||||||
|        Pdfpage.resources = |  | ||||||
|          combine_pdf_resources pdf u.Pdfpage.resources o.Pdfpage.resources} |  | ||||||
|  |  | ||||||
| (* Alter bookmark destinations given a hash table of (old page reference |  | ||||||
|  * number, new page reference number) pairings *) |  | ||||||
| let change_destination t = function |  | ||||||
|    Pdfdest.XYZ (Pdfdest.PageObject p, a, b, c) -> |  | ||||||
|      Pdfdest.XYZ (Pdfdest.PageObject (Hashtbl.find t p), a, b, c) |  | ||||||
|  | Pdfdest.Fit (Pdfdest.PageObject p) -> |  | ||||||
|      Pdfdest.Fit (Pdfdest.PageObject (Hashtbl.find t p)) |  | ||||||
|  | Pdfdest.FitH (Pdfdest.PageObject p, x) -> |  | ||||||
|      Pdfdest.FitH (Pdfdest.PageObject (Hashtbl.find t p), x) |  | ||||||
|  | Pdfdest.FitV (Pdfdest.PageObject p, x) -> |  | ||||||
|      Pdfdest.FitV (Pdfdest.PageObject (Hashtbl.find t p), x) |  | ||||||
|  | Pdfdest.FitR (Pdfdest.PageObject p, a, b, c, d) -> |  | ||||||
|      Pdfdest.FitR (Pdfdest.PageObject (Hashtbl.find t p), a, b, c, d) |  | ||||||
|  | Pdfdest.FitB (Pdfdest.PageObject p) -> |  | ||||||
|      Pdfdest.Fit (Pdfdest.PageObject (Hashtbl.find t p)) |  | ||||||
|  | Pdfdest.FitBH (Pdfdest.PageObject p, x) -> |  | ||||||
|      Pdfdest.FitBH (Pdfdest.PageObject (Hashtbl.find t p), x) |  | ||||||
|  | Pdfdest.FitBV (Pdfdest.PageObject p, x) -> |  | ||||||
|      Pdfdest.FitBV (Pdfdest.PageObject (Hashtbl.find t p), x) |  | ||||||
|  | x -> x |  | ||||||
|  |  | ||||||
| let change_bookmark t m = |  | ||||||
|   {m with Pdfmarks.target = |  | ||||||
|     try change_destination t m.Pdfmarks.target with Not_found -> m.Pdfmarks.target} |  | ||||||
|  |  | ||||||
| let stamp (fast : bool) scale_to_fit isover range over pdf = |  | ||||||
|   let marks = Pdfmarks.read_bookmarks pdf in |  | ||||||
|   let marks_refnumbers = Pdf.page_reference_numbers pdf in |  | ||||||
|   let pdf = Pdfmarks.remove_bookmarks pdf in |  | ||||||
|   let over = Pdfmarks.remove_bookmarks over in |  | ||||||
|   let pageseqs = ilist 1 (Pdfpage.endpage pdf) in |  | ||||||
|     let over_firstpage_pdf = |  | ||||||
|       match Pdfpage.pages_of_pagetree over with |  | ||||||
|       | [] -> error "empty PDF" |  | ||||||
|       | h::_ -> Pdfpage.change_pages true over [h] |  | ||||||
|     in |  | ||||||
|       let merged = |  | ||||||
|         Pdfmerge.merge_pdfs |  | ||||||
|           ~rotations:[Pdfmerge.DNR; Pdfmerge.DNR] |  | ||||||
|           false false ["a"; "b"] [pdf; over_firstpage_pdf] [pageseqs; [1]] |  | ||||||
|       in |  | ||||||
|         let renamed_pdf = |  | ||||||
|           Pdfpage.change_pages true |  | ||||||
|             merged (Pdfpage.renumber_pages merged (Pdfpage.pages_of_pagetree merged)) |  | ||||||
|         in |  | ||||||
|           let renamed_pages = Pdfpage.pages_of_pagetree renamed_pdf in |  | ||||||
|             let under_pages, over_page = |  | ||||||
|               all_but_last renamed_pages, last renamed_pages |  | ||||||
|             in |  | ||||||
|               let new_pages = |  | ||||||
|                 map2 |  | ||||||
|                   (fun pageseq under_page -> |  | ||||||
|                     do_stamp fast scale_to_fit isover renamed_pdf |  | ||||||
|                     (if mem pageseq range then over_page else |  | ||||||
|                       Pdfpage.blankpage Pdfpaper.a4) |  | ||||||
|                     under_page over) |  | ||||||
|                   pageseqs |  | ||||||
|                   under_pages  |  | ||||||
|               in |  | ||||||
|                 let changed = Pdfpage.change_pages true renamed_pdf new_pages in |  | ||||||
|                 let new_refnumbers = Pdf.page_reference_numbers changed in |  | ||||||
|                 let changetable = hashtable_of_dictionary (List.combine marks_refnumbers new_refnumbers) in |  | ||||||
|                 let new_marks = map (change_bookmark changetable) marks in |  | ||||||
|                 Pdfmarks.add_bookmarks new_marks changed |  | ||||||
|  |  | ||||||
| (* Combine pages from two PDFs. For now, assume equal length. *) |  | ||||||
|  |  | ||||||
| (* If [over] has more pages than [under], chop the excess. If the converse, pad |  | ||||||
| [over] to the same length *) |  | ||||||
| let equalize_pages under over = |  | ||||||
|   let length_under = Pdfpage.endpage under |  | ||||||
|   in let length_over = Pdfpage.endpage over |  | ||||||
|   in |  | ||||||
|     if length_over > length_under then |  | ||||||
|       under, |  | ||||||
|       (Pdfpage.change_pages true over (take (Pdfpage.pages_of_pagetree over) length_under)) |  | ||||||
|     else if length_under > length_over then |  | ||||||
|       under, |  | ||||||
|       Pdfpage.change_pages true |  | ||||||
|         over |  | ||||||
|         (Pdfpage.pages_of_pagetree over @ |  | ||||||
|            (many (Pdfpage.blankpage Pdfpaper.a4) (length_under - length_over))) |  | ||||||
|     else |  | ||||||
|       under, over |  | ||||||
|  |  | ||||||
| let combine_pages (fast : bool) under over scaletofit swap equalize = |  | ||||||
|   let marks_under = Pdfmarks.read_bookmarks under in |  | ||||||
|   let marks_over = Pdfmarks.read_bookmarks over in |  | ||||||
|   let under, over = if equalize then equalize_pages under over else under, over in |  | ||||||
|     let under_length = Pdfpage.endpage under |  | ||||||
|     in let over_length = Pdfpage.endpage over in |  | ||||||
|       if under_length <> over_length then raise (Pdf.PDFError "combine_pages: not of equal length") else |  | ||||||
|       let pageseqs_under = ilist 1 (Pdfpage.endpage under) |  | ||||||
|       in let pageseqs_over = ilist 1 (Pdfpage.endpage over) in |  | ||||||
|         let merged = |  | ||||||
|           Pdfmerge.merge_pdfs ~rotations: [Pdfmerge.DNR; Pdfmerge.DNR] false false ["a"; "b"] [under; over] [pageseqs_under; pageseqs_over] in |  | ||||||
|           let renamed_pdf = |  | ||||||
|             Pdfpage.change_pages true |  | ||||||
|               merged (Pdfpage.renumber_pages merged (Pdfpage.pages_of_pagetree merged)) |  | ||||||
|           in |  | ||||||
|             let under_pages, over_pages = |  | ||||||
|               cleave (Pdfpage.pages_of_pagetree renamed_pdf) under_length |  | ||||||
|             in |  | ||||||
|               let new_pages = |  | ||||||
|                 map2 (fun o u -> do_stamp fast scaletofit (not swap) renamed_pdf o u over) over_pages under_pages |  | ||||||
|               in |  | ||||||
|                 Pdfmarks.add_bookmarks (marks_under @ marks_over) (Pdfpage.change_pages true renamed_pdf new_pages) |  | ||||||
|  |  | ||||||
| (* \section{Split at bookmarks} *) | (* \section{Split at bookmarks} *) | ||||||
|  |  | ||||||
| @@ -1627,56 +1478,6 @@ let print_fonts pdf = | |||||||
|   flprint |   flprint | ||||||
|     (fold_left ( ^ ) "" (map string_of_font (list_fonts pdf))) |     (fold_left ( ^ ) "" (map string_of_font (list_fonts pdf))) | ||||||
|  |  | ||||||
| (* \section{Nobbling for Demo Version} *) |  | ||||||
| let nobble_page pdf _ page = |  | ||||||
|   let minx, miny, maxx, maxy = |  | ||||||
|     (* Use cropbox if available *) |  | ||||||
|     Pdf.parse_rectangle |  | ||||||
|       (match Pdf.lookup_direct pdf "/CropBox" page.Pdfpage.rest with |  | ||||||
|        | Some r -> r |  | ||||||
|        | None -> page.Pdfpage.mediabox) |  | ||||||
|   in |  | ||||||
|     let fontdict = |  | ||||||
|       match Pdf.lookup_direct pdf "/Font" page.Pdfpage.resources with |  | ||||||
|       | None -> Pdf.Dictionary [] |  | ||||||
|       | Some d -> d |  | ||||||
|     in |  | ||||||
|       let fontname = Pdf.unique_key "F" fontdict in |  | ||||||
|     let width = maxx -. minx in let height = maxy -. miny in |  | ||||||
|       let scalex = |  | ||||||
|         (width *. 1000.) /. float (Pdfstandard14.textwidth false Pdftext.Helvetica "DEMO") |  | ||||||
|       in |  | ||||||
|         let page' = |  | ||||||
|           let font = |  | ||||||
|             Pdf.Dictionary |  | ||||||
|               [("/Type", Pdf.Name "/Font"); |  | ||||||
|                ("/Subtype", Pdf.Name "/Type1"); |  | ||||||
|                ("/BaseFont", Pdf.Name "/Helvetica")] |  | ||||||
|           in let ops = |  | ||||||
|             [Pdfops.Op_BMC "/CPDFSTAMP"; |  | ||||||
|              Pdfops.Op_cm |  | ||||||
|                (Pdftransform.matrix_of_transform |  | ||||||
|                   [Pdftransform.Translate (minx, miny +. height /. 2.)]); |  | ||||||
|              Pdfops.Op_gs "/gs0"; |  | ||||||
|              Pdfops.Op_BT; |  | ||||||
|              Pdfops.Op_Tf (fontname, scalex); |  | ||||||
|              Pdfops.Op_Tj "DEMO"; |  | ||||||
|              Pdfops.Op_ET; |  | ||||||
|              Pdfops.Op_EMC] |  | ||||||
|           in |  | ||||||
|             {(Pdfpage.blankpage Pdfpaper.a4) with |  | ||||||
|                 Pdfpage.mediabox = page.Pdfpage.mediabox; |  | ||||||
|                 Pdfpage.content = [Pdfops.stream_of_ops ops]; |  | ||||||
|                 Pdfpage.resources = |  | ||||||
|                   Pdf.Dictionary |  | ||||||
|                     [("/Font", Pdf.Dictionary [(fontname, font)]); |  | ||||||
|                      ("/ExtGState", Pdf.Dictionary |  | ||||||
|                         ["/gs0", |  | ||||||
|                         Pdf.Dictionary["/Type", Pdf.Name "/ExtGState"; "/ca", Pdf.Real 0.2]]); |  | ||||||
|                     ] |  | ||||||
|             } |  | ||||||
|         in |  | ||||||
|           do_stamp false false true pdf page' page (Pdf.empty ()) |  | ||||||
|  |  | ||||||
| (* \section{Superimpose text, page numbers etc.} *) | (* \section{Superimpose text, page numbers etc.} *) | ||||||
|  |  | ||||||
| @@ -2397,6 +2198,230 @@ let hflip_pdf ?(fast=false) pdf range = | |||||||
|   in |   in | ||||||
|     process_pages (flip_page ~fast transform_op pdf) pdf range |     process_pages (flip_page ~fast transform_op pdf) pdf range | ||||||
|  |  | ||||||
|  | let stamp_shift_of_position sw sh w h p = | ||||||
|  |   let half x = x /. 2. in | ||||||
|  |     match p with | ||||||
|  |     | PosCentre (ox, oy) -> ox -. half sw, oy | ||||||
|  |     | PosLeft (ox, oy) -> ox, oy | ||||||
|  |     | PosRight (ox, oy) -> ox -. sw, oy | ||||||
|  |     | Top o -> half w -. half sw, h -. o -. sh | ||||||
|  |     | TopLeft o -> o, h -. sh -. o | ||||||
|  |     | TopRight o -> w -. sw -. o, h -. sh -. o | ||||||
|  |     | Left o -> o, half h -. half sh | ||||||
|  |     | BottomLeft o -> o, o | ||||||
|  |     | Bottom o -> half w -. half sw, o | ||||||
|  |     | BottomRight o -> w -. sw, o  | ||||||
|  |     | Right o -> w -. sw -. o, half h -. half sh | ||||||
|  |     | Diagonal | ReverseDiagonal | Centre -> half w -. half sw, half h -. half sh | ||||||
|  |  | ||||||
|  | let do_stamp fast position scale_to_fit isover pdf o u opdf = | ||||||
|  |   (* Scale page stamp o to fit page u *) | ||||||
|  |   let sxmin, symin, sxmax, symax = | ||||||
|  |     Pdf.parse_rectangle | ||||||
|  |       (match Pdf.lookup_direct pdf "/CropBox" o.Pdfpage.rest with | Some r -> r | None -> o.Pdfpage.mediabox) | ||||||
|  |   in let txmin, tymin, txmax, tymax = | ||||||
|  |     Pdf.parse_rectangle | ||||||
|  |       (match Pdf.lookup_direct pdf "/CropBox" u.Pdfpage.rest with | Some r -> r | None -> u.Pdfpage.mediabox) | ||||||
|  |   in | ||||||
|  |     let o = | ||||||
|  |       if scale_to_fit then | ||||||
|  |         let xmag = (txmax -. txmin) /. (sxmax -. sxmin) in | ||||||
|  |           let ymag = (tymax -. tymin) /. (symax -. symin) in | ||||||
|  |             let scale = | ||||||
|  |               if xmag < 0.999 && ymag < 0.999 then | ||||||
|  |                 if xmag > ymag then xmag else ymag | ||||||
|  |               else if xmag >= 1.001 && ymag >= 1.001 then | ||||||
|  |                 if xmag > ymag then ymag else xmag | ||||||
|  |               else if xmag >= 1.001 then ymag | ||||||
|  |               else xmag | ||||||
|  |             in | ||||||
|  |               let dx = txmin +. ((txmax -. txmin) -. (sxmax -. sxmin) *. scale) /. 2. in | ||||||
|  |                 let dy = tymin +. ((tymax -. tymin) -. (symax -. symin) *. scale) /. 2. in | ||||||
|  |                   let scale_op = | ||||||
|  |                     Pdfops.Op_cm | ||||||
|  |                       (Pdftransform.matrix_of_transform | ||||||
|  |                          [Pdftransform.Translate (dx, dy); | ||||||
|  |                           Pdftransform.Scale ((sxmin, symin), scale, scale)]) | ||||||
|  |                   in | ||||||
|  |                     Pdfpage.prepend_operators pdf [scale_op] ~fast o | ||||||
|  |       else | ||||||
|  |         let sw = sxmax -. sxmin | ||||||
|  |         and sh = symax -. symin | ||||||
|  |         and w = txmax -. txmin | ||||||
|  |         and h = tymax -. tymin in | ||||||
|  |           let dx, dy = stamp_shift_of_position sw sh w h position in | ||||||
|  |             let translate_op = | ||||||
|  |               Pdfops.Op_cm | ||||||
|  |                 (Pdftransform.matrix_of_transform [Pdftransform.Translate (dx, dy)]) | ||||||
|  |             in | ||||||
|  |               Pdfpage.prepend_operators pdf [translate_op] ~fast o | ||||||
|  |     in | ||||||
|  |       {u with | ||||||
|  |          Pdfpage.content = | ||||||
|  |            (if isover then ( @ ) else ( @@ )) | ||||||
|  |            [protect_removeme pdf u.Pdfpage.resources u.Pdfpage.content] | ||||||
|  |              [protect_removeme pdf o.Pdfpage.resources o.Pdfpage.content]; | ||||||
|  |          Pdfpage.resources = | ||||||
|  |            combine_pdf_resources pdf u.Pdfpage.resources o.Pdfpage.resources} | ||||||
|  |  | ||||||
|  | (* Alter bookmark destinations given a hash table of (old page reference | ||||||
|  |  * number, new page reference number) pairings *) | ||||||
|  | let change_destination t = function | ||||||
|  |    Pdfdest.XYZ (Pdfdest.PageObject p, a, b, c) -> | ||||||
|  |      Pdfdest.XYZ (Pdfdest.PageObject (Hashtbl.find t p), a, b, c) | ||||||
|  |  | Pdfdest.Fit (Pdfdest.PageObject p) -> | ||||||
|  |      Pdfdest.Fit (Pdfdest.PageObject (Hashtbl.find t p)) | ||||||
|  |  | Pdfdest.FitH (Pdfdest.PageObject p, x) -> | ||||||
|  |      Pdfdest.FitH (Pdfdest.PageObject (Hashtbl.find t p), x) | ||||||
|  |  | Pdfdest.FitV (Pdfdest.PageObject p, x) -> | ||||||
|  |      Pdfdest.FitV (Pdfdest.PageObject (Hashtbl.find t p), x) | ||||||
|  |  | Pdfdest.FitR (Pdfdest.PageObject p, a, b, c, d) -> | ||||||
|  |      Pdfdest.FitR (Pdfdest.PageObject (Hashtbl.find t p), a, b, c, d) | ||||||
|  |  | Pdfdest.FitB (Pdfdest.PageObject p) -> | ||||||
|  |      Pdfdest.Fit (Pdfdest.PageObject (Hashtbl.find t p)) | ||||||
|  |  | Pdfdest.FitBH (Pdfdest.PageObject p, x) -> | ||||||
|  |      Pdfdest.FitBH (Pdfdest.PageObject (Hashtbl.find t p), x) | ||||||
|  |  | Pdfdest.FitBV (Pdfdest.PageObject p, x) -> | ||||||
|  |      Pdfdest.FitBV (Pdfdest.PageObject (Hashtbl.find t p), x) | ||||||
|  |  | x -> x | ||||||
|  |  | ||||||
|  | let change_bookmark t m = | ||||||
|  |   {m with Pdfmarks.target = | ||||||
|  |     try change_destination t m.Pdfmarks.target with Not_found -> m.Pdfmarks.target} | ||||||
|  |  | ||||||
|  | let stamp position fast scale_to_fit isover range over pdf = | ||||||
|  |   let marks = Pdfmarks.read_bookmarks pdf in | ||||||
|  |   let marks_refnumbers = Pdf.page_reference_numbers pdf in | ||||||
|  |   let pdf = Pdfmarks.remove_bookmarks pdf in | ||||||
|  |   let over = Pdfmarks.remove_bookmarks over in | ||||||
|  |   let pageseqs = ilist 1 (Pdfpage.endpage pdf) in | ||||||
|  |     let over_firstpage_pdf = | ||||||
|  |       match Pdfpage.pages_of_pagetree over with | ||||||
|  |       | [] -> error "empty PDF" | ||||||
|  |       | h::_ -> Pdfpage.change_pages true over [h] | ||||||
|  |     in | ||||||
|  |       let merged = | ||||||
|  |         Pdfmerge.merge_pdfs | ||||||
|  |           ~rotations:[Pdfmerge.DNR; Pdfmerge.DNR] | ||||||
|  |           false false ["a"; "b"] [pdf; over_firstpage_pdf] [pageseqs; [1]] | ||||||
|  |       in | ||||||
|  |         let renamed_pdf = | ||||||
|  |           Pdfpage.change_pages true | ||||||
|  |             merged (Pdfpage.renumber_pages merged (Pdfpage.pages_of_pagetree merged)) | ||||||
|  |         in | ||||||
|  |           let renamed_pages = Pdfpage.pages_of_pagetree renamed_pdf in | ||||||
|  |             let under_pages, over_page = | ||||||
|  |               all_but_last renamed_pages, last renamed_pages | ||||||
|  |             in | ||||||
|  |               let new_pages = | ||||||
|  |                 map2 | ||||||
|  |                   (fun pageseq under_page -> | ||||||
|  |                     do_stamp fast position scale_to_fit isover renamed_pdf | ||||||
|  |                     (if mem pageseq range then over_page else | ||||||
|  |                       Pdfpage.blankpage Pdfpaper.a4) | ||||||
|  |                     under_page over) | ||||||
|  |                   pageseqs | ||||||
|  |                   under_pages  | ||||||
|  |               in | ||||||
|  |                 let changed = Pdfpage.change_pages true renamed_pdf new_pages in | ||||||
|  |                 let new_refnumbers = Pdf.page_reference_numbers changed in | ||||||
|  |                 let changetable = hashtable_of_dictionary (List.combine marks_refnumbers new_refnumbers) in | ||||||
|  |                 let new_marks = map (change_bookmark changetable) marks in | ||||||
|  |                 Pdfmarks.add_bookmarks new_marks changed | ||||||
|  |  | ||||||
|  | (* Combine pages from two PDFs. For now, assume equal length. *) | ||||||
|  |  | ||||||
|  | (* If [over] has more pages than [under], chop the excess. If the converse, pad | ||||||
|  | [over] to the same length *) | ||||||
|  | let equalize_pages under over = | ||||||
|  |   let length_under = Pdfpage.endpage under | ||||||
|  |   in let length_over = Pdfpage.endpage over | ||||||
|  |   in | ||||||
|  |     if length_over > length_under then | ||||||
|  |       under, | ||||||
|  |       (Pdfpage.change_pages true over (take (Pdfpage.pages_of_pagetree over) length_under)) | ||||||
|  |     else if length_under > length_over then | ||||||
|  |       under, | ||||||
|  |       Pdfpage.change_pages true | ||||||
|  |         over | ||||||
|  |         (Pdfpage.pages_of_pagetree over @ | ||||||
|  |            (many (Pdfpage.blankpage Pdfpaper.a4) (length_under - length_over))) | ||||||
|  |     else | ||||||
|  |       under, over | ||||||
|  |  | ||||||
|  | let combine_pages (fast : bool) under over scaletofit swap equalize = | ||||||
|  |   let marks_under = Pdfmarks.read_bookmarks under in | ||||||
|  |   let marks_over = Pdfmarks.read_bookmarks over in | ||||||
|  |   let under, over = if equalize then equalize_pages under over else under, over in | ||||||
|  |     let under_length = Pdfpage.endpage under | ||||||
|  |     in let over_length = Pdfpage.endpage over in | ||||||
|  |       if under_length <> over_length then raise (Pdf.PDFError "combine_pages: not of equal length") else | ||||||
|  |       let pageseqs_under = ilist 1 (Pdfpage.endpage under) | ||||||
|  |       in let pageseqs_over = ilist 1 (Pdfpage.endpage over) in | ||||||
|  |         let merged = | ||||||
|  |           Pdfmerge.merge_pdfs ~rotations: [Pdfmerge.DNR; Pdfmerge.DNR] false false ["a"; "b"] [under; over] [pageseqs_under; pageseqs_over] in | ||||||
|  |           let renamed_pdf = | ||||||
|  |             Pdfpage.change_pages true | ||||||
|  |               merged (Pdfpage.renumber_pages merged (Pdfpage.pages_of_pagetree merged)) | ||||||
|  |           in | ||||||
|  |             let under_pages, over_pages = | ||||||
|  |               cleave (Pdfpage.pages_of_pagetree renamed_pdf) under_length | ||||||
|  |             in | ||||||
|  |               let new_pages = | ||||||
|  |                 map2 (fun o u -> do_stamp fast (BottomLeft 0.) scaletofit (not swap) renamed_pdf o u over) over_pages under_pages | ||||||
|  |               in | ||||||
|  |                 Pdfmarks.add_bookmarks (marks_under @ marks_over) (Pdfpage.change_pages true renamed_pdf new_pages) | ||||||
|  |  | ||||||
|  | let nobble_page pdf _ page = | ||||||
|  |   let minx, miny, maxx, maxy = | ||||||
|  |     (* Use cropbox if available *) | ||||||
|  |     Pdf.parse_rectangle | ||||||
|  |       (match Pdf.lookup_direct pdf "/CropBox" page.Pdfpage.rest with | ||||||
|  |        | Some r -> r | ||||||
|  |        | None -> page.Pdfpage.mediabox) | ||||||
|  |   in | ||||||
|  |     let fontdict = | ||||||
|  |       match Pdf.lookup_direct pdf "/Font" page.Pdfpage.resources with | ||||||
|  |       | None -> Pdf.Dictionary [] | ||||||
|  |       | Some d -> d | ||||||
|  |     in | ||||||
|  |       let fontname = Pdf.unique_key "F" fontdict in | ||||||
|  |     let width = maxx -. minx in let height = maxy -. miny in | ||||||
|  |       let scalex = | ||||||
|  |         (width *. 1000.) /. float (Pdfstandard14.textwidth false Pdftext.Helvetica "DEMO") | ||||||
|  |       in | ||||||
|  |         let page' = | ||||||
|  |           let font = | ||||||
|  |             Pdf.Dictionary | ||||||
|  |               [("/Type", Pdf.Name "/Font"); | ||||||
|  |                ("/Subtype", Pdf.Name "/Type1"); | ||||||
|  |                ("/BaseFont", Pdf.Name "/Helvetica")] | ||||||
|  |           in let ops = | ||||||
|  |             [Pdfops.Op_BMC "/CPDFSTAMP"; | ||||||
|  |              Pdfops.Op_cm | ||||||
|  |                (Pdftransform.matrix_of_transform | ||||||
|  |                   [Pdftransform.Translate (minx, miny +. height /. 2.)]); | ||||||
|  |              Pdfops.Op_gs "/gs0"; | ||||||
|  |              Pdfops.Op_BT; | ||||||
|  |              Pdfops.Op_Tf (fontname, scalex); | ||||||
|  |              Pdfops.Op_Tj "DEMO"; | ||||||
|  |              Pdfops.Op_ET; | ||||||
|  |              Pdfops.Op_EMC] | ||||||
|  |           in | ||||||
|  |             {(Pdfpage.blankpage Pdfpaper.a4) with | ||||||
|  |                 Pdfpage.mediabox = page.Pdfpage.mediabox; | ||||||
|  |                 Pdfpage.content = [Pdfops.stream_of_ops ops]; | ||||||
|  |                 Pdfpage.resources = | ||||||
|  |                   Pdf.Dictionary | ||||||
|  |                     [("/Font", Pdf.Dictionary [(fontname, font)]); | ||||||
|  |                      ("/ExtGState", Pdf.Dictionary | ||||||
|  |                         ["/gs0", | ||||||
|  |                         Pdf.Dictionary["/Type", Pdf.Name "/ExtGState"; "/ca", Pdf.Real 0.2]]); | ||||||
|  |                     ] | ||||||
|  |             } | ||||||
|  |         in | ||||||
|  |           do_stamp false (BottomLeft 0.) false true pdf page' page (Pdf.empty ()) | ||||||
|  |  | ||||||
| (* \section{Set media box} *) | (* \section{Set media box} *) | ||||||
| let set_mediabox x y w h pdf range = | let set_mediabox x y w h pdf range = | ||||||
|   let crop_page _ page = |   let crop_page _ page = | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								cpdf.mli
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								cpdf.mli
									
									
									
									
									
								
							| @@ -14,6 +14,23 @@ exception HardError of string | |||||||
| (** Two exceptions recommended for use with the library, though currently not | (** Two exceptions recommended for use with the library, though currently not | ||||||
| raised by any function in this module. Cpdfcommand uses them extensively. *) | raised by any function in this module. Cpdfcommand uses them extensively. *) | ||||||
|  |  | ||||||
|  | (** Possible positions for adding text and other uses. See cpdfmanual.pdf *) | ||||||
|  | type position = | ||||||
|  |   | PosCentre of float * float | ||||||
|  |   | PosLeft of float * float | ||||||
|  |   | PosRight of float * float | ||||||
|  |   | Top of float | ||||||
|  |   | TopLeft of float | ||||||
|  |   | TopRight of float | ||||||
|  |   | Left of float | ||||||
|  |   | BottomLeft of float | ||||||
|  |   | Bottom of float | ||||||
|  |   | BottomRight of float | ||||||
|  |   | Right of float | ||||||
|  |   | Diagonal | ||||||
|  |   | ReverseDiagonal | ||||||
|  |   | Centre | ||||||
|  |  | ||||||
| (** {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 *) | ||||||
| @@ -178,10 +195,10 @@ swapped.  If [fast] is true, the PDFs are assumed to be well-formed and no | |||||||
| fixes are done. *) | fixes are done. *) | ||||||
| val combine_pages : bool -> Pdf.t -> Pdf.t -> bool -> bool -> bool -> Pdf.t | val combine_pages : bool -> Pdf.t -> Pdf.t -> bool -> bool -> bool -> Pdf.t | ||||||
|  |  | ||||||
| (** [stamp scale_to_fit fast isover range over pdf] stamps the first page of | (** [stamp scale_to_fit position fast isover range over pdf] stamps the first page of | ||||||
| [over] over each page of the PDF. The arguments have the same meaning as in | [over] over each page of the PDF. The arguments have the same meaning as in | ||||||
| [combine_pages]. *) | [combine_pages]. *) | ||||||
| val stamp : bool -> bool -> bool -> int list -> Pdf.t -> Pdf.t -> Pdf.t | val stamp : position -> bool -> bool -> bool -> int list -> Pdf.t -> Pdf.t -> Pdf.t | ||||||
|  |  | ||||||
| (** {2 Splitting PDFs} *) | (** {2 Splitting PDFs} *) | ||||||
|  |  | ||||||
| @@ -217,22 +234,6 @@ val list_fonts : Pdf.t -> (int * string * string * string * string) list | |||||||
| (** Expand the string "now" to a PDF date string, ignoring any other string *) | (** Expand the string "now" to a PDF date string, ignoring any other string *) | ||||||
| val expand_date : string -> string | val expand_date : string -> string | ||||||
|  |  | ||||||
| (** Possible positions for adding text and other uses. See cpdfmanual.pdf *) |  | ||||||
| type position = |  | ||||||
|   | PosCentre of float * float |  | ||||||
|   | PosLeft of float * float |  | ||||||
|   | PosRight of float * float |  | ||||||
|   | Top of float |  | ||||||
|   | TopLeft of float |  | ||||||
|   | TopRight of float |  | ||||||
|   | Left of float |  | ||||||
|   | BottomLeft of float |  | ||||||
|   | Bottom of float |  | ||||||
|   | BottomRight of float |  | ||||||
|   | Right of float |  | ||||||
|   | Diagonal |  | ||||||
|   | ReverseDiagonal |  | ||||||
|   | Centre |  | ||||||
|  |  | ||||||
| (** Produce a debug string of a [position] *) | (** Produce a debug string of a [position] *) | ||||||
| val string_of_position : position -> string | val string_of_position : position -> string | ||||||
|   | |||||||
| @@ -864,10 +864,13 @@ let setaddbookmarks s = | |||||||
|   setop (AddBookmarks s) () |   setop (AddBookmarks s) () | ||||||
|  |  | ||||||
| let setstampon f = | let setstampon f = | ||||||
|   setop (StampOn f) () |   setop (StampOn f) (); | ||||||
|  |   (* Due to an earlier bad decision (default position), we have this nasty hack *) | ||||||
|  |   if args.position = Cpdf.TopLeft 100. then args.position <- Cpdf.BottomLeft 0. | ||||||
|  |  | ||||||
| let setstampunder f = | let setstampunder f = | ||||||
|   setop (StampUnder f) () |   setop (StampUnder f) (); | ||||||
|  |   if args.position = Cpdf.TopLeft 100. then args.position <- Cpdf.BottomLeft 0. | ||||||
|  |  | ||||||
| let setstamponmulti f = | let setstamponmulti f = | ||||||
|   setop (StampOn f) (); |   setop (StampOn f) (); | ||||||
| @@ -3099,12 +3102,7 @@ let go () = | |||||||
|   | Some Split -> |   | Some Split -> | ||||||
|       begin match args.inputs, args.out with |       begin match args.inputs, args.out with | ||||||
|         | [(f, ranges, _, _, _)], File output_spec -> |         | [(f, ranges, _, _, _)], File output_spec -> | ||||||
|             let pdf = get_single_pdf args.op true |             let pdf = get_single_pdf args.op true in | ||||||
|             (*and filename = |  | ||||||
|               match f with |  | ||||||
|               | InFile n -> n |  | ||||||
|               | _ -> ""*) |  | ||||||
|             in |  | ||||||
|               let enc = |               let enc = | ||||||
|                 match args.crypt_method with |                 match args.crypt_method with | ||||||
|                 | "" -> None |                 | "" -> None | ||||||
| @@ -3245,7 +3243,6 @@ let go () = | |||||||
|       begin match args.inputs with |       begin match args.inputs with | ||||||
|       | [(k, _, _, _, _) as input] -> |       | [(k, _, _, _, _) as input] -> | ||||||
|           let pdf = get_pdf_from_input_kind input args.op k in |           let pdf = get_pdf_from_input_kind input args.op k in | ||||||
|             (* Convert from string to int *) |  | ||||||
|             let topage = |             let topage = | ||||||
|               try |               try | ||||||
|                match args.topage with |                match args.topage with | ||||||
| @@ -3344,7 +3341,6 @@ let go () = | |||||||
|       in |       in | ||||||
|         let overpdf = if args.uprightstamp then Cpdf.upright ~fast:args.fast (ilist 1 (Pdfpage.endpage overpdf)) overpdf else overpdf in |         let overpdf = if args.uprightstamp then Cpdf.upright ~fast:args.fast (ilist 1 (Pdfpage.endpage overpdf)) overpdf else overpdf in | ||||||
|           let pdf = get_single_pdf args.op false in |           let pdf = get_single_pdf args.op false in | ||||||
|             (*let marks = Pdfmarks.read_bookmarks pdf in*) |  | ||||||
|             let range = parse_pagespec pdf (get_pagespec ()) in |             let range = parse_pagespec pdf (get_pagespec ()) in | ||||||
|               let pdf = |               let pdf = | ||||||
|                 if args.ismulti |                 if args.ismulti | ||||||
| @@ -3352,10 +3348,8 @@ let go () = | |||||||
|                     let overpdf = equalize_pages_extend pdf overpdf in |                     let overpdf = equalize_pages_extend pdf overpdf in | ||||||
|                       Cpdf.combine_pages args.fast pdf overpdf true false false |                       Cpdf.combine_pages args.fast pdf overpdf true false false | ||||||
|                   else |                   else | ||||||
|                     Cpdf.stamp args.fast args.scale_stamp_to_fit true range overpdf pdf |                     Cpdf.stamp args.position args.fast args.scale_stamp_to_fit true range overpdf pdf | ||||||
|               in |               in | ||||||
|                 (*let p = Pdfmarks.add_bookmarks marks pdf in |  | ||||||
|                 Pdfwrite.debug_whole_pdf p;*) |  | ||||||
|                 write_pdf false pdf |                 write_pdf false pdf | ||||||
|   | Some (StampUnder under) -> |   | Some (StampUnder under) -> | ||||||
|       let underpdf = |       let underpdf = | ||||||
| @@ -3365,16 +3359,16 @@ let go () = | |||||||
|       in |       in | ||||||
|         let underpdf = if args.uprightstamp then Cpdf.upright ~fast:args.fast (ilist 1 (Pdfpage.endpage underpdf)) underpdf else underpdf in |         let underpdf = if args.uprightstamp then Cpdf.upright ~fast:args.fast (ilist 1 (Pdfpage.endpage underpdf)) underpdf else underpdf in | ||||||
|           let pdf = get_single_pdf args.op false in |           let pdf = get_single_pdf args.op false in | ||||||
|             (*let marks = Pdfmarks.read_bookmarks pdf in*) |  | ||||||
|             let range = parse_pagespec pdf (get_pagespec ()) in |             let range = parse_pagespec pdf (get_pagespec ()) in | ||||||
|               let pdf = |               let pdf = | ||||||
|                 if args.ismulti |                 if args.ismulti | ||||||
|                  then |                  then | ||||||
|                    let underpdf = equalize_pages_extend pdf underpdf in |                    let underpdf = equalize_pages_extend pdf underpdf in | ||||||
|                      Cpdf.combine_pages args.fast pdf underpdf true true false |                      Cpdf.combine_pages args.fast pdf underpdf true true false | ||||||
|                  else Cpdf.stamp args.fast args.scale_stamp_to_fit false range underpdf pdf |                  else | ||||||
|  |                    Cpdf.stamp args.position args.fast args.scale_stamp_to_fit false range underpdf pdf | ||||||
|               in |               in | ||||||
|                 write_pdf false pdf (*(Pdfmarks.add_bookmarks marks pdf)*) |                 write_pdf false pdf | ||||||
|   | Some (CombinePages over) -> |   | Some (CombinePages over) -> | ||||||
|       write_pdf false |       write_pdf false | ||||||
|         (Cpdf.combine_pages args.fast (get_single_pdf args.op false) (pdfread_pdf_of_file None None over) false false true) |         (Cpdf.combine_pages args.fast (get_single_pdf args.op false) (pdfread_pdf_of_file None None over) false false true) | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								cpdfmanual.pdf
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cpdfmanual.pdf
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -6,6 +6,7 @@ | |||||||
| % Explain page labels in -page-info | % Explain page labels in -page-info | ||||||
| % Correct the documentation for -diagonal and -reverse-diagonal | % Correct the documentation for -diagonal and -reverse-diagonal | ||||||
| % Explain octal escape sequences in meta fields with -raw | % Explain octal escape sequences in meta fields with -raw | ||||||
|  | % is scale_to_fit in do_stamp documented? | ||||||
|  |  | ||||||
| \documentclass[a4paper,makeidx]{memoir} | \documentclass[a4paper,makeidx]{memoir} | ||||||
| \usepackage{palatino} | \usepackage{palatino} | ||||||
| @@ -1160,7 +1161,9 @@ writing to \texttt{out.pdf}. A watermark should go underneath each page: | |||||||
|     \small\verb!cpdf -stamp-under topsecret.pdf in.pdf -o out.pdf! |     \small\verb!cpdf -stamp-under topsecret.pdf in.pdf -o out.pdf! | ||||||
|   \end{framed} |   \end{framed} | ||||||
|  |  | ||||||
|   \noindent The \texttt{-combine-pages} operation takes two PDF files and stamps each | \noindent The position commands in \Sref{position} can be used to locate the stamp more precisely (they are calculated relative to the crop box of the stamp). Or, preprocess the stamp with \texttt{-shift} first. | ||||||
|  |  | ||||||
|  |   The \texttt{-combine-pages} operation takes two PDF files and stamps each | ||||||
| page of one over each page of the other. The length of the output is the same | page of one over each page of the other. The length of the output is the same | ||||||
| as the length of the ``under'' file. For instance: | as the length of the ``under'' file. For instance: | ||||||
|   \begin{framed} |   \begin{framed} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user