Repository: dotemacs/pdfboxing
Pull request #11 (2015-06-08 20:52:55)

Added namespace for splitting pdfs.
Had to use ByteArrays to ensure AcroFields were carried over to merged PDF. 


Patch:
diff --git a/.nrepl-port b/.nrepl-port
deleted file mode 100644
index 849edab..0000000
--- a/.nrepl-port
+++ /dev/null
@@ -1,1 +0,0 @@
-52242
\ No newline at end of file
diff --git a/README.md b/README.md
index 1b5768b..f29aa49 100644
--- a/README.md
+++ b/README.md
@@ -26,9 +26,23 @@ Clojure PDF manipulation library & wrapper for [PDFBox](http://pdfbox.apache.org
 ### Split a PDF into mutliple PDDocuments
 ```clojure
  (require '[pdfboxing.split :as pdf])
- (pdf/split-pdfs {:input "pdfs/clojure.pdf" :start 1 :end 8}) ;=> (list of PDDocument pages 1 through 8)
- (pdf/split-pdfs {:input "pdfs/clojure.pdf"}) ;=> splits the pdf into single pages as a list of PDDocument
- ```
+```
+List of PDDocument pages 1 through 8
+```clojure
+ (pdf/split-pdf :input "pdfs/clojure.pdf" :start 1 :end 8)
+```
+Splits the PDF into single pages as a list of PDDocument
+```clojure
+ (pdf/split-pdf :input "pdfs/clojure.pdf")
+```
+Splits the PDF in half and writes them to disk as clojure-1.pdf and clojure-2.pdf
+```clojure
+ (pdf/split-pdf-at :input "pdfs/clojure.pdf")
+```
+Splits into two PDFs, the first having 5 pages and second has rest
+```clojure
+ (pdf/split-pdf-at :input "pdfs/clojure.pdf" :split 5)
+```
 
 ### List form fields of a PDF
 
diff --git a/src/pdfboxing/merge.clj b/src/pdfboxing/merge.clj
index 8ebe012..f3290f5 100644
--- a/src/pdfboxing/merge.clj
+++ b/src/pdfboxing/merge.clj
@@ -34,4 +34,4 @@
       (doseq [f input]
         (.addSource merger (FileInputStream. (File. f))))
       (.setDestinationFileName merger output)
-    (.mergeDocuments merger)))
+      (.mergeDocuments merger)))
diff --git a/src/pdfboxing/split.clj b/src/pdfboxing/split.clj
index 80f6456..3d630a6 100644
--- a/src/pdfboxing/split.clj
+++ b/src/pdfboxing/split.clj
@@ -1,8 +1,9 @@
 (ns pdfboxing.split
   (:require [pdfboxing.common :as common]
-            [pdfboxing.merge :as merge])
-  (:import [org.apache.pdfbox.util Splitter]
-           [java.io File FileInputStream]))
+            [pdfboxing.merge :as merge]
+            [pdfboxing.info :as info])
+  (:import [org.apache.pdfbox.util Splitter PDFMergerUtility]
+           [org.apache.pdfbox.pdmodel PDDocument]))
 
 (defn check-if-integer
   [coll]
@@ -10,19 +11,54 @@
     true
     (merge/throw-exception ":start and :end may only be integers")))
 
-
-(defn arg-check [input start end]
-  (let [int-args [start end]]
+(defn arg-check [input start end split]
+  (let [int-args [start end split]]
     (if (string? input)
       (merge/check-for-pdfs  [input])
       (merge/throw-exception "input must be a string"))
     (check-if-integer (filter (complement nil?) int-args))))
 
+(defn pddocument->byte-array
+  "Given a PDDocument, produce an InputStream"
+  [doc]
+  (with-open [stream (java.io.ByteArrayOutputStream.)]
+    (.save doc stream)
+    (.toByteArray stream)))
+
+(defn byte-array->input-stream
+  [ba]
+  (java.io.ByteArrayInputStream. ba))
+
+(defn pddocument->input-stream
+  [doc]
+  (byte-array->input-stream (pddocument->byte-array doc)))
+
+(defn merge-pddocuments
+  "Given a list of PDDocument, merge them and save."
+  [& {:keys [docs output]}]
+  (let [merger (PDFMergerUtility.)
+        sources (map pddocument->input-stream docs)]
+    (.addSources merger sources)
+    (.setDestinationFileName merger output)
+    (.mergeDocuments merger)))
+
 (defn split-pdf
   "split pdf into pages"
-  [{:keys [input start end] }]
-  {:pre [(arg-check input start end)]}
-  (let [splitter (Splitter.)]
+  [& {:keys [input start end split]}]
+  {:pre [(arg-check input start end split)]}
+  (let [doc (common/load-pdf input)
+        splitter (Splitter.)]
     (when start (.setStartPage splitter start))
     (when end (.setEndPage splitter end))
-    (.split splitter (common/load-pdf input))))
+    (when split (.setSplitAtPage splitter split))
+    (into [] (.split splitter doc))))
+
+(defn split-pdf-at
+  "splits a pdf into two documents and writes them to disk"
+  [& {:keys [input split]}]
+  (let [base-name (first (clojure.string/split input #".pdf"))
+        f-names (for [x (range 1 3)] (str base-name "-" x ".pdf"))
+        pages (split-pdf :input input)
+        doc-1 (take (or split (/ (count pages) 2)) pages)
+        doc-2 (drop (or split (/ (count pages) 2)) pages)]
+    (map #(merge-pddocuments :docs %1 :output %2) [doc-1 doc-2] f-names)))


Test patch:
diff --git a/test/pdfboxing/split_test.clj b/test/pdfboxing/split_test.clj
index e8207b3..7a5da11 100644
--- a/test/pdfboxing/split_test.clj
+++ b/test/pdfboxing/split_test.clj
@@ -4,10 +4,29 @@
             [pdfboxing.common :as common]
             [pdfboxing.split :as split]))
 
+(deftest pdf-input-arg-check
+  "Test to ensure that the invalid input throws meaningful exception"
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input 12 :start 1 :end 2)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input 12)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input 12 :start 1)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input 12 :end 2)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input 12 :start 0)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :start 1 :end 2)))
+  (is (thrown? IllegalArgumentException #"input must be a string" (split/split-pdf :input []))))
+
+(deftest pdf-start-arg-check
+  "Test to ensure that :start and :end are valid integers"
+  (is (thrown? IllegalArgumentException #":start and :end may only be integers" (split/split-pdf :input "test/pdfs/multi-page.pdf" :start :end)))
+  (is (thrown? IllegalArgumentException #":start and :end may only be integers" (split/split-pdf :input "test/pdfs/multi-page.pdf" :end "foo")))
+  (is (thrown? IllegalArgumentException #":start and :end may only be integers" (split/split-pdf :input "test/pdfs/multi-page.pdf" :start "bar")))
+  (is (thrown? IllegalArgumentException #":start and :end may only be integers" (split/split-pdf :input "test/pdfs/multi-page.pdf" :end "foo" :start "bar"))))
+
+
 (deftest pdf-file-split
   "Test that the multi-page PDF was turned into multiple PDDocuments"
   (let [file "tests/pdfs/multi-page.pdf"
-        splitting-outcome (split/split-pdf {:input "test/pdfs/multi-page.pdf"})]
+        splitting-outcome (split/split-pdf :input "test/pdfs/multi-page.pdf")]
     (is (true? (> (count splitting-outcome) 0)))
     (doall (for [doc splitting-outcome]
              (is (instance? org.apache.pdfbox.pdmodel.PDDocument doc))))))
+