From 37bbe3f3989112bb5907e488ad8ebfb40f034175 Mon Sep 17 00:00:00 2001 From: gered Date: Wed, 16 Aug 2017 18:46:31 -0400 Subject: [PATCH] move watermark functionality to separate namespace --- src/clj_htmltopdf/core.clj | 75 +++------------------------------ src/clj_htmltopdf/watermark.clj | 67 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 src/clj_htmltopdf/watermark.clj diff --git a/src/clj_htmltopdf/core.clj b/src/clj_htmltopdf/core.clj index 1aa1504..a3f8639 100644 --- a/src/clj_htmltopdf/core.clj +++ b/src/clj_htmltopdf/core.clj @@ -2,18 +2,13 @@ (:require [clojure.java.io :as io] [hiccup.page :as h] - [clj-htmltopdf.options :as o]) + [clj-htmltopdf.options :as o] + [clj-htmltopdf.watermark :as w]) (:import - [java.awt.geom Point2D$Float] [java.io InputStream OutputStream PipedInputStream PipedOutputStream] [com.openhtmltopdf DOMBuilder] [com.openhtmltopdf.pdfboxout PdfRendererBuilder] [com.openhtmltopdf.util XRLog] - [org.apache.pdfbox.pdmodel PDDocument PDPage PDPageContentStream PDPageContentStream$AppendMode] - [org.apache.pdfbox.pdmodel.common PDRectangle] - [org.apache.pdfbox.pdmodel.font PDType1Font PDFont] - [org.apache.pdfbox.pdmodel.graphics.state PDExtendedGraphicsState] - [org.apache.pdfbox.util Matrix] [org.jsoup Jsoup] [org.jsoup.nodes Document])) @@ -52,7 +47,7 @@ html-doc)) (defn write-pdf! - [^Document html-doc ^String base-uri] + ^InputStream [^Document html-doc ^String base-uri] (let [builder (PdfRendererBuilder.)] (.withW3cDocument builder (DOMBuilder/jsoup2DOM html-doc) base-uri) (let [piped-in (PipedInputStream.) @@ -63,71 +58,15 @@ (.run builder))) piped-in))) -(def watermark-fonts - {"times-roman" PDType1Font/TIMES_ROMAN - "times-bold" PDType1Font/TIMES_BOLD - "times-italic" PDType1Font/TIMES_ITALIC - "times-bolditalic" PDType1Font/TIMES_BOLD_ITALIC - "helvetica" PDType1Font/HELVETICA - "helvetica-bold" PDType1Font/HELVETICA_BOLD - "helvetica-oblique" PDType1Font/HELVETICA_OBLIQUE - "helvetica-boldoblique" PDType1Font/HELVETICA_BOLD_OBLIQUE - "courier" PDType1Font/COURIER - "courier-bold" PDType1Font/COURIER_BOLD - "courier-oblique" PDType1Font/COURIER_OBLIQUE - "courier-boldoblique" PDType1Font/COURIER_BOLD_OBLIQUE}) - -(defn render-watermark! - [^PDPage page ^PDPageContentStream cs options] - (let [font (or (get watermark-fonts (:font options)) - (get watermark-fonts "helvetica-bold")) - font-size (float (or (:font-size options) 36.0)) - font-color (or (:color options) [0 0 0]) - text (:text options) - text-width (/ (* (.getStringWidth font text) font-size) 1000.0) - text-height (/ (* (.getHeight (.getFontBoundingBox (.getFontDescriptor ^PDFont font))) font-size) 1000.0) - rotation (float (or (:rotation options) 0)) - page-size (.getMediaBox page) - page-width (.getWidth page-size) - page-height (.getHeight page-size) - x (if (= :center (:x options)) (/ page-width 2) (float (:x options))) - y (if (= :center (:y options)) (/ page-height 2) (float (:y options))) - transform (doto (Matrix.) - (.translate x y) - (.rotate (Math/toRadians rotation)) - (.translate (- (/ text-width 2)) (- (/ text-height 2))))] - (when (:opacity options) - (let [r0 (PDExtendedGraphicsState.)] - (.setNonStrokingAlphaConstant r0 (float (:opacity options))) - (.setGraphicsStateParameters cs r0))) - (.beginText cs) - (.setFont cs font font-size) - (.setNonStrokingColor cs (int (nth font-color 0)) (int (nth font-color 1)) (int (nth font-color 2))) - (.setTextMatrix cs transform) - (.showText cs text) - (.endText cs))) - -(defn write-watermark! - [^InputStream pdf ^OutputStream out {:keys [watermark] :as options}] - (with-open [doc (PDDocument/load pdf)] - (doseq [^PDPage page (.getPages doc)] - (let [cs (PDPageContentStream. doc page PDPageContentStream$AppendMode/APPEND true true)] - (with-open [cs cs] - (if (map? watermark) - (render-watermark! page cs watermark) - (watermark page cs))))) - (.save doc out) - out)) - (defn ->pdf [in out & [options]] (let [options (o/get-final-options options) html-doc (prepare-html in options)] (configure-logging! options) - (let [result (write-pdf! html-doc (o/->base-uri options)) - out (->output-stream out)] + (let [pdf (write-pdf! html-doc (o/->base-uri options)) + out (->output-stream out)] (if (:watermark options) - (write-watermark! result out options) + (w/write-watermark! pdf out options) (with-open [os out] - (io/copy result os) + (io/copy pdf os) os))))) diff --git a/src/clj_htmltopdf/watermark.clj b/src/clj_htmltopdf/watermark.clj new file mode 100644 index 0000000..ef46dae --- /dev/null +++ b/src/clj_htmltopdf/watermark.clj @@ -0,0 +1,67 @@ +(ns clj-htmltopdf.watermark + (:import + [java.io InputStream OutputStream] + [org.apache.pdfbox.pdmodel PDDocument PDPage PDPageContentStream PDPageContentStream$AppendMode] + [org.apache.pdfbox.pdmodel.common PDRectangle] + [org.apache.pdfbox.pdmodel.font PDType1Font PDFont] + [org.apache.pdfbox.pdmodel.graphics.state PDExtendedGraphicsState] + [org.apache.pdfbox.util Matrix])) + +; TODO: this is a temporary measure to allow at least _some_ font customizability for watermarks +; until something more comprehensive can be implemented such as allowing loading of external +; TTF fonts via PDTrueTypeFont.loadTTF() +(def watermark-fonts + {"times-roman" PDType1Font/TIMES_ROMAN + "times-bold" PDType1Font/TIMES_BOLD + "times-italic" PDType1Font/TIMES_ITALIC + "times-bolditalic" PDType1Font/TIMES_BOLD_ITALIC + "helvetica" PDType1Font/HELVETICA + "helvetica-bold" PDType1Font/HELVETICA_BOLD + "helvetica-oblique" PDType1Font/HELVETICA_OBLIQUE + "helvetica-boldoblique" PDType1Font/HELVETICA_BOLD_OBLIQUE + "courier" PDType1Font/COURIER + "courier-bold" PDType1Font/COURIER_BOLD + "courier-oblique" PDType1Font/COURIER_OBLIQUE + "courier-boldoblique" PDType1Font/COURIER_BOLD_OBLIQUE}) + +(defn render-watermark! + [^PDPage page ^PDPageContentStream cs options] + (let [font (or (get watermark-fonts (:font options)) + (get watermark-fonts "helvetica-bold")) + font-size (float (or (:font-size options) 36.0)) + font-color (or (:color options) [0 0 0]) + text (:text options) + text-width (/ (* (.getStringWidth font text) font-size) 1000.0) + text-height (/ (* (.getHeight (.getFontBoundingBox (.getFontDescriptor ^PDFont font))) font-size) 1000.0) + rotation (float (or (:rotation options) 0)) + page-size (.getMediaBox page) + page-width (.getWidth page-size) + page-height (.getHeight page-size) + x (if (= :center (:x options)) (/ page-width 2) (float (:x options))) + y (if (= :center (:y options)) (/ page-height 2) (float (:y options))) + transform (doto (Matrix.) + (.translate x y) + (.rotate (Math/toRadians rotation)) + (.translate (- (/ text-width 2)) (- (/ text-height 2))))] + (when (:opacity options) + (let [r0 (PDExtendedGraphicsState.)] + (.setNonStrokingAlphaConstant r0 (float (:opacity options))) + (.setGraphicsStateParameters cs r0))) + (.beginText cs) + (.setFont cs font font-size) + (.setNonStrokingColor cs (int (nth font-color 0)) (int (nth font-color 1)) (int (nth font-color 2))) + (.setTextMatrix cs transform) + (.showText cs text) + (.endText cs))) + +(defn write-watermark! + [^InputStream pdf ^OutputStream out {:keys [watermark] :as options}] + (with-open [doc (PDDocument/load pdf)] + (doseq [^PDPage page (.getPages doc)] + (let [cs (PDPageContentStream. doc page PDPageContentStream$AppendMode/APPEND true true)] + (with-open [cs cs] + (if (map? watermark) + (render-watermark! page cs watermark) + (watermark page cs))))) + (.save doc out) + out))