I am trying to add a watermark to a PDF specifically with PDFBox. I've been able to get the image to appear on each page, but it loses the background transparency because it appears as though PDJpeg converts it to a JPG. Perhaps there's a way to do it using PDXObjectImage.
Here is what I have written thus far:
public static void watermarkPDF(PDDocument pdf) throws IOException
{
// Load watermark
BufferedImage buffered = ImageIO.read(new File("C:\\PDF_Test\\watermark.png"));
PDJpeg watermark = new PDJpeg(pdf, buffered);
// Loop through pages in PDF
List pages = pdf.getDocumentCatalog().getAllPages();
Iterator iter = pages.iterator();
while(iter.hasNext())
{
PDPage page = (PDPage)iter.next();
// Add watermark to individual page
PDPageContentStream stream = new PDPageContentStream(pdf, page, true, false);
stream.drawImage(watermark, 100, 0);
stream.close();
}
try
{
pdf.save("C:\\PDF_Test\\watermarktest.pdf");
}
catch (COSVisitorException e)
{
e.printStackTrace();
}
}
UPDATED ANSWER (Better version with easy way to watermark, thanks to the commentators below and #okok who provided input with his answer)
If you are using PDFBox 1.8.10 or above, you can add watermark to your PDF document easily with better control over what pages needs to be watermarked. Assuming you have a one page PDF document that has the watermark image, you can overlay this on the document you want to watermark as follows.
Sample Code using 1.8.10
import java.util.HashMap;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.Overlay;
public class TestPDF {
public static void main(String[] args) throws Exception{
PDDocument realDoc = PDDocument.load("originaldocument.pdf");
//the above is the document you want to watermark
//for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.
HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
for(int i=0; i<realDoc.getPageCount(); i++){
overlayGuide.put(i+1, "watermark.pdf");
//watermark.pdf is the document which is a one page PDF with your watermark image in it. Notice here that you can skip pages from being watermarked.
}
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
overlay.setOutputFile("final.pdf");
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
overlay.overlay(overlayGuide,false);
//final.pdf will have the original PDF with watermarks.
Sample using PDFBox 2.0.0 Release candidate
import java.io.File;
import java.util.HashMap;
import org.apache.pdfbox.multipdf.Overlay;
import org.apache.pdfbox.pdmodel.PDDocument;
public class TestPDF {
public static void main(String[] args) throws Exception{
PDDocument realDoc = PDDocument.load(new File("originaldocument.pdf"));
//the above is the document you want to watermark
//for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.
HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
for(int i=0; i<realDoc.getNumberOfPages(); i++){
overlayGuide.put(i+1, "watermark.pdf");
//watermark.pdf is the document which is a one page PDF with your watermark image in it.
//Notice here, you can skip pages from being watermarked.
}
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
overlay.setOutputFile("final.pdf");
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
overlay.overlay(overlayGuide);
}
}
If you want to use the new package org.apache.pdfbox.tools.OverlayPDF for overlays you can do this way. (Thanks the poster below)
String[] overlayArgs = {"C:/Examples/foreground.pdf", "C:/Examples/background.pdf", "C:/Examples/resulting.pdf"};
OverlayPDF.main(overlayArgs);
System.out.println("Overlay finished.");
OLD ANSWER Inefficient way, not recommended.
Well, OP asked how to do it in PDFBox, the first answer looks like an example using iText. Creating a watermark in PDFBox is really simple. The trick is, you should have an empty PDF document with the watermark image. Then all you have to do is Overlay this watermark document on the document that you want to add the watermark to.
PDDocument watermarkDoc = PDDocument.load("watermark.pdf");
//Assuming your empty document with watermark image in it.
PDDocument realDoc = PDDocument.load("document-to-be-watermarked.pdf");
//Let's say this is your document that you want to watermark. For example sake, I am opening a new one, you would already have a reference to PDDocument if you are creating one
Overlay overlay = new Overlay();
overlay.overlay(realDoc,watermarkDoc);
watermarkDoc.save("document-now-watermarked.pdf");
Caution: You should make sure you match the number of pages in both document..Otherwise, you would end up with a document with number of pages matching the one which has least number of pages. You can manipulate the watermark document and duplicate the pages to match your document.
Hope this helps.!
Just made this piece of code to add (transparent) images (jpg, png, gif) to a pdf page with pdfbox:
/**
* Draw an image to the specified coordinates onto a single page. <br>
* Also scaled the image with the specified factor.
*
* #author Nick Russler
* #param document PDF document the image should be written to.
* #param pdfpage Page number of the page in which the image should be written to.
* #param x X coordinate on the page where the left bottom corner of the image should be located. Regard that 0 is the left bottom of the pdf page.
* #param y Y coordinate on the page where the left bottom corner of the image should be located.
* #param scale Factor used to resize the image.
* #param imageFilePath Filepath of the image that is written to the PDF.
* #throws IOException
*/
public static void addImageToPage(PDDocument document, int pdfpage, int x, int y, float scale, String imageFilePath) throws IOException {
// Convert the image to TYPE_4BYTE_ABGR so PDFBox won't throw exceptions (e.g. for transparent png's).
BufferedImage tmp_image = ImageIO.read(new File(imageFilePath));
BufferedImage image = new BufferedImage(tmp_image.getWidth(), tmp_image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
image.createGraphics().drawRenderedImage(tmp_image, null);
PDXObjectImage ximage = new PDPixelMap(document, image);
PDPage page = (PDPage)document.getDocumentCatalog().getAllPages().get(pdfpage);
PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
contentStream.drawXObject(ximage, x, y, ximage.getWidth()*scale, ximage.getHeight()*scale);
contentStream.close();
}
Example:
public static void main(String[] args) throws Exception {
String pdfFilePath = "C:/Users/Nick/Desktop/pdf-test.pdf";
String signatureImagePath = "C:/Users/Nick/Desktop/signature.png";
int page = 0;
PDDocument document = PDDocument.load(pdfFilePath);
addImageToPage(document, page, 0, 0, 0.5f, signatureImagePath);
document.save("C:/Users/Nick/Desktop/pdf-test-neu.pdf");
}
This worked for me with jdk 1.7 and bcmail-jdk16-140.jar, bcprov-jdk16-140.jar, commons-logging-1.1.3.jar, fontbox-1.8.3.jar, jempbox-1.8.3.jar and pdfbox-1.8.3.jar.
#Androidman : Addition to https://stackoverflow.com/a/9382212/7802973
It seems like many methods are removed with each version of PDFBox. So that code will not work on PDFBox 2.0.7.
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
// ** The method setOutputFile(String) is undefined for the type Overlay **
overlay.setOutputFile("final.pdf")
Instead, use void org.apache.pdfbox.pdmodel.PDDocument.save(String fileName), I think:
PDDocument realDoc = PDDocument.load(new File("originaldocument.pdf"));
//the above is the document you want to watermark
//for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.
HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
for(int i=0; i<realDoc.getNumberOfPages(); i++){
overlayGuide.put(i+1, "watermark.pdf");
//watermark.pdf is the document which is a one page PDF with your watermark image in it.
//Notice here, you can skip pages from being watermarked.
}
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
overlay.overlay(overlayGuide).save("final.pdf");
overlay.close();
Edit:
I am using org.apache.pdfbox.tools.OverlayPDF for overlays now and it works just fine. The code looks like this:
String[] overlayArgs = {"C:/Examples/foreground.pdf", "C:/Examples/background.pdf", "C:/Examples/resulting.pdf"};
OverlayPDF.main(overlayArgs);
System.out.println("Overlay finished.");
As I had not enough reputation to add a comment, I thought it'd be appropiate to add this in a new answer.
There is another Overlay class within util package, that saves you from creating a pdf with same number of pages as the source document and then doing the overlay.
To understand its usage, take a look at pdfbox source code, specifically the OverlayPDF class.
This is how I was able to add a text watermark with the date using PdfBox 2.0.x in C#. It centres the watermark at the top of the page.
public static void AddWatermark(string fileName)
{
StringBuilder sb = new StringBuilder();
sb.Append("watermark_text ");
sb.Append(DateTime.Now.ToString());
string waterMarkText = sb.ToString();
PDDocument origDoc = PDDocument.load(new java.io.File(fileName));
PDPageTree allPages = origDoc.getPages();
PDFont font = PDType1Font.HELVETICA_BOLD;
for (int i = 0, len = allPages.getCount(); i < len; ++i)
{
PDPage pg = (PDPage)allPages.get(i);
AddWatermarkText(origDoc, pg, font, waterMarkText);
}
origDoc.save(fileName);
origDoc.close();
}
static void AddWatermarkText(PDDocument doc, PDPage page, PDFont font, string text)
{
using (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true))
{
float fontHeight = 30;
float width = page.getMediaBox().getWidth();
float height = page.getMediaBox().getHeight();
float stringWidth = font.getStringWidth(text) / 1000 * fontHeight;
float x = (width / 2) - (stringWidth / 2);
float y = height - 25;
cs.setFont(font, fontHeight);
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setNonStrokingAlphaConstant(new java.lang.Float(0.2f));
gs.setStrokingAlphaConstant(new java.lang.Float(0.2f));
gs.setBlendMode(BlendMode.MULTIPLY);
gs.setLineWidth(new java.lang.Float(3f));
cs.setGraphicsStateParameters(gs);
cs.setNonStrokingColor(Color.red);
cs.setStrokingColor(Color.red);
cs.beginText();
cs.newLineAtOffset(x, y);
cs.showText(text);
cs.endText();
}
}
pdfbox c# watermark pdf
Adding to the answer by #Droidman If your using version 2.0.21 and above you can directly overlay on PDDocument. Sample code as follows.
PDDocument realDoc = pdfGenerator.getDocument();
HashMap<Integer, PDDocument> overlayGuide = new HashMap<>();
for (int i = 0; i < realDoc.getNumberOfPages(); i++) {
overlayGuide.put(i + 1, document);
}
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
overlay.overlayDocuments(overlayGuide);
Look at this method, whitch add a watermark image in pdf sources using PDFBOX library
/**
* Coloca una imagen como marca de agua en un pdf en una posición especifica
*
* #param buffer
* flujo de bytes que contiene el pdf original
*
* #param imageFileName
* nombre del archivo de la imagen a colocar
*
* #param x
* posición x de la imagen en el pdf
*
* #param y
* posición y de la imagen en el pdf
*
* #param under
* determina si la marca se pone encima o por debajo de la factura
*
* #return flujo de bytes resultante que contiene el pdf modificado
*
* #throws IOException
* #throws DocumentException
*/
public static byte[] addWaterMarkImageToPDF(byte[] buffer,
String imageFileName, int x, int y, boolean under) throws IOException,
DocumentException {
logger.debug("Agregando marca de agua:"+imageFileName);
PdfReader reader = new PdfReader(buffer);
ByteArrayOutputStream arrayOutput = new ByteArrayOutputStream();
OutputStream output = new BufferedOutputStream(arrayOutput);
PdfStamper stamper = new PdfStamper(reader, output);
com.lowagie.text.Image img = com.lowagie.text.Image
.getInstance(imageFileName);
img.setAbsolutePosition(x, y);
img.scalePercent(SCALE_PER);
PdfContentByte pdfContent;
int total = reader.getNumberOfPages() + 1;
for (int i = 1; i < total; i++) {
pdfContent = (under)?stamper.getUnderContent(i):
stamper.getOverContent(i);
pdfContent.addImage(img);
}
stamper.close();
output.flush();
output.close();
return arrayOutput.toByteArray();
}
Related
I'm using itextpdf 5.5, can't change it to 7.
I have the problem with background image.
I have a document (text and tables) without stamp and I want to add stamp to it.
This is how I download existing doc.
PdfReader pdfReader = new PdfReader("/doc.pdf");
PdfImportedPage page = writer.getImportedPage(pdfReader, 1);
PdfContentByte pcb = writer.getDirectContent();
pcb.addTemplate(page, 0,0);
And this is how I download stamp image and add it to my doc.
PdfContentByte canvas = writer.getDirectContentUnder();
URL resource = getClass().getResource(getStamp());
Image background = new Jpeg(resource);
background.scaleToFit(463F, 132F);
background.setAbsolutePosition(275F, 100F);
canvas.addImage(background);
But when I download my document - I don't see the stamp. I tried to change getDirectContent() to getDirectContentUnder() when I download my doc but this leads to the opposite situation - my stamp isn't in background.
My first doc is generated this way.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4);
try {
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
Paragraph title = new Paragraph(formatUtil.msg("my.header"), fontBold);
title.setAlignment(Element.ALIGN_CENTER);
document.add(title);
Template tmpl = fmConfig.getConfiguration().getTemplate("template.ftl");
Map<String, Object> params = new HashMap<>();
StringWriter writer = new StringWriter();
params.put("param", "param");
tmpl.process(params, writer);
document.add(new Paragraph(writer.toString(), fontCommon));
PdfPTable table = new PdfPTable(2);
document.add(table);
PdfContentByte canvas = writer.getDirectContentUnder();
Image background = new Jpeg(getClass().getResource("background.jpg"));
background.scaleAbsolute(PageSize.A4);
background.setAbsolutePosition(0,0);
canvas.addImage(background);
} finally {
if (document.isOpen()) {
document.close();
}
}
In comments it became clear that the task was to add some content (a bitmap image) to a PDF so that it is over the background (another bitmap image) added to the UnderContent during generation and under the text in the original DirectContent.
iText does not contain high-level code for such content manipulation. While the structure of iText generated PDFs would allow for such code, PDFs generated or manipulated by other PDF libraries may have a different structure; the structure in iText generated PDFs may even be changed during post-processing using other libraries. Thus, it is understandable that no high-level feature for this is provided by iText.
To implement the task nonetheless, therefore, we have to base our code on lower level iText APIs. In this context we can make use of the PdfContentStreamEditor helper class from this answer which already abstracts some details away. (That question originally is about iTextSharp for C# but further down in the answer Java versions of the code also are provided.)
In detail, we extend the PdfContentStreamEditor to remove the former UnderContent (and provide it as list of instructions). Now we can in a first step apply this editor to your file and in a second step add this former UnderContent plus an image over it to the intermediary file. (This could also be done in a single step but that would require a more complex, less maintainable editor class.)
First the new UnderContentRemover content stream editor class:
public class UnderContentRemover extends PdfContentStreamEditor {
/**
* Clears state of {#link UnderContentRemover}, in particular
* the collected content. Use this if you use this instance for
* multiple edit runs.
*/
public void clear() {
afterUnderContent = false;
underContent.clear();
depth = 0;
}
/**
* Retrieves the collected UnderContent instructions
*/
public List<List<PdfObject>> getUnderContent() {
return new ArrayList<List<PdfObject>>(underContent);
}
/**
* Adds the given instructions (which may previously have been
* retrieved using {#link #getUnderContent()}) to the given
* {#link PdfContentByte} instance.
*/
public static void write (PdfContentByte canvas, List<List<PdfObject>> operations) throws IOException {
for (List<PdfObject> operands : operations) {
int index = 0;
for (PdfObject object : operands) {
object.toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(operands.size() > ++index ? (byte) ' ' : (byte) '\n');
}
}
}
protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException {
String operatorString = operator.toString();
if (afterUnderContent) {
super.write(processor, operator, operands);
return;
} else if ("q".equals(operatorString)) {
depth++;
} else if ("Q".equals(operatorString)) {
depth--;
if (depth < 1)
afterUnderContent = true;
} else if (depth == 0) {
afterUnderContent = true;
super.write(processor, operator, operands);
return;
}
underContent.add(new ArrayList<>(operands));
}
boolean afterUnderContent = false;
List<List<PdfObject>> underContent = new ArrayList<>();
int depth = 0;
}
(UnderContentRemover)
As you see, its write method stores the leading instructions forwarded to it in the underContent list until it finds the restore-graphics-state (Q) instruction matching the initial save-graphics-state instruction (q). After that it instead forwards all further instructions to the parent write implementation which writes them to the edited page content.
We can use this for the task at hand as follows:
PdfReader pdfReader = new PdfReader(YOUR_DOCUMENT);
List<List<List<PdfObject>>> underContentByPage = new ArrayList<>();
byte[] sourceWithoutUnderContent = null;
try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ) {
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
UnderContentRemover underContentRemover = new UnderContentRemover();
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
underContentRemover.clear();
underContentRemover.editPage(pdfStamper, i);
underContentByPage.add(underContentRemover.getUnderContent());
}
pdfStamper.close();
pdfReader.close();
sourceWithoutUnderContent = outputStream.toByteArray();
}
Image background = YOUR_IMAGE_TO_ADD_INBETWEEN;
background.scaleToFit(463F, 132F);
background.setAbsolutePosition(275F, 100F);
pdfReader = new PdfReader(sourceWithoutUnderContent);
byte[] sourceWithStampInbetween = null;
try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ) {
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
PdfContentByte canvas = pdfStamper.getUnderContent(i);
UnderContentRemover.write(canvas, underContentByPage.get(i-1));
canvas.addImage(background);
}
pdfStamper.close();
pdfReader.close();
sourceWithStampInbetween = outputStream.toByteArray();
}
Files.write(new File("PdfLikeVladimirSafonov-WithStampInbetween.pdf").toPath(), sourceWithStampInbetween);
(AddImageInBetween test testForVladimirSafonov)
I have a small (quarter inch) one page PDF I created with PDFBOX with text (A). I want to put that small one page PDF (A) on the top of an existing PDF page (B), preserving the existing content of the PDF page (B). In the end, I will have a one page PDF, representing the small PDF on top(A), and the existing PDF intact making up the rest (B). How can I accomplish this with PDFBOX?
To join two pages one atop the other onto one target page, you can make use of the PDFBox LayerUtility for importing pages as form XObjects in a fashion similar to PDFBox SuperimposePage example, e.g. with this helper method:
void join(PDDocument target, PDDocument topSource, PDDocument bottomSource) throws IOException {
LayerUtility layerUtility = new LayerUtility(target);
PDFormXObject topForm = layerUtility.importPageAsForm(topSource, 0);
PDFormXObject bottomForm = layerUtility.importPageAsForm(bottomSource, 0);
float height = topForm.getBBox().getHeight() + bottomForm.getBBox().getHeight();
float width, topMargin, bottomMargin;
if (topForm.getBBox().getWidth() > bottomForm.getBBox().getWidth()) {
width = topForm.getBBox().getWidth();
topMargin = 0;
bottomMargin = (topForm.getBBox().getWidth() - bottomForm.getBBox().getWidth()) / 2;
} else {
width = bottomForm.getBBox().getWidth();
topMargin = (bottomForm.getBBox().getWidth() - topForm.getBBox().getWidth()) / 2;
bottomMargin = 0;
}
PDPage targetPage = new PDPage(new PDRectangle(width, height));
target.addPage(targetPage);
PDPageContentStream contentStream = new PDPageContentStream(target, targetPage);
if (bottomMargin != 0)
contentStream.transform(Matrix.getTranslateInstance(bottomMargin, 0));
contentStream.drawForm(bottomForm);
contentStream.transform(Matrix.getTranslateInstance(topMargin - bottomMargin, bottomForm.getBBox().getHeight()));
contentStream.drawForm(topForm);
contentStream.close();
}
(JoinPages method join)
You use it like this:
try ( PDDocument document = new PDDocument();
PDDocument top = ...;
PDDocument bottom = ...) {
join(document, top, bottom);
document.save("joinedPage.pdf");
}
(JoinPages test testJoinSmallAndBig)
The result looks like this:
Just as an additional point to #mkl's answer.
If anybody is looking to scale the PDFs before placing them on the page use,
contentStream.transform(Matrix.getScaleInstance(<scaling factor in x axis>, <scaling factor in y axis>)); //where 1 is the scaling factor if you want the page as the original size
This way you can rescale your PDFs.
I would like to draw a vector image on a PDF with Apache PDFBox.
This is the code I use to draw regular images
PDPage page = (PDPage) document.getDocumentCatalog().getAllPages().get(1);
PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
BufferedImage _prevImage = ImageIO.read(new FileInputStream("path/to/image.png"));
PDPixelMap prevImage = new PDPixelMap(document, _prevImage);
contentStream.drawXObject(prevImage, prevX, prevY, imageWidth, imageHeight);
If I use a svg or wmf image instead of png, the resulting PDF document comes corrupted.
The main reason I want the image to be a vector image is that with PNG or JPG the image looks horrible, I think it gets somehow compressed so it looks bad. With vector images this shouldn't happen (well, when I export svg paths as PDF in Inkscape it doesn't happen, vector paths are preserved).
Is there a way to draw a svg or wmf (or other vector) to PDF using Apache PDFBox?
I'm currently using PDFBox 1.8, if that matters.
See the library pdfbox-graphics2d, touted in this Jira.
You can draw the SVG, via Batik or Salamander or whatever, onto the class PdfBoxGraphics2D, which is parallel to iText's template.createGraphics(). See the GitHub page for samples.
PDDocument document = ...;
PDPage page = ...; // page whereon to draw
String svgXML = "<svg>...</svg>";
double leftX = ...;
double bottomY = ...; // PDFBox coordinates are oriented bottom-up!
// I set these to the SVG size, which I calculated via Salamander.
// Maybe it doesn't matter, as long as the SVG fits on the graphic.
float graphicsWidth = ...;
float graphicsHeight = ...;
// Draw the SVG onto temporary graphics.
var graphics = new PdfBoxGraphics2D(document, graphicsWidth, graphicsHeight);
try {
int x = 0;
int y = 0;
drawSVG(svg, graphics, x, y); // with Batik, Salamander, or whatever you like
} finally {
graphics.dispose();
}
// Graphics are not visible till a PDFormXObject is added.
var xform = graphics.getXFormObject();
try (var contentWriter = new PDPageContentStream(document, page, AppendMode.APPEND, false)) { // false = don't compress
// XForm objects have to be placed via transform,
// since they cannot be placed via coordinates like images.
var transform = AffineTransform.getTranslateInstance(leftX, bottomY);
xform.setMatrix(transform);
// Now the graphics become visible.
contentWriter.drawForm(xform);
}
And ... in case you want also to scale the SVG graphics to 25% size:
// Way 1: Scale the SVG beforehand
svgXML = String.format("<svg transform=\"scale(%f)\">%s</svg>", .25, svgXML);
// Way 2: Scale in the transform (before calling xform.setMatrix())
transform.concatenate(AffineTransform.getScaleInstance(.25, .25));
I do this, but not directly.
In first transform your SVG documents in PDF documents with FOP librairy and Batik.
https://xmlgraphics.apache.org/fop/dev/design/svg.html.
In second times, you can use LayerUtility in pdfbox to transform your new pdf document in PDXObjectForm. After that, just needs to include PDXObjectForm in your final pdf documents.
The final working solution for me that loads an SVG file and overlays it on a PDF file (this renders the SVG in a 500x500 box at (0,0) coordinate which is bottom left of the PDF document):
package com.example.svgadder;
import java.io.*;
import java.nio.*;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;
import java.awt.geom.AffineTransform;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
public class App
{
public static void main( String[] args ) throws Exception {
App app = new App();
}
public App() throws Exception {
// loading PDF and SVG files
File pdfFile = new File("input.pdf");
File svgFile = new File("input.svg");
PDDocument doc = PDDocument.load(pdfFile);
PDPage page = doc.getPage(0);
SVGUniverse svgUniverse = new SVGUniverse();
SVGDiagram diagram = svgUniverse.getDiagram(svgUniverse.loadSVG(f.toURL()));
PdfBoxGraphics2D graphics = new PdfBoxGraphics2D(doc, 500, 500);
try {
diagram.render(graphics);
} finally {
graphics.dispose();
}
PDFormXObject xform = graphics.getXFormObject();
try (PDPageContentStream contentWriter = new PDPageContentStream(doc, page, AppendMode.APPEND, false)) {
AffineTransform transform = AffineTransform.getTranslateInstance(0, 0);
xform.setMatrix(transform);
contentWriter.drawForm(xform);
}
doc.save("res.pdf");
doc.close();
}
}
Please use svgSalamander from here:
https://github.com/mgarin/svgSalamander
Please use what Coemgenus suggested for scaling your final overlaid SVG. I tried the 2nd option and it works well.
Nirmal
I'm trying to add underlined text to a blank pdf page using PDFBox, but I haven't been able to find any examples online. All questions on stackoverflow point to extracting underlined text, but not creating it. Has this function not been implemented for PDFBox? Looking at the PDFBox documentation, it seems that fonts are pre-rendered as bold, italic, and regular.
For example, Times New Roman Regular is denoted as:
PDFont font = PDType1Font.TIMES_ROMAN.
Times New Roman Bold is denoted as:
PDFont font = PDType1Font.TIMES_BOLD
Italicized is denoted as:
PDFont font = PDType1Font.TIMES_ITALIC
There seems to be no underlined option. Is there anyway to underline text, or is this not a feature?
I'm not sure if this is a better alternative or not, but I followed Tilman Hausherr and drew a line in comparison to my text. For instance, I have the following:
public processPDF(int xOne, int yOne, int xTwo, int yTwo)
{
//create pdf and its contents for one page
PDDocument document = new PDDocument();
File file = new File("hello.pdf");
PDPage page = new PDPage();
PDFont font = PDType1Font.HELVETICA_BOLD;
PDPageContentStream contentStream;
try {
//create content stream
contentStream = new PDPageContentStream(document, page);
//being to create our text for our page
contentStream.beginText();
contentStream.setFont( font, largeTitle);
//position of text
contentStream.moveTextPositionByAmount(xOne, yOne, xTwo, yTwo);
contentStream.drawString("Hello");
contentStream.endText();
//begin to draw our line
contentStream.drawLine(xOne, yOne - .5, xTwo, yYwo - .5);
//close and save document
document.save(file);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
where our parameters xOne, yOne, xTwo, and yTwo are our locations of the text. The line has us subtract .5 from yOne and yTwo to move it a pinch below our text location, ultimately setting it to look like underlined text.
There may be better ways, but this was the route I went.
I use below function for underlined the string.
public class UnderlineText {
PDFont font = PDType1Font.HELVETICA_BOLD;
float fontSize = 10f;
String str = "Hello";
public static void main(String[] args) {
new UnderlineText().generatePDF(20, 200);
}
public void generatePDF(int sX, int sY)
{
//create pdf and its contents for one page
PDDocument document = new PDDocument();
File file = new File("underlinePdfbox.pdf");
PDPage page = new PDPage();
PDPageContentStream contentStream;
try {
document.addPage(page);
//create content stream
contentStream = new PDPageContentStream(document, page);
//being text for our page
contentStream.beginText();
contentStream.setFont( font, fontSize);
contentStream.newLineAtOffset(sX, sY);
contentStream.showText(str);
contentStream.endText();
//Draw Underline
drawLine(contentStream, str, 1, sX, sY, -2);
//close and save document
contentStream.close();
document.save(file);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void drawLine(PDPageContentStream contentStream, String text, float lineWidth, float sx, float sy, float linePosition) throws IOException {
//Calculate String width
float stringWidth = fontSize * font.getStringWidth(str) / 1000;
float lineEndPoint = sx + stringWidth;
//begin to draw our line
contentStream.setLineWidth(lineWidth);
contentStream.moveTo(sx, sy + linePosition);
contentStream.lineTo(lineEndPoint, sy + linePosition);
contentStream.stroke();
}
}
drawLine is a function which i created for drawing a line for specific string. You can adjust line as per specification using position attribute.
Minus (-) value in position field create under line. you can use positive value for over-line and stroke-line.(For example -2 for underline, 10 for over-line, 2 for stroke-line for above code)
Also you can manage the width for line.
Try this answer:
highlight text using pdfbox when it's location in the pdf is known
This method using PDAnnotationTextMarkup, it has four values
/**
* The types of annotation.
*/
public static final String SUB_TYPE_HIGHLIGHT = "Highlight";
/**
* The types of annotation.
*/
public static final String SUB_TYPE_UNDERLINE = "Underline";
/**
* The types of annotation.
*/
public static final String SUB_TYPE_SQUIGGLY = "Squiggly";
/**
* The types of annotation.
*/
public static final String SUB_TYPE_STRIKEOUT = "StrikeOut";
Hope it helps
Is there a way I can edit a PDF from Java?
I have a PDF document which contains placeholders for text that I need to be replaced using Java, but all the libraries that I saw created PDF from scratch and small editing functionality.
Is there anyway I can edit a PDF or is this impossible?
You can do it with iText. I tested it with following code. It adds a chunk of text and a red circle over each page of an existing PDF.
/* requires itextpdf-5.1.2.jar or similar */
import java.io.*;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;
public class AddContentToPDF {
public static void main(String[] args) throws IOException, DocumentException {
/* example inspired from "iText in action" (2006), chapter 2 */
PdfReader reader = new PdfReader("C:/temp/Bubi.pdf"); // input PDF
PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream("C:/temp/Bubi_modified.pdf")); // output PDF
BaseFont bf = BaseFont.createFont(
BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); // set font
//loop on pages (1-based)
for (int i=1; i<=reader.getNumberOfPages(); i++){
// get object for writing over the existing content;
// you can also use getUnderContent for writing in the bottom layer
PdfContentByte over = stamper.getOverContent(i);
// write text
over.beginText();
over.setFontAndSize(bf, 10); // set font and size
over.setTextMatrix(107, 740); // set x,y position (0,0 is at the bottom left)
over.showText("I can write at page " + i); // set text
over.endText();
// draw a red circle
over.setRGBColorStroke(0xFF, 0x00, 0x00);
over.setLineWidth(5f);
over.ellipse(250, 450, 350, 550);
over.stroke();
}
stamper.close();
}
}
I modified the code found a bit and it was working as follows
public class Principal {
public static final String SRC = "C:/tmp/244558.pdf";
public static final String DEST = "C:/tmp/244558-2.pdf";
public static void main(String[] args) throws IOException, DocumentException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new Principal().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
PdfArray refs = null;
if (dict.get(PdfName.CONTENTS).isArray()) {
refs = dict.getAsArray(PdfName.CONTENTS);
} else if (dict.get(PdfName.CONTENTS).isIndirect()) {
refs = new PdfArray(dict.get(PdfName.CONTENTS));
}
for (int i = 0; i < refs.getArrayList().size(); i++) {
PRStream stream = (PRStream) refs.getDirectObject(i);
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("NULA", "Nulo").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
}
Take a look at iText and this sample code
Take a look at aspose and this sample code
I've done this using LibreOffice Draw.
You start by manually opening a pdf in Draw, checking that it renders OK, and saving it as a Draw .odg file.
That's a zipped xml file, so you can modify it in code to find and replace the placeholders.
Next (from code) you use a command line call to Draw to generate the pdf.
Success!
The main issue is that Draw doesn't handle fonts embedded in a pdf. If the font isn't also installed on your system - then it will render oddly, as Draw will replace it with a standard one that inevitably has different sizing.
If this approach is of interest, I'll put together some shareable code.