diff --git a/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl b/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl index f258ed70b..62d8cd71e 100644 --- a/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl +++ b/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl @@ -288,6 +288,14 @@ lower corner.
  • pdfpage: Page to import from the PDF file.
  • +If you want to have this watermark in the foreground you should use +[@htmlCode] + +[/@htmlCode] + + +To have this placed on every page, you should put the object into the header of the page. + [@h3]JFreeGraph[/@h3] For simple charts you can use the builtin objects for JFreeGraph. Note: You must specify the dependency to JFreeMarker @@ -341,4 +349,4 @@ Note: This only works in Acrobat Reader, all other PDF Viewer ignore this featur - \ No newline at end of file + diff --git a/openhtmltopdf-examples/src/main/resources/freemarker/watermark.pdf b/openhtmltopdf-examples/src/main/resources/freemarker/watermark.pdf new file mode 100644 index 000000000..d867731da Binary files /dev/null and b/openhtmltopdf-examples/src/main/resources/freemarker/watermark.pdf differ diff --git a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/StandardObjectDrawerFactory.java b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/StandardObjectDrawerFactory.java index b14c5af4a..21bbc0b7f 100644 --- a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/StandardObjectDrawerFactory.java +++ b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/StandardObjectDrawerFactory.java @@ -3,6 +3,7 @@ import com.openhtmltopdf.objects.jfreechart.JFreeChartBarDiagramObjectDrawer; import com.openhtmltopdf.objects.jfreechart.JFreeChartPieDiagramObjectDrawer; import com.openhtmltopdf.objects.pdf.MergeBackgroundPdfDrawer; +import com.openhtmltopdf.objects.pdf.ForegroundPdfDrawer; import com.openhtmltopdf.render.DefaultObjectDrawerFactory; /** @@ -14,6 +15,7 @@ public static void registerStandardObjects(DefaultObjectDrawerFactory factory) { factory.registerDrawer("jfreechart/pie", new JFreeChartPieDiagramObjectDrawer()); factory.registerDrawer("jfreechart/bar", new JFreeChartBarDiagramObjectDrawer()); factory.registerDrawer("pdf/background",new MergeBackgroundPdfDrawer()); + factory.registerDrawer("pdf/foreground", new ForegroundPdfDrawer()); } public StandardObjectDrawerFactory() { diff --git a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/ForegroundPdfDrawer.java b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/ForegroundPdfDrawer.java new file mode 100644 index 000000000..5da1cb53d --- /dev/null +++ b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/ForegroundPdfDrawer.java @@ -0,0 +1,67 @@ +package com.openhtmltopdf.objects.pdf; + +import com.openhtmltopdf.extend.OutputDevice; +import com.openhtmltopdf.pdfboxout.PdfBoxOutputDevice; +import com.openhtmltopdf.render.RenderingContext; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.multipdf.LayerUtility; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; +import org.apache.pdfbox.util.Charsets; +import org.w3c.dom.Element; + +import java.awt.*; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +public class ForegroundPdfDrawer extends PdfDrawerBase +{ + @Override + public Map drawObject(Element e, double x, double y, double width, double height, + OutputDevice outputDevice, RenderingContext ctx, int dotsPerPixel) + { + + /* + * We can only do something if this is a PDF. + */ + if (!(outputDevice instanceof PdfBoxOutputDevice)) + return null; + + PdfBoxOutputDevice pdfBoxOutputDevice = (PdfBoxOutputDevice) outputDevice; + + try + { + LayerUtility layerUtility = new LayerUtility(pdfBoxOutputDevice.getWriter()); + PDFormXObject pdFormXObject = importPageAsXForm(ctx, e, pdfBoxOutputDevice, + layerUtility); + PDPage page = pdfBoxOutputDevice.getPage(); + + /* + * This ensures that the Contents of the page is a COSArray. The first entry in + * the array is just a save state (e.g. 'q'), the last one is just a restore 'Q'. + * We can override that to add the XForm. + */ + layerUtility.wrapInSaveRestore(page); + COSArray cosArray = (COSArray) page.getCOSObject() + .getDictionaryObject(COSName.CONTENTS); + + COSStream restoreStateAndPlaceWatermark = (COSStream) cosArray.get(cosArray.size() - 1); + OutputStream watermarkOutputStream = restoreStateAndPlaceWatermark.createOutputStream(); + watermarkOutputStream.write("Q\nq\n".getBytes(Charsets.US_ASCII)); + COSName name = page.getResources().add(pdFormXObject); + name.writePDF(watermarkOutputStream); + watermarkOutputStream.write(' '); + watermarkOutputStream.write("Do\n".getBytes(Charsets.US_ASCII)); + watermarkOutputStream.write("Q\n".getBytes(Charsets.US_ASCII)); + watermarkOutputStream.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + return null; + } +} diff --git a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/MergeBackgroundPdfDrawer.java b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/MergeBackgroundPdfDrawer.java index 024bb995f..d68d0f13e 100644 --- a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/MergeBackgroundPdfDrawer.java +++ b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/MergeBackgroundPdfDrawer.java @@ -2,126 +2,71 @@ import java.awt.*; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; import java.util.Map; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSStream; -import org.apache.pdfbox.io.RandomAccessBuffer; import org.apache.pdfbox.multipdf.LayerUtility; -import org.apache.pdfbox.pdfparser.PDFParser; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.util.Charsets; import org.w3c.dom.Element; -import com.openhtmltopdf.extend.FSObjectDrawer; import com.openhtmltopdf.extend.OutputDevice; import com.openhtmltopdf.pdfboxout.PdfBoxOutputDevice; import com.openhtmltopdf.render.RenderingContext; -public class MergeBackgroundPdfDrawer implements FSObjectDrawer { - private final Map>> formMap = new HashMap>>(); +public class MergeBackgroundPdfDrawer extends PdfDrawerBase +{ - @Override - public Map drawObject(Element e, double x, double y, double width, double height, - OutputDevice outputDevice, RenderingContext ctx, int dotsPerPixel) { + @Override + public Map drawObject(Element e, double x, double y, double width, double height, + OutputDevice outputDevice, RenderingContext ctx, int dotsPerPixel) + { - /* - * We can only do something if this is a PDF. - */ - if (!(outputDevice instanceof PdfBoxOutputDevice)) - return null; + /* + * We can only do something if this is a PDF. + */ + if (!(outputDevice instanceof PdfBoxOutputDevice)) + return null; - String pdfsrc = e.getAttribute("pdfsrc"); - String pdfpageValue = e.getAttribute("pdfpage"); - if (pdfpageValue == null || pdfpageValue.isEmpty()) - pdfpageValue = "1"; - int pdfpage = Integer.parseInt(pdfpageValue); + PdfBoxOutputDevice pdfBoxOutputDevice = (PdfBoxOutputDevice) outputDevice; - PdfBoxOutputDevice pdfBoxOutputDevice = (PdfBoxOutputDevice) outputDevice; - String url = ctx.getUac().resolveURI(pdfsrc); - SoftReference> mapWeakReference = formMap - .get(new PDFBoxDeviceReference(pdfBoxOutputDevice)); - Map map = null; - if (mapWeakReference != null) - map = mapWeakReference.get(); - if (map == null) { - map = new HashMap(); - formMap.put(new PDFBoxDeviceReference(pdfBoxOutputDevice), - new SoftReference>(map)); - } - try { - PDFormXObject pdFormXObject = map.get(url); - LayerUtility layerUtility = new LayerUtility(pdfBoxOutputDevice.getWriter()); - if (pdFormXObject == null) { - InputStream inputStream = new URL(url).openStream(); - try { - PDFParser pdfParser = new PDFParser(new RandomAccessBuffer(inputStream)); - pdfParser.parse(); - pdFormXObject = layerUtility.importPageAsForm(pdfParser.getPDDocument(), pdfpage - 1); - pdfParser.getPDDocument().close(); - } finally { - inputStream.close(); - } - map.put(url, pdFormXObject); - } - PDPage page = pdfBoxOutputDevice.getPage(); + try + { + LayerUtility layerUtility = new LayerUtility(pdfBoxOutputDevice.getWriter()); + PDFormXObject pdFormXObject = importPageAsXForm(ctx,e, pdfBoxOutputDevice, layerUtility); + PDPage page = pdfBoxOutputDevice.getPage(); - /* - * This ensures that the Contents of the page is a COSArray. The first entry in - * the array is just a save state (e.g. 'q'). We can override it to add the - * XForm. - */ - layerUtility.wrapInSaveRestore(page); - COSArray cosArray = (COSArray) page.getCOSObject().getDictionaryObject(COSName.CONTENTS); - COSStream saveStateAndPlacePageBackgroundStream = (COSStream) cosArray.get(0); - OutputStream saveAndPlaceStream = saveStateAndPlacePageBackgroundStream.createOutputStream(); - saveAndPlaceStream.write("q\n".getBytes(Charsets.US_ASCII)); - COSName name = page.getResources().add(pdFormXObject); - name.writePDF(saveAndPlaceStream); - saveAndPlaceStream.write(' '); - saveAndPlaceStream.write("Do\n".getBytes(Charsets.US_ASCII)); - saveAndPlaceStream.write("Q\n".getBytes(Charsets.US_ASCII)); - saveAndPlaceStream.write("q\n".getBytes(Charsets.US_ASCII)); - saveAndPlaceStream.close(); + /* + * This ensures that the Contents of the page is a COSArray. The first entry in + * the array is just a save state (e.g. 'q'). We can override it to add the + * XForm. + */ + layerUtility.wrapInSaveRestore(page); + COSArray cosArray = (COSArray) page.getCOSObject() + .getDictionaryObject(COSName.CONTENTS); + COSStream saveStateAndPlacePageBackgroundStream = (COSStream) cosArray.get(0); + OutputStream saveAndPlaceStream = saveStateAndPlacePageBackgroundStream + .createOutputStream(); + saveAndPlaceStream.write("q\n".getBytes(Charsets.US_ASCII)); + COSName name = page.getResources().add(pdFormXObject); + name.writePDF(saveAndPlaceStream); + saveAndPlaceStream.write(' '); + saveAndPlaceStream.write("Do\n".getBytes(Charsets.US_ASCII)); + saveAndPlaceStream.write("Q\n".getBytes(Charsets.US_ASCII)); + saveAndPlaceStream.write("q\n".getBytes(Charsets.US_ASCII)); + saveAndPlaceStream.close(); - } catch (MalformedURLException e1) { - e1.printStackTrace(); - } catch (IOException e1) { - e1.printStackTrace(); - } + } + catch (IOException e1) + { + e1.printStackTrace(); + } + return null; + } - return null; - } - - private static class PDFBoxDeviceReference extends WeakReference { - PDFBoxDeviceReference(PdfBoxOutputDevice referent) { - super(referent); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof PDFBoxDeviceReference) { - return ((PDFBoxDeviceReference) obj).get() == get(); - } - return super.equals(obj); - } - - @Override - public int hashCode() { - PdfBoxOutputDevice pdfBoxOutputDevice = get(); - if (pdfBoxOutputDevice != null) - return pdfBoxOutputDevice.hashCode(); - return 0; - } - } } diff --git a/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/PdfDrawerBase.java b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/PdfDrawerBase.java new file mode 100644 index 000000000..f3d03f7d6 --- /dev/null +++ b/openhtmltopdf-objects/src/main/java/com/openhtmltopdf/objects/pdf/PdfDrawerBase.java @@ -0,0 +1,99 @@ +package com.openhtmltopdf.objects.pdf; + +import com.openhtmltopdf.extend.FSObjectDrawer; +import com.openhtmltopdf.pdfboxout.PdfBoxOutputDevice; +import com.openhtmltopdf.render.RenderingContext; +import org.apache.pdfbox.io.RandomAccessBuffer; +import org.apache.pdfbox.multipdf.LayerUtility; +import org.apache.pdfbox.pdfparser.PDFParser; +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +public abstract class PdfDrawerBase implements FSObjectDrawer +{ + private final Map>> formMap = new HashMap>>(); + + protected PDFormXObject importPageAsXForm(RenderingContext ctx, Element e, + PdfBoxOutputDevice pdfBoxOutputDevice, LayerUtility layerUtility) throws IOException + { + + Map map = getFormCacheMap(pdfBoxOutputDevice); + int pdfpage = getPageNumber(e); + String pdfsrc = e.getAttribute("pdfsrc"); + String url = ctx.getUac().resolveURI(pdfsrc); + + PDFormXObject pdFormXObject = map.get(url); + if (pdFormXObject == null) + { + try (InputStream inputStream = new URL(url).openStream()) + { + PDFParser pdfParser = new PDFParser(new RandomAccessBuffer(inputStream)); + pdfParser.parse(); + pdFormXObject = layerUtility + .importPageAsForm(pdfParser.getPDDocument(), pdfpage - 1); + pdfParser.getPDDocument().close(); + } + map.put(url, pdFormXObject); + } + return pdFormXObject; + } + + protected Map getFormCacheMap(PdfBoxOutputDevice pdfBoxOutputDevice) + { + SoftReference> mapWeakReference = formMap + .get(new PDFBoxDeviceReference(pdfBoxOutputDevice)); + Map map = null; + if (mapWeakReference != null) + map = mapWeakReference.get(); + if (map == null) + { + map = new HashMap(); + formMap.put(new PDFBoxDeviceReference(pdfBoxOutputDevice), + new SoftReference>(map)); + } + return map; + } + + protected int getPageNumber(Element e) + { + String pdfpageValue = e.getAttribute("pdfpage"); + if (pdfpageValue == null || pdfpageValue.isEmpty()) + pdfpageValue = "1"; + return Integer.parseInt(pdfpageValue); + } + + private static class PDFBoxDeviceReference extends WeakReference + { + PDFBoxDeviceReference(PdfBoxOutputDevice referent) + { + super(referent); + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof PDFBoxDeviceReference) + { + return ((PDFBoxDeviceReference) obj).get() == get(); + } + return super.equals(obj); + } + + @Override + public int hashCode() + { + PdfBoxOutputDevice pdfBoxOutputDevice = get(); + if (pdfBoxOutputDevice != null) + return pdfBoxOutputDevice.hashCode(); + return 0; + } + } +}