render basic page/pdf options as css so they take effect in output pdfs

This commit is contained in:
Gered 2017-04-02 12:07:00 -04:00
parent 0b3ba9ccce
commit 48354a2ce9
2 changed files with 88 additions and 16 deletions

View file

@ -1,13 +1,18 @@
(ns clj-htmltopdf.core (ns clj-htmltopdf.core
(:require (:require
[clojure.java.io :as io] [clojure.java.io :as io]
[hiccup.page :as h]) [clojure.string :as string]
[hiccup.page :as h]
[clj-htmltopdf.css :as css]
[clj-htmltopdf.options :as o])
(:import (:import
[java.io OutputStream] [java.io OutputStream]
[com.openhtmltopdf DOMBuilder] [com.openhtmltopdf DOMBuilder]
[com.openhtmltopdf.pdfboxout PdfRendererBuilder] [com.openhtmltopdf.pdfboxout PdfRendererBuilder]
[com.openhtmltopdf.util XRLog] [com.openhtmltopdf.util XRLog]
[org.jsoup Jsoup])) [org.jsoup Jsoup]
[org.jsoup.nodes Document Element]
[org.jsoup.parser Tag]))
(defn- read-html (defn- read-html
[in] [in]
@ -29,35 +34,36 @@
(catch Exception ex (catch Exception ex
(throw (Exception. "Error preparing an OutputStream from output given." ex))))) (throw (Exception. "Error preparing an OutputStream from output given." ex)))))
(defn- parse-html5 (defn- parse-jsoup-html
[^String html] [^String html]
(try (try
(let [parsed-doc (Jsoup/parse html)] (Jsoup/parse html)
(DOMBuilder/jsoup2DOM parsed-doc))
(catch Exception ex (catch Exception ex
(throw (Exception. "Error parsing input as HTML5." ex))))) (throw (Exception. "Error parsing input HTML to Jsoup Document." ex)))))
(def default-options (defn- set-jsoup-html-doc
{:logging? false [^PdfRendererBuilder builder jsoup-doc base-uri]
:base-uri "" (try
:include-base-css? true (let [doc (DOMBuilder/jsoup2DOM jsoup-doc)]
:page {:size :letter (.withW3cDocument builder doc base-uri))
:orientation :portrait}}) (catch Exception ex
(throw (Exception. "Error setting org.w3c.dom.Document HTML." ex)))))
(defn ->pdf (defn ->pdf
[in out & [options]] [in out & [options]]
(let [options (merge default-options options) (let [options (merge o/default-options options)
builder (PdfRendererBuilder.) builder (PdfRendererBuilder.)
base-uri (str (:base-uri options))
html (read-html in) html (read-html in)
html-doc (parse-html5 html) html-doc (parse-jsoup-html html)
html-doc (o/inject-options-into-html html-doc options)
output (->output-stream out)] output (->output-stream out)]
html-doc
(if (:logging? options) (if (:logging? options)
(let [logger (:logger options)] (let [logger (:logger options)]
(if logger (XRLog/setLoggerImpl logger)) (if logger (XRLog/setLoggerImpl logger))
(XRLog/setLoggingEnabled true)) (XRLog/setLoggingEnabled true))
(XRLog/setLoggingEnabled false)) (XRLog/setLoggingEnabled false))
(.withW3cDocument builder html-doc base-uri) (set-jsoup-html-doc builder html-doc (o/->base-uri options))
(with-open [os output] (with-open [os output]
(.toStream builder os) (.toStream builder os)
(try (try

View file

@ -0,0 +1,66 @@
(ns clj-htmltopdf.options
(:require
[clojure.java.io :as io]
[clojure.string :as string]
[clj-htmltopdf.css :as css])
(:import
[org.jsoup.nodes Document Element]
[org.jsoup.parser Tag]))
(def default-options
{:logging? false
:base-uri ""
:include-base-css? true
:page {:size :letter
:orientation :portrait
:margin "1.0in"}})
(defn ->base-uri
[options]
(str (:base-uri options)))
(defn ->page-size-css
[{:keys [size orientation] :as page-options}]
(if (or size orientation)
(string/trim
(str
(cond
(keyword? size) (name size)
(sequential? size) (string/join " " size)
:else size)
" "
(if (keyword? orientation)
(name orientation)
orientation)))))
(defn page-options->css
[page-options]
(let [styles (->> [[:size (->page-size-css page-options)]]
(into [])
(remove #(nil? (second %)))
(reduce #(assoc %1 (first %2) (second %2)) {}))]
[["@page" styles]]))
(defn append-page-options-style-tag
^Element [^Element parent options]
(let [styles (-> (:page options)
(page-options->css)
(css/css->str))
element (.appendElement parent "style")]
(.attr element "type" "text/css")
(.text element styles)))
(defn append-base-css-link-tag
^Element [^Element parent]
(let [element (.appendElement parent "link")]
(.attr element "type" "text/css")
(.attr element "rel" "stylesheet")
(.attr element "href" (str (io/resource "htmltopdf-base.css")))))
(defn inject-options-into-html
[^Document doc options]
(let [base-uri (->base-uri options)
head-tag (-> doc (.select "head") (.first))]
(append-page-options-style-tag head-tag options)
(if (:include-base-css? options) (append-base-css-link-tag head-tag))
doc))