Skip to content
Browse files

Merge pull request #2 from fmw/master

support for generating documents with Apache FOP.

This is the first initial step showing how Apache FOP integration/abstraction could occur.
  • Loading branch information...
2 parents 406d031 + 249a943 commit e446b2648ecc58aaf4b936506f4e3b03ba40a0bc @KushalP committed
Showing with 934 additions and 1 deletion.
  1. +3 −1 project.clj
  2. +104 −0 resources/fo/dummy-invoice.fo
  3. +208 −0 src/camelot/fo.clj
  4. +619 −0 test/camelot/test/fo.clj
View
4 project.clj
@@ -4,5 +4,7 @@
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.4.0"]
- [org.apache.pdfbox/pdfbox "1.6.0"]]
+ [org.clojure/data.xml "0.0.3"]
+ [org.apache.pdfbox/pdfbox "1.6.0"]
+ [org.apache.xmlgraphics/fop "1.0"]]
:dev-dependencies [[lein-clojars "0.8.0"]])
View
104 resources/fo/dummy-invoice.fo
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="first" margin-right="1.5cm" margin-left="1.5cm" margin-bottom="2cm" margin-top="1cm" page-width="211mm" page-height="297mm">
+ <fo:region-body margin-top="0cm"/>
+ <fo:region-before extent="1cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="first">
+ <fo:static-content flow-name="xsl-region-before">
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">Telephone: +31 (0)6 48012240</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">Street: IJskelderstraat 30</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">Postal code: 5046 NK</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">City: Tilburg</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">Chamber of Commerce: 18068751</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">VAT: 1903.14.849.B01</fo:block>
+ <fo:block font-size="10pt" line-height="14pt" text-align="end">Bank: Rabobank 3285.04.165</fo:block>
+ </fo:static-content>
+ <fo:static-content flow-name="xsl-region-after">
+ <fo:block text-align="left" font-size="10pt" line-height="14pt"/>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block text-align="left" line-height="14pt" font-size="35pt" space-after.optimum="15pt" space-before.optimum="0pt">Vixu.com</fo:block>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt" space-after.optimum="20pt" space-before.optimum="100pt">
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">BigCo</fo:block>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Mr. John Doe</fo:block>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Harteveltstraat 1</fo:block>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">2586 EL Den Haag</fo:block>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">The Netherlands</fo:block>
+ </fo:block>
+ <fo:block text-align="left" line-height="14pt" font-weight="bold" font-size="16" space-after.optimum="20pt">Invoice</fo:block>
+ <fo:table border-width="0.5pt" space-after.optimum="30pt">
+ <fo:table-column column-width="4cm"/>
+ <fo:table-column column-width="5cm"/>
+ <fo:table-body>
+ <fo:table-row border-width="0.5pt" keep-with-next="always">
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Date:</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">05/02/2012</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Invoice number:</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">BAZ01</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:table border-width="0.5pt">
+ <fo:table-column column-width="14cm"/>
+ <fo:table-column column-width="3cm"/>
+ <fo:table-body>
+ <fo:table-row border-width="0.5pt" keep-with-next="always">
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Vixu.com basic subscription from March 1st 2012 to March 1st 2013:</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block font-size="10pt" line-height="14pt" text-align="right">€ 1188.00</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Discount (10%):</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block font-size="10pt" line-height="14pt" text-align="right">- € 118.80</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Subtotal:</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block font-size="10pt" line-height="14pt" text-align="right">€ 1070.00</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Value Added Tax (19%):</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block font-size="10pt" line-height="14pt" text-align="right">€ 203.30</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row font-weight="bold">
+ <fo:table-cell>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt">Total:</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block font-size="10pt" line-height="14pt" text-align="right">€ 1273.30</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block text-align="left" font-size="10pt" line-height="14pt" space-before.optimum="35pt">You are kindly requested to pay within 7 days. Please wire the amount due to Rabobank account number 3285.04.165.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
View
208 src/camelot/fo.clj
@@ -0,0 +1,208 @@
+;; src/camelot/fo.clj: Apache FOP support for Camelot
+;; Copyright 2012, F.M. (Filip) de Waard <fmw@vixu.com>.
+
+(ns camelot.fo
+ (:require [clojure.data.xml :as xml])
+ (:import [java.io File BufferedOutputStream FileOutputStream StringReader]
+ [org.apache.fop.apps FopFactory Fop MimeConstants]
+ [javax.xml.transform Transformer TransformerFactory]
+ [javax.xml.transform.stream StreamSource]
+ [javax.xml.transform.sax SAXResult]))
+
+(defn layout
+ "Defines a :fo:layout-master-set using the given attribute map
+ (i.e. :margin-right, :margin-left, :margin-bottom :margin-top,
+ :page-width, :page-height, region-before, region-body, region-after;
+ the last three being attribute maps for subnodes)."
+ [{:keys [margin-right
+ margin-left
+ margin-bottom
+ margin-top
+ page-width
+ page-height
+ region-body
+ region-before
+ region-after]}]
+ (xml/element
+ :fo:layout-master-set
+ {}
+ (xml/element
+ :fo:simple-page-master
+ {:master-name "first"
+ :margin-right (or margin-right "1.5cm")
+ :margin-left (or margin-left "1.5cm")
+ :margin-bottom (or margin-bottom "2cm")
+ :margin-top (or margin-top "1cm")
+ :page-width (or page-width "211mm")
+ :page-height (or page-height "297mm")}
+ (xml/element
+ :fo:region-body
+ (if (map? region-body)
+ region-body
+ {:margin-top "0cm"}))
+ (xml/element
+ :fo:region-before
+ (if (map? region-before)
+ region-before
+ {:extent "1cm"}))
+ (xml/element
+ :fo:region-after
+ (if (map? region-after)
+ region-after
+ {:extent "1.5cm"})))))
+
+(defn block?
+ "Returns true if the provided argument is a block"
+ [x]
+ (and (= (class x) clojure.data.xml.Element) (= (:tag x) :fo:block)))
+
+(defn block
+ "Defines a fo:block with the given attributes (optional) and
+ content (which is a string or a sequence of blocks)..
+
+ E.g. <fo:block font-size=\"10pt\"
+ line-height=\"14pt\"
+ text-align=\"end\">
+ Hic sunt dracones
+ </fo:block>"
+ ([content]
+ (block {} content))
+ ([attrs content]
+ (let [new-attrs (assoc attrs
+ :line-height (or (:line-height attrs) "14pt")
+ :font-size (or (:font-size attrs) "10pt")
+ :text-align (or (:text-align attrs) "left"))]
+ (cond
+ (string? content)
+ (xml/element :fo:block new-attrs content)
+ (every? block? content)
+ (apply (partial xml/element :fo:block new-attrs) content)))))
+
+(defn table-cell
+ "Defines a fo:table-cell with either a provided string
+ that returns a cell with default formatting or a (block)
+ with custom formatting."
+ [value]
+ (xml/element :fo:table-cell
+ {}
+ (cond
+ (string? value)
+ (block value)
+ (block? value)
+ value)))
+
+(defn table-row-has-attributes?
+ "Returns true if the provided sequence starts with a map
+ that isn't a block."
+ [row]
+ (and (map? (first row)) (not (block? (first row)))))
+
+(defn table-row
+ "Defines a table row with an optional first argument
+ containing an argument map, followed by the cell values."
+ [& row]
+ (let [has-attrs? (table-row-has-attributes? row)
+ attrs (if has-attrs?
+ (first row)
+ {})
+ values (if has-attrs?
+ (rest row)
+ row)]
+ (apply (partial xml/element :fo:table-row attrs)
+ (map table-cell values))))
+
+(defn table-column
+ "Defines a table column with the provided attributes (optional)
+ and width."
+ ([width]
+ (table-column {} width))
+ ([attrs width]
+ (xml/element :fo:table-column
+ (assoc attrs
+ :column-width width)
+ nil)))
+
+(defn table
+ "Defines a table with rows generated from a provided
+ sequence of vectors representing the individual rows.
+
+ If there is no attribute map provided for the first row
+ border-width=\"0.5pt\" and keep-with-next=\"always\"
+ are used by default for this row"
+ ([columns rows]
+ (table {} columns rows))
+ ([attrs columns rows]
+ (apply (partial xml/element
+ :fo:table
+ (if (not-empty attrs)
+ attrs
+ {:border-width "0.5pt"}))
+ (conj
+ (vec (map table-column columns))
+ (apply
+ (partial xml/element
+ :fo:table-body
+ {})
+ (map-indexed (fn [i row]
+ (apply
+ table-row
+ (if (and (= i 0)
+ (not (table-row-has-attributes? row)))
+ (cons {:border-width "0.5pt"
+ :keep-with-next "always"}
+ row)
+ row)))
+ rows))))))
+
+(defn region [type name & blocks]
+ "Defines a fo:static-content region with the given
+ type (:static-content or :flow) and name that contains
+ the given blocks.
+
+ E.g. <fo:static-content flow-name=\"xsl-region-before\">
+ <fo:block font-size=\"10pt\"
+ line-height=\"14pt\"
+ text-align=\"end\">Hic sunt dracones</fo:block>
+ </fo:static-content>"
+ (apply
+ (partial xml/element
+ (cond
+ (= type :static-content)
+ :fo:static-content
+ (= type :flow)
+ :fo:flow)
+ {:flow-name name})
+ blocks))
+
+(def header
+ (partial region :static-content "xsl-region-before"))
+
+(def footer
+ (partial region :static-content "xsl-region-after"))
+
+(def body
+ (partial region :flow "xsl-region-body"))
+
+(defn document
+ "Constructs a document with the :header-blocks and :footer-blocks
+ from the provided settings map and all provided blocks."
+ [settings & body-blocks]
+ (xml/element
+ :fo:root
+ {:xmlns:fo "http://www.w3.org/1999/XSL/Format"}
+ (layout (or (:layout settings) {}))
+ (xml/element
+ :fo:page-sequence {:master-reference "first"}
+ (apply header (or (:header-blocks settings) [(block "")]))
+ (apply footer (or (:footer-blocks settings) [(block "")]))
+ (apply body body-blocks))))
+
+(defn write-pdf!
+ "Writes the provided document to-file as a PDF document."
+ [document to-file]
+ (with-open [out (BufferedOutputStream. (FileOutputStream. to-file))]
+ (.transform (.newTransformer (TransformerFactory/newInstance))
+ (StreamSource. (StringReader. (xml/emit-str document)))
+ (SAXResult. (.getDefaultHandler
+ (.newFop (FopFactory/newInstance)
+ MimeConstants/MIME_PDF out))))))
View
619 test/camelot/test/fo.clj
@@ -0,0 +1,619 @@
+;; test/camelot/test/fo.clj tests for Apache FOP code
+;; Copyright 2012, F.M. (Filip) de Waard <fmw@vixu.com>.
+
+(ns camelot.test.fo
+ (:use [clojure.test]
+ [camelot.test.helpers]
+ [camelot.fo] :reload)
+ (:require [clojure.data.xml :as xml])
+ (:import [java.io File]
+ [org.apache.pdfbox.util PDFTextStripper]
+ [org.apache.pdfbox.pdmodel PDDocument]))
+
+(deftest test-layout
+ (is (= (layout {})
+ #clojure.data.xml.Element{
+ :tag :fo:layout-master-set
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:simple-page-master
+ :attrs {:master-name "first"
+ :margin-right "1.5cm"
+ :margin-left "1.5cm"
+ :margin-bottom "2cm"
+ :margin-top "1cm"
+ :page-width "211mm"
+ :page-height "297mm"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:region-body
+ :attrs {:margin-top "0cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:region-before
+ :attrs {:extent "1cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:region-after
+ :attrs {:extent "1.5cm"}
+ :content ()})})}))
+
+ (is (= (layout {:margin-right "1:42cm"
+ :margin-left "2:42cm"
+ :margin-bottom "3:42cm"
+ :margin-top "4:42cm"
+ :page-width "5:42mm"
+ :page-height "6:42mm"
+ :region-body {:margin-top "3cm"}
+ :region-before {:extent "2cm"}
+ :region-after {:extent "1cm"}})
+ #clojure.data.xml.Element{
+ :tag :fo:layout-master-set
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:simple-page-master
+ :attrs {:master-name "first"
+ :margin-right "1:42cm"
+ :margin-left "2:42cm"
+ :margin-bottom "3:42cm"
+ :margin-top "4:42cm"
+ :page-width "5:42mm"
+ :page-height "6:42mm"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:region-body
+ :attrs {:margin-top "3cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:region-before
+ :attrs {:extent "2cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:region-after
+ :attrs {:extent "1cm"}
+ :content ()})})})))
+
+(deftest test-block?
+ (are [v] (not (block? v))
+ nil
+ {:tag :fo:block}
+ (xml/element :fo:not-block {} "hey"))
+
+ (are [v] (block? v)
+ (xml/element :fo:block {} "hey")
+ (block "hey")))
+
+(deftest test-block
+ (is (= (block "Hic sunt dracones.")
+ #clojure.data.xml.Element{:tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("Hic sunt dracones.")}))
+
+ (is (= (block {:space-before.optimum "10pt"
+ :space-after.optimum "20pt"
+ :text-align "end"}
+ "Hic sunt dracones.")
+ #clojure.data.xml.Element{:tag :fo:block
+ :attrs {:text-align "end"
+ :font-size "10pt"
+ :line-height "14pt"
+ :space-before.optimum "10pt"
+ :space-after.optimum "20pt"}
+ :content ("Hic sunt dracones.")}))
+
+ (is (= (block [(block "A") (block "B")])
+ #clojure.data.xml.Element{:tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content
+ (#clojure.data.xml.Element{:tag
+ :fo:block
+ :attrs
+ {:text-align
+ "left"
+ :font-size
+ "10pt"
+ :line-height
+ "14pt"}
+ :content
+ ("A")}
+ #clojure.data.xml.Element{:tag
+ :fo:block
+ :attrs
+ {:text-align
+ "left"
+ :font-size
+ "10pt"
+ :line-height
+ "14pt"}
+ :content
+ ("B")})})))
+
+(deftest test-table-cell
+ (is (= (table-cell "Foo")
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag
+ :fo:block
+ :attrs
+ {:text-align
+ "left"
+ :font-size
+ "10pt"
+ :line-height
+ "14pt"}
+ :content
+ ("Foo")})}))
+
+ (is (= (table-cell (block {:text-align "right"} "Foo"))
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag
+ :fo:block
+ :attrs
+ {:text-align
+ "right"
+ :font-size
+ "10pt"
+ :line-height
+ "14pt"}
+ :content
+ ("Foo")})})))
+
+(deftest test-table-row-has-attributes?
+ (is (table-row-has-attributes? [{:foo "bar"} "A" "B" "C"]))
+ (are [v] (not (table-row-has-attributes? v))
+ [(block "A") "B" "C"]
+ ["A" "B" "C"]))
+
+(deftest test-table-row
+ (is
+ (=
+ (table-row "A" "B" "C")
+ #clojure.data.xml.Element{:tag :fo:table-row
+ :attrs
+ {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("A")})}
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("B")})}
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("C")})})}))
+
+ (is
+ (=
+ (table-row (block {:font-weight "bold"} "A") "B" "C")
+ #clojure.data.xml.Element{:tag :fo:table-row
+ :attrs
+ {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"
+ :font-weight "bold"}
+ :content ("A")})}
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("B")})}
+ #clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("C")})})}))
+
+ (is
+ (=
+ (table-row {:border-width "0.5pt" :keep-with-next "always"}
+ (block {:font-weight "bold"} "A"))
+ #clojure.data.xml.Element{:tag :fo:table-row
+ :attrs
+ {:border-width "0.5pt" :keep-with-next "always"}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{:tag :fo:block
+ :attrs
+ {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"
+ :font-weight "bold"}
+ :content ("A")})})})))
+
+(deftest test-table-column
+ (is (= (table-column "10cm")
+ #clojure.data.xml.Element{:tag :fo:table-column
+ :attrs {:column-width "10cm"}
+ :content ()}))
+
+ (is (= (table-column {:column-height "3cm"} "10cm")
+ #clojure.data.xml.Element{:tag :fo:table-column
+ :attrs {:column-width "10cm"
+ :column-height "3cm"}
+ :content ()})))
+
+(deftest test-table
+ (is (= (table ["5cm" "5cm"] [["a" "b"] ["c" "d"]])
+ #clojure.data.xml.Element{
+ :tag :fo:table
+ :attrs {:border-width "0.5pt"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-body
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {:border-width "0.5pt"
+ :keep-with-next "always"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("a")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("b")})})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("c")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("d")})})})})}))
+
+ (is (= (table {:foo "bar"} ["5cm" "5cm"] [["a" "b"] ["c" "d"]])
+ #clojure.data.xml.Element{
+ :tag :fo:table
+ :attrs {:foo "bar"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-body
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {:border-width "0.5pt"
+ :keep-with-next "always"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("a")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("b")})})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("c")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("d")})})})})}))
+
+ (is (= (table ["5cm" "5cm"] [[{:foo "bar"} "a" "b"] ["c" "d"]])
+ #clojure.data.xml.Element{
+ :tag :fo:table
+ :attrs {:border-width "0.5pt"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-column
+ :attrs {:column-width "5cm"}
+ :content ()}
+ #clojure.data.xml.Element{
+ :tag :fo:table-body
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {:foo "bar"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("a")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("b")})})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-row
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("c")})}
+ #clojure.data.xml.Element{
+ :tag :fo:table-cell
+ :attrs {}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("d")})})})})})))
+
+(deftest test-region
+ (is (= (region :static-content "xsl-region-before")
+ (header)
+ #clojure.data.xml.Element{:tag :fo:static-content
+ :attrs {:flow-name "xsl-region-before"}
+ :content ()}))
+
+ (is (= (region :static-content "xsl-region-before" (block "A") (block "B"))
+ (header (block "A") (block "B"))
+ #clojure.data.xml.Element{:tag :fo:static-content
+ :attrs {:flow-name "xsl-region-before"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("A")}
+ #clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("B")})}))
+
+
+ (is (= (region :static-content "xsl-region-after")
+ (footer)
+ #clojure.data.xml.Element{:tag :fo:static-content
+ :attrs {:flow-name "xsl-region-after"}
+ :content ()}))
+
+ (is (= (region :static-content "xsl-region-after" (block "A") (block "B"))
+ (footer (block "A") (block "B"))
+ #clojure.data.xml.Element{:tag :fo:static-content
+ :attrs {:flow-name "xsl-region-after"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("A")}
+ #clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("B")})}))
+
+ (is (= (region :flow "xsl-region-body")
+ (body)
+ #clojure.data.xml.Element{:tag :fo:flow
+ :attrs {:flow-name "xsl-region-body"}
+ :content ()}))
+
+ (is (= (region :flow "xsl-region-body" (block "A") (block "B"))
+ (body (block "A") (block "B"))
+ #clojure.data.xml.Element{:tag :fo:flow
+ :attrs {:flow-name "xsl-region-body"}
+ :content
+ (#clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("A")}
+ #clojure.data.xml.Element{
+ :tag :fo:block
+ :attrs {:text-align "left"
+ :font-size "10pt"
+ :line-height "14pt"}
+ :content ("B")})})))
+
+(deftest test-document
+ (is
+ (=
+ (slurp "resources/fo/dummy-invoice.fo")
+ (xml/indent-str
+ (document {:header-blocks (map #(block {:text-align "end"} %)
+ ["Telephone: +31 (0)6 48012240"
+ "Street: IJskelderstraat 30"
+ "Postal code: 5046 NK"
+ "City: Tilburg"
+ "Chamber of Commerce: 18068751"
+ "VAT: 1903.14.849.B01"
+ "Bank: Rabobank 3285.04.165"])}
+ (block {:font-size "35pt"
+ :space-before.optimum "0pt"
+ :space-after.optimum "15pt"}
+ "Vixu.com")
+ (block {:space-before.optimum "100pt"
+ :space-after.optimum "20pt"}
+ (map block ["BigCo"
+ "Mr. John Doe"
+ "Harteveltstraat 1"
+ "2586 EL Den Haag"
+ "The Netherlands"]))
+ (block {:font-weight "bold"
+ :font-size "16"
+ :space-after.optimum "20pt"}
+ "Invoice")
+ (table {:border-width "0.5pt" :space-after.optimum "30pt"}
+ ["4cm" "5cm"]
+ [["Date:" "05/02/2012"]
+ ["Invoice number:" "BAZ01"]])
+ (table {:border-width "0.5pt"}
+ ["14cm" "3cm"]
+ [[(str "Vixu.com basic subscription from "
+ "March 1st 2012 to March 1st 2013:")
+ (block {:text-align "right"} "1188.00")]
+ ["Discount (10%):"
+ (block {:text-align "right"} "- € 118.80")]
+ ["Subtotal:"
+ (block {:text-align "right"} "1070.00")]
+ ["Value Added Tax (19%):"
+ (block {:text-align "right"} "203.30")]
+ [{:font-weight "bold"}
+ "Total:"
+ (block {:text-align "right"} "1273.30")]])
+ (block {:space-before.optimum "35pt"}
+ (str "You are kindly requested to pay within 7 days. "
+ "Please wire the amount due to Rabobank account "
+ "number 3285.04.165.")))))))
+
+(deftest test-write-pdf!
+ (let [filename (temp-pdf-filename)]
+ (do
+ (write-pdf!
+ (document {}
+ (block {:font-size "35pt"
+ :space-before.optimum "0pt"
+ :space-after.optimum "15pt"}
+ "Vixu.com"))
+ filename))
+
+ (is (= (.getText (PDFTextStripper.)
+ (PDDocument/load
+ (File. filename)))
+ "Vixu.com\n"))))

0 comments on commit e446b26

Please sign in to comment.
Something went wrong with that request. Please try again.