Skip to content

Commit

Permalink
danfickle#472 Refactor functinality of MergeBackgroundPdfDrawer into …
Browse files Browse the repository at this point in the history
…a baseclass, and add ForegroundPdfDrawer which

always puts the PDF in front of the page.
  • Loading branch information
rototor committed Oct 16, 2020
1 parent e44fb86 commit ffb5cc6
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ lower corner.
<li><b>pdfpage</b>: Page to import from the PDF file.</li>
</ul>

If you want to have this watermark in the foreground you should use
[@htmlCode]
<object type="pdf/foreground" pdfsrc="watermark.pdf" pdfpage="1" style="width:1px;height:1px"></object>
[/@htmlCode]
<object type="pdf/foreground" pdfsrc="watermark.pdf" pdfpage="1" style="width:1px;height:1px"></object>

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
Expand Down Expand Up @@ -341,4 +349,4 @@ Note: This only works in Acrobat Reader, all other PDF Viewer ignore this featur
</div>

</body>
</html>
</html>
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Shape, String> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<PDFBoxDeviceReference, SoftReference<Map<String, PDFormXObject>>> formMap = new HashMap<PDFBoxDeviceReference, SoftReference<Map<String, PDFormXObject>>>();
public class MergeBackgroundPdfDrawer extends PdfDrawerBase
{

@Override
public Map<Shape, String> drawObject(Element e, double x, double y, double width, double height,
OutputDevice outputDevice, RenderingContext ctx, int dotsPerPixel) {
@Override
public Map<Shape, String> 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<Map<String, PDFormXObject>> mapWeakReference = formMap
.get(new PDFBoxDeviceReference(pdfBoxOutputDevice));
Map<String, PDFormXObject> map = null;
if (mapWeakReference != null)
map = mapWeakReference.get();
if (map == null) {
map = new HashMap<String, PDFormXObject>();
formMap.put(new PDFBoxDeviceReference(pdfBoxOutputDevice),
new SoftReference<Map<String, PDFormXObject>>(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<PdfBoxOutputDevice> {
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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<PDFBoxDeviceReference, SoftReference<Map<String, PDFormXObject>>> formMap = new HashMap<PDFBoxDeviceReference, SoftReference<Map<String, PDFormXObject>>>();

protected PDFormXObject importPageAsXForm(RenderingContext ctx, Element e,
PdfBoxOutputDevice pdfBoxOutputDevice, LayerUtility layerUtility) throws IOException
{

Map<String, PDFormXObject> 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<String, PDFormXObject> getFormCacheMap(PdfBoxOutputDevice pdfBoxOutputDevice)
{
SoftReference<Map<String, PDFormXObject>> mapWeakReference = formMap
.get(new PDFBoxDeviceReference(pdfBoxOutputDevice));
Map<String, PDFormXObject> map = null;
if (mapWeakReference != null)
map = mapWeakReference.get();
if (map == null)
{
map = new HashMap<String, PDFormXObject>();
formMap.put(new PDFBoxDeviceReference(pdfBoxOutputDevice),
new SoftReference<Map<String, PDFormXObject>>(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<PdfBoxOutputDevice>
{
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;
}
}
}

0 comments on commit ffb5cc6

Please sign in to comment.